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