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