refclock_leitch.c revision 182007
1/* 2 * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock 3 */ 4 5#ifdef HAVE_CONFIG_H 6# include <config.h> 7#endif 8 9#if defined(REFCLOCK) && defined(CLOCK_LEITCH) 10 11#include "ntpd.h" 12#include "ntp_io.h" 13#include "ntp_refclock.h" 14#include "ntp_unixtime.h" 15 16#include <stdio.h> 17#include <ctype.h> 18 19#ifdef STREAM 20#include <stropts.h> 21#if defined(LEITCHCLK) 22#include <sys/clkdefs.h> 23#endif /* LEITCHCLK */ 24#endif /* STREAM */ 25 26#include "ntp_stdlib.h" 27 28 29/* 30 * Driver for Leitch CSD-5300 Master Clock System 31 * 32 * COMMANDS: 33 * DATE: D <CR> 34 * TIME: T <CR> 35 * STATUS: S <CR> 36 * LOOP: L <CR> 37 * 38 * FORMAT: 39 * DATE: YYMMDD<CR> 40 * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/ 41 * second bondaried on the stop bit of the <CR> 42 * second boundaries at '/' above. 43 * STATUS: G (good), D (diag fail), T (time not provided) or 44 * P (last phone update failed) 45 */ 46#define MAXUNITS 1 /* max number of LEITCH units */ 47#define LEITCHREFID "ATOM" /* reference id */ 48#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver" 49#define LEITCH232 "/dev/leitch%d" /* name of radio device */ 50#define SPEED232 B300 /* uart speed (300 baud) */ 51#ifdef DEBUG 52#define leitch_send(A,M) \ 53if (debug) fprintf(stderr,"write leitch %s\n",M); \ 54if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\ 55 if (debug) \ 56 fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \ 57 else \ 58 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);} 59#else 60#define leitch_send(A,M) \ 61if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\ 62 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);} 63#endif 64 65#define STATE_IDLE 0 66#define STATE_DATE 1 67#define STATE_TIME1 2 68#define STATE_TIME2 3 69#define STATE_TIME3 4 70 71/* 72 * LEITCH unit control structure 73 */ 74struct leitchunit { 75 struct peer *peer; 76 struct refclockio leitchio; 77 u_char unit; 78 short year; 79 short yearday; 80 short month; 81 short day; 82 short hour; 83 short second; 84 short minute; 85 short state; 86 u_short fudge1; 87 l_fp reftime1; 88 l_fp reftime2; 89 l_fp reftime3; 90 l_fp codetime1; 91 l_fp codetime2; 92 l_fp codetime3; 93 u_long yearstart; 94}; 95 96/* 97 * Function prototypes 98 */ 99static void leitch_init P((void)); 100static int leitch_start P((int, struct peer *)); 101static void leitch_shutdown P((int, struct peer *)); 102static void leitch_poll P((int, struct peer *)); 103static void leitch_control P((int, struct refclockstat *, struct refclockstat *, struct peer *)); 104#define leitch_buginfo noentry 105static void leitch_receive P((struct recvbuf *)); 106static void leitch_process P((struct leitchunit *)); 107#if 0 108static void leitch_timeout P((struct peer *)); 109#endif 110static int leitch_get_date P((struct recvbuf *, struct leitchunit *)); 111static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int)); 112static int days_per_year P((int)); 113 114static struct leitchunit leitchunits[MAXUNITS]; 115static u_char unitinuse[MAXUNITS]; 116static u_char stratumtouse[MAXUNITS]; 117static u_int32 refid[MAXUNITS]; 118 119static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 120 121/* 122 * Transfer vector 123 */ 124struct refclock refclock_leitch = { 125 leitch_start, leitch_shutdown, leitch_poll, 126 leitch_control, leitch_init, leitch_buginfo, NOFLAGS 127}; 128 129/* 130 * leitch_init - initialize internal leitch driver data 131 */ 132static void 133leitch_init(void) 134{ 135 int i; 136 137 memset((char*)leitchunits, 0, sizeof(leitchunits)); 138 memset((char*)unitinuse, 0, sizeof(unitinuse)); 139 for (i = 0; i < MAXUNITS; i++) 140 memcpy((char *)&refid[i], LEITCHREFID, 4); 141} 142 143/* 144 * leitch_shutdown - shut down a LEITCH clock 145 */ 146static void 147leitch_shutdown( 148 int unit, 149 struct peer *peer 150 ) 151{ 152#ifdef DEBUG 153 if (debug) 154 fprintf(stderr, "leitch_shutdown()\n"); 155#endif 156} 157 158/* 159 * leitch_poll - called by the transmit procedure 160 */ 161static void 162leitch_poll( 163 int unit, 164 struct peer *peer 165 ) 166{ 167 struct leitchunit *leitch; 168 169 /* start the state machine rolling */ 170 171#ifdef DEBUG 172 if (debug) 173 fprintf(stderr, "leitch_poll()\n"); 174#endif 175 if (unit >= MAXUNITS) { 176 /* XXXX syslog it */ 177 return; 178 } 179 180 leitch = &leitchunits[unit]; 181 182 if (leitch->state != STATE_IDLE) { 183 /* reset and wait for next poll */ 184 /* XXXX syslog it */ 185 leitch->state = STATE_IDLE; 186 } else { 187 leitch_send(leitch,"D\r"); 188 leitch->state = STATE_DATE; 189 } 190} 191 192static void 193leitch_control( 194 int unit, 195 struct refclockstat *in, 196 struct refclockstat *out, 197 struct peer *passed_peer 198 ) 199{ 200 if (unit >= MAXUNITS) { 201 msyslog(LOG_ERR, 202 "leitch_control: unit %d invalid", unit); 203 return; 204 } 205 206 if (in) { 207 if (in->haveflags & CLK_HAVEVAL1) 208 stratumtouse[unit] = (u_char)(in->fudgeval1); 209 if (in->haveflags & CLK_HAVEVAL2) 210 refid[unit] = in->fudgeval2; 211 if (unitinuse[unit]) { 212 struct peer *peer; 213 214 peer = (&leitchunits[unit])->peer; 215 peer->stratum = stratumtouse[unit]; 216 peer->refid = refid[unit]; 217 } 218 } 219 220 if (out) { 221 memset((char *)out, 0, sizeof (struct refclockstat)); 222 out->type = REFCLK_ATOM_LEITCH; 223 out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2; 224 out->fudgeval1 = (int32)stratumtouse[unit]; 225 out->fudgeval2 = refid[unit]; 226 out->p_lastcode = ""; 227 out->clockdesc = LEITCH_DESCRIPTION; 228 } 229} 230 231/* 232 * leitch_start - open the LEITCH devices and initialize data for processing 233 */ 234static int 235leitch_start( 236 int unit, 237 struct peer *peer 238 ) 239{ 240 struct leitchunit *leitch; 241 int fd232; 242 char leitchdev[20]; 243 244 /* 245 * Check configuration info. 246 */ 247 if (unit >= MAXUNITS) { 248 msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit); 249 return (0); 250 } 251 252 if (unitinuse[unit]) { 253 msyslog(LOG_ERR, "leitch_start: unit %d in use", unit); 254 return (0); 255 } 256 257 /* 258 * Open serial port. 259 */ 260 (void) sprintf(leitchdev, LEITCH232, unit); 261 fd232 = open(leitchdev, O_RDWR, 0777); 262 if (fd232 == -1) { 263 msyslog(LOG_ERR, 264 "leitch_start: open of %s: %m", leitchdev); 265 return (0); 266 } 267 268 leitch = &leitchunits[unit]; 269 memset((char*)leitch, 0, sizeof(*leitch)); 270 271#if defined(HAVE_SYSV_TTYS) 272 /* 273 * System V serial line parameters (termio interface) 274 * 275 */ 276 { struct termio ttyb; 277 if (ioctl(fd232, TCGETA, &ttyb) < 0) { 278 msyslog(LOG_ERR, 279 "leitch_start: ioctl(%s, TCGETA): %m", leitchdev); 280 goto screwed; 281 } 282 ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL; 283 ttyb.c_oflag = 0; 284 ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD; 285 ttyb.c_lflag = ICANON; 286 ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0'; 287 if (ioctl(fd232, TCSETA, &ttyb) < 0) { 288 msyslog(LOG_ERR, 289 "leitch_start: ioctl(%s, TCSETA): %m", leitchdev); 290 goto screwed; 291 } 292 } 293#endif /* HAVE_SYSV_TTYS */ 294#if defined(HAVE_TERMIOS) 295 /* 296 * POSIX serial line parameters (termios interface) 297 * 298 * The LEITCHCLK option provides timestamping at the driver level. 299 * It requires the tty_clk streams module. 300 */ 301 { struct termios ttyb, *ttyp; 302 303 ttyp = &ttyb; 304 if (tcgetattr(fd232, ttyp) < 0) { 305 msyslog(LOG_ERR, 306 "leitch_start: tcgetattr(%s): %m", leitchdev); 307 goto screwed; 308 } 309 ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; 310 ttyp->c_oflag = 0; 311 ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; 312 ttyp->c_lflag = ICANON; 313 ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; 314 if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { 315 msyslog(LOG_ERR, 316 "leitch_start: tcsetattr(%s): %m", leitchdev); 317 goto screwed; 318 } 319 if (tcflush(fd232, TCIOFLUSH) < 0) { 320 msyslog(LOG_ERR, 321 "leitch_start: tcflush(%s): %m", leitchdev); 322 goto screwed; 323 } 324 } 325#endif /* HAVE_TERMIOS */ 326#ifdef STREAM 327#if defined(LEITCHCLK) 328 if (ioctl(fd232, I_PUSH, "clk") < 0) 329 msyslog(LOG_ERR, 330 "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev); 331 if (ioctl(fd232, CLK_SETSTR, "\n") < 0) 332 msyslog(LOG_ERR, 333 "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev); 334#endif /* LEITCHCLK */ 335#endif /* STREAM */ 336#if defined(HAVE_BSD_TTYS) 337 /* 338 * 4.3bsd serial line parameters (sgttyb interface) 339 * 340 * The LEITCHCLK option provides timestamping at the driver level. 341 * It requires the tty_clk line discipline and 4.3bsd or later. 342 */ 343 { struct sgttyb ttyb; 344#if defined(LEITCHCLK) 345 int ldisc = CLKLDISC; 346#endif /* LEITCHCLK */ 347 348 if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { 349 msyslog(LOG_ERR, 350 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev); 351 goto screwed; 352 } 353 ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; 354#if defined(LEITCHCLK) 355 ttyb.sg_erase = ttyb.sg_kill = '\r'; 356 ttyb.sg_flags = RAW; 357#else 358 ttyb.sg_erase = ttyb.sg_kill = '\0'; 359 ttyb.sg_flags = EVENP|ODDP|CRMOD; 360#endif /* LEITCHCLK */ 361 if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { 362 msyslog(LOG_ERR, 363 "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev); 364 goto screwed; 365 } 366#if defined(LEITCHCLK) 367 if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { 368 msyslog(LOG_ERR, 369 "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev); 370 goto screwed; 371 } 372#endif /* LEITCHCLK */ 373 } 374#endif /* HAVE_BSD_TTYS */ 375 376 /* 377 * Set up the structures 378 */ 379 leitch->peer = peer; 380 leitch->unit = unit; 381 leitch->state = STATE_IDLE; 382 leitch->fudge1 = 15; /* 15ms */ 383 384 leitch->leitchio.clock_recv = leitch_receive; 385 leitch->leitchio.srcclock = (caddr_t) leitch; 386 leitch->leitchio.datalen = 0; 387 leitch->leitchio.fd = fd232; 388 if (!io_addclock(&leitch->leitchio)) { 389 goto screwed; 390 } 391 392 /* 393 * All done. Initialize a few random peer variables, then 394 * return success. 395 */ 396 peer->precision = 0; 397 peer->stratum = stratumtouse[unit]; 398 peer->refid = refid[unit]; 399 unitinuse[unit] = 1; 400 return(1); 401 402 /* 403 * Something broke; abandon ship. 404 */ 405 screwed: 406 close(fd232); 407 return(0); 408} 409 410/* 411 * leitch_receive - receive data from the serial interface on a leitch 412 * clock 413 */ 414static void 415leitch_receive( 416 struct recvbuf *rbufp 417 ) 418{ 419 struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock; 420 421#ifdef DEBUG 422 if (debug) 423 fprintf(stderr, "leitch_recieve(%*.*s)\n", 424 rbufp->recv_length, rbufp->recv_length, 425 rbufp->recv_buffer); 426#endif 427 if (rbufp->recv_length != 7) 428 return; /* The date is return with a trailing newline, 429 discard it. */ 430 431 switch (leitch->state) { 432 case STATE_IDLE: /* unexpected, discard and resync */ 433 return; 434 case STATE_DATE: 435 if (!leitch_get_date(rbufp,leitch)) { 436 leitch->state = STATE_IDLE; 437 break; 438 } 439 leitch_send(leitch,"T\r"); 440#ifdef DEBUG 441 if (debug) 442 fprintf(stderr, "%u\n",leitch->yearday); 443#endif 444 leitch->state = STATE_TIME1; 445 break; 446 case STATE_TIME1: 447 if (!leitch_get_time(rbufp,leitch,1)) { 448 } 449 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 450 leitch->second, 1, rbufp->recv_time.l_ui, 451 &leitch->yearstart, &leitch->reftime1.l_ui)) { 452 leitch->state = STATE_IDLE; 453 break; 454 } 455 leitch->reftime1.l_uf = 0; 456#ifdef DEBUG 457 if (debug) 458 fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui); 459#endif 460 MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf); 461 leitch->codetime1 = rbufp->recv_time; 462 leitch->state = STATE_TIME2; 463 break; 464 case STATE_TIME2: 465 if (!leitch_get_time(rbufp,leitch,2)) { 466 } 467 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 468 leitch->second, 1, rbufp->recv_time.l_ui, 469 &leitch->yearstart, &leitch->reftime2.l_ui)) { 470 leitch->state = STATE_IDLE; 471 break; 472 } 473#ifdef DEBUG 474 if (debug) 475 fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui); 476#endif 477 MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf); 478 leitch->codetime2 = rbufp->recv_time; 479 leitch->state = STATE_TIME3; 480 break; 481 case STATE_TIME3: 482 if (!leitch_get_time(rbufp,leitch,3)) { 483 } 484 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 485 leitch->second, GMT, rbufp->recv_time.l_ui, 486 &leitch->yearstart, &leitch->reftime3.l_ui)) { 487 leitch->state = STATE_IDLE; 488 break; 489 } 490#ifdef DEBUG 491 if (debug) 492 fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui); 493#endif 494 MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf); 495 leitch->codetime3 = rbufp->recv_time; 496 leitch_process(leitch); 497 leitch->state = STATE_IDLE; 498 break; 499 default: 500 msyslog(LOG_ERR, 501 "leitech_receive: invalid state %d unit %d", 502 leitch->state, leitch->unit); 503 } 504} 505 506/* 507 * leitch_process - process a pile of samples from the clock 508 * 509 * This routine uses a three-stage median filter to calculate offset and 510 * dispersion. reduce jitter. The dispersion is calculated as the span 511 * of the filter (max - min), unless the quality character (format 2) is 512 * non-blank, in which case the dispersion is calculated on the basis of 513 * the inherent tolerance of the internal radio oscillator, which is 514 * +-2e-5 according to the radio specifications. 515 */ 516static void 517leitch_process( 518 struct leitchunit *leitch 519 ) 520{ 521 l_fp off; 522 l_fp tmp_fp; 523 /*double doffset;*/ 524 525 off = leitch->reftime1; 526 L_SUB(&off,&leitch->codetime1); 527 tmp_fp = leitch->reftime2; 528 L_SUB(&tmp_fp,&leitch->codetime2); 529 if (L_ISGEQ(&off,&tmp_fp)) 530 off = tmp_fp; 531 tmp_fp = leitch->reftime3; 532 L_SUB(&tmp_fp,&leitch->codetime3); 533 534 if (L_ISGEQ(&off,&tmp_fp)) 535 off = tmp_fp; 536 /*LFPTOD(&off, doffset);*/ 537 refclock_receive(leitch->peer); 538} 539 540/* 541 * days_per_year 542 */ 543static int 544days_per_year( 545 int year 546 ) 547{ 548 if (year%4) { /* not a potential leap year */ 549 return (365); 550 } else { 551 if (year % 100) { /* is a leap year */ 552 return (366); 553 } else { 554 if (year % 400) { 555 return (365); 556 } else { 557 return (366); 558 } 559 } 560 } 561} 562 563static int 564leitch_get_date( 565 struct recvbuf *rbufp, 566 struct leitchunit *leitch 567 ) 568{ 569 int i; 570 571 if (rbufp->recv_length < 6) 572 return(0); 573#undef BAD /* confict: defined as (-1) in AIX sys/param.h */ 574#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9') 575 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5)) 576 return(0); 577#define ATOB(A) ((rbufp->recv_buffer[A])-'0') 578 leitch->year = ATOB(0)*10 + ATOB(1); 579 leitch->month = ATOB(2)*10 + ATOB(3); 580 leitch->day = ATOB(4)*10 + ATOB(5); 581 582 /* sanity checks */ 583 if (leitch->month > 12) 584 return(0); 585 if (leitch->day > days_in_month[leitch->month-1]) 586 return(0); 587 588 /* calculate yearday */ 589 i = 0; 590 leitch->yearday = leitch->day; 591 592 while ( i < (leitch->month-1) ) 593 leitch->yearday += days_in_month[i++]; 594 595 if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) && 596 leitch->month > 2) 597 leitch->yearday--; 598 599 return(1); 600} 601 602/* 603 * leitch_get_time 604 */ 605static int 606leitch_get_time( 607 struct recvbuf *rbufp, 608 struct leitchunit *leitch, 609 int which 610 ) 611{ 612 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5)) 613 return(0); 614 leitch->hour = ATOB(0)*10 +ATOB(1); 615 leitch->minute = ATOB(2)*10 +ATOB(3); 616 leitch->second = ATOB(4)*10 +ATOB(5); 617 618 if ((leitch->hour > 23) || (leitch->minute > 60) || 619 (leitch->second > 60)) 620 return(0); 621 return(1); 622} 623 624#else 625int refclock_leitch_bs; 626#endif /* REFCLOCK */ 627