1 /* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
2 * chutest - test the CHU clock
7 #include <sys/socket.h>
8 #include <netinet/in.h>
14 #include "../include/ntp_fp.h"
15 #include "../include/ntp.h"
16 #include "../include/ntp_unixtime.h"
20 # ifdef HAVE_SYS_CHUDEFS_H
21 #include <sys/chudefs.h>
28 # ifdef HAVE_SYS_CHUDEFS_H
29 #include <sys/chudefs.h>
35 #define NCHUCHARS (10)
38 u_char codechars[NCHUCHARS]; /* code characters */
39 u_char ncodechars; /* number of code characters */
40 u_char chustatus; /* not used currently */
41 struct timeval codetimes[NCHUCHARS]; /* arrival times */
46 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
51 int dofilter = 0; /* set to 1 when we should run filter algorithm */
52 int showtimes = 0; /* set to 1 when we should show char arrival times */
53 int doprocess = 0; /* set to 1 when we do processing analogous to driver */
55 int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
58 int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
61 struct timeval lasttv;
62 struct chucode chudata;
64 extern u_long ustotslo[];
65 extern u_long ustotsmid[];
66 extern u_long ustotshi[];
69 * main - parse arguments and handle options
79 extern int ntp_optind;
80 extern char *ntp_optarg;
84 while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
97 (void) fprintf(stderr,
98 "%s: CHU line discipline not available on this machine\n",
118 if (errflg || ntp_optind+1 != argc) {
120 (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
124 (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
129 (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
136 (void) gettimeofday(&lasttv, (struct timezone *)0);
137 c = openterm(argv[ntp_optind]);
155 * openterm - open a port to the CHU clock
166 (void) fprintf(stderr, "Doing open...");
167 if ((s = open(dev, O_RDONLY, 0777)) < 0)
168 error("open(%s)", dev, "");
170 (void) fprintf(stderr, "open okay\n");
173 (void) fprintf(stderr, "Setting exclusive use...");
174 if (ioctl(s, TIOCEXCL, (char *)0) < 0)
175 error("ioctl(TIOCEXCL)", "", "");
177 (void) fprintf(stderr, "done\n");
179 ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
180 ttyb.sg_erase = ttyb.sg_kill = 0;
181 ttyb.sg_flags = EVENP|ODDP|RAW;
183 (void) fprintf(stderr, "Setting baud rate et al...");
184 if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
185 error("ioctl(TIOCSETP, raw)", "", "");
187 (void) fprintf(stderr, "done\n");
194 (void) fprintf(stderr, "Switching to CHU ldisc...");
196 if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
197 error("ioctl(TIOCSETD, CHULDISC)", "", "");
199 (void) fprintf(stderr, "okay\n");
206 (void) fprintf(stderr, "Poping off streams...");
207 while (ioctl(s, I_POP, 0) >=0) ;
209 (void) fprintf(stderr, "okay\n");
211 (void) fprintf(stderr, "Pushing CHU stream...");
212 if (ioctl(s, I_PUSH, "chu") < 0)
213 error("ioctl(I_PUSH, \"chu\")", "", "");
215 (void) fprintf(stderr, "okay\n");
223 * process_raw - process characters in raw mode
233 struct timeval difftv;
235 while ((n = read(s, &c, sizeof(char))) > 0) {
236 (void) gettimeofday(&tv, (struct timezone *)0);
238 raw_filter((unsigned int)c, &tv);
240 difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
241 difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
242 if (difftv.tv_usec < 0) {
244 difftv.tv_usec += 1000000;
246 (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
247 c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
254 (void) fprintf(stderr, "%s: zero returned on read\n", progname);
257 error("read()", "", "");
262 * raw_filter - run the line discipline filter over raw data
270 static struct timeval diffs[10] = { 0 };
275 if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
277 (void) fprintf(stderr,
278 "character %02x failed BCD test\n");
279 chudata.ncodechars = 0;
283 if (chudata.ncodechars > 0) {
284 diff.tv_sec = tv->tv_sec
285 - chudata.codetimes[chudata.ncodechars].tv_sec;
286 diff.tv_usec = tv->tv_usec
287 - chudata.codetimes[chudata.ncodechars].tv_usec;
288 if (diff.tv_usec < 0) {
290 diff.tv_usec += 1000000;
292 if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
294 (void) fprintf(stderr,
295 "character %02x failed time test\n");
296 chudata.ncodechars = 0;
301 chudata.codechars[chudata.ncodechars] = c;
302 chudata.codetimes[chudata.ncodechars] = *tv;
303 if (chudata.ncodechars > 0)
304 diffs[chudata.ncodechars] = diff;
305 if (++chudata.ncodechars == 10) {
307 TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
309 chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
313 for (i = 0; i < chudata.ncodechars; i++) {
314 (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
315 chudata.codechars[i] & 0xf,
316 (chudata.codechars[i] >>4 ) & 0xf,
317 chudata.codetimes[i].tv_sec,
318 chudata.codetimes[i].tv_usec,
319 diffs[i].tv_sec, diffs[i].tv_usec);
322 chudata.ncodechars = 0;
329 * process_ldisc - process line discipline
343 while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
344 if (n != sizeof chu) {
345 (void) fprintf(stderr, "Expected %d, got %d\n",
351 TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
353 chufilter(&chu, &ts);
355 for (i = 0; i < NCHUCHARS; i++) {
357 diff.tv_sec = diff.tv_usec = 0;
359 diff.tv_sec = chu.codetimes[i].tv_sec
360 - chu.codetimes[i-1].tv_sec;
361 diff.tv_usec = chu.codetimes[i].tv_usec
362 - chu.codetimes[i-1].tv_usec;
363 if (diff.tv_usec < 0) {
365 diff.tv_usec += 1000000;
368 (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
369 chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
370 chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
371 diff.tv_sec, diff.tv_usec);
376 (void) fprintf(stderr, "%s: zero returned on read\n", progname);
379 error("read()", "", "");
385 * error - print an error message
394 (void) fprintf(stderr, "%s: ", progname);
395 (void) fprintf(stderr, fmt, s1, s2);
396 (void) fprintf(stderr, ": ");
404 #define MAXUNITS 4 /* maximum number of CHU units permitted */
405 #define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
406 #define NCHUCODES 9 /* expect 9 CHU codes per minute */
409 * When CHU is operating optimally we want the primary clock distance
410 * to come out at 300 ms. Thus, peer.distance in the CHU peer structure
411 * is set to 290 ms and we compute delays which are at least 10 ms long.
412 * The following are 290 ms and 10 ms expressed in u_fp format
414 #define CHUDISTANCE 0x00004a3d
415 #define CHUBASEDELAY 0x0000028f
418 * To compute a quality for the estimate (a pseudo delay) we add a
419 * fixed 10 ms for each missing code in the minute and add to this
420 * the sum of the differences between the remaining offsets and the
421 * estimated sample offset.
423 #define CHUDELAYPENALTY 0x0000028f
426 * Other constant stuff
428 #define CHUPRECISION (-9) /* what the heck */
429 #define CHUREFID "CHU\0"
432 * Default fudge factors
434 #define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
435 #define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
438 * Hacks to avoid excercising the multiplier. I have no pride.
440 #define MULBY10(x) (((x)<<3) + ((x)<<1))
441 #define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
442 #define MULBY24(x) (((x)<<4) + ((x)<<3))
445 * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
446 * as an l_fp fraction, NZPOBITS is the number of significant bits
449 #define ZEROPTONE 0x1999999a
453 * The CHU table. This gives the expected time of arrival of each
454 * character after the on-time second and is computed as follows:
455 * The CHU time code is sent at 300 bps. Your average UART will
456 * synchronize at the edge of the start bit and will consider the
457 * character complete at the center of the first stop bit, i.e.
458 * 0.031667 ms later. Thus the expected time of each interrupt
459 * is the start bit time plus 0.031667 seconds. These times are
460 * in chutable[]. To this we add such things as propagation delay
461 * and delay fudge factor.
463 #define CHARDELAY 0x081b4e80
465 static u_long chutable[NCHUCHARS] = {
466 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */
467 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */
468 0x34395810 + CHARDELAY, /* 0.204 (exactly) */
469 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */
470 0x472b020c + CHARDELAY, /* 0.278 (exactly) */
471 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */
472 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */
473 0x63958106 + CHARDELAY, /* 0.389 (exactly) */
474 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */
475 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */
479 * Keep the fudge factors separately so they can be set even
480 * when no clock is configured.
482 static l_fp propagation_delay;
483 static l_fp fudgefactor;
484 static l_fp offset_fudge;
487 * We keep track of the start of the year, watching for changes.
488 * We also keep track of whether the year is a leap year or not.
489 * All because stupid CHU doesn't include the year in the time code.
491 static u_long yearstart;
494 * Imported from the timer module
496 extern u_long current_time;
497 extern struct event timerqueue[];
500 * Time conversion tables imported from the library
502 extern u_long ustotslo[];
503 extern u_long ustotsmid[];
504 extern u_long ustotshi[];
508 * init_chu - initialize internal chu driver data
515 * Initialize fudge factors to default.
517 propagation_delay.l_ui = 0;
518 propagation_delay.l_uf = DEFPROPDELAY;
519 fudgefactor.l_ui = 0;
520 fudgefactor.l_uf = DEFFILTFUDGE;
521 offset_fudge = propagation_delay;
522 L_ADD(&offset_fudge, &fudgefactor);
530 struct chucode *chuc,
535 register u_long date_ui;
537 register u_char *code;
544 int day, hour, minute, second;
545 static u_char lastcode[NCHUCHARS];
546 extern u_long calyearstart();
547 extern char *mfptoa();
549 extern char *prettydate();
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.
560 for (i = 0; i < NCHUCHARS; i++)
561 printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
562 ((chuc->codechars[i]>>4) & 0xf) + '0');
566 * Format check. Make sure the two halves match.
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");
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.
580 for (i = 0; i < NCHUCHARS/2; i++) {
581 *code++ = chuc->codechars[i] & 0xf;
582 *code++ = (chuc->codechars[i] >> 4) & 0xf;
586 * If the first nibble isn't a 6, we're up the creek
590 (void) printf("Bad format, no 6 at start\n");
595 * Collect the day, the hour, the minute and the second.
598 day = MULBY10(day) + *code++;
599 day = MULBY10(day) + *code++;
601 hour = MULBY10(hour) + *code++;
603 minute = MULBY10(minute) + *code++;
605 second = MULBY10(second) + *code++;
608 * Sanity check the day and time. Note that this
609 * only occurs on the 31st through the 39th second
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);
621 * Compute seconds into the year.
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 */
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.
634 date_ui = tmp + yearstart;
635 if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
636 && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
637 goto codeokay; /* looks good */
640 * Trouble. Next check is to see if the year rolled over and, if
641 * so, try again with the new year's start.
643 date_ui = calyearstart(rtime->l_ui);
644 if (date_ui != yearstart) {
647 (void) printf("time %u, code %u, difference %d\n",
648 date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
649 if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
650 && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
651 goto codeokay; /* okay this time */
656 printf("yearstart %s\n", prettydate(&ts));
657 printf("received %s\n", prettydate(rtime));
659 printf("date_ui %s\n", prettydate(&ts));
662 * Here we know the year start matches the current system
663 * time. One remaining possibility is that the time code
664 * is in the year previous to that of the system time. This
665 * is only worth checking if the receive timestamp is less
666 * than CLOCK_WAYTOOBIG seconds into the new year.
668 if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
669 date_ui = tmp + calyearstart(yearstart - CLOCK_WAYTOOBIG);
670 if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
675 * One last possibility is that the time stamp is in the year
676 * following the year the system is in. Try this one before
679 date_ui = tmp + calyearstart(yearstart + (400*24*60*60)); /* 400 days */
680 if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
681 printf("Date hopelessly off\n");
682 return; /* hopeless, let it sync to other peers */
688 * We've now got the integral seconds part of the time code (we hope).
689 * The fractional part comes from the table. We next compute
690 * the offsets for each character.
692 for (i = 0; i < NCHUCHARS; i++) {
693 register u_long tmp2;
695 off[i].l_ui = date_ui;
696 off[i].l_uf = chutable[i];
697 tmp = chuc->codetimes[i].tv_sec + JAN_1970;
698 TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
699 M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
703 * Here is a *big* problem. What one would normally
704 * do here on a machine with lots of clock bits (say
705 * a Vax or the gizmo board) is pick the most positive
706 * offset and the estimate, since this is the one that
707 * is most likely suffered the smallest interrupt delay.
708 * The trouble is that the low order clock bit on an IBM
709 * RT, which is the machine I had in mind when doing this,
710 * ticks at just under the millisecond mark. This isn't
711 * precise enough. What we can do to improve this is to
712 * average all 10 samples and rely on the second level
713 * filtering to pick the least delayed estimate. Trouble
714 * is, this means we have to divide a 64 bit fixed point
715 * number by 10, a procedure which really sucks. Oh, well.
716 * First compute the sum.
720 for (i = 0; i < NCHUCHARS; i++)
721 M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
722 if (M_ISNEG(date_ui, tmp))
728 * Here is a multiply-by-0.1 optimization that should apply
729 * just about everywhere. If the magnitude of the sum
730 * is less than 9 we don't have to worry about overflow
731 * out of a 64 bit product, even after rounding.
733 if (date_ui < 9 || date_ui > 0xfffffff7) {
734 register u_long prod_ui;
735 register u_long prod_uf;
737 prod_ui = prod_uf = 0;
739 * This code knows the low order bit in 0.1 is zero
741 for (i = 1; i < NZPOBITS; i++) {
742 M_LSHIFT(date_ui, tmp);
743 if (ZEROPTONE & (1<<i))
744 M_ADD(prod_ui, prod_uf, date_ui, tmp);
748 * Done, round it correctly. Prod_ui contains the
751 if (prod_uf & 0x80000000)
754 date_ui = 0xffffffff;
759 * date_ui is integral part, tmp is fraction.
762 register u_long prod_ovr;
763 register u_long prod_ui;
764 register u_long prod_uf;
765 register u_long highbits;
767 prod_ovr = prod_ui = prod_uf = 0;
769 highbits = 0xffffffff; /* sign extend */
773 * This code knows the low order bit in 0.1 is zero
775 for (i = 1; i < NZPOBITS; i++) {
776 M_LSHIFT3(highbits, date_ui, tmp);
777 if (ZEROPTONE & (1<<i))
778 M_ADD3(prod_ovr, prod_uf, prod_ui,
779 highbits, date_ui, tmp);
782 if (prod_uf & 0x80000000)
783 M_ADDUF(prod_ovr, prod_ui, (u_long)1);
789 * At this point we have the mean offset, with the integral
790 * part in date_ui and the fractional part in tmp. Store
791 * it in the structure.
794 * Add in fudge factor.
796 M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
799 * Find the minimun and maximum offset
802 for (i = 1; i < NCHUCHARS; i++) {
803 if (L_ISGEQ(&off[i], &off[imax])) {
805 } else if (L_ISGEQ(&off[imin], &off[i])) {
810 L_ADD(&off[imin], &offset_fudge);
812 L_ADD(&off[imax], &offset_fudge);
813 (void) printf("mean %s, min %s, max %s\n",
814 mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
815 lfptoa(&off[imax], 8));