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