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