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