2 * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
8 #if defined(REFCLOCK) && defined(CLOCK_LEITCH)
16 #include "ntp_refclock.h"
17 #include "ntp_unixtime.h"
21 #if defined(LEITCHCLK)
22 #include <sys/clkdefs.h>
23 #endif /* LEITCHCLK */
26 #include "ntp_stdlib.h"
30 * Driver for Leitch CSD-5300 Master Clock System
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)
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) \
52 if (debug) fprintf(stderr,"write leitch %s\n",M); \
53 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
55 fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
57 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
66 * LEITCH unit control structure
70 struct refclockio leitchio;
93 static void leitch_init P((void));
94 static int leitch_start P((int, struct peer *));
95 static void leitch_shutdown P((int, struct peer *));
96 static void leitch_poll P((int, struct peer *));
97 static void leitch_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
98 #define leitch_buginfo noentry
99 static void leitch_receive P((struct recvbuf *));
100 static void leitch_process P((struct leitchunit *));
102 static void leitch_timeout P((struct peer *));
104 static int leitch_get_date P((struct recvbuf *, struct leitchunit *));
105 static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int));
106 static int days_per_year P((int));
108 static struct leitchunit leitchunits[MAXUNITS];
109 static u_char unitinuse[MAXUNITS];
110 static u_char stratumtouse[MAXUNITS];
111 static u_int32 refid[MAXUNITS];
113 static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
118 struct refclock refclock_leitch = {
119 leitch_start, leitch_shutdown, leitch_poll,
120 leitch_control, leitch_init, leitch_buginfo, NOFLAGS
124 * leitch_init - initialize internal leitch driver data
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);
138 * leitch_shutdown - shut down a LEITCH clock
148 fprintf(stderr, "leitch_shutdown()\n");
153 * leitch_poll - called by the transmit procedure
161 struct leitchunit *leitch;
163 /* start the state machine rolling */
167 fprintf(stderr, "leitch_poll()\n");
169 if (unit > MAXUNITS) {
174 leitch = &leitchunits[unit];
176 if (leitch->state != STATE_IDLE) {
177 /* reset and wait for next poll */
179 leitch->state = STATE_IDLE;
181 leitch_send(leitch,"D\r");
182 leitch->state = STATE_DATE;
189 struct refclockstat *in,
190 struct refclockstat *out,
191 struct peer *passed_peer
194 if (unit >= MAXUNITS) {
196 "leitch_control: unit %d invalid", unit);
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]) {
208 peer = (&leitchunits[unit])->peer;
209 peer->stratum = stratumtouse[unit];
210 peer->refid = refid[unit];
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;
226 * leitch_start - open the LEITCH devices and initialize data for processing
234 struct leitchunit *leitch;
239 * Check configuration info.
241 if (unit >= MAXUNITS) {
242 msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
246 if (unitinuse[unit]) {
247 msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
254 (void) sprintf(leitchdev, LEITCH232, unit);
255 fd232 = open(leitchdev, O_RDWR, 0777);
258 "leitch_start: open of %s: %m", leitchdev);
262 leitch = &leitchunits[unit];
263 memset((char*)leitch, 0, sizeof(*leitch));
265 #if defined(HAVE_SYSV_TTYS)
267 * System V serial line parameters (termio interface)
270 { struct termio ttyb;
271 if (ioctl(fd232, TCGETA, &ttyb) < 0) {
273 "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
276 ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
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) {
283 "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
287 #endif /* HAVE_SYSV_TTYS */
288 #if defined(HAVE_TERMIOS)
290 * POSIX serial line parameters (termios interface)
292 * The LEITCHCLK option provides timestamping at the driver level.
293 * It requires the tty_clk streams module.
295 { struct termios ttyb, *ttyp;
298 if (tcgetattr(fd232, ttyp) < 0) {
300 "leitch_start: tcgetattr(%s): %m", leitchdev);
303 ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
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) {
310 "leitch_start: tcsetattr(%s): %m", leitchdev);
313 if (tcflush(fd232, TCIOFLUSH) < 0) {
315 "leitch_start: tcflush(%s): %m", leitchdev);
319 #endif /* HAVE_TERMIOS */
321 #if defined(LEITCHCLK)
322 if (ioctl(fd232, I_PUSH, "clk") < 0)
324 "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
325 if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
327 "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
328 #endif /* LEITCHCLK */
330 #if defined(HAVE_BSD_TTYS)
332 * 4.3bsd serial line parameters (sgttyb interface)
334 * The LEITCHCLK option provides timestamping at the driver level.
335 * It requires the tty_clk line discipline and 4.3bsd or later.
337 { struct sgttyb ttyb;
338 #if defined(LEITCHCLK)
339 int ldisc = CLKLDISC;
340 #endif /* LEITCHCLK */
342 if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
344 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
347 ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
348 #if defined(LEITCHCLK)
349 ttyb.sg_erase = ttyb.sg_kill = '\r';
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) {
357 "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
360 #if defined(LEITCHCLK)
361 if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
363 "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
366 #endif /* LEITCHCLK */
368 #endif /* HAVE_BSD_TTYS */
371 * Set up the structures
375 leitch->state = STATE_IDLE;
376 leitch->fudge1 = 15; /* 15ms */
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)) {
387 * All done. Initialize a few random peer variables, then
391 peer->stratum = stratumtouse[unit];
392 peer->refid = refid[unit];
397 * Something broke; abandon ship.
405 * leitch_receive - receive data from the serial interface on a leitch
410 struct recvbuf *rbufp
413 struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
417 fprintf(stderr, "leitch_recieve(%*.*s)\n",
418 rbufp->recv_length, rbufp->recv_length,
421 if (rbufp->recv_length != 7)
422 return; /* The date is return with a trailing newline,
425 switch (leitch->state) {
426 case STATE_IDLE: /* unexpected, discard and resync */
429 if (!leitch_get_date(rbufp,leitch)) {
430 leitch->state = STATE_IDLE;
433 leitch_send(leitch,"T\r");
436 fprintf(stderr, "%u\n",leitch->yearday);
438 leitch->state = STATE_TIME1;
441 if (!leitch_get_time(rbufp,leitch,1)) {
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;
451 fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
453 MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
454 leitch->codetime1 = rbufp->recv_time;
455 leitch->state = STATE_TIME2;
458 if (!leitch_get_time(rbufp,leitch,2)) {
460 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
461 leitch->second, 1, rbufp->recv_time.l_ui,
462 &leitch->yearstart, &leitch->reftime2.l_ui)) {
463 leitch->state = STATE_IDLE;
468 fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
470 MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
471 leitch->codetime2 = rbufp->recv_time;
472 leitch->state = STATE_TIME3;
475 if (!leitch_get_time(rbufp,leitch,3)) {
477 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
478 leitch->second, GMT, rbufp->recv_time.l_ui,
479 &leitch->yearstart, &leitch->reftime3.l_ui)) {
480 leitch->state = STATE_IDLE;
485 fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
487 MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
488 leitch->codetime3 = rbufp->recv_time;
489 leitch_process(leitch);
490 leitch->state = STATE_IDLE;
494 "leitech_receive: invalid state %d unit %d",
495 leitch->state, leitch->unit);
500 * leitch_process - process a pile of samples from the clock
502 * This routine uses a three-stage median filter to calculate offset and
503 * dispersion. reduce jitter. The dispersion is calculated as the span
504 * of the filter (max - min), unless the quality character (format 2) is
505 * non-blank, in which case the dispersion is calculated on the basis of
506 * the inherent tolerance of the internal radio oscillator, which is
507 * +-2e-5 according to the radio specifications.
511 struct leitchunit *leitch
518 off = leitch->reftime1;
519 L_SUB(&off,&leitch->codetime1);
520 tmp_fp = leitch->reftime2;
521 L_SUB(&tmp_fp,&leitch->codetime2);
522 if (L_ISGEQ(&off,&tmp_fp))
524 tmp_fp = leitch->reftime3;
525 L_SUB(&tmp_fp,&leitch->codetime3);
527 if (L_ISGEQ(&off,&tmp_fp))
529 /*LFPTOD(&off, doffset);*/
530 refclock_receive(leitch->peer);
541 if (year%4) { /* not a potential leap year */
544 if (year % 100) { /* is a leap year */
558 struct recvbuf *rbufp,
559 struct leitchunit *leitch
564 if (rbufp->recv_length < 6)
566 #undef BAD /* confict: defined as (-1) in AIX sys/param.h */
567 #define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
568 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
570 #define ATOB(A) ((rbufp->recv_buffer[A])-'0')
571 leitch->year = ATOB(0)*10 + ATOB(1);
572 leitch->month = ATOB(2)*10 + ATOB(3);
573 leitch->day = ATOB(4)*10 + ATOB(5);
576 if (leitch->month > 12)
578 if (leitch->day > days_in_month[leitch->month-1])
581 /* calculate yearday */
583 leitch->yearday = leitch->day;
585 while ( i < (leitch->month-1) )
586 leitch->yearday += days_in_month[i++];
588 if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
600 struct recvbuf *rbufp,
601 struct leitchunit *leitch,
605 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
607 leitch->hour = ATOB(0)*10 +ATOB(1);
608 leitch->minute = ATOB(2)*10 +ATOB(3);
609 leitch->second = ATOB(4)*10 +ATOB(5);
611 if ((leitch->hour > 23) || (leitch->minute > 60) ||
612 (leitch->second > 60))
618 int refclock_leitch_bs;
619 #endif /* REFCLOCK */