refclock_leitch.c revision 290001
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