1 /* $NetBSD: refclock_leitch.c,v 1.3 2006/03/18 14:08:29 kardel Exp $ */
4 * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
11 #if defined(REFCLOCK) && defined(CLOCK_LEITCH)
15 #include "ntp_refclock.h"
16 #include "ntp_unixtime.h"
23 #if defined(LEITCHCLK)
24 #include <sys/clkdefs.h>
25 #endif /* LEITCHCLK */
28 #include "ntp_stdlib.h"
32 * Driver for Leitch CSD-5300 Master Clock System
42 * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
43 * second bondaried on the stop bit of the <CR>
44 * second boundaries at '/' above.
45 * STATUS: G (good), D (diag fail), T (time not provided) or
46 * P (last phone update failed)
48 #define MAXUNITS 1 /* max number of LEITCH units */
49 #define LEITCHREFID "ATOM" /* reference id */
50 #define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
51 #define LEITCH232 "/dev/leitch%d" /* name of radio device */
52 #define SPEED232 B300 /* uart speed (300 baud) */
54 #define leitch_send(A,M) \
55 if (debug) fprintf(stderr,"write leitch %s\n",M); \
56 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
58 fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
60 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
62 #define leitch_send(A,M) \
63 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
64 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
74 * LEITCH unit control structure
78 struct refclockio leitchio
;
101 static void leitch_init
P((void));
102 static int leitch_start
P((int, struct peer
*));
103 static void leitch_shutdown
P((int, struct peer
*));
104 static void leitch_poll
P((int, struct peer
*));
105 static void leitch_control
P((int, struct refclockstat
*, struct refclockstat
*, struct peer
*));
106 #define leitch_buginfo noentry
107 static void leitch_receive
P((struct recvbuf
*));
108 static void leitch_process
P((struct leitchunit
*));
110 static void leitch_timeout
P((struct peer
*));
112 static int leitch_get_date
P((struct recvbuf
*, struct leitchunit
*));
113 static int leitch_get_time
P((struct recvbuf
*, struct leitchunit
*, int));
114 static int days_per_year
P((int));
116 static struct leitchunit leitchunits
[MAXUNITS
];
117 static u_char unitinuse
[MAXUNITS
];
118 static u_char stratumtouse
[MAXUNITS
];
119 static u_int32 refid
[MAXUNITS
];
121 static char days_in_month
[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
126 struct refclock refclock_leitch
= {
127 leitch_start
, leitch_shutdown
, leitch_poll
,
128 leitch_control
, leitch_init
, leitch_buginfo
, NOFLAGS
132 * leitch_init - initialize internal leitch driver data
139 memset((char*)leitchunits
, 0, sizeof(leitchunits
));
140 memset((char*)unitinuse
, 0, sizeof(unitinuse
));
141 for (i
= 0; i
< MAXUNITS
; i
++)
142 memcpy((char *)&refid
[i
], LEITCHREFID
, 4);
146 * leitch_shutdown - shut down a LEITCH clock
156 fprintf(stderr
, "leitch_shutdown()\n");
161 * leitch_poll - called by the transmit procedure
169 struct leitchunit
*leitch
;
171 /* start the state machine rolling */
175 fprintf(stderr
, "leitch_poll()\n");
177 if (unit
>= MAXUNITS
) {
182 leitch
= &leitchunits
[unit
];
184 if (leitch
->state
!= STATE_IDLE
) {
185 /* reset and wait for next poll */
187 leitch
->state
= STATE_IDLE
;
189 leitch_send(leitch
,"D\r");
190 leitch
->state
= STATE_DATE
;
197 struct refclockstat
*in
,
198 struct refclockstat
*out
,
199 struct peer
*passed_peer
202 if (unit
>= MAXUNITS
) {
204 "leitch_control: unit %d invalid", unit
);
209 if (in
->haveflags
& CLK_HAVEVAL1
)
210 stratumtouse
[unit
] = (u_char
)(in
->fudgeval1
);
211 if (in
->haveflags
& CLK_HAVEVAL2
)
212 refid
[unit
] = in
->fudgeval2
;
213 if (unitinuse
[unit
]) {
216 peer
= (&leitchunits
[unit
])->peer
;
217 peer
->stratum
= stratumtouse
[unit
];
218 peer
->refid
= refid
[unit
];
223 memset((char *)out
, 0, sizeof (struct refclockstat
));
224 out
->type
= REFCLK_ATOM_LEITCH
;
225 out
->haveflags
= CLK_HAVEVAL1
| CLK_HAVEVAL2
;
226 out
->fudgeval1
= (int32
)stratumtouse
[unit
];
227 out
->fudgeval2
= refid
[unit
];
228 out
->p_lastcode
= "";
229 out
->clockdesc
= LEITCH_DESCRIPTION
;
234 * leitch_start - open the LEITCH devices and initialize data for processing
242 struct leitchunit
*leitch
;
247 * Check configuration info.
249 if (unit
>= MAXUNITS
) {
250 msyslog(LOG_ERR
, "leitch_start: unit %d invalid", unit
);
254 if (unitinuse
[unit
]) {
255 msyslog(LOG_ERR
, "leitch_start: unit %d in use", unit
);
262 (void) sprintf(leitchdev
, LEITCH232
, unit
);
263 fd232
= open(leitchdev
, O_RDWR
, 0777);
266 "leitch_start: open of %s: %m", leitchdev
);
270 leitch
= &leitchunits
[unit
];
271 memset((char*)leitch
, 0, sizeof(*leitch
));
273 #if defined(HAVE_SYSV_TTYS)
275 * System V serial line parameters (termio interface)
278 { struct termio ttyb
;
279 if (ioctl(fd232
, TCGETA
, &ttyb
) < 0) {
281 "leitch_start: ioctl(%s, TCGETA): %m", leitchdev
);
284 ttyb
.c_iflag
= IGNBRK
|IGNPAR
|ICRNL
;
286 ttyb
.c_cflag
= SPEED232
|CS8
|CLOCAL
|CREAD
;
287 ttyb
.c_lflag
= ICANON
;
288 ttyb
.c_cc
[VERASE
] = ttyb
.c_cc
[VKILL
] = '\0';
289 if (ioctl(fd232
, TCSETA
, &ttyb
) < 0) {
291 "leitch_start: ioctl(%s, TCSETA): %m", leitchdev
);
295 #endif /* HAVE_SYSV_TTYS */
296 #if defined(HAVE_TERMIOS)
298 * POSIX serial line parameters (termios interface)
300 * The LEITCHCLK option provides timestamping at the driver level.
301 * It requires the tty_clk streams module.
303 { struct termios ttyb
, *ttyp
;
306 if (tcgetattr(fd232
, ttyp
) < 0) {
308 "leitch_start: tcgetattr(%s): %m", leitchdev
);
311 ttyp
->c_iflag
= IGNBRK
|IGNPAR
|ICRNL
;
313 ttyp
->c_cflag
= SPEED232
|CS8
|CLOCAL
|CREAD
;
314 ttyp
->c_lflag
= ICANON
;
315 ttyp
->c_cc
[VERASE
] = ttyp
->c_cc
[VKILL
] = '\0';
316 if (tcsetattr(fd232
, TCSANOW
, ttyp
) < 0) {
318 "leitch_start: tcsetattr(%s): %m", leitchdev
);
321 if (tcflush(fd232
, TCIOFLUSH
) < 0) {
323 "leitch_start: tcflush(%s): %m", leitchdev
);
327 #endif /* HAVE_TERMIOS */
329 #if defined(LEITCHCLK)
330 if (ioctl(fd232
, I_PUSH
, "clk") < 0)
332 "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev
);
333 if (ioctl(fd232
, CLK_SETSTR
, "\n") < 0)
335 "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev
);
336 #endif /* LEITCHCLK */
338 #if defined(HAVE_BSD_TTYS)
340 * 4.3bsd serial line parameters (sgttyb interface)
342 * The LEITCHCLK option provides timestamping at the driver level.
343 * It requires the tty_clk line discipline and 4.3bsd or later.
345 { struct sgttyb ttyb
;
346 #if defined(LEITCHCLK)
347 int ldisc
= CLKLDISC
;
348 #endif /* LEITCHCLK */
350 if (ioctl(fd232
, TIOCGETP
, &ttyb
) < 0) {
352 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev
);
355 ttyb
.sg_ispeed
= ttyb
.sg_ospeed
= SPEED232
;
356 #if defined(LEITCHCLK)
357 ttyb
.sg_erase
= ttyb
.sg_kill
= '\r';
360 ttyb
.sg_erase
= ttyb
.sg_kill
= '\0';
361 ttyb
.sg_flags
= EVENP
|ODDP
|CRMOD
;
362 #endif /* LEITCHCLK */
363 if (ioctl(fd232
, TIOCSETP
, &ttyb
) < 0) {
365 "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev
);
368 #if defined(LEITCHCLK)
369 if (ioctl(fd232
, TIOCSETD
, &ldisc
) < 0) {
371 "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev
);
374 #endif /* LEITCHCLK */
376 #endif /* HAVE_BSD_TTYS */
379 * Set up the structures
383 leitch
->state
= STATE_IDLE
;
384 leitch
->fudge1
= 15; /* 15ms */
386 leitch
->leitchio
.clock_recv
= leitch_receive
;
387 leitch
->leitchio
.srcclock
= (caddr_t
) leitch
;
388 leitch
->leitchio
.datalen
= 0;
389 leitch
->leitchio
.fd
= fd232
;
390 if (!io_addclock(&leitch
->leitchio
)) {
395 * All done. Initialize a few random peer variables, then
399 peer
->stratum
= stratumtouse
[unit
];
400 peer
->refid
= refid
[unit
];
405 * Something broke; abandon ship.
413 * leitch_receive - receive data from the serial interface on a leitch
418 struct recvbuf
*rbufp
421 struct leitchunit
*leitch
= (struct leitchunit
*)rbufp
->recv_srcclock
;
425 fprintf(stderr
, "leitch_recieve(%*.*s)\n",
426 rbufp
->recv_length
, rbufp
->recv_length
,
429 if (rbufp
->recv_length
!= 7)
430 return; /* The date is return with a trailing newline,
433 switch (leitch
->state
) {
434 case STATE_IDLE
: /* unexpected, discard and resync */
437 if (!leitch_get_date(rbufp
,leitch
)) {
438 leitch
->state
= STATE_IDLE
;
441 leitch_send(leitch
,"T\r");
444 fprintf(stderr
, "%u\n",leitch
->yearday
);
446 leitch
->state
= STATE_TIME1
;
449 if (!leitch_get_time(rbufp
,leitch
,1)) {
451 if (!clocktime(leitch
->yearday
,leitch
->hour
,leitch
->minute
,
452 leitch
->second
, 1, rbufp
->recv_time
.l_ui
,
453 &leitch
->yearstart
, &leitch
->reftime1
.l_ui
)) {
454 leitch
->state
= STATE_IDLE
;
457 leitch
->reftime1
.l_uf
= 0;
460 fprintf(stderr
, "%lu\n", (u_long
)leitch
->reftime1
.l_ui
);
462 MSUTOTSF(leitch
->fudge1
, leitch
->reftime1
.l_uf
);
463 leitch
->codetime1
= rbufp
->recv_time
;
464 leitch
->state
= STATE_TIME2
;
467 if (!leitch_get_time(rbufp
,leitch
,2)) {
469 if (!clocktime(leitch
->yearday
,leitch
->hour
,leitch
->minute
,
470 leitch
->second
, 1, rbufp
->recv_time
.l_ui
,
471 &leitch
->yearstart
, &leitch
->reftime2
.l_ui
)) {
472 leitch
->state
= STATE_IDLE
;
477 fprintf(stderr
, "%lu\n", (u_long
)leitch
->reftime2
.l_ui
);
479 MSUTOTSF(leitch
->fudge1
, leitch
->reftime2
.l_uf
);
480 leitch
->codetime2
= rbufp
->recv_time
;
481 leitch
->state
= STATE_TIME3
;
484 if (!leitch_get_time(rbufp
,leitch
,3)) {
486 if (!clocktime(leitch
->yearday
,leitch
->hour
,leitch
->minute
,
487 leitch
->second
, GMT
, rbufp
->recv_time
.l_ui
,
488 &leitch
->yearstart
, &leitch
->reftime3
.l_ui
)) {
489 leitch
->state
= STATE_IDLE
;
494 fprintf(stderr
, "%lu\n", (u_long
)leitch
->reftime3
.l_ui
);
496 MSUTOTSF(leitch
->fudge1
, leitch
->reftime3
.l_uf
);
497 leitch
->codetime3
= rbufp
->recv_time
;
498 leitch_process(leitch
);
499 leitch
->state
= STATE_IDLE
;
503 "leitech_receive: invalid state %d unit %d",
504 leitch
->state
, leitch
->unit
);
509 * leitch_process - process a pile of samples from the clock
511 * This routine uses a three-stage median filter to calculate offset and
512 * dispersion. reduce jitter. The dispersion is calculated as the span
513 * of the filter (max - min), unless the quality character (format 2) is
514 * non-blank, in which case the dispersion is calculated on the basis of
515 * the inherent tolerance of the internal radio oscillator, which is
516 * +-2e-5 according to the radio specifications.
520 struct leitchunit
*leitch
527 off
= leitch
->reftime1
;
528 L_SUB(&off
,&leitch
->codetime1
);
529 tmp_fp
= leitch
->reftime2
;
530 L_SUB(&tmp_fp
,&leitch
->codetime2
);
531 if (L_ISGEQ(&off
,&tmp_fp
))
533 tmp_fp
= leitch
->reftime3
;
534 L_SUB(&tmp_fp
,&leitch
->codetime3
);
536 if (L_ISGEQ(&off
,&tmp_fp
))
538 /*LFPTOD(&off, doffset);*/
539 refclock_receive(leitch
->peer
);
550 if (year
%4) { /* not a potential leap year */
553 if (year
% 100) { /* is a leap year */
567 struct recvbuf
*rbufp
,
568 struct leitchunit
*leitch
573 if (rbufp
->recv_length
< 6)
575 #undef BAD /* confict: defined as (-1) in AIX sys/param.h */
576 #define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
577 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
579 #define ATOB(A) ((rbufp->recv_buffer[A])-'0')
580 leitch
->year
= ATOB(0)*10 + ATOB(1);
581 leitch
->month
= ATOB(2)*10 + ATOB(3);
582 leitch
->day
= ATOB(4)*10 + ATOB(5);
585 if (leitch
->month
> 12)
587 if (leitch
->day
> days_in_month
[leitch
->month
-1])
590 /* calculate yearday */
592 leitch
->yearday
= leitch
->day
;
594 while ( i
< (leitch
->month
-1) )
595 leitch
->yearday
+= days_in_month
[i
++];
597 if ((days_per_year((leitch
->year
>90?1900:2000)+leitch
->year
)==365) &&
609 struct recvbuf
*rbufp
,
610 struct leitchunit
*leitch
,
614 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
616 leitch
->hour
= ATOB(0)*10 +ATOB(1);
617 leitch
->minute
= ATOB(2)*10 +ATOB(3);
618 leitch
->second
= ATOB(4)*10 +ATOB(5);
620 if ((leitch
->hour
> 23) || (leitch
->minute
> 60) ||
621 (leitch
->second
> 60))
627 int refclock_leitch_bs
;
628 #endif /* REFCLOCK */