xref: /netbsd-src/external/bsd/ntp/dist/clockstuff/chutest.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
1*cdfa2a7eSchristos /*	$NetBSD: chutest.c,v 1.6 2020/05/25 20:47:19 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
4abb0f93cSkardel  * chutest - test the CHU clock
5abb0f93cSkardel  */
6abb0f93cSkardel 
78585484eSchristos #ifdef HAVE_CONFIG_H
88585484eSchristos # include <config.h>
98585484eSchristos #endif
10abb0f93cSkardel #include <stdio.h>
118585484eSchristos #include <fcntl.h>
128585484eSchristos #ifdef HAVE_UNISTD_H
138585484eSchristos # include <unistd.h>
148585484eSchristos #endif
158585484eSchristos #ifdef HAVE_STROPTS_H
168585484eSchristos # include <stropts.h>
178585484eSchristos #else
188585484eSchristos # ifdef HAVE_SYS_STROPTS_H
198585484eSchristos #  include <sys/stropts.h>
208585484eSchristos # endif
218585484eSchristos #endif
22abb0f93cSkardel #include <sys/types.h>
23abb0f93cSkardel #include <sys/socket.h>
24abb0f93cSkardel #include <netinet/in.h>
25abb0f93cSkardel #include <sys/ioctl.h>
26abb0f93cSkardel #include <sys/time.h>
27abb0f93cSkardel #include <sys/file.h>
288585484eSchristos #ifdef HAVE_TERMIOS_H
298585484eSchristos # include <termios.h>
308585484eSchristos #else
318585484eSchristos # ifdef HAVE_SGTTY_H
32abb0f93cSkardel #  include <sgtty.h>
338585484eSchristos # endif
348585484eSchristos #endif
35abb0f93cSkardel 
368585484eSchristos #include "ntp_fp.h"
378585484eSchristos #include "ntp.h"
388585484eSchristos #include "ntp_unixtime.h"
398585484eSchristos #include "ntp_calendar.h"
40abb0f93cSkardel 
41abb0f93cSkardel #ifdef CHULDISC
42abb0f93cSkardel # ifdef HAVE_SYS_CHUDEFS_H
43abb0f93cSkardel #  include <sys/chudefs.h>
44abb0f93cSkardel # endif
45abb0f93cSkardel #endif
46abb0f93cSkardel 
478585484eSchristos 
48abb0f93cSkardel #ifndef CHULDISC
49abb0f93cSkardel #define	NCHUCHARS	(10)
50abb0f93cSkardel 
51abb0f93cSkardel struct chucode {
52abb0f93cSkardel 	u_char codechars[NCHUCHARS];	/* code characters */
53abb0f93cSkardel 	u_char ncodechars;		/* number of code characters */
54abb0f93cSkardel 	u_char chustatus;		/* not used currently */
55abb0f93cSkardel 	struct timeval codetimes[NCHUCHARS];	/* arrival times */
56abb0f93cSkardel };
57abb0f93cSkardel #endif
58abb0f93cSkardel 
59abb0f93cSkardel #define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
60abb0f93cSkardel 
61af12ab5eSchristos char const *progname;
62abb0f93cSkardel 
63abb0f93cSkardel int dofilter = 0;	/* set to 1 when we should run filter algorithm */
64abb0f93cSkardel int showtimes = 0;	/* set to 1 when we should show char arrival times */
65abb0f93cSkardel int doprocess = 0;	/* set to 1 when we do processing analogous to driver */
66abb0f93cSkardel #ifdef CHULDISC
67abb0f93cSkardel int usechuldisc = 0;	/* set to 1 when CHU line discipline should be used */
68abb0f93cSkardel #endif
69abb0f93cSkardel #ifdef STREAM
70abb0f93cSkardel int usechuldisc = 0;	/* set to 1 when CHU line discipline should be used */
71abb0f93cSkardel #endif
72abb0f93cSkardel 
73abb0f93cSkardel struct timeval lasttv;
74abb0f93cSkardel struct chucode chudata;
75abb0f93cSkardel 
768585484eSchristos void	error(char *fmt, char *s1, char *s2);
778585484eSchristos void	init_chu(void);
788585484eSchristos int	openterm(char *dev);
798585484eSchristos int	process_raw(int s);
808585484eSchristos int	process_ldisc(int s);
818585484eSchristos void	raw_filter(unsigned int c, struct timeval *tv);
828585484eSchristos void	chufilter(struct chucode *chuc,	l_fp *rtime);
838585484eSchristos 
84abb0f93cSkardel 
85abb0f93cSkardel /*
86abb0f93cSkardel  * main - parse arguments and handle options
87abb0f93cSkardel  */
88abb0f93cSkardel int
main(int argc,char * argv[])89abb0f93cSkardel main(
90abb0f93cSkardel 	int argc,
91abb0f93cSkardel 	char *argv[]
92abb0f93cSkardel 	)
93abb0f93cSkardel {
94abb0f93cSkardel 	int c;
95abb0f93cSkardel 	int errflg = 0;
96abb0f93cSkardel 	extern int ntp_optind;
97abb0f93cSkardel 
98abb0f93cSkardel 	progname = argv[0];
99abb0f93cSkardel 	while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
100abb0f93cSkardel 	    switch (c) {
101abb0f93cSkardel 		case 'c':
102abb0f93cSkardel #ifdef STREAM
103abb0f93cSkardel 		    usechuldisc = 1;
104abb0f93cSkardel 		    break;
105abb0f93cSkardel #endif
106abb0f93cSkardel #ifdef CHULDISC
107abb0f93cSkardel 		    usechuldisc = 1;
108abb0f93cSkardel 		    break;
109abb0f93cSkardel #endif
110abb0f93cSkardel #ifndef STREAM
111abb0f93cSkardel #ifndef CHULDISC
112abb0f93cSkardel 		    (void) fprintf(stderr,
113abb0f93cSkardel 				   "%s: CHU line discipline not available on this machine\n",
114abb0f93cSkardel 				   progname);
115abb0f93cSkardel 		    exit(2);
116abb0f93cSkardel #endif
117abb0f93cSkardel #endif
118abb0f93cSkardel 		case 'd':
119abb0f93cSkardel 		    ++debug;
120abb0f93cSkardel 		    break;
121abb0f93cSkardel 		case 'f':
122abb0f93cSkardel 		    dofilter = 1;
123abb0f93cSkardel 		    break;
124abb0f93cSkardel 		case 'p':
125abb0f93cSkardel 		    doprocess = 1;
126abb0f93cSkardel 		case 't':
127abb0f93cSkardel 		    showtimes = 1;
128abb0f93cSkardel 		    break;
129abb0f93cSkardel 		default:
130abb0f93cSkardel 		    errflg++;
131abb0f93cSkardel 		    break;
132abb0f93cSkardel 	    }
133abb0f93cSkardel 	if (errflg || ntp_optind+1 != argc) {
134abb0f93cSkardel #ifdef STREAM
135abb0f93cSkardel 		(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
136abb0f93cSkardel 			       progname);
137abb0f93cSkardel #endif
138abb0f93cSkardel #ifdef CHULDISC
139abb0f93cSkardel 		(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
140abb0f93cSkardel 			       progname);
141abb0f93cSkardel #endif
142abb0f93cSkardel #ifndef STREAM
143abb0f93cSkardel #ifndef CHULDISC
144abb0f93cSkardel 		(void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
145abb0f93cSkardel 			       progname);
146abb0f93cSkardel #endif
147abb0f93cSkardel #endif
148abb0f93cSkardel 		exit(2);
149abb0f93cSkardel 	}
150abb0f93cSkardel 
151abb0f93cSkardel 	(void) gettimeofday(&lasttv, (struct timezone *)0);
152abb0f93cSkardel 	c = openterm(argv[ntp_optind]);
153abb0f93cSkardel 	init_chu();
154abb0f93cSkardel #ifdef STREAM
155abb0f93cSkardel 	if (usechuldisc)
156abb0f93cSkardel 	    process_ldisc(c);
157abb0f93cSkardel 	else
158abb0f93cSkardel #endif
159abb0f93cSkardel #ifdef CHULDISC
160abb0f93cSkardel 	    if (usechuldisc)
161abb0f93cSkardel 		process_ldisc(c);
162abb0f93cSkardel 	    else
163abb0f93cSkardel #endif
164abb0f93cSkardel 		process_raw(c);
165abb0f93cSkardel 	/*NOTREACHED*/
166abb0f93cSkardel }
167abb0f93cSkardel 
168abb0f93cSkardel 
169abb0f93cSkardel /*
170abb0f93cSkardel  * openterm - open a port to the CHU clock
171abb0f93cSkardel  */
172abb0f93cSkardel int
openterm(char * dev)173abb0f93cSkardel openterm(
174abb0f93cSkardel 	char *dev
175abb0f93cSkardel 	)
176abb0f93cSkardel {
177abb0f93cSkardel 	int s;
178abb0f93cSkardel 	struct sgttyb ttyb;
179abb0f93cSkardel 
180abb0f93cSkardel 	if (debug)
181abb0f93cSkardel 	    (void) fprintf(stderr, "Doing open...");
182abb0f93cSkardel 	if ((s = open(dev, O_RDONLY, 0777)) < 0)
183abb0f93cSkardel 	    error("open(%s)", dev, "");
184abb0f93cSkardel 	if (debug)
185abb0f93cSkardel 	    (void) fprintf(stderr, "open okay\n");
186abb0f93cSkardel 
187abb0f93cSkardel 	if (debug)
188abb0f93cSkardel 	    (void) fprintf(stderr, "Setting exclusive use...");
189abb0f93cSkardel 	if (ioctl(s, TIOCEXCL, (char *)0) < 0)
190abb0f93cSkardel 	    error("ioctl(TIOCEXCL)", "", "");
191abb0f93cSkardel 	if (debug)
192abb0f93cSkardel 	    (void) fprintf(stderr, "done\n");
193abb0f93cSkardel 
194abb0f93cSkardel 	ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
195abb0f93cSkardel 	ttyb.sg_erase = ttyb.sg_kill = 0;
196abb0f93cSkardel 	ttyb.sg_flags = EVENP|ODDP|RAW;
197abb0f93cSkardel 	if (debug)
198abb0f93cSkardel 	    (void) fprintf(stderr, "Setting baud rate et al...");
199abb0f93cSkardel 	if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
200abb0f93cSkardel 	    error("ioctl(TIOCSETP, raw)", "", "");
201abb0f93cSkardel 	if (debug)
202abb0f93cSkardel 	    (void) fprintf(stderr, "done\n");
203abb0f93cSkardel 
204abb0f93cSkardel #ifdef CHULDISC
205abb0f93cSkardel 	if (usechuldisc) {
206abb0f93cSkardel 		int ldisc;
207abb0f93cSkardel 
208abb0f93cSkardel 		if (debug)
209abb0f93cSkardel 		    (void) fprintf(stderr, "Switching to CHU ldisc...");
210abb0f93cSkardel 		ldisc = CHULDISC;
211abb0f93cSkardel 		if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
212abb0f93cSkardel 		    error("ioctl(TIOCSETD, CHULDISC)", "", "");
213abb0f93cSkardel 		if (debug)
214abb0f93cSkardel 		    (void) fprintf(stderr, "okay\n");
215abb0f93cSkardel 	}
216abb0f93cSkardel #endif
217abb0f93cSkardel #ifdef STREAM
218abb0f93cSkardel 	if (usechuldisc) {
219abb0f93cSkardel 
220abb0f93cSkardel 		if (debug)
221abb0f93cSkardel 		    (void) fprintf(stderr, "Poping off streams...");
222abb0f93cSkardel 		while (ioctl(s, I_POP, 0) >=0) ;
223abb0f93cSkardel 		if (debug)
224abb0f93cSkardel 		    (void) fprintf(stderr, "okay\n");
225abb0f93cSkardel 		if (debug)
226abb0f93cSkardel 		    (void) fprintf(stderr, "Pushing CHU stream...");
227abb0f93cSkardel 		if (ioctl(s, I_PUSH, "chu") < 0)
228abb0f93cSkardel 		    error("ioctl(I_PUSH, \"chu\")", "", "");
229abb0f93cSkardel 		if (debug)
230abb0f93cSkardel 		    (void) fprintf(stderr, "okay\n");
231abb0f93cSkardel 	}
232abb0f93cSkardel #endif
233abb0f93cSkardel 	return s;
234abb0f93cSkardel }
235abb0f93cSkardel 
236abb0f93cSkardel 
237abb0f93cSkardel /*
238abb0f93cSkardel  * process_raw - process characters in raw mode
239abb0f93cSkardel  */
240abb0f93cSkardel int
process_raw(int s)241abb0f93cSkardel process_raw(
242abb0f93cSkardel 	int s
243abb0f93cSkardel 	)
244abb0f93cSkardel {
245abb0f93cSkardel 	u_char c;
246abb0f93cSkardel 	int n;
247abb0f93cSkardel 	struct timeval tv;
248abb0f93cSkardel 	struct timeval difftv;
249abb0f93cSkardel 
250abb0f93cSkardel 	while ((n = read(s, &c, sizeof(char))) > 0) {
251abb0f93cSkardel 		(void) gettimeofday(&tv, (struct timezone *)0);
252abb0f93cSkardel 		if (dofilter)
253abb0f93cSkardel 		    raw_filter((unsigned int)c, &tv);
254abb0f93cSkardel 		else {
255abb0f93cSkardel 			difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
256abb0f93cSkardel 			difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
257abb0f93cSkardel 			if (difftv.tv_usec < 0) {
258abb0f93cSkardel 				difftv.tv_sec--;
259abb0f93cSkardel 				difftv.tv_usec += 1000000;
260abb0f93cSkardel 			}
261abb0f93cSkardel 			(void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
262abb0f93cSkardel 				      c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
263abb0f93cSkardel 				      difftv.tv_usec);
264abb0f93cSkardel 			lasttv = tv;
265abb0f93cSkardel 		}
266abb0f93cSkardel 	}
267abb0f93cSkardel 
268abb0f93cSkardel 	if (n == 0) {
269abb0f93cSkardel 		(void) fprintf(stderr, "%s: zero returned on read\n", progname);
270abb0f93cSkardel 		exit(1);
271abb0f93cSkardel 	} else
272abb0f93cSkardel 	    error("read()", "", "");
273abb0f93cSkardel }
274abb0f93cSkardel 
275abb0f93cSkardel 
276abb0f93cSkardel /*
277abb0f93cSkardel  * raw_filter - run the line discipline filter over raw data
278abb0f93cSkardel  */
2798585484eSchristos void
raw_filter(unsigned int c,struct timeval * tv)280abb0f93cSkardel raw_filter(
281abb0f93cSkardel 	unsigned int c,
282abb0f93cSkardel 	struct timeval *tv
283abb0f93cSkardel 	)
284abb0f93cSkardel {
2858585484eSchristos 	static struct timeval diffs[10];
286abb0f93cSkardel 	struct timeval diff;
287abb0f93cSkardel 	l_fp ts;
288abb0f93cSkardel 
289abb0f93cSkardel 	if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
290abb0f93cSkardel 		if (debug)
291abb0f93cSkardel 		    (void) fprintf(stderr,
2928585484eSchristos 				   "character %02x failed BCD test\n", c);
293abb0f93cSkardel 		chudata.ncodechars = 0;
294abb0f93cSkardel 		return;
295abb0f93cSkardel 	}
296abb0f93cSkardel 
297abb0f93cSkardel 	if (chudata.ncodechars > 0) {
298abb0f93cSkardel 		diff.tv_sec = tv->tv_sec
299abb0f93cSkardel 			- chudata.codetimes[chudata.ncodechars].tv_sec;
300abb0f93cSkardel 		diff.tv_usec = tv->tv_usec
301abb0f93cSkardel 			- chudata.codetimes[chudata.ncodechars].tv_usec;
302abb0f93cSkardel 		if (diff.tv_usec < 0) {
303abb0f93cSkardel 			diff.tv_sec--;
304abb0f93cSkardel 			diff.tv_usec += 1000000;
305abb0f93cSkardel 		} /*
306abb0f93cSkardel 		    if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
307abb0f93cSkardel 		    if (debug)
308abb0f93cSkardel 		    (void) fprintf(stderr,
309abb0f93cSkardel 		    "character %02x failed time test\n");
310abb0f93cSkardel 		    chudata.ncodechars = 0;
311abb0f93cSkardel 		    return;
312abb0f93cSkardel 		    } */
313abb0f93cSkardel 	}
314abb0f93cSkardel 
315abb0f93cSkardel 	chudata.codechars[chudata.ncodechars] = c;
316abb0f93cSkardel 	chudata.codetimes[chudata.ncodechars] = *tv;
317abb0f93cSkardel 	if (chudata.ncodechars > 0)
318abb0f93cSkardel 	    diffs[chudata.ncodechars] = diff;
319abb0f93cSkardel 	if (++chudata.ncodechars == 10) {
320abb0f93cSkardel 		if (doprocess) {
321abb0f93cSkardel 			TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
322abb0f93cSkardel 			ts.l_ui += JAN_1970;
323abb0f93cSkardel 			chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
324abb0f93cSkardel 		} else {
325abb0f93cSkardel 			register int i;
326abb0f93cSkardel 
327abb0f93cSkardel 			for (i = 0; i < chudata.ncodechars; i++) {
328abb0f93cSkardel 				(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
329abb0f93cSkardel 					      chudata.codechars[i] & 0xf,
330abb0f93cSkardel 					      (chudata.codechars[i] >>4 ) & 0xf,
331abb0f93cSkardel 					      chudata.codetimes[i].tv_sec,
332abb0f93cSkardel 					      chudata.codetimes[i].tv_usec,
333abb0f93cSkardel 					      diffs[i].tv_sec, diffs[i].tv_usec);
334abb0f93cSkardel 			}
335abb0f93cSkardel 		}
336abb0f93cSkardel 		chudata.ncodechars = 0;
337abb0f93cSkardel 	}
338abb0f93cSkardel }
339abb0f93cSkardel 
340abb0f93cSkardel 
341abb0f93cSkardel /* #ifdef CHULDISC*/
342abb0f93cSkardel /*
343abb0f93cSkardel  * process_ldisc - process line discipline
344abb0f93cSkardel  */
345abb0f93cSkardel int
process_ldisc(int s)346abb0f93cSkardel process_ldisc(
347abb0f93cSkardel 	int s
348abb0f93cSkardel 	)
349abb0f93cSkardel {
350abb0f93cSkardel 	struct chucode chu;
351abb0f93cSkardel 	int n;
352abb0f93cSkardel 	register int i;
353abb0f93cSkardel 	struct timeval diff;
354abb0f93cSkardel 	l_fp ts;
355abb0f93cSkardel 	void chufilter();
356abb0f93cSkardel 
357abb0f93cSkardel 	while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
358abb0f93cSkardel 		if (n != sizeof chu) {
359abb0f93cSkardel 			(void) fprintf(stderr, "Expected %d, got %d\n",
360abb0f93cSkardel 				       sizeof chu, n);
361abb0f93cSkardel 			continue;
362abb0f93cSkardel 		}
363abb0f93cSkardel 
364abb0f93cSkardel 		if (doprocess) {
365abb0f93cSkardel 			TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
366abb0f93cSkardel 			ts.l_ui += JAN_1970;
367abb0f93cSkardel 			chufilter(&chu, &ts);
368abb0f93cSkardel 		} else {
369abb0f93cSkardel 			for (i = 0; i < NCHUCHARS; i++) {
370abb0f93cSkardel 				if (i == 0)
371abb0f93cSkardel 				    diff.tv_sec = diff.tv_usec = 0;
372abb0f93cSkardel 				else {
373abb0f93cSkardel 					diff.tv_sec = chu.codetimes[i].tv_sec
374abb0f93cSkardel 						- chu.codetimes[i-1].tv_sec;
375abb0f93cSkardel 					diff.tv_usec = chu.codetimes[i].tv_usec
376abb0f93cSkardel 						- chu.codetimes[i-1].tv_usec;
377abb0f93cSkardel 					if (diff.tv_usec < 0) {
378abb0f93cSkardel 						diff.tv_sec--;
379abb0f93cSkardel 						diff.tv_usec += 1000000;
380abb0f93cSkardel 					}
381abb0f93cSkardel 				}
382abb0f93cSkardel 				(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
383abb0f93cSkardel 					      chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
384abb0f93cSkardel 					      chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
385abb0f93cSkardel 					      diff.tv_sec, diff.tv_usec);
386abb0f93cSkardel 			}
387abb0f93cSkardel 		}
388abb0f93cSkardel 	}
389abb0f93cSkardel 	if (n == 0) {
390abb0f93cSkardel 		(void) fprintf(stderr, "%s: zero returned on read\n", progname);
391abb0f93cSkardel 		exit(1);
392abb0f93cSkardel 	} else
393abb0f93cSkardel 	    error("read()", "", "");
394abb0f93cSkardel }
395abb0f93cSkardel /*#endif*/
396abb0f93cSkardel 
397abb0f93cSkardel 
398abb0f93cSkardel /*
399abb0f93cSkardel  * error - print an error message
400abb0f93cSkardel  */
401abb0f93cSkardel void
error(char * fmt,char * s1,char * s2)402abb0f93cSkardel error(
403abb0f93cSkardel 	char *fmt,
404abb0f93cSkardel 	char *s1,
405abb0f93cSkardel 	char *s2
406abb0f93cSkardel 	)
407abb0f93cSkardel {
408abb0f93cSkardel 	(void) fprintf(stderr, "%s: ", progname);
409abb0f93cSkardel 	(void) fprintf(stderr, fmt, s1, s2);
410abb0f93cSkardel 	(void) fprintf(stderr, ": ");
411abb0f93cSkardel 	perror("");
412abb0f93cSkardel 	exit(1);
413abb0f93cSkardel }
414abb0f93cSkardel 
415abb0f93cSkardel /*
416abb0f93cSkardel  * Definitions
417abb0f93cSkardel  */
418abb0f93cSkardel #define	MAXUNITS	4	/* maximum number of CHU units permitted */
419abb0f93cSkardel #define	CHUDEV	"/dev/chu%d"	/* device we open.  %d is unit number */
420abb0f93cSkardel #define	NCHUCODES	9	/* expect 9 CHU codes per minute */
421abb0f93cSkardel 
422abb0f93cSkardel /*
423abb0f93cSkardel  * When CHU is operating optimally we want the primary clock distance
424abb0f93cSkardel  * to come out at 300 ms.  Thus, peer.distance in the CHU peer structure
425abb0f93cSkardel  * is set to 290 ms and we compute delays which are at least 10 ms long.
426abb0f93cSkardel  * The following are 290 ms and 10 ms expressed in u_fp format
427abb0f93cSkardel  */
428abb0f93cSkardel #define	CHUDISTANCE	0x00004a3d
429abb0f93cSkardel #define	CHUBASEDELAY	0x0000028f
430abb0f93cSkardel 
431abb0f93cSkardel /*
432abb0f93cSkardel  * To compute a quality for the estimate (a pseudo delay) we add a
433abb0f93cSkardel  * fixed 10 ms for each missing code in the minute and add to this
434abb0f93cSkardel  * the sum of the differences between the remaining offsets and the
435abb0f93cSkardel  * estimated sample offset.
436abb0f93cSkardel  */
437abb0f93cSkardel #define	CHUDELAYPENALTY	0x0000028f
438abb0f93cSkardel 
439abb0f93cSkardel /*
440abb0f93cSkardel  * Other constant stuff
441abb0f93cSkardel  */
442abb0f93cSkardel #define	CHUPRECISION	(-9)		/* what the heck */
443abb0f93cSkardel #define	CHUREFID	"CHU\0"
444abb0f93cSkardel 
445abb0f93cSkardel /*
446abb0f93cSkardel  * Default fudge factors
447abb0f93cSkardel  */
448abb0f93cSkardel #define	DEFPROPDELAY	0x00624dd3	/* 0.0015 seconds, 1.5 ms */
449abb0f93cSkardel #define	DEFFILTFUDGE	0x000d1b71	/* 0.0002 seconds, 200 us */
450abb0f93cSkardel 
451abb0f93cSkardel /*
452abb0f93cSkardel  * Hacks to avoid excercising the multiplier.  I have no pride.
453abb0f93cSkardel  */
454abb0f93cSkardel #define	MULBY10(x)	(((x)<<3) + ((x)<<1))
455abb0f93cSkardel #define	MULBY60(x)	(((x)<<6) - ((x)<<2))	/* watch overflow */
456abb0f93cSkardel #define	MULBY24(x)	(((x)<<4) + ((x)<<3))
457abb0f93cSkardel 
458abb0f93cSkardel /*
459abb0f93cSkardel  * Constants for use when multiplying by 0.1.  ZEROPTONE is 0.1
460abb0f93cSkardel  * as an l_fp fraction, NZPOBITS is the number of significant bits
461abb0f93cSkardel  * in ZEROPTONE.
462abb0f93cSkardel  */
463abb0f93cSkardel #define	ZEROPTONE	0x1999999a
464abb0f93cSkardel #define	NZPOBITS	29
465abb0f93cSkardel 
466abb0f93cSkardel /*
467abb0f93cSkardel  * The CHU table.  This gives the expected time of arrival of each
468abb0f93cSkardel  * character after the on-time second and is computed as follows:
469abb0f93cSkardel  * The CHU time code is sent at 300 bps.  Your average UART will
470abb0f93cSkardel  * synchronize at the edge of the start bit and will consider the
471abb0f93cSkardel  * character complete at the center of the first stop bit, i.e.
472abb0f93cSkardel  * 0.031667 ms later.  Thus the expected time of each interrupt
473abb0f93cSkardel  * is the start bit time plus 0.031667 seconds.  These times are
474abb0f93cSkardel  * in chutable[].  To this we add such things as propagation delay
475abb0f93cSkardel  * and delay fudge factor.
476abb0f93cSkardel  */
477abb0f93cSkardel #define	CHARDELAY	0x081b4e80
478abb0f93cSkardel 
479abb0f93cSkardel static u_long chutable[NCHUCHARS] = {
480abb0f93cSkardel 	0x2147ae14 + CHARDELAY,		/* 0.130 (exactly) */
481abb0f93cSkardel 	0x2ac08312 + CHARDELAY,		/* 0.167 (exactly) */
482abb0f93cSkardel 	0x34395810 + CHARDELAY,		/* 0.204 (exactly) */
483abb0f93cSkardel 	0x3db22d0e + CHARDELAY,		/* 0.241 (exactly) */
484abb0f93cSkardel 	0x472b020c + CHARDELAY,		/* 0.278 (exactly) */
485abb0f93cSkardel 	0x50a3d70a + CHARDELAY,		/* 0.315 (exactly) */
486abb0f93cSkardel 	0x5a1cac08 + CHARDELAY,		/* 0.352 (exactly) */
487abb0f93cSkardel 	0x63958106 + CHARDELAY,		/* 0.389 (exactly) */
488abb0f93cSkardel 	0x6d0e5604 + CHARDELAY,		/* 0.426 (exactly) */
489abb0f93cSkardel 	0x76872b02 + CHARDELAY,		/* 0.463 (exactly) */
490abb0f93cSkardel };
491abb0f93cSkardel 
492abb0f93cSkardel /*
493abb0f93cSkardel  * Keep the fudge factors separately so they can be set even
494abb0f93cSkardel  * when no clock is configured.
495abb0f93cSkardel  */
496abb0f93cSkardel static l_fp propagation_delay;
497abb0f93cSkardel static l_fp fudgefactor;
498abb0f93cSkardel static l_fp offset_fudge;
499abb0f93cSkardel 
500abb0f93cSkardel /*
501abb0f93cSkardel  * We keep track of the start of the year, watching for changes.
502abb0f93cSkardel  * We also keep track of whether the year is a leap year or not.
503abb0f93cSkardel  * All because stupid CHU doesn't include the year in the time code.
504abb0f93cSkardel  */
505abb0f93cSkardel static u_long yearstart;
506abb0f93cSkardel 
507abb0f93cSkardel /*
508abb0f93cSkardel  * Imported from the timer module
509abb0f93cSkardel  */
510abb0f93cSkardel extern u_long current_time;
511abb0f93cSkardel extern struct event timerqueue[];
512abb0f93cSkardel 
513abb0f93cSkardel /*
514abb0f93cSkardel  * init_chu - initialize internal chu driver data
515abb0f93cSkardel  */
516abb0f93cSkardel void
init_chu(void)517abb0f93cSkardel init_chu(void)
518abb0f93cSkardel {
519abb0f93cSkardel 
520abb0f93cSkardel 	/*
521abb0f93cSkardel 	 * Initialize fudge factors to default.
522abb0f93cSkardel 	 */
523abb0f93cSkardel 	propagation_delay.l_ui = 0;
524abb0f93cSkardel 	propagation_delay.l_uf = DEFPROPDELAY;
525abb0f93cSkardel 	fudgefactor.l_ui = 0;
526abb0f93cSkardel 	fudgefactor.l_uf = DEFFILTFUDGE;
527abb0f93cSkardel 	offset_fudge = propagation_delay;
528abb0f93cSkardel 	L_ADD(&offset_fudge, &fudgefactor);
529abb0f93cSkardel 
530abb0f93cSkardel 	yearstart = 0;
531abb0f93cSkardel }
532abb0f93cSkardel 
533abb0f93cSkardel 
534abb0f93cSkardel void
chufilter(struct chucode * chuc,l_fp * rtime)535abb0f93cSkardel chufilter(
536abb0f93cSkardel 	struct chucode *chuc,
537abb0f93cSkardel 	l_fp *rtime
538abb0f93cSkardel 	)
539abb0f93cSkardel {
540abb0f93cSkardel 	register int i;
541abb0f93cSkardel 	register u_long date_ui;
542abb0f93cSkardel 	register u_long tmp;
543abb0f93cSkardel 	register u_char *code;
544abb0f93cSkardel 	int isneg;
545abb0f93cSkardel 	int imin;
546abb0f93cSkardel 	int imax;
547abb0f93cSkardel 	u_long reftime;
548abb0f93cSkardel 	l_fp off[NCHUCHARS];
549abb0f93cSkardel 	l_fp ts;
550abb0f93cSkardel 	int day, hour, minute, second;
551abb0f93cSkardel 	static u_char lastcode[NCHUCHARS];
552abb0f93cSkardel 
553abb0f93cSkardel 	/*
554abb0f93cSkardel 	 * We'll skip the checks made in the kernel, but assume they've
555abb0f93cSkardel 	 * been done.  This means that all characters are BCD and
556abb0f93cSkardel 	 * the intercharacter spacing isn't unreasonable.
557abb0f93cSkardel 	 */
558abb0f93cSkardel 
559abb0f93cSkardel 	/*
560abb0f93cSkardel 	 * print the code
561abb0f93cSkardel 	 */
562abb0f93cSkardel 	for (i = 0; i < NCHUCHARS; i++)
563abb0f93cSkardel 	    printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
564abb0f93cSkardel 		   ((chuc->codechars[i]>>4) & 0xf) + '0');
565abb0f93cSkardel 	printf("\n");
566abb0f93cSkardel 
567abb0f93cSkardel 	/*
568abb0f93cSkardel 	 * Format check.  Make sure the two halves match.
569abb0f93cSkardel 	 */
570abb0f93cSkardel 	for (i = 0; i < NCHUCHARS/2; i++)
571abb0f93cSkardel 	    if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
572abb0f93cSkardel 		    (void) printf("Bad format, halves don't match\n");
573abb0f93cSkardel 		    return;
574abb0f93cSkardel 	    }
575abb0f93cSkardel 
576abb0f93cSkardel 	/*
577abb0f93cSkardel 	 * Break out the code into the BCD nibbles.  Only need to fiddle
578abb0f93cSkardel 	 * with the first half since both are identical.  Note the first
579abb0f93cSkardel 	 * BCD character is the low order nibble, the second the high order.
580abb0f93cSkardel 	 */
581abb0f93cSkardel 	code = lastcode;
582abb0f93cSkardel 	for (i = 0; i < NCHUCHARS/2; i++) {
583abb0f93cSkardel 		*code++ = chuc->codechars[i] & 0xf;
584abb0f93cSkardel 		*code++ = (chuc->codechars[i] >> 4) & 0xf;
585abb0f93cSkardel 	}
586abb0f93cSkardel 
587abb0f93cSkardel 	/*
588abb0f93cSkardel 	 * If the first nibble isn't a 6, we're up the creek
589abb0f93cSkardel 	 */
590abb0f93cSkardel 	code = lastcode;
591abb0f93cSkardel 	if (*code++ != 6) {
592abb0f93cSkardel 		(void) printf("Bad format, no 6 at start\n");
593abb0f93cSkardel 		return;
594abb0f93cSkardel 	}
595abb0f93cSkardel 
596abb0f93cSkardel 	/*
597abb0f93cSkardel 	 * Collect the day, the hour, the minute and the second.
598abb0f93cSkardel 	 */
599abb0f93cSkardel 	day = *code++;
600abb0f93cSkardel 	day = MULBY10(day) + *code++;
601abb0f93cSkardel 	day = MULBY10(day) + *code++;
602abb0f93cSkardel 	hour = *code++;
603abb0f93cSkardel 	hour = MULBY10(hour) + *code++;
604abb0f93cSkardel 	minute = *code++;
605abb0f93cSkardel 	minute = MULBY10(minute) + *code++;
606abb0f93cSkardel 	second = *code++;
607abb0f93cSkardel 	second = MULBY10(second) + *code++;
608abb0f93cSkardel 
609abb0f93cSkardel 	/*
610abb0f93cSkardel 	 * Sanity check the day and time.  Note that this
611abb0f93cSkardel 	 * only occurs on the 31st through the 39th second
612abb0f93cSkardel 	 * of the minute.
613abb0f93cSkardel 	 */
614abb0f93cSkardel 	if (day < 1 || day > 366
615abb0f93cSkardel 	    || hour > 23 || minute > 59
616abb0f93cSkardel 	    || second < 31 || second > 39) {
617abb0f93cSkardel 		(void) printf("Failed date sanity check: %d %d %d %d\n",
618abb0f93cSkardel 			      day, hour, minute, second);
619abb0f93cSkardel 		return;
620abb0f93cSkardel 	}
621abb0f93cSkardel 
622abb0f93cSkardel 	/*
623abb0f93cSkardel 	 * Compute seconds into the year.
624abb0f93cSkardel 	 */
625abb0f93cSkardel 	tmp = (u_long)(MULBY24((day-1)) + hour);	/* hours */
626abb0f93cSkardel 	tmp = MULBY60(tmp) + (u_long)minute;		/* minutes */
627abb0f93cSkardel 	tmp = MULBY60(tmp) + (u_long)second;		/* seconds */
628abb0f93cSkardel 
629abb0f93cSkardel 	/*
630abb0f93cSkardel 	 * Now the fun begins.  We demand that the received time code
631abb0f93cSkardel 	 * be within CLOCK_WAYTOOBIG of the receive timestamp, but
632abb0f93cSkardel 	 * there is uncertainty about the year the timestamp is in.
633abb0f93cSkardel 	 * Use the current year start for the first check, this should
634abb0f93cSkardel 	 * work most of the time.
635abb0f93cSkardel 	 */
636abb0f93cSkardel 	date_ui = tmp + yearstart;
6378585484eSchristos #define CLOCK_WAYTOOBIG 1000 /* revived from ancient sources */
638abb0f93cSkardel 	if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
639abb0f93cSkardel 	    && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
640abb0f93cSkardel 	    goto codeokay;	/* looks good */
641abb0f93cSkardel 
642abb0f93cSkardel 	/*
643abb0f93cSkardel 	 * Trouble.  Next check is to see if the year rolled over and, if
644abb0f93cSkardel 	 * so, try again with the new year's start.
645abb0f93cSkardel 	 */
6468585484eSchristos 	date_ui = calyearstart(rtime->l_ui, NULL);
647abb0f93cSkardel 	if (date_ui != yearstart) {
648abb0f93cSkardel 		yearstart = date_ui;
649abb0f93cSkardel 		date_ui += tmp;
650abb0f93cSkardel 		(void) printf("time %u, code %u, difference %d\n",
651abb0f93cSkardel 			      date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
652abb0f93cSkardel 		if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
653abb0f93cSkardel 		    && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
654abb0f93cSkardel 		    goto codeokay;	/* okay this time */
655abb0f93cSkardel 	}
656abb0f93cSkardel 
657abb0f93cSkardel 	ts.l_uf = 0;
658abb0f93cSkardel 	ts.l_ui = yearstart;
659abb0f93cSkardel 	printf("yearstart %s\n", prettydate(&ts));
660abb0f93cSkardel 	printf("received %s\n", prettydate(rtime));
661abb0f93cSkardel 	ts.l_ui = date_ui;
662abb0f93cSkardel 	printf("date_ui %s\n", prettydate(&ts));
663abb0f93cSkardel 
664abb0f93cSkardel 	/*
665abb0f93cSkardel 	 * Here we know the year start matches the current system
666abb0f93cSkardel 	 * time.  One remaining possibility is that the time code
667abb0f93cSkardel 	 * is in the year previous to that of the system time.  This
668abb0f93cSkardel 	 * is only worth checking if the receive timestamp is less
669abb0f93cSkardel 	 * than CLOCK_WAYTOOBIG seconds into the new year.
670abb0f93cSkardel 	 */
671abb0f93cSkardel 	if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
6728585484eSchristos 		date_ui = tmp;
6738585484eSchristos 		date_ui += calyearstart(yearstart - CLOCK_WAYTOOBIG,
6748585484eSchristos 					NULL);
675abb0f93cSkardel 		if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
676abb0f93cSkardel 		    goto codeokay;
677abb0f93cSkardel 	}
678abb0f93cSkardel 
679abb0f93cSkardel 	/*
680abb0f93cSkardel 	 * One last possibility is that the time stamp is in the year
681abb0f93cSkardel 	 * following the year the system is in.  Try this one before
682abb0f93cSkardel 	 * giving up.
683abb0f93cSkardel 	 */
6848585484eSchristos 	date_ui = tmp;
6858585484eSchristos 	date_ui += calyearstart(yearstart + (400 * SECSPERDAY),
6868585484eSchristos 				NULL);
687abb0f93cSkardel 	if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
688abb0f93cSkardel 		printf("Date hopelessly off\n");
689abb0f93cSkardel 		return;		/* hopeless, let it sync to other peers */
690abb0f93cSkardel 	}
691abb0f93cSkardel 
692abb0f93cSkardel     codeokay:
693abb0f93cSkardel 	reftime = date_ui;
694abb0f93cSkardel 	/*
695abb0f93cSkardel 	 * We've now got the integral seconds part of the time code (we hope).
696abb0f93cSkardel 	 * The fractional part comes from the table.  We next compute
697abb0f93cSkardel 	 * the offsets for each character.
698abb0f93cSkardel 	 */
699abb0f93cSkardel 	for (i = 0; i < NCHUCHARS; i++) {
700abb0f93cSkardel 		register u_long tmp2;
701abb0f93cSkardel 
702abb0f93cSkardel 		off[i].l_ui = date_ui;
703abb0f93cSkardel 		off[i].l_uf = chutable[i];
704abb0f93cSkardel 		tmp = chuc->codetimes[i].tv_sec + JAN_1970;
705abb0f93cSkardel 		TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
706abb0f93cSkardel 		M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
707abb0f93cSkardel 	}
708abb0f93cSkardel 
709abb0f93cSkardel 	/*
710abb0f93cSkardel 	 * Here is a *big* problem.  What one would normally
711abb0f93cSkardel 	 * do here on a machine with lots of clock bits (say
712abb0f93cSkardel 	 * a Vax or the gizmo board) is pick the most positive
713abb0f93cSkardel 	 * offset and the estimate, since this is the one that
714abb0f93cSkardel 	 * is most likely suffered the smallest interrupt delay.
715abb0f93cSkardel 	 * The trouble is that the low order clock bit on an IBM
716abb0f93cSkardel 	 * RT, which is the machine I had in mind when doing this,
717abb0f93cSkardel 	 * ticks at just under the millisecond mark.  This isn't
718abb0f93cSkardel 	 * precise enough.  What we can do to improve this is to
719abb0f93cSkardel 	 * average all 10 samples and rely on the second level
720abb0f93cSkardel 	 * filtering to pick the least delayed estimate.  Trouble
721abb0f93cSkardel 	 * is, this means we have to divide a 64 bit fixed point
722abb0f93cSkardel 	 * number by 10, a procedure which really sucks.  Oh, well.
723abb0f93cSkardel 	 * First compute the sum.
724abb0f93cSkardel 	 */
725abb0f93cSkardel 	date_ui = 0;
726abb0f93cSkardel 	tmp = 0;
727abb0f93cSkardel 	for (i = 0; i < NCHUCHARS; i++)
728abb0f93cSkardel 	    M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
729abb0f93cSkardel 	if (M_ISNEG(date_ui, tmp))
730abb0f93cSkardel 	    isneg = 1;
731abb0f93cSkardel 	else
732abb0f93cSkardel 	    isneg = 0;
733abb0f93cSkardel 
734abb0f93cSkardel 	/*
735abb0f93cSkardel 	 * Here is a multiply-by-0.1 optimization that should apply
736abb0f93cSkardel 	 * just about everywhere.  If the magnitude of the sum
737abb0f93cSkardel 	 * is less than 9 we don't have to worry about overflow
738abb0f93cSkardel 	 * out of a 64 bit product, even after rounding.
739abb0f93cSkardel 	 */
740abb0f93cSkardel 	if (date_ui < 9 || date_ui > 0xfffffff7) {
741abb0f93cSkardel 		register u_long prod_ui;
742abb0f93cSkardel 		register u_long prod_uf;
743abb0f93cSkardel 
744abb0f93cSkardel 		prod_ui = prod_uf = 0;
745abb0f93cSkardel 		/*
746abb0f93cSkardel 		 * This code knows the low order bit in 0.1 is zero
747abb0f93cSkardel 		 */
748abb0f93cSkardel 		for (i = 1; i < NZPOBITS; i++) {
749abb0f93cSkardel 			M_LSHIFT(date_ui, tmp);
750abb0f93cSkardel 			if (ZEROPTONE & (1<<i))
751abb0f93cSkardel 			    M_ADD(prod_ui, prod_uf, date_ui, tmp);
752abb0f93cSkardel 		}
753abb0f93cSkardel 
754abb0f93cSkardel 		/*
755abb0f93cSkardel 		 * Done, round it correctly.  Prod_ui contains the
756abb0f93cSkardel 		 * fraction.
757abb0f93cSkardel 		 */
758abb0f93cSkardel 		if (prod_uf & 0x80000000)
759abb0f93cSkardel 		    prod_ui++;
760abb0f93cSkardel 		if (isneg)
761abb0f93cSkardel 		    date_ui = 0xffffffff;
762abb0f93cSkardel 		else
763abb0f93cSkardel 		    date_ui = 0;
764abb0f93cSkardel 		tmp = prod_ui;
765abb0f93cSkardel 		/*
766abb0f93cSkardel 		 * date_ui is integral part, tmp is fraction.
767abb0f93cSkardel 		 */
768abb0f93cSkardel 	} else {
769abb0f93cSkardel 		register u_long prod_ovr;
770abb0f93cSkardel 		register u_long prod_ui;
771abb0f93cSkardel 		register u_long prod_uf;
772abb0f93cSkardel 		register u_long highbits;
773abb0f93cSkardel 
774abb0f93cSkardel 		prod_ovr = prod_ui = prod_uf = 0;
775abb0f93cSkardel 		if (isneg)
776abb0f93cSkardel 		    highbits = 0xffffffff;	/* sign extend */
777abb0f93cSkardel 		else
778abb0f93cSkardel 		    highbits = 0;
779abb0f93cSkardel 		/*
780abb0f93cSkardel 		 * This code knows the low order bit in 0.1 is zero
781abb0f93cSkardel 		 */
782abb0f93cSkardel 		for (i = 1; i < NZPOBITS; i++) {
783abb0f93cSkardel 			M_LSHIFT3(highbits, date_ui, tmp);
784abb0f93cSkardel 			if (ZEROPTONE & (1<<i))
785abb0f93cSkardel 			    M_ADD3(prod_ovr, prod_uf, prod_ui,
786abb0f93cSkardel 				   highbits, date_ui, tmp);
787abb0f93cSkardel 		}
788abb0f93cSkardel 
789abb0f93cSkardel 		if (prod_uf & 0x80000000)
790abb0f93cSkardel 		    M_ADDUF(prod_ovr, prod_ui, (u_long)1);
791abb0f93cSkardel 		date_ui = prod_ovr;
792abb0f93cSkardel 		tmp = prod_ui;
793abb0f93cSkardel 	}
794abb0f93cSkardel 
795abb0f93cSkardel 	/*
796abb0f93cSkardel 	 * At this point we have the mean offset, with the integral
797abb0f93cSkardel 	 * part in date_ui and the fractional part in tmp.  Store
798abb0f93cSkardel 	 * it in the structure.
799abb0f93cSkardel 	 */
800abb0f93cSkardel 	/*
801abb0f93cSkardel 	 * Add in fudge factor.
802abb0f93cSkardel 	 */
803abb0f93cSkardel 	M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
804abb0f93cSkardel 
805abb0f93cSkardel 	/*
806abb0f93cSkardel 	 * Find the minimun and maximum offset
807abb0f93cSkardel 	 */
808abb0f93cSkardel 	imin = imax = 0;
809abb0f93cSkardel 	for (i = 1; i < NCHUCHARS; i++) {
810abb0f93cSkardel 		if (L_ISGEQ(&off[i], &off[imax])) {
811abb0f93cSkardel 			imax = i;
812abb0f93cSkardel 		} else if (L_ISGEQ(&off[imin], &off[i])) {
813abb0f93cSkardel 			imin = i;
814abb0f93cSkardel 		}
815abb0f93cSkardel 	}
816abb0f93cSkardel 
817abb0f93cSkardel 	L_ADD(&off[imin], &offset_fudge);
818abb0f93cSkardel 	if (imin != imax)
819abb0f93cSkardel 	    L_ADD(&off[imax], &offset_fudge);
820abb0f93cSkardel 	(void) printf("mean %s, min %s, max %s\n",
821abb0f93cSkardel 		      mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
822abb0f93cSkardel 		      lfptoa(&off[imax], 8));
823abb0f93cSkardel }
824