154359Sroberto/* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
254359Sroberto * chutest - test the CHU clock
354359Sroberto */
454359Sroberto
5290001Sglebius#ifdef HAVE_CONFIG_H
6290001Sglebius# include <config.h>
7290001Sglebius#endif
854359Sroberto#include <stdio.h>
9290001Sglebius#include <fcntl.h>
10290001Sglebius#ifdef HAVE_UNISTD_H
11290001Sglebius# include <unistd.h>
12290001Sglebius#endif
13290001Sglebius#ifdef HAVE_STROPTS_H
14290001Sglebius# include <stropts.h>
15290001Sglebius#else
16290001Sglebius# ifdef HAVE_SYS_STROPTS_H
17290001Sglebius#  include <sys/stropts.h>
18290001Sglebius# endif
19290001Sglebius#endif
2054359Sroberto#include <sys/types.h>
2154359Sroberto#include <sys/socket.h>
2254359Sroberto#include <netinet/in.h>
2354359Sroberto#include <sys/ioctl.h>
2454359Sroberto#include <sys/time.h>
2554359Sroberto#include <sys/file.h>
26290001Sglebius#ifdef HAVE_TERMIOS_H
27290001Sglebius# include <termios.h>
28290001Sglebius#else
29290001Sglebius# ifdef HAVE_SGTTY_H
30290001Sglebius#  include <sgtty.h>
31290001Sglebius# endif
32290001Sglebius#endif
3354359Sroberto
34290001Sglebius#include "ntp_fp.h"
35290001Sglebius#include "ntp.h"
36290001Sglebius#include "ntp_unixtime.h"
37290001Sglebius#include "ntp_calendar.h"
3854359Sroberto
3954359Sroberto#ifdef CHULDISC
4054359Sroberto# ifdef HAVE_SYS_CHUDEFS_H
41290001Sglebius#  include <sys/chudefs.h>
42290001Sglebius# endif
4354359Sroberto#endif
4454359Sroberto
4554359Sroberto
4654359Sroberto#ifndef CHULDISC
4754359Sroberto#define	NCHUCHARS	(10)
4854359Sroberto
4954359Srobertostruct chucode {
5054359Sroberto	u_char codechars[NCHUCHARS];	/* code characters */
5154359Sroberto	u_char ncodechars;		/* number of code characters */
5254359Sroberto	u_char chustatus;		/* not used currently */
5354359Sroberto	struct timeval codetimes[NCHUCHARS];	/* arrival times */
5454359Sroberto};
5554359Sroberto#endif
5654359Sroberto
5754359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
5854359Sroberto
59290001Sglebiuschar const *progname;
6054359Sroberto
6154359Srobertoint dofilter = 0;	/* set to 1 when we should run filter algorithm */
6254359Srobertoint showtimes = 0;	/* set to 1 when we should show char arrival times */
6354359Srobertoint doprocess = 0;	/* set to 1 when we do processing analogous to driver */
6454359Sroberto#ifdef CHULDISC
6554359Srobertoint usechuldisc = 0;	/* set to 1 when CHU line discipline should be used */
6654359Sroberto#endif
6754359Sroberto#ifdef STREAM
6854359Srobertoint usechuldisc = 0;	/* set to 1 when CHU line discipline should be used */
6954359Sroberto#endif
7054359Sroberto
7154359Srobertostruct timeval lasttv;
7254359Srobertostruct chucode chudata;
7354359Sroberto
74290001Sglebiusvoid	error(char *fmt, char *s1, char *s2);
75290001Sglebiusvoid	init_chu(void);
76290001Sglebiusint	openterm(char *dev);
77290001Sglebiusint	process_raw(int s);
78290001Sglebiusint	process_ldisc(int s);
79290001Sglebiusvoid	raw_filter(unsigned int c, struct timeval *tv);
80290001Sglebiusvoid	chufilter(struct chucode *chuc,	l_fp *rtime);
8154359Sroberto
82290001Sglebius
8354359Sroberto/*
8454359Sroberto * main - parse arguments and handle options
8554359Sroberto */
8654359Srobertoint
8754359Srobertomain(
8854359Sroberto	int argc,
8954359Sroberto	char *argv[]
9054359Sroberto	)
9154359Sroberto{
9254359Sroberto	int c;
9354359Sroberto	int errflg = 0;
9454359Sroberto	extern int ntp_optind;
9554359Sroberto
9654359Sroberto	progname = argv[0];
9754359Sroberto	while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
9854359Sroberto	    switch (c) {
9954359Sroberto		case 'c':
10054359Sroberto#ifdef STREAM
10154359Sroberto		    usechuldisc = 1;
10254359Sroberto		    break;
10354359Sroberto#endif
10454359Sroberto#ifdef CHULDISC
10554359Sroberto		    usechuldisc = 1;
10654359Sroberto		    break;
10754359Sroberto#endif
10854359Sroberto#ifndef STREAM
10954359Sroberto#ifndef CHULDISC
11054359Sroberto		    (void) fprintf(stderr,
11154359Sroberto				   "%s: CHU line discipline not available on this machine\n",
11254359Sroberto				   progname);
11354359Sroberto		    exit(2);
11454359Sroberto#endif
11554359Sroberto#endif
11654359Sroberto		case 'd':
11754359Sroberto		    ++debug;
11854359Sroberto		    break;
11954359Sroberto		case 'f':
12054359Sroberto		    dofilter = 1;
12154359Sroberto		    break;
12254359Sroberto		case 'p':
12354359Sroberto		    doprocess = 1;
12454359Sroberto		case 't':
12554359Sroberto		    showtimes = 1;
12654359Sroberto		    break;
12754359Sroberto		default:
12854359Sroberto		    errflg++;
12954359Sroberto		    break;
13054359Sroberto	    }
13154359Sroberto	if (errflg || ntp_optind+1 != argc) {
13254359Sroberto#ifdef STREAM
13354359Sroberto		(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
13454359Sroberto			       progname);
13554359Sroberto#endif
13654359Sroberto#ifdef CHULDISC
13754359Sroberto		(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
13854359Sroberto			       progname);
13954359Sroberto#endif
14054359Sroberto#ifndef STREAM
14154359Sroberto#ifndef CHULDISC
14254359Sroberto		(void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
14354359Sroberto			       progname);
14454359Sroberto#endif
14554359Sroberto#endif
14654359Sroberto		exit(2);
14754359Sroberto	}
14854359Sroberto
14954359Sroberto	(void) gettimeofday(&lasttv, (struct timezone *)0);
15054359Sroberto	c = openterm(argv[ntp_optind]);
15154359Sroberto	init_chu();
15254359Sroberto#ifdef STREAM
15354359Sroberto	if (usechuldisc)
15454359Sroberto	    process_ldisc(c);
15554359Sroberto	else
15654359Sroberto#endif
15754359Sroberto#ifdef CHULDISC
15854359Sroberto	    if (usechuldisc)
15954359Sroberto		process_ldisc(c);
16054359Sroberto	    else
16154359Sroberto#endif
16254359Sroberto		process_raw(c);
16354359Sroberto	/*NOTREACHED*/
16454359Sroberto}
16554359Sroberto
16654359Sroberto
16754359Sroberto/*
16854359Sroberto * openterm - open a port to the CHU clock
16954359Sroberto */
17054359Srobertoint
17154359Srobertoopenterm(
17254359Sroberto	char *dev
17354359Sroberto	)
17454359Sroberto{
17554359Sroberto	int s;
17654359Sroberto	struct sgttyb ttyb;
17754359Sroberto
17854359Sroberto	if (debug)
17954359Sroberto	    (void) fprintf(stderr, "Doing open...");
18054359Sroberto	if ((s = open(dev, O_RDONLY, 0777)) < 0)
18154359Sroberto	    error("open(%s)", dev, "");
18254359Sroberto	if (debug)
18354359Sroberto	    (void) fprintf(stderr, "open okay\n");
18454359Sroberto
18554359Sroberto	if (debug)
18654359Sroberto	    (void) fprintf(stderr, "Setting exclusive use...");
18754359Sroberto	if (ioctl(s, TIOCEXCL, (char *)0) < 0)
18854359Sroberto	    error("ioctl(TIOCEXCL)", "", "");
18954359Sroberto	if (debug)
19054359Sroberto	    (void) fprintf(stderr, "done\n");
19154359Sroberto
19254359Sroberto	ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
19354359Sroberto	ttyb.sg_erase = ttyb.sg_kill = 0;
19454359Sroberto	ttyb.sg_flags = EVENP|ODDP|RAW;
19554359Sroberto	if (debug)
19654359Sroberto	    (void) fprintf(stderr, "Setting baud rate et al...");
19754359Sroberto	if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
19854359Sroberto	    error("ioctl(TIOCSETP, raw)", "", "");
19954359Sroberto	if (debug)
20054359Sroberto	    (void) fprintf(stderr, "done\n");
20154359Sroberto
20254359Sroberto#ifdef CHULDISC
20354359Sroberto	if (usechuldisc) {
20454359Sroberto		int ldisc;
20554359Sroberto
20654359Sroberto		if (debug)
20754359Sroberto		    (void) fprintf(stderr, "Switching to CHU ldisc...");
20854359Sroberto		ldisc = CHULDISC;
20954359Sroberto		if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
21054359Sroberto		    error("ioctl(TIOCSETD, CHULDISC)", "", "");
21154359Sroberto		if (debug)
21254359Sroberto		    (void) fprintf(stderr, "okay\n");
21354359Sroberto	}
21454359Sroberto#endif
21554359Sroberto#ifdef STREAM
21654359Sroberto	if (usechuldisc) {
21754359Sroberto
21854359Sroberto		if (debug)
21954359Sroberto		    (void) fprintf(stderr, "Poping off streams...");
22054359Sroberto		while (ioctl(s, I_POP, 0) >=0) ;
22154359Sroberto		if (debug)
22254359Sroberto		    (void) fprintf(stderr, "okay\n");
22354359Sroberto		if (debug)
22454359Sroberto		    (void) fprintf(stderr, "Pushing CHU stream...");
22554359Sroberto		if (ioctl(s, I_PUSH, "chu") < 0)
22654359Sroberto		    error("ioctl(I_PUSH, \"chu\")", "", "");
22754359Sroberto		if (debug)
22854359Sroberto		    (void) fprintf(stderr, "okay\n");
22954359Sroberto	}
23054359Sroberto#endif
23154359Sroberto	return s;
23254359Sroberto}
23354359Sroberto
23454359Sroberto
23554359Sroberto/*
23654359Sroberto * process_raw - process characters in raw mode
23754359Sroberto */
23854359Srobertoint
23954359Srobertoprocess_raw(
24054359Sroberto	int s
24154359Sroberto	)
24254359Sroberto{
24354359Sroberto	u_char c;
24454359Sroberto	int n;
24554359Sroberto	struct timeval tv;
24654359Sroberto	struct timeval difftv;
24754359Sroberto
24854359Sroberto	while ((n = read(s, &c, sizeof(char))) > 0) {
24954359Sroberto		(void) gettimeofday(&tv, (struct timezone *)0);
25054359Sroberto		if (dofilter)
25154359Sroberto		    raw_filter((unsigned int)c, &tv);
25254359Sroberto		else {
25354359Sroberto			difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
25454359Sroberto			difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
25554359Sroberto			if (difftv.tv_usec < 0) {
25654359Sroberto				difftv.tv_sec--;
25754359Sroberto				difftv.tv_usec += 1000000;
25854359Sroberto			}
25954359Sroberto			(void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
26054359Sroberto				      c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
26154359Sroberto				      difftv.tv_usec);
26254359Sroberto			lasttv = tv;
26354359Sroberto		}
26454359Sroberto	}
26554359Sroberto
26654359Sroberto	if (n == 0) {
26754359Sroberto		(void) fprintf(stderr, "%s: zero returned on read\n", progname);
26854359Sroberto		exit(1);
26954359Sroberto	} else
27054359Sroberto	    error("read()", "", "");
27154359Sroberto}
27254359Sroberto
27354359Sroberto
27454359Sroberto/*
27554359Sroberto * raw_filter - run the line discipline filter over raw data
27654359Sroberto */
277290001Sglebiusvoid
27854359Srobertoraw_filter(
27954359Sroberto	unsigned int c,
28054359Sroberto	struct timeval *tv
28154359Sroberto	)
28254359Sroberto{
283290001Sglebius	static struct timeval diffs[10];
28454359Sroberto	struct timeval diff;
28554359Sroberto	l_fp ts;
28654359Sroberto
28754359Sroberto	if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
28854359Sroberto		if (debug)
28954359Sroberto		    (void) fprintf(stderr,
290290001Sglebius				   "character %02x failed BCD test\n", c);
29154359Sroberto		chudata.ncodechars = 0;
29254359Sroberto		return;
29354359Sroberto	}
29454359Sroberto
29554359Sroberto	if (chudata.ncodechars > 0) {
29654359Sroberto		diff.tv_sec = tv->tv_sec
29754359Sroberto			- chudata.codetimes[chudata.ncodechars].tv_sec;
29854359Sroberto		diff.tv_usec = tv->tv_usec
29954359Sroberto			- chudata.codetimes[chudata.ncodechars].tv_usec;
30054359Sroberto		if (diff.tv_usec < 0) {
30154359Sroberto			diff.tv_sec--;
30254359Sroberto			diff.tv_usec += 1000000;
30354359Sroberto		} /*
30454359Sroberto		    if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
30554359Sroberto		    if (debug)
30654359Sroberto		    (void) fprintf(stderr,
30754359Sroberto		    "character %02x failed time test\n");
30854359Sroberto		    chudata.ncodechars = 0;
30954359Sroberto		    return;
31054359Sroberto		    } */
31154359Sroberto	}
31254359Sroberto
31354359Sroberto	chudata.codechars[chudata.ncodechars] = c;
31454359Sroberto	chudata.codetimes[chudata.ncodechars] = *tv;
31554359Sroberto	if (chudata.ncodechars > 0)
31654359Sroberto	    diffs[chudata.ncodechars] = diff;
31754359Sroberto	if (++chudata.ncodechars == 10) {
31854359Sroberto		if (doprocess) {
31954359Sroberto			TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
32054359Sroberto			ts.l_ui += JAN_1970;
32154359Sroberto			chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
32254359Sroberto		} else {
32354359Sroberto			register int i;
32454359Sroberto
32554359Sroberto			for (i = 0; i < chudata.ncodechars; i++) {
32654359Sroberto				(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
32754359Sroberto					      chudata.codechars[i] & 0xf,
32854359Sroberto					      (chudata.codechars[i] >>4 ) & 0xf,
32954359Sroberto					      chudata.codetimes[i].tv_sec,
33054359Sroberto					      chudata.codetimes[i].tv_usec,
33154359Sroberto					      diffs[i].tv_sec, diffs[i].tv_usec);
33254359Sroberto			}
33354359Sroberto		}
33454359Sroberto		chudata.ncodechars = 0;
33554359Sroberto	}
33654359Sroberto}
33754359Sroberto
33854359Sroberto
33954359Sroberto/* #ifdef CHULDISC*/
34054359Sroberto/*
34154359Sroberto * process_ldisc - process line discipline
34254359Sroberto */
34354359Srobertoint
34454359Srobertoprocess_ldisc(
34554359Sroberto	int s
34654359Sroberto	)
34754359Sroberto{
34854359Sroberto	struct chucode chu;
34954359Sroberto	int n;
35054359Sroberto	register int i;
35154359Sroberto	struct timeval diff;
35254359Sroberto	l_fp ts;
35354359Sroberto	void chufilter();
35454359Sroberto
35554359Sroberto	while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
35654359Sroberto		if (n != sizeof chu) {
35754359Sroberto			(void) fprintf(stderr, "Expected %d, got %d\n",
35854359Sroberto				       sizeof chu, n);
35954359Sroberto			continue;
36054359Sroberto		}
36154359Sroberto
36254359Sroberto		if (doprocess) {
36354359Sroberto			TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
36454359Sroberto			ts.l_ui += JAN_1970;
36554359Sroberto			chufilter(&chu, &ts);
36654359Sroberto		} else {
36754359Sroberto			for (i = 0; i < NCHUCHARS; i++) {
36854359Sroberto				if (i == 0)
36954359Sroberto				    diff.tv_sec = diff.tv_usec = 0;
37054359Sroberto				else {
37154359Sroberto					diff.tv_sec = chu.codetimes[i].tv_sec
37254359Sroberto						- chu.codetimes[i-1].tv_sec;
37354359Sroberto					diff.tv_usec = chu.codetimes[i].tv_usec
37454359Sroberto						- chu.codetimes[i-1].tv_usec;
37554359Sroberto					if (diff.tv_usec < 0) {
37654359Sroberto						diff.tv_sec--;
37754359Sroberto						diff.tv_usec += 1000000;
37854359Sroberto					}
37954359Sroberto				}
38054359Sroberto				(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
38154359Sroberto					      chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
38254359Sroberto					      chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
38354359Sroberto					      diff.tv_sec, diff.tv_usec);
38454359Sroberto			}
38554359Sroberto		}
38654359Sroberto	}
38754359Sroberto	if (n == 0) {
38854359Sroberto		(void) fprintf(stderr, "%s: zero returned on read\n", progname);
38954359Sroberto		exit(1);
39054359Sroberto	} else
39154359Sroberto	    error("read()", "", "");
39254359Sroberto}
39354359Sroberto/*#endif*/
39454359Sroberto
39554359Sroberto
39654359Sroberto/*
39754359Sroberto * error - print an error message
39854359Sroberto */
39954359Srobertovoid
40054359Srobertoerror(
40154359Sroberto	char *fmt,
40254359Sroberto	char *s1,
40354359Sroberto	char *s2
40454359Sroberto	)
40554359Sroberto{
40654359Sroberto	(void) fprintf(stderr, "%s: ", progname);
40754359Sroberto	(void) fprintf(stderr, fmt, s1, s2);
40854359Sroberto	(void) fprintf(stderr, ": ");
40954359Sroberto	perror("");
41054359Sroberto	exit(1);
41154359Sroberto}
41254359Sroberto
41354359Sroberto/*
41454359Sroberto * Definitions
41554359Sroberto */
41654359Sroberto#define	MAXUNITS	4	/* maximum number of CHU units permitted */
41754359Sroberto#define	CHUDEV	"/dev/chu%d"	/* device we open.  %d is unit number */
41854359Sroberto#define	NCHUCODES	9	/* expect 9 CHU codes per minute */
41954359Sroberto
42054359Sroberto/*
42154359Sroberto * When CHU is operating optimally we want the primary clock distance
42254359Sroberto * to come out at 300 ms.  Thus, peer.distance in the CHU peer structure
42354359Sroberto * is set to 290 ms and we compute delays which are at least 10 ms long.
42454359Sroberto * The following are 290 ms and 10 ms expressed in u_fp format
42554359Sroberto */
42654359Sroberto#define	CHUDISTANCE	0x00004a3d
42754359Sroberto#define	CHUBASEDELAY	0x0000028f
42854359Sroberto
42954359Sroberto/*
43054359Sroberto * To compute a quality for the estimate (a pseudo delay) we add a
43154359Sroberto * fixed 10 ms for each missing code in the minute and add to this
43254359Sroberto * the sum of the differences between the remaining offsets and the
43354359Sroberto * estimated sample offset.
43454359Sroberto */
43554359Sroberto#define	CHUDELAYPENALTY	0x0000028f
43654359Sroberto
43754359Sroberto/*
43854359Sroberto * Other constant stuff
43954359Sroberto */
44054359Sroberto#define	CHUPRECISION	(-9)		/* what the heck */
44154359Sroberto#define	CHUREFID	"CHU\0"
44254359Sroberto
44354359Sroberto/*
44454359Sroberto * Default fudge factors
44554359Sroberto */
44654359Sroberto#define	DEFPROPDELAY	0x00624dd3	/* 0.0015 seconds, 1.5 ms */
44754359Sroberto#define	DEFFILTFUDGE	0x000d1b71	/* 0.0002 seconds, 200 us */
44854359Sroberto
44954359Sroberto/*
45054359Sroberto * Hacks to avoid excercising the multiplier.  I have no pride.
45154359Sroberto */
45254359Sroberto#define	MULBY10(x)	(((x)<<3) + ((x)<<1))
45354359Sroberto#define	MULBY60(x)	(((x)<<6) - ((x)<<2))	/* watch overflow */
45454359Sroberto#define	MULBY24(x)	(((x)<<4) + ((x)<<3))
45554359Sroberto
45654359Sroberto/*
45754359Sroberto * Constants for use when multiplying by 0.1.  ZEROPTONE is 0.1
45854359Sroberto * as an l_fp fraction, NZPOBITS is the number of significant bits
45954359Sroberto * in ZEROPTONE.
46054359Sroberto */
46154359Sroberto#define	ZEROPTONE	0x1999999a
46254359Sroberto#define	NZPOBITS	29
46354359Sroberto
46454359Sroberto/*
46554359Sroberto * The CHU table.  This gives the expected time of arrival of each
46654359Sroberto * character after the on-time second and is computed as follows:
46754359Sroberto * The CHU time code is sent at 300 bps.  Your average UART will
46854359Sroberto * synchronize at the edge of the start bit and will consider the
46954359Sroberto * character complete at the center of the first stop bit, i.e.
47054359Sroberto * 0.031667 ms later.  Thus the expected time of each interrupt
47154359Sroberto * is the start bit time plus 0.031667 seconds.  These times are
47254359Sroberto * in chutable[].  To this we add such things as propagation delay
47354359Sroberto * and delay fudge factor.
47454359Sroberto */
47554359Sroberto#define	CHARDELAY	0x081b4e80
47654359Sroberto
47754359Srobertostatic u_long chutable[NCHUCHARS] = {
47854359Sroberto	0x2147ae14 + CHARDELAY,		/* 0.130 (exactly) */
47954359Sroberto	0x2ac08312 + CHARDELAY,		/* 0.167 (exactly) */
48054359Sroberto	0x34395810 + CHARDELAY,		/* 0.204 (exactly) */
48154359Sroberto	0x3db22d0e + CHARDELAY,		/* 0.241 (exactly) */
48254359Sroberto	0x472b020c + CHARDELAY,		/* 0.278 (exactly) */
48354359Sroberto	0x50a3d70a + CHARDELAY,		/* 0.315 (exactly) */
48454359Sroberto	0x5a1cac08 + CHARDELAY,		/* 0.352 (exactly) */
48554359Sroberto	0x63958106 + CHARDELAY,		/* 0.389 (exactly) */
48654359Sroberto	0x6d0e5604 + CHARDELAY,		/* 0.426 (exactly) */
48754359Sroberto	0x76872b02 + CHARDELAY,		/* 0.463 (exactly) */
48854359Sroberto};
48954359Sroberto
49054359Sroberto/*
49154359Sroberto * Keep the fudge factors separately so they can be set even
49254359Sroberto * when no clock is configured.
49354359Sroberto */
49454359Srobertostatic l_fp propagation_delay;
49554359Srobertostatic l_fp fudgefactor;
49654359Srobertostatic l_fp offset_fudge;
49754359Sroberto
49854359Sroberto/*
49954359Sroberto * We keep track of the start of the year, watching for changes.
50054359Sroberto * We also keep track of whether the year is a leap year or not.
50154359Sroberto * All because stupid CHU doesn't include the year in the time code.
50254359Sroberto */
50354359Srobertostatic u_long yearstart;
50454359Sroberto
50554359Sroberto/*
50654359Sroberto * Imported from the timer module
50754359Sroberto */
50854359Srobertoextern u_long current_time;
50954359Srobertoextern struct event timerqueue[];
51054359Sroberto
51154359Sroberto/*
51254359Sroberto * init_chu - initialize internal chu driver data
51354359Sroberto */
51454359Srobertovoid
51554359Srobertoinit_chu(void)
51654359Sroberto{
51754359Sroberto
51854359Sroberto	/*
51954359Sroberto	 * Initialize fudge factors to default.
52054359Sroberto	 */
52154359Sroberto	propagation_delay.l_ui = 0;
52254359Sroberto	propagation_delay.l_uf = DEFPROPDELAY;
52354359Sroberto	fudgefactor.l_ui = 0;
52454359Sroberto	fudgefactor.l_uf = DEFFILTFUDGE;
52554359Sroberto	offset_fudge = propagation_delay;
52654359Sroberto	L_ADD(&offset_fudge, &fudgefactor);
52754359Sroberto
52854359Sroberto	yearstart = 0;
52954359Sroberto}
53054359Sroberto
53154359Sroberto
53254359Srobertovoid
53354359Srobertochufilter(
53454359Sroberto	struct chucode *chuc,
53554359Sroberto	l_fp *rtime
53654359Sroberto	)
53754359Sroberto{
53854359Sroberto	register int i;
53954359Sroberto	register u_long date_ui;
54054359Sroberto	register u_long tmp;
54154359Sroberto	register u_char *code;
54254359Sroberto	int isneg;
54354359Sroberto	int imin;
54454359Sroberto	int imax;
54554359Sroberto	u_long reftime;
54654359Sroberto	l_fp off[NCHUCHARS];
54754359Sroberto	l_fp ts;
54854359Sroberto	int day, hour, minute, second;
54954359Sroberto	static u_char lastcode[NCHUCHARS];
55054359Sroberto
55154359Sroberto	/*
55254359Sroberto	 * We'll skip the checks made in the kernel, but assume they've
55354359Sroberto	 * been done.  This means that all characters are BCD and
55454359Sroberto	 * the intercharacter spacing isn't unreasonable.
55554359Sroberto	 */
55654359Sroberto
55754359Sroberto	/*
55854359Sroberto	 * print the code
55954359Sroberto	 */
56054359Sroberto	for (i = 0; i < NCHUCHARS; i++)
56154359Sroberto	    printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
56254359Sroberto		   ((chuc->codechars[i]>>4) & 0xf) + '0');
56354359Sroberto	printf("\n");
56454359Sroberto
56554359Sroberto	/*
56654359Sroberto	 * Format check.  Make sure the two halves match.
56754359Sroberto	 */
56854359Sroberto	for (i = 0; i < NCHUCHARS/2; i++)
56954359Sroberto	    if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
57054359Sroberto		    (void) printf("Bad format, halves don't match\n");
57154359Sroberto		    return;
57254359Sroberto	    }
57354359Sroberto
57454359Sroberto	/*
57554359Sroberto	 * Break out the code into the BCD nibbles.  Only need to fiddle
57654359Sroberto	 * with the first half since both are identical.  Note the first
57754359Sroberto	 * BCD character is the low order nibble, the second the high order.
57854359Sroberto	 */
57954359Sroberto	code = lastcode;
58054359Sroberto	for (i = 0; i < NCHUCHARS/2; i++) {
58154359Sroberto		*code++ = chuc->codechars[i] & 0xf;
58254359Sroberto		*code++ = (chuc->codechars[i] >> 4) & 0xf;
58354359Sroberto	}
58454359Sroberto
58554359Sroberto	/*
58654359Sroberto	 * If the first nibble isn't a 6, we're up the creek
58754359Sroberto	 */
58854359Sroberto	code = lastcode;
58954359Sroberto	if (*code++ != 6) {
59054359Sroberto		(void) printf("Bad format, no 6 at start\n");
59154359Sroberto		return;
59254359Sroberto	}
59354359Sroberto
59454359Sroberto	/*
59554359Sroberto	 * Collect the day, the hour, the minute and the second.
59654359Sroberto	 */
59754359Sroberto	day = *code++;
59854359Sroberto	day = MULBY10(day) + *code++;
59954359Sroberto	day = MULBY10(day) + *code++;
60054359Sroberto	hour = *code++;
60154359Sroberto	hour = MULBY10(hour) + *code++;
60254359Sroberto	minute = *code++;
60354359Sroberto	minute = MULBY10(minute) + *code++;
60454359Sroberto	second = *code++;
60554359Sroberto	second = MULBY10(second) + *code++;
60654359Sroberto
60754359Sroberto	/*
60854359Sroberto	 * Sanity check the day and time.  Note that this
60954359Sroberto	 * only occurs on the 31st through the 39th second
61054359Sroberto	 * of the minute.
61154359Sroberto	 */
61254359Sroberto	if (day < 1 || day > 366
61354359Sroberto	    || hour > 23 || minute > 59
61454359Sroberto	    || second < 31 || second > 39) {
61554359Sroberto		(void) printf("Failed date sanity check: %d %d %d %d\n",
61654359Sroberto			      day, hour, minute, second);
61754359Sroberto		return;
61854359Sroberto	}
61954359Sroberto
62054359Sroberto	/*
62154359Sroberto	 * Compute seconds into the year.
62254359Sroberto	 */
62354359Sroberto	tmp = (u_long)(MULBY24((day-1)) + hour);	/* hours */
62454359Sroberto	tmp = MULBY60(tmp) + (u_long)minute;		/* minutes */
62554359Sroberto	tmp = MULBY60(tmp) + (u_long)second;		/* seconds */
62654359Sroberto
62754359Sroberto	/*
62854359Sroberto	 * Now the fun begins.  We demand that the received time code
62954359Sroberto	 * be within CLOCK_WAYTOOBIG of the receive timestamp, but
63054359Sroberto	 * there is uncertainty about the year the timestamp is in.
63154359Sroberto	 * Use the current year start for the first check, this should
63254359Sroberto	 * work most of the time.
63354359Sroberto	 */
63454359Sroberto	date_ui = tmp + yearstart;
635290001Sglebius#define CLOCK_WAYTOOBIG 1000 /* revived from ancient sources */
63654359Sroberto	if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
63754359Sroberto	    && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
63854359Sroberto	    goto codeokay;	/* looks good */
63954359Sroberto
64054359Sroberto	/*
64154359Sroberto	 * Trouble.  Next check is to see if the year rolled over and, if
64254359Sroberto	 * so, try again with the new year's start.
64354359Sroberto	 */
644290001Sglebius	date_ui = calyearstart(rtime->l_ui, NULL);
64554359Sroberto	if (date_ui != yearstart) {
64654359Sroberto		yearstart = date_ui;
64754359Sroberto		date_ui += tmp;
64854359Sroberto		(void) printf("time %u, code %u, difference %d\n",
64954359Sroberto			      date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
65054359Sroberto		if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
65154359Sroberto		    && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
65254359Sroberto		    goto codeokay;	/* okay this time */
65354359Sroberto	}
65454359Sroberto
65554359Sroberto	ts.l_uf = 0;
65654359Sroberto	ts.l_ui = yearstart;
65754359Sroberto	printf("yearstart %s\n", prettydate(&ts));
65854359Sroberto	printf("received %s\n", prettydate(rtime));
65954359Sroberto	ts.l_ui = date_ui;
66054359Sroberto	printf("date_ui %s\n", prettydate(&ts));
66154359Sroberto
66254359Sroberto	/*
66354359Sroberto	 * Here we know the year start matches the current system
66454359Sroberto	 * time.  One remaining possibility is that the time code
66554359Sroberto	 * is in the year previous to that of the system time.  This
66654359Sroberto	 * is only worth checking if the receive timestamp is less
66754359Sroberto	 * than CLOCK_WAYTOOBIG seconds into the new year.
66854359Sroberto	 */
66954359Sroberto	if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
670290001Sglebius		date_ui = tmp;
671290001Sglebius		date_ui += calyearstart(yearstart - CLOCK_WAYTOOBIG,
672290001Sglebius					NULL);
67354359Sroberto		if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
67454359Sroberto		    goto codeokay;
67554359Sroberto	}
67654359Sroberto
67754359Sroberto	/*
67854359Sroberto	 * One last possibility is that the time stamp is in the year
67954359Sroberto	 * following the year the system is in.  Try this one before
68054359Sroberto	 * giving up.
68154359Sroberto	 */
682290001Sglebius	date_ui = tmp;
683290001Sglebius	date_ui += calyearstart(yearstart + (400 * SECSPERDAY),
684290001Sglebius				NULL);
68554359Sroberto	if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
68654359Sroberto		printf("Date hopelessly off\n");
68754359Sroberto		return;		/* hopeless, let it sync to other peers */
68854359Sroberto	}
68954359Sroberto
69054359Sroberto    codeokay:
69154359Sroberto	reftime = date_ui;
69254359Sroberto	/*
69354359Sroberto	 * We've now got the integral seconds part of the time code (we hope).
69454359Sroberto	 * The fractional part comes from the table.  We next compute
69554359Sroberto	 * the offsets for each character.
69654359Sroberto	 */
69754359Sroberto	for (i = 0; i < NCHUCHARS; i++) {
69854359Sroberto		register u_long tmp2;
69954359Sroberto
70054359Sroberto		off[i].l_ui = date_ui;
70154359Sroberto		off[i].l_uf = chutable[i];
70254359Sroberto		tmp = chuc->codetimes[i].tv_sec + JAN_1970;
70354359Sroberto		TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
70454359Sroberto		M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
70554359Sroberto	}
70654359Sroberto
70754359Sroberto	/*
70854359Sroberto	 * Here is a *big* problem.  What one would normally
70954359Sroberto	 * do here on a machine with lots of clock bits (say
71054359Sroberto	 * a Vax or the gizmo board) is pick the most positive
71154359Sroberto	 * offset and the estimate, since this is the one that
71254359Sroberto	 * is most likely suffered the smallest interrupt delay.
71354359Sroberto	 * The trouble is that the low order clock bit on an IBM
71454359Sroberto	 * RT, which is the machine I had in mind when doing this,
71554359Sroberto	 * ticks at just under the millisecond mark.  This isn't
71654359Sroberto	 * precise enough.  What we can do to improve this is to
71754359Sroberto	 * average all 10 samples and rely on the second level
71854359Sroberto	 * filtering to pick the least delayed estimate.  Trouble
71954359Sroberto	 * is, this means we have to divide a 64 bit fixed point
72054359Sroberto	 * number by 10, a procedure which really sucks.  Oh, well.
72154359Sroberto	 * First compute the sum.
72254359Sroberto	 */
72354359Sroberto	date_ui = 0;
72454359Sroberto	tmp = 0;
72554359Sroberto	for (i = 0; i < NCHUCHARS; i++)
72654359Sroberto	    M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
72754359Sroberto	if (M_ISNEG(date_ui, tmp))
72854359Sroberto	    isneg = 1;
72954359Sroberto	else
73054359Sroberto	    isneg = 0;
73154359Sroberto
73254359Sroberto	/*
73354359Sroberto	 * Here is a multiply-by-0.1 optimization that should apply
73454359Sroberto	 * just about everywhere.  If the magnitude of the sum
73554359Sroberto	 * is less than 9 we don't have to worry about overflow
73654359Sroberto	 * out of a 64 bit product, even after rounding.
73754359Sroberto	 */
73854359Sroberto	if (date_ui < 9 || date_ui > 0xfffffff7) {
73954359Sroberto		register u_long prod_ui;
74054359Sroberto		register u_long prod_uf;
74154359Sroberto
74254359Sroberto		prod_ui = prod_uf = 0;
74354359Sroberto		/*
74454359Sroberto		 * This code knows the low order bit in 0.1 is zero
74554359Sroberto		 */
74654359Sroberto		for (i = 1; i < NZPOBITS; i++) {
74754359Sroberto			M_LSHIFT(date_ui, tmp);
74854359Sroberto			if (ZEROPTONE & (1<<i))
74954359Sroberto			    M_ADD(prod_ui, prod_uf, date_ui, tmp);
75054359Sroberto		}
75154359Sroberto
75254359Sroberto		/*
75354359Sroberto		 * Done, round it correctly.  Prod_ui contains the
75454359Sroberto		 * fraction.
75554359Sroberto		 */
75654359Sroberto		if (prod_uf & 0x80000000)
75754359Sroberto		    prod_ui++;
75854359Sroberto		if (isneg)
75954359Sroberto		    date_ui = 0xffffffff;
76054359Sroberto		else
76154359Sroberto		    date_ui = 0;
76254359Sroberto		tmp = prod_ui;
76354359Sroberto		/*
76454359Sroberto		 * date_ui is integral part, tmp is fraction.
76554359Sroberto		 */
76654359Sroberto	} else {
76754359Sroberto		register u_long prod_ovr;
76854359Sroberto		register u_long prod_ui;
76954359Sroberto		register u_long prod_uf;
77054359Sroberto		register u_long highbits;
77154359Sroberto
77254359Sroberto		prod_ovr = prod_ui = prod_uf = 0;
77354359Sroberto		if (isneg)
77454359Sroberto		    highbits = 0xffffffff;	/* sign extend */
77554359Sroberto		else
77654359Sroberto		    highbits = 0;
77754359Sroberto		/*
77854359Sroberto		 * This code knows the low order bit in 0.1 is zero
77954359Sroberto		 */
78054359Sroberto		for (i = 1; i < NZPOBITS; i++) {
78154359Sroberto			M_LSHIFT3(highbits, date_ui, tmp);
78254359Sroberto			if (ZEROPTONE & (1<<i))
78354359Sroberto			    M_ADD3(prod_ovr, prod_uf, prod_ui,
78454359Sroberto				   highbits, date_ui, tmp);
78554359Sroberto		}
78654359Sroberto
78754359Sroberto		if (prod_uf & 0x80000000)
78854359Sroberto		    M_ADDUF(prod_ovr, prod_ui, (u_long)1);
78954359Sroberto		date_ui = prod_ovr;
79054359Sroberto		tmp = prod_ui;
79154359Sroberto	}
79254359Sroberto
79354359Sroberto	/*
79454359Sroberto	 * At this point we have the mean offset, with the integral
79554359Sroberto	 * part in date_ui and the fractional part in tmp.  Store
79654359Sroberto	 * it in the structure.
79754359Sroberto	 */
79854359Sroberto	/*
79954359Sroberto	 * Add in fudge factor.
80054359Sroberto	 */
80154359Sroberto	M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
80254359Sroberto
80354359Sroberto	/*
80454359Sroberto	 * Find the minimun and maximum offset
80554359Sroberto	 */
80654359Sroberto	imin = imax = 0;
80754359Sroberto	for (i = 1; i < NCHUCHARS; i++) {
80854359Sroberto		if (L_ISGEQ(&off[i], &off[imax])) {
80954359Sroberto			imax = i;
81054359Sroberto		} else if (L_ISGEQ(&off[imin], &off[i])) {
81154359Sroberto			imin = i;
81254359Sroberto		}
81354359Sroberto	}
81454359Sroberto
81554359Sroberto	L_ADD(&off[imin], &offset_fudge);
81654359Sroberto	if (imin != imax)
81754359Sroberto	    L_ADD(&off[imax], &offset_fudge);
81854359Sroberto	(void) printf("mean %s, min %s, max %s\n",
81954359Sroberto		      mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
82054359Sroberto		      lfptoa(&off[imax], 8));
82154359Sroberto}
822