154359Sroberto/*
254359Sroberto * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
354359Sroberto */
482498Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
682498Sroberto# include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_LEITCH)
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_refclock.h"
1454359Sroberto#include "ntp_unixtime.h"
1554359Sroberto
1682498Sroberto#include <stdio.h>
1782498Sroberto#include <ctype.h>
1882498Sroberto
1954359Sroberto#ifdef STREAM
2054359Sroberto#include <stropts.h>
2154359Sroberto#if defined(LEITCHCLK)
2254359Sroberto#include <sys/clkdefs.h>
2354359Sroberto#endif /* LEITCHCLK */
2454359Sroberto#endif /* STREAM */
2554359Sroberto
2654359Sroberto#include "ntp_stdlib.h"
2754359Sroberto
2854359Sroberto
2954359Sroberto/*
3054359Sroberto * Driver for Leitch CSD-5300 Master Clock System
3154359Sroberto *
3254359Sroberto * COMMANDS:
3354359Sroberto *	DATE:	D <CR>
3454359Sroberto *	TIME:	T <CR>
3554359Sroberto *	STATUS:	S <CR>
3654359Sroberto *	LOOP:	L <CR>
3754359Sroberto *
3854359Sroberto * FORMAT:
3954359Sroberto *	DATE: YYMMDD<CR>
4054359Sroberto *	TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
4154359Sroberto *		second bondaried on the stop bit of the <CR>
4254359Sroberto *		second boundaries at '/' above.
4354359Sroberto *	STATUS: G (good), D (diag fail), T (time not provided) or
4454359Sroberto *		P (last phone update failed)
4554359Sroberto */
4654359Sroberto#define MAXUNITS 1		/* max number of LEITCH units */
4754359Sroberto#define LEITCHREFID	"ATOM"	/* reference id */
4854359Sroberto#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
4954359Sroberto#define LEITCH232 "/dev/leitch%d"	/* name of radio device */
5054359Sroberto#define SPEED232 B300		/* uart speed (300 baud) */
51182007Sroberto#ifdef DEBUG
5254359Sroberto#define leitch_send(A,M) \
5354359Srobertoif (debug) fprintf(stderr,"write leitch %s\n",M); \
5454359Srobertoif ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
55182007Sroberto	if (debug) \
56182007Sroberto	    fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
57182007Sroberto	else \
58182007Sroberto	    msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
59182007Sroberto#else
60182007Sroberto#define leitch_send(A,M) \
61182007Srobertoif ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
62182007Sroberto	msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
63182007Sroberto#endif
64182007Sroberto
6554359Sroberto#define STATE_IDLE 0
6654359Sroberto#define STATE_DATE 1
6754359Sroberto#define STATE_TIME1 2
6854359Sroberto#define STATE_TIME2 3
6954359Sroberto#define STATE_TIME3 4
7054359Sroberto
7154359Sroberto/*
7254359Sroberto * LEITCH unit control structure
7354359Sroberto */
7454359Srobertostruct leitchunit {
7554359Sroberto	struct peer *peer;
7654359Sroberto	struct refclockio leitchio;
7754359Sroberto	u_char unit;
7854359Sroberto	short year;
7954359Sroberto	short yearday;
8054359Sroberto	short month;
8154359Sroberto	short day;
8254359Sroberto	short hour;
8354359Sroberto	short second;
8454359Sroberto	short minute;
8554359Sroberto	short state;
8654359Sroberto	u_short fudge1;
8754359Sroberto	l_fp reftime1;
8854359Sroberto	l_fp reftime2;
8954359Sroberto	l_fp reftime3;
9054359Sroberto	l_fp codetime1;
9154359Sroberto	l_fp codetime2;
9254359Sroberto	l_fp codetime3;
9354359Sroberto	u_long yearstart;
9454359Sroberto};
9554359Sroberto
9654359Sroberto/*
9754359Sroberto * Function prototypes
9854359Sroberto */
9954359Srobertostatic	void	leitch_init	P((void));
10054359Srobertostatic	int	leitch_start	P((int, struct peer *));
10154359Srobertostatic	void	leitch_shutdown	P((int, struct peer *));
10254359Srobertostatic	void	leitch_poll	P((int, struct peer *));
10354359Srobertostatic	void	leitch_control	P((int, struct refclockstat *, struct refclockstat *, struct peer *));
10454359Sroberto#define	leitch_buginfo	noentry
10554359Srobertostatic	void	leitch_receive	P((struct recvbuf *));
10654359Srobertostatic	void	leitch_process	P((struct leitchunit *));
10754359Sroberto#if 0
10854359Srobertostatic	void	leitch_timeout	P((struct peer *));
10954359Sroberto#endif
11054359Srobertostatic	int	leitch_get_date	P((struct recvbuf *, struct leitchunit *));
11154359Srobertostatic	int	leitch_get_time	P((struct recvbuf *, struct leitchunit *, int));
11254359Srobertostatic	int	days_per_year		P((int));
11354359Sroberto
11454359Srobertostatic struct leitchunit leitchunits[MAXUNITS];
11554359Srobertostatic u_char unitinuse[MAXUNITS];
11654359Srobertostatic u_char stratumtouse[MAXUNITS];
11754359Srobertostatic u_int32 refid[MAXUNITS];
11854359Sroberto
11954359Srobertostatic	char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
12054359Sroberto
12154359Sroberto/*
12254359Sroberto * Transfer vector
12354359Sroberto */
12454359Srobertostruct	refclock refclock_leitch = {
12554359Sroberto	leitch_start, leitch_shutdown, leitch_poll,
12654359Sroberto	leitch_control, leitch_init, leitch_buginfo, NOFLAGS
12754359Sroberto};
12854359Sroberto
12954359Sroberto/*
13054359Sroberto * leitch_init - initialize internal leitch driver data
13154359Sroberto */
13254359Srobertostatic void
13354359Srobertoleitch_init(void)
13454359Sroberto{
13554359Sroberto	int i;
13654359Sroberto
13754359Sroberto	memset((char*)leitchunits, 0, sizeof(leitchunits));
13854359Sroberto	memset((char*)unitinuse, 0, sizeof(unitinuse));
13954359Sroberto	for (i = 0; i < MAXUNITS; i++)
14054359Sroberto	    memcpy((char *)&refid[i], LEITCHREFID, 4);
14154359Sroberto}
14254359Sroberto
14354359Sroberto/*
14454359Sroberto * leitch_shutdown - shut down a LEITCH clock
14554359Sroberto */
14654359Srobertostatic void
14754359Srobertoleitch_shutdown(
14854359Sroberto	int unit,
14954359Sroberto	struct peer *peer
15054359Sroberto	)
15154359Sroberto{
15254359Sroberto#ifdef DEBUG
15354359Sroberto	if (debug)
15454359Sroberto	    fprintf(stderr, "leitch_shutdown()\n");
15554359Sroberto#endif
15654359Sroberto}
15754359Sroberto
15854359Sroberto/*
15954359Sroberto * leitch_poll - called by the transmit procedure
16054359Sroberto */
16154359Srobertostatic void
16254359Srobertoleitch_poll(
16354359Sroberto	int unit,
16454359Sroberto	struct peer *peer
16554359Sroberto	)
16654359Sroberto{
16754359Sroberto	struct leitchunit *leitch;
16854359Sroberto
16954359Sroberto	/* start the state machine rolling */
17054359Sroberto
17154359Sroberto#ifdef DEBUG
17254359Sroberto	if (debug)
17354359Sroberto	    fprintf(stderr, "leitch_poll()\n");
17454359Sroberto#endif
175182007Sroberto	if (unit >= MAXUNITS) {
17654359Sroberto		/* XXXX syslog it */
17754359Sroberto		return;
17854359Sroberto	}
17954359Sroberto
18054359Sroberto	leitch = &leitchunits[unit];
18154359Sroberto
18254359Sroberto	if (leitch->state != STATE_IDLE) {
18354359Sroberto		/* reset and wait for next poll */
18454359Sroberto		/* XXXX syslog it */
18554359Sroberto		leitch->state = STATE_IDLE;
18654359Sroberto	} else {
18754359Sroberto		leitch_send(leitch,"D\r");
18854359Sroberto		leitch->state = STATE_DATE;
18954359Sroberto	}
19054359Sroberto}
19154359Sroberto
19254359Srobertostatic void
19354359Srobertoleitch_control(
19454359Sroberto	int unit,
19554359Sroberto	struct refclockstat *in,
19654359Sroberto	struct refclockstat *out,
19754359Sroberto	struct peer *passed_peer
19854359Sroberto	)
19954359Sroberto{
20054359Sroberto	if (unit >= MAXUNITS) {
20154359Sroberto		msyslog(LOG_ERR,
20254359Sroberto			"leitch_control: unit %d invalid", unit);
20354359Sroberto		return;
20454359Sroberto	}
20554359Sroberto
20654359Sroberto	if (in) {
20754359Sroberto		if (in->haveflags & CLK_HAVEVAL1)
20854359Sroberto		    stratumtouse[unit] = (u_char)(in->fudgeval1);
20954359Sroberto		if (in->haveflags & CLK_HAVEVAL2)
21054359Sroberto		    refid[unit] = in->fudgeval2;
21154359Sroberto		if (unitinuse[unit]) {
21254359Sroberto			struct peer *peer;
21354359Sroberto
21454359Sroberto			peer = (&leitchunits[unit])->peer;
21554359Sroberto			peer->stratum = stratumtouse[unit];
21654359Sroberto			peer->refid = refid[unit];
21754359Sroberto		}
21854359Sroberto	}
21954359Sroberto
22054359Sroberto	if (out) {
22154359Sroberto		memset((char *)out, 0, sizeof (struct refclockstat));
22254359Sroberto		out->type = REFCLK_ATOM_LEITCH;
22354359Sroberto		out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
22454359Sroberto		out->fudgeval1 = (int32)stratumtouse[unit];
22554359Sroberto		out->fudgeval2 = refid[unit];
22654359Sroberto		out->p_lastcode = "";
22754359Sroberto		out->clockdesc = LEITCH_DESCRIPTION;
22854359Sroberto	}
22954359Sroberto}
23054359Sroberto
23154359Sroberto/*
23254359Sroberto * leitch_start - open the LEITCH devices and initialize data for processing
23354359Sroberto */
23454359Srobertostatic int
23554359Srobertoleitch_start(
23654359Sroberto	int unit,
23754359Sroberto	struct peer *peer
23854359Sroberto	)
23954359Sroberto{
24054359Sroberto	struct leitchunit *leitch;
24154359Sroberto	int fd232;
24254359Sroberto	char leitchdev[20];
24354359Sroberto
24454359Sroberto	/*
24554359Sroberto	 * Check configuration info.
24654359Sroberto	 */
24754359Sroberto	if (unit >= MAXUNITS) {
24854359Sroberto		msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
24954359Sroberto		return (0);
25054359Sroberto	}
25154359Sroberto
25254359Sroberto	if (unitinuse[unit]) {
25354359Sroberto		msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
25454359Sroberto		return (0);
25554359Sroberto	}
25654359Sroberto
25754359Sroberto	/*
25854359Sroberto	 * Open serial port.
25954359Sroberto	 */
26054359Sroberto	(void) sprintf(leitchdev, LEITCH232, unit);
26154359Sroberto	fd232 = open(leitchdev, O_RDWR, 0777);
26254359Sroberto	if (fd232 == -1) {
26354359Sroberto		msyslog(LOG_ERR,
26454359Sroberto			"leitch_start: open of %s: %m", leitchdev);
26554359Sroberto		return (0);
26654359Sroberto	}
26754359Sroberto
26854359Sroberto	leitch = &leitchunits[unit];
26954359Sroberto	memset((char*)leitch, 0, sizeof(*leitch));
27054359Sroberto
27154359Sroberto#if defined(HAVE_SYSV_TTYS)
27254359Sroberto	/*
27354359Sroberto	 * System V serial line parameters (termio interface)
27454359Sroberto	 *
27554359Sroberto	 */
27654359Sroberto	{	struct termio ttyb;
27754359Sroberto	if (ioctl(fd232, TCGETA, &ttyb) < 0) {
27854359Sroberto		msyslog(LOG_ERR,
27954359Sroberto			"leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
28054359Sroberto		goto screwed;
28154359Sroberto	}
28254359Sroberto	ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
28354359Sroberto	ttyb.c_oflag = 0;
28454359Sroberto	ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
28554359Sroberto	ttyb.c_lflag = ICANON;
28654359Sroberto	ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
28754359Sroberto	if (ioctl(fd232, TCSETA, &ttyb) < 0) {
28854359Sroberto		msyslog(LOG_ERR,
28954359Sroberto			"leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
29054359Sroberto		goto screwed;
29154359Sroberto	}
29254359Sroberto	}
29354359Sroberto#endif /* HAVE_SYSV_TTYS */
29454359Sroberto#if defined(HAVE_TERMIOS)
29554359Sroberto	/*
29654359Sroberto	 * POSIX serial line parameters (termios interface)
29754359Sroberto	 *
29854359Sroberto	 * The LEITCHCLK option provides timestamping at the driver level.
29954359Sroberto	 * It requires the tty_clk streams module.
30054359Sroberto	 */
30154359Sroberto	{	struct termios ttyb, *ttyp;
30254359Sroberto
30354359Sroberto	ttyp = &ttyb;
30454359Sroberto	if (tcgetattr(fd232, ttyp) < 0) {
30554359Sroberto		msyslog(LOG_ERR,
30654359Sroberto			"leitch_start: tcgetattr(%s): %m", leitchdev);
30754359Sroberto		goto screwed;
30854359Sroberto	}
30954359Sroberto	ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
31054359Sroberto	ttyp->c_oflag = 0;
31154359Sroberto	ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
31254359Sroberto	ttyp->c_lflag = ICANON;
31354359Sroberto	ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
31454359Sroberto	if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
31554359Sroberto		msyslog(LOG_ERR,
31654359Sroberto			"leitch_start: tcsetattr(%s): %m", leitchdev);
31754359Sroberto		goto screwed;
31854359Sroberto	}
31954359Sroberto	if (tcflush(fd232, TCIOFLUSH) < 0) {
32054359Sroberto		msyslog(LOG_ERR,
32154359Sroberto			"leitch_start: tcflush(%s): %m", leitchdev);
32254359Sroberto		goto screwed;
32354359Sroberto	}
32454359Sroberto	}
32554359Sroberto#endif /* HAVE_TERMIOS */
32654359Sroberto#ifdef STREAM
32754359Sroberto#if defined(LEITCHCLK)
32854359Sroberto	if (ioctl(fd232, I_PUSH, "clk") < 0)
32954359Sroberto	    msyslog(LOG_ERR,
33054359Sroberto		    "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
33154359Sroberto	if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
33254359Sroberto	    msyslog(LOG_ERR,
33354359Sroberto		    "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
33454359Sroberto#endif /* LEITCHCLK */
33554359Sroberto#endif /* STREAM */
33654359Sroberto#if defined(HAVE_BSD_TTYS)
33754359Sroberto	/*
33854359Sroberto	 * 4.3bsd serial line parameters (sgttyb interface)
33954359Sroberto	 *
34054359Sroberto	 * The LEITCHCLK option provides timestamping at the driver level.
34154359Sroberto	 * It requires the tty_clk line discipline and 4.3bsd or later.
34254359Sroberto	 */
34354359Sroberto	{	struct sgttyb ttyb;
34454359Sroberto#if defined(LEITCHCLK)
34554359Sroberto	int ldisc = CLKLDISC;
34654359Sroberto#endif /* LEITCHCLK */
34754359Sroberto
34854359Sroberto	if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
34954359Sroberto		msyslog(LOG_ERR,
35054359Sroberto			"leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
35154359Sroberto		goto screwed;
35254359Sroberto	}
35354359Sroberto	ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
35454359Sroberto#if defined(LEITCHCLK)
35554359Sroberto	ttyb.sg_erase = ttyb.sg_kill = '\r';
35654359Sroberto	ttyb.sg_flags = RAW;
35754359Sroberto#else
35854359Sroberto	ttyb.sg_erase = ttyb.sg_kill = '\0';
35954359Sroberto	ttyb.sg_flags = EVENP|ODDP|CRMOD;
36054359Sroberto#endif /* LEITCHCLK */
36154359Sroberto	if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
36254359Sroberto		msyslog(LOG_ERR,
36354359Sroberto			"leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
36454359Sroberto		goto screwed;
36554359Sroberto	}
36654359Sroberto#if defined(LEITCHCLK)
36754359Sroberto	if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
36854359Sroberto		msyslog(LOG_ERR,
36954359Sroberto			"leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
37054359Sroberto		goto screwed;
37154359Sroberto	}
37254359Sroberto#endif /* LEITCHCLK */
37354359Sroberto	}
37454359Sroberto#endif /* HAVE_BSD_TTYS */
37554359Sroberto
37654359Sroberto	/*
37754359Sroberto	 * Set up the structures
37854359Sroberto	 */
37954359Sroberto	leitch->peer = peer;
38054359Sroberto	leitch->unit = unit;
38154359Sroberto	leitch->state = STATE_IDLE;
38254359Sroberto	leitch->fudge1 = 15;	/* 15ms */
38354359Sroberto
38454359Sroberto	leitch->leitchio.clock_recv = leitch_receive;
38554359Sroberto	leitch->leitchio.srcclock = (caddr_t) leitch;
38654359Sroberto	leitch->leitchio.datalen = 0;
38754359Sroberto	leitch->leitchio.fd = fd232;
38854359Sroberto	if (!io_addclock(&leitch->leitchio)) {
38954359Sroberto		goto screwed;
39054359Sroberto	}
39154359Sroberto
39254359Sroberto	/*
39354359Sroberto	 * All done.  Initialize a few random peer variables, then
39454359Sroberto	 * return success.
39554359Sroberto	 */
39654359Sroberto	peer->precision = 0;
39754359Sroberto	peer->stratum = stratumtouse[unit];
39854359Sroberto	peer->refid = refid[unit];
39954359Sroberto	unitinuse[unit] = 1;
40054359Sroberto	return(1);
40154359Sroberto
40254359Sroberto	/*
40354359Sroberto	 * Something broke; abandon ship.
40454359Sroberto	 */
40554359Sroberto    screwed:
40654359Sroberto	close(fd232);
40754359Sroberto	return(0);
40854359Sroberto}
40954359Sroberto
41054359Sroberto/*
41154359Sroberto * leitch_receive - receive data from the serial interface on a leitch
41254359Sroberto * clock
41354359Sroberto */
41454359Srobertostatic void
41554359Srobertoleitch_receive(
41654359Sroberto	struct recvbuf *rbufp
41754359Sroberto	)
41854359Sroberto{
41954359Sroberto	struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
42054359Sroberto
42154359Sroberto#ifdef DEBUG
42254359Sroberto	if (debug)
42354359Sroberto	    fprintf(stderr, "leitch_recieve(%*.*s)\n",
42454359Sroberto		    rbufp->recv_length, rbufp->recv_length,
42554359Sroberto		    rbufp->recv_buffer);
42654359Sroberto#endif
42754359Sroberto	if (rbufp->recv_length != 7)
42854359Sroberto	    return; /* The date is return with a trailing newline,
42954359Sroberto		       discard it. */
43054359Sroberto
43154359Sroberto	switch (leitch->state) {
43254359Sroberto	    case STATE_IDLE:	/* unexpected, discard and resync */
43354359Sroberto		return;
43454359Sroberto	    case STATE_DATE:
43554359Sroberto		if (!leitch_get_date(rbufp,leitch)) {
43654359Sroberto			leitch->state = STATE_IDLE;
43754359Sroberto			break;
43854359Sroberto		}
43954359Sroberto		leitch_send(leitch,"T\r");
44054359Sroberto#ifdef DEBUG
44154359Sroberto		if (debug)
44254359Sroberto		    fprintf(stderr, "%u\n",leitch->yearday);
44354359Sroberto#endif
44454359Sroberto		leitch->state = STATE_TIME1;
44554359Sroberto		break;
44654359Sroberto	    case STATE_TIME1:
44754359Sroberto		if (!leitch_get_time(rbufp,leitch,1)) {
44854359Sroberto		}
44954359Sroberto		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
45054359Sroberto			       leitch->second, 1, rbufp->recv_time.l_ui,
45154359Sroberto			       &leitch->yearstart, &leitch->reftime1.l_ui)) {
45254359Sroberto			leitch->state = STATE_IDLE;
45354359Sroberto			break;
45454359Sroberto		}
455132451Sroberto		leitch->reftime1.l_uf = 0;
45654359Sroberto#ifdef DEBUG
45754359Sroberto		if (debug)
45854359Sroberto		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
45954359Sroberto#endif
46054359Sroberto		MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
46154359Sroberto		leitch->codetime1 = rbufp->recv_time;
46254359Sroberto		leitch->state = STATE_TIME2;
46354359Sroberto		break;
46454359Sroberto	    case STATE_TIME2:
46554359Sroberto		if (!leitch_get_time(rbufp,leitch,2)) {
46654359Sroberto		}
46754359Sroberto		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
46854359Sroberto			       leitch->second, 1, rbufp->recv_time.l_ui,
46954359Sroberto			       &leitch->yearstart, &leitch->reftime2.l_ui)) {
47054359Sroberto			leitch->state = STATE_IDLE;
47154359Sroberto			break;
47254359Sroberto		}
47354359Sroberto#ifdef DEBUG
47454359Sroberto		if (debug)
47554359Sroberto		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
47654359Sroberto#endif
47754359Sroberto		MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
47854359Sroberto		leitch->codetime2 = rbufp->recv_time;
47954359Sroberto		leitch->state = STATE_TIME3;
48054359Sroberto		break;
48154359Sroberto	    case STATE_TIME3:
48254359Sroberto		if (!leitch_get_time(rbufp,leitch,3)) {
48354359Sroberto		}
48454359Sroberto		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
48554359Sroberto			       leitch->second, GMT, rbufp->recv_time.l_ui,
48654359Sroberto			       &leitch->yearstart, &leitch->reftime3.l_ui)) {
48754359Sroberto			leitch->state = STATE_IDLE;
48854359Sroberto			break;
48954359Sroberto		}
49054359Sroberto#ifdef DEBUG
49154359Sroberto		if (debug)
49254359Sroberto		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
49354359Sroberto#endif
49454359Sroberto		MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
49554359Sroberto		leitch->codetime3 = rbufp->recv_time;
49654359Sroberto		leitch_process(leitch);
49754359Sroberto		leitch->state = STATE_IDLE;
49854359Sroberto		break;
49954359Sroberto	    default:
50054359Sroberto		msyslog(LOG_ERR,
50154359Sroberto			"leitech_receive: invalid state %d unit %d",
50254359Sroberto			leitch->state, leitch->unit);
50354359Sroberto	}
50454359Sroberto}
50554359Sroberto
50654359Sroberto/*
50754359Sroberto * leitch_process - process a pile of samples from the clock
50854359Sroberto *
50954359Sroberto * This routine uses a three-stage median filter to calculate offset and
51054359Sroberto * dispersion. reduce jitter. The dispersion is calculated as the span
51154359Sroberto * of the filter (max - min), unless the quality character (format 2) is
51254359Sroberto * non-blank, in which case the dispersion is calculated on the basis of
51354359Sroberto * the inherent tolerance of the internal radio oscillator, which is
51454359Sroberto * +-2e-5 according to the radio specifications.
51554359Sroberto */
51654359Srobertostatic void
51754359Srobertoleitch_process(
51854359Sroberto	struct leitchunit *leitch
51954359Sroberto	)
52054359Sroberto{
52154359Sroberto	l_fp off;
52254359Sroberto	l_fp tmp_fp;
52354359Sroberto      /*double doffset;*/
52454359Sroberto
52554359Sroberto	off = leitch->reftime1;
52654359Sroberto	L_SUB(&off,&leitch->codetime1);
52754359Sroberto	tmp_fp = leitch->reftime2;
52854359Sroberto	L_SUB(&tmp_fp,&leitch->codetime2);
52954359Sroberto	if (L_ISGEQ(&off,&tmp_fp))
53054359Sroberto	    off = tmp_fp;
53154359Sroberto	tmp_fp = leitch->reftime3;
53254359Sroberto	L_SUB(&tmp_fp,&leitch->codetime3);
53354359Sroberto
53454359Sroberto	if (L_ISGEQ(&off,&tmp_fp))
53554359Sroberto	    off = tmp_fp;
53654359Sroberto      /*LFPTOD(&off, doffset);*/
53754359Sroberto	refclock_receive(leitch->peer);
53854359Sroberto}
53954359Sroberto
54054359Sroberto/*
54154359Sroberto * days_per_year
54254359Sroberto */
54354359Srobertostatic int
54454359Srobertodays_per_year(
54554359Sroberto	int year
54654359Sroberto	)
54754359Sroberto{
54854359Sroberto	if (year%4) {	/* not a potential leap year */
54954359Sroberto		return (365);
55054359Sroberto	} else {
55154359Sroberto		if (year % 100) {	/* is a leap year */
55254359Sroberto			return (366);
55354359Sroberto		} else {
55454359Sroberto			if (year % 400) {
55554359Sroberto				return (365);
55654359Sroberto			} else {
55754359Sroberto				return (366);
55854359Sroberto			}
55954359Sroberto		}
56054359Sroberto	}
56154359Sroberto}
56254359Sroberto
56354359Srobertostatic int
56454359Srobertoleitch_get_date(
56554359Sroberto	struct recvbuf *rbufp,
56654359Sroberto	struct leitchunit *leitch
56754359Sroberto	)
56854359Sroberto{
56954359Sroberto	int i;
57054359Sroberto
57154359Sroberto	if (rbufp->recv_length < 6)
57254359Sroberto	    return(0);
57354359Sroberto#undef  BAD    /* confict: defined as (-1) in AIX sys/param.h */
57454359Sroberto#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
57554359Sroberto	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
57654359Sroberto	    return(0);
57754359Sroberto#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
57854359Sroberto	leitch->year = ATOB(0)*10 + ATOB(1);
57954359Sroberto	leitch->month = ATOB(2)*10 + ATOB(3);
58054359Sroberto	leitch->day = ATOB(4)*10 + ATOB(5);
58154359Sroberto
58254359Sroberto	/* sanity checks */
58354359Sroberto	if (leitch->month > 12)
58454359Sroberto	    return(0);
58554359Sroberto	if (leitch->day > days_in_month[leitch->month-1])
58654359Sroberto	    return(0);
58754359Sroberto
58854359Sroberto	/* calculate yearday */
58954359Sroberto	i = 0;
59054359Sroberto	leitch->yearday = leitch->day;
59154359Sroberto
59254359Sroberto	while ( i < (leitch->month-1) )
59354359Sroberto	    leitch->yearday += days_in_month[i++];
59454359Sroberto
59554359Sroberto	if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
59654359Sroberto	    leitch->month > 2)
59754359Sroberto	    leitch->yearday--;
59854359Sroberto
59954359Sroberto	return(1);
60054359Sroberto}
60154359Sroberto
60254359Sroberto/*
60354359Sroberto * leitch_get_time
60454359Sroberto */
60554359Srobertostatic int
60654359Srobertoleitch_get_time(
60754359Sroberto	struct recvbuf *rbufp,
60854359Sroberto	struct leitchunit *leitch,
60954359Sroberto	int which
61054359Sroberto	)
61154359Sroberto{
61254359Sroberto	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
61354359Sroberto	    return(0);
61454359Sroberto	leitch->hour = ATOB(0)*10 +ATOB(1);
61554359Sroberto	leitch->minute = ATOB(2)*10 +ATOB(3);
61654359Sroberto	leitch->second = ATOB(4)*10 +ATOB(5);
61754359Sroberto
61854359Sroberto	if ((leitch->hour > 23) || (leitch->minute > 60) ||
61954359Sroberto	    (leitch->second > 60))
62054359Sroberto	    return(0);
62154359Sroberto	return(1);
62254359Sroberto}
62354359Sroberto
62454359Sroberto#else
62554359Srobertoint refclock_leitch_bs;
62654359Sroberto#endif /* REFCLOCK */
627