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