154359Sroberto/* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp 254359Sroberto * chutest - test the CHU clock 354359Sroberto */ 454359Sroberto 5290001Sglebius#ifdef HAVE_CONFIG_H 6290001Sglebius# include <config.h> 7290001Sglebius#endif 854359Sroberto#include <stdio.h> 9290001Sglebius#include <fcntl.h> 10290001Sglebius#ifdef HAVE_UNISTD_H 11290001Sglebius# include <unistd.h> 12290001Sglebius#endif 13290001Sglebius#ifdef HAVE_STROPTS_H 14290001Sglebius# include <stropts.h> 15290001Sglebius#else 16290001Sglebius# ifdef HAVE_SYS_STROPTS_H 17290001Sglebius# include <sys/stropts.h> 18290001Sglebius# endif 19290001Sglebius#endif 2054359Sroberto#include <sys/types.h> 2154359Sroberto#include <sys/socket.h> 2254359Sroberto#include <netinet/in.h> 2354359Sroberto#include <sys/ioctl.h> 2454359Sroberto#include <sys/time.h> 2554359Sroberto#include <sys/file.h> 26290001Sglebius#ifdef HAVE_TERMIOS_H 27290001Sglebius# include <termios.h> 28290001Sglebius#else 29290001Sglebius# ifdef HAVE_SGTTY_H 30290001Sglebius# include <sgtty.h> 31290001Sglebius# endif 32290001Sglebius#endif 3354359Sroberto 34290001Sglebius#include "ntp_fp.h" 35290001Sglebius#include "ntp.h" 36290001Sglebius#include "ntp_unixtime.h" 37290001Sglebius#include "ntp_calendar.h" 3854359Sroberto 3954359Sroberto#ifdef CHULDISC 4054359Sroberto# ifdef HAVE_SYS_CHUDEFS_H 41290001Sglebius# include <sys/chudefs.h> 42290001Sglebius# endif 4354359Sroberto#endif 4454359Sroberto 4554359Sroberto 4654359Sroberto#ifndef CHULDISC 4754359Sroberto#define NCHUCHARS (10) 4854359Sroberto 4954359Srobertostruct chucode { 5054359Sroberto u_char codechars[NCHUCHARS]; /* code characters */ 5154359Sroberto u_char ncodechars; /* number of code characters */ 5254359Sroberto u_char chustatus; /* not used currently */ 5354359Sroberto struct timeval codetimes[NCHUCHARS]; /* arrival times */ 5454359Sroberto}; 5554359Sroberto#endif 5654359Sroberto 5754359Sroberto#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) 5854359Sroberto 59290001Sglebiuschar const *progname; 6054359Sroberto 6154359Srobertoint dofilter = 0; /* set to 1 when we should run filter algorithm */ 6254359Srobertoint showtimes = 0; /* set to 1 when we should show char arrival times */ 6354359Srobertoint doprocess = 0; /* set to 1 when we do processing analogous to driver */ 6454359Sroberto#ifdef CHULDISC 6554359Srobertoint usechuldisc = 0; /* set to 1 when CHU line discipline should be used */ 6654359Sroberto#endif 6754359Sroberto#ifdef STREAM 6854359Srobertoint usechuldisc = 0; /* set to 1 when CHU line discipline should be used */ 6954359Sroberto#endif 7054359Sroberto 7154359Srobertostruct timeval lasttv; 7254359Srobertostruct chucode chudata; 7354359Sroberto 74290001Sglebiusvoid error(char *fmt, char *s1, char *s2); 75290001Sglebiusvoid init_chu(void); 76290001Sglebiusint openterm(char *dev); 77290001Sglebiusint process_raw(int s); 78290001Sglebiusint process_ldisc(int s); 79290001Sglebiusvoid raw_filter(unsigned int c, struct timeval *tv); 80290001Sglebiusvoid chufilter(struct chucode *chuc, l_fp *rtime); 8154359Sroberto 82290001Sglebius 8354359Sroberto/* 8454359Sroberto * main - parse arguments and handle options 8554359Sroberto */ 8654359Srobertoint 8754359Srobertomain( 8854359Sroberto int argc, 8954359Sroberto char *argv[] 9054359Sroberto ) 9154359Sroberto{ 9254359Sroberto int c; 9354359Sroberto int errflg = 0; 9454359Sroberto extern int ntp_optind; 9554359Sroberto 9654359Sroberto progname = argv[0]; 9754359Sroberto while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF) 9854359Sroberto switch (c) { 9954359Sroberto case 'c': 10054359Sroberto#ifdef STREAM 10154359Sroberto usechuldisc = 1; 10254359Sroberto break; 10354359Sroberto#endif 10454359Sroberto#ifdef CHULDISC 10554359Sroberto usechuldisc = 1; 10654359Sroberto break; 10754359Sroberto#endif 10854359Sroberto#ifndef STREAM 10954359Sroberto#ifndef CHULDISC 11054359Sroberto (void) fprintf(stderr, 11154359Sroberto "%s: CHU line discipline not available on this machine\n", 11254359Sroberto progname); 11354359Sroberto exit(2); 11454359Sroberto#endif 11554359Sroberto#endif 11654359Sroberto case 'd': 11754359Sroberto ++debug; 11854359Sroberto break; 11954359Sroberto case 'f': 12054359Sroberto dofilter = 1; 12154359Sroberto break; 12254359Sroberto case 'p': 12354359Sroberto doprocess = 1; 12454359Sroberto case 't': 12554359Sroberto showtimes = 1; 12654359Sroberto break; 12754359Sroberto default: 12854359Sroberto errflg++; 12954359Sroberto break; 13054359Sroberto } 13154359Sroberto if (errflg || ntp_optind+1 != argc) { 13254359Sroberto#ifdef STREAM 13354359Sroberto (void) fprintf(stderr, "usage: %s [-dft] tty_device\n", 13454359Sroberto progname); 13554359Sroberto#endif 13654359Sroberto#ifdef CHULDISC 13754359Sroberto (void) fprintf(stderr, "usage: %s [-dft] tty_device\n", 13854359Sroberto progname); 13954359Sroberto#endif 14054359Sroberto#ifndef STREAM 14154359Sroberto#ifndef CHULDISC 14254359Sroberto (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n", 14354359Sroberto progname); 14454359Sroberto#endif 14554359Sroberto#endif 14654359Sroberto exit(2); 14754359Sroberto } 14854359Sroberto 14954359Sroberto (void) gettimeofday(&lasttv, (struct timezone *)0); 15054359Sroberto c = openterm(argv[ntp_optind]); 15154359Sroberto init_chu(); 15254359Sroberto#ifdef STREAM 15354359Sroberto if (usechuldisc) 15454359Sroberto process_ldisc(c); 15554359Sroberto else 15654359Sroberto#endif 15754359Sroberto#ifdef CHULDISC 15854359Sroberto if (usechuldisc) 15954359Sroberto process_ldisc(c); 16054359Sroberto else 16154359Sroberto#endif 16254359Sroberto process_raw(c); 16354359Sroberto /*NOTREACHED*/ 16454359Sroberto} 16554359Sroberto 16654359Sroberto 16754359Sroberto/* 16854359Sroberto * openterm - open a port to the CHU clock 16954359Sroberto */ 17054359Srobertoint 17154359Srobertoopenterm( 17254359Sroberto char *dev 17354359Sroberto ) 17454359Sroberto{ 17554359Sroberto int s; 17654359Sroberto struct sgttyb ttyb; 17754359Sroberto 17854359Sroberto if (debug) 17954359Sroberto (void) fprintf(stderr, "Doing open..."); 18054359Sroberto if ((s = open(dev, O_RDONLY, 0777)) < 0) 18154359Sroberto error("open(%s)", dev, ""); 18254359Sroberto if (debug) 18354359Sroberto (void) fprintf(stderr, "open okay\n"); 18454359Sroberto 18554359Sroberto if (debug) 18654359Sroberto (void) fprintf(stderr, "Setting exclusive use..."); 18754359Sroberto if (ioctl(s, TIOCEXCL, (char *)0) < 0) 18854359Sroberto error("ioctl(TIOCEXCL)", "", ""); 18954359Sroberto if (debug) 19054359Sroberto (void) fprintf(stderr, "done\n"); 19154359Sroberto 19254359Sroberto ttyb.sg_ispeed = ttyb.sg_ospeed = B300; 19354359Sroberto ttyb.sg_erase = ttyb.sg_kill = 0; 19454359Sroberto ttyb.sg_flags = EVENP|ODDP|RAW; 19554359Sroberto if (debug) 19654359Sroberto (void) fprintf(stderr, "Setting baud rate et al..."); 19754359Sroberto if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0) 19854359Sroberto error("ioctl(TIOCSETP, raw)", "", ""); 19954359Sroberto if (debug) 20054359Sroberto (void) fprintf(stderr, "done\n"); 20154359Sroberto 20254359Sroberto#ifdef CHULDISC 20354359Sroberto if (usechuldisc) { 20454359Sroberto int ldisc; 20554359Sroberto 20654359Sroberto if (debug) 20754359Sroberto (void) fprintf(stderr, "Switching to CHU ldisc..."); 20854359Sroberto ldisc = CHULDISC; 20954359Sroberto if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0) 21054359Sroberto error("ioctl(TIOCSETD, CHULDISC)", "", ""); 21154359Sroberto if (debug) 21254359Sroberto (void) fprintf(stderr, "okay\n"); 21354359Sroberto } 21454359Sroberto#endif 21554359Sroberto#ifdef STREAM 21654359Sroberto if (usechuldisc) { 21754359Sroberto 21854359Sroberto if (debug) 21954359Sroberto (void) fprintf(stderr, "Poping off streams..."); 22054359Sroberto while (ioctl(s, I_POP, 0) >=0) ; 22154359Sroberto if (debug) 22254359Sroberto (void) fprintf(stderr, "okay\n"); 22354359Sroberto if (debug) 22454359Sroberto (void) fprintf(stderr, "Pushing CHU stream..."); 22554359Sroberto if (ioctl(s, I_PUSH, "chu") < 0) 22654359Sroberto error("ioctl(I_PUSH, \"chu\")", "", ""); 22754359Sroberto if (debug) 22854359Sroberto (void) fprintf(stderr, "okay\n"); 22954359Sroberto } 23054359Sroberto#endif 23154359Sroberto return s; 23254359Sroberto} 23354359Sroberto 23454359Sroberto 23554359Sroberto/* 23654359Sroberto * process_raw - process characters in raw mode 23754359Sroberto */ 23854359Srobertoint 23954359Srobertoprocess_raw( 24054359Sroberto int s 24154359Sroberto ) 24254359Sroberto{ 24354359Sroberto u_char c; 24454359Sroberto int n; 24554359Sroberto struct timeval tv; 24654359Sroberto struct timeval difftv; 24754359Sroberto 24854359Sroberto while ((n = read(s, &c, sizeof(char))) > 0) { 24954359Sroberto (void) gettimeofday(&tv, (struct timezone *)0); 25054359Sroberto if (dofilter) 25154359Sroberto raw_filter((unsigned int)c, &tv); 25254359Sroberto else { 25354359Sroberto difftv.tv_sec = tv.tv_sec - lasttv.tv_sec; 25454359Sroberto difftv.tv_usec = tv.tv_usec - lasttv.tv_usec; 25554359Sroberto if (difftv.tv_usec < 0) { 25654359Sroberto difftv.tv_sec--; 25754359Sroberto difftv.tv_usec += 1000000; 25854359Sroberto } 25954359Sroberto (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n", 26054359Sroberto c, tv.tv_sec, tv.tv_usec, difftv.tv_sec, 26154359Sroberto difftv.tv_usec); 26254359Sroberto lasttv = tv; 26354359Sroberto } 26454359Sroberto } 26554359Sroberto 26654359Sroberto if (n == 0) { 26754359Sroberto (void) fprintf(stderr, "%s: zero returned on read\n", progname); 26854359Sroberto exit(1); 26954359Sroberto } else 27054359Sroberto error("read()", "", ""); 27154359Sroberto} 27254359Sroberto 27354359Sroberto 27454359Sroberto/* 27554359Sroberto * raw_filter - run the line discipline filter over raw data 27654359Sroberto */ 277290001Sglebiusvoid 27854359Srobertoraw_filter( 27954359Sroberto unsigned int c, 28054359Sroberto struct timeval *tv 28154359Sroberto ) 28254359Sroberto{ 283290001Sglebius static struct timeval diffs[10]; 28454359Sroberto struct timeval diff; 28554359Sroberto l_fp ts; 28654359Sroberto 28754359Sroberto if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) { 28854359Sroberto if (debug) 28954359Sroberto (void) fprintf(stderr, 290290001Sglebius "character %02x failed BCD test\n", c); 29154359Sroberto chudata.ncodechars = 0; 29254359Sroberto return; 29354359Sroberto } 29454359Sroberto 29554359Sroberto if (chudata.ncodechars > 0) { 29654359Sroberto diff.tv_sec = tv->tv_sec 29754359Sroberto - chudata.codetimes[chudata.ncodechars].tv_sec; 29854359Sroberto diff.tv_usec = tv->tv_usec 29954359Sroberto - chudata.codetimes[chudata.ncodechars].tv_usec; 30054359Sroberto if (diff.tv_usec < 0) { 30154359Sroberto diff.tv_sec--; 30254359Sroberto diff.tv_usec += 1000000; 30354359Sroberto } /* 30454359Sroberto if (diff.tv_sec != 0 || diff.tv_usec > 900000) { 30554359Sroberto if (debug) 30654359Sroberto (void) fprintf(stderr, 30754359Sroberto "character %02x failed time test\n"); 30854359Sroberto chudata.ncodechars = 0; 30954359Sroberto return; 31054359Sroberto } */ 31154359Sroberto } 31254359Sroberto 31354359Sroberto chudata.codechars[chudata.ncodechars] = c; 31454359Sroberto chudata.codetimes[chudata.ncodechars] = *tv; 31554359Sroberto if (chudata.ncodechars > 0) 31654359Sroberto diffs[chudata.ncodechars] = diff; 31754359Sroberto if (++chudata.ncodechars == 10) { 31854359Sroberto if (doprocess) { 31954359Sroberto TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts); 32054359Sroberto ts.l_ui += JAN_1970; 32154359Sroberto chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]); 32254359Sroberto } else { 32354359Sroberto register int i; 32454359Sroberto 32554359Sroberto for (i = 0; i < chudata.ncodechars; i++) { 32654359Sroberto (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n", 32754359Sroberto chudata.codechars[i] & 0xf, 32854359Sroberto (chudata.codechars[i] >>4 ) & 0xf, 32954359Sroberto chudata.codetimes[i].tv_sec, 33054359Sroberto chudata.codetimes[i].tv_usec, 33154359Sroberto diffs[i].tv_sec, diffs[i].tv_usec); 33254359Sroberto } 33354359Sroberto } 33454359Sroberto chudata.ncodechars = 0; 33554359Sroberto } 33654359Sroberto} 33754359Sroberto 33854359Sroberto 33954359Sroberto/* #ifdef CHULDISC*/ 34054359Sroberto/* 34154359Sroberto * process_ldisc - process line discipline 34254359Sroberto */ 34354359Srobertoint 34454359Srobertoprocess_ldisc( 34554359Sroberto int s 34654359Sroberto ) 34754359Sroberto{ 34854359Sroberto struct chucode chu; 34954359Sroberto int n; 35054359Sroberto register int i; 35154359Sroberto struct timeval diff; 35254359Sroberto l_fp ts; 35354359Sroberto void chufilter(); 35454359Sroberto 35554359Sroberto while ((n = read(s, (char *)&chu, sizeof chu)) > 0) { 35654359Sroberto if (n != sizeof chu) { 35754359Sroberto (void) fprintf(stderr, "Expected %d, got %d\n", 35854359Sroberto sizeof chu, n); 35954359Sroberto continue; 36054359Sroberto } 36154359Sroberto 36254359Sroberto if (doprocess) { 36354359Sroberto TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts); 36454359Sroberto ts.l_ui += JAN_1970; 36554359Sroberto chufilter(&chu, &ts); 36654359Sroberto } else { 36754359Sroberto for (i = 0; i < NCHUCHARS; i++) { 36854359Sroberto if (i == 0) 36954359Sroberto diff.tv_sec = diff.tv_usec = 0; 37054359Sroberto else { 37154359Sroberto diff.tv_sec = chu.codetimes[i].tv_sec 37254359Sroberto - chu.codetimes[i-1].tv_sec; 37354359Sroberto diff.tv_usec = chu.codetimes[i].tv_usec 37454359Sroberto - chu.codetimes[i-1].tv_usec; 37554359Sroberto if (diff.tv_usec < 0) { 37654359Sroberto diff.tv_sec--; 37754359Sroberto diff.tv_usec += 1000000; 37854359Sroberto } 37954359Sroberto } 38054359Sroberto (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n", 38154359Sroberto chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf, 38254359Sroberto chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec, 38354359Sroberto diff.tv_sec, diff.tv_usec); 38454359Sroberto } 38554359Sroberto } 38654359Sroberto } 38754359Sroberto if (n == 0) { 38854359Sroberto (void) fprintf(stderr, "%s: zero returned on read\n", progname); 38954359Sroberto exit(1); 39054359Sroberto } else 39154359Sroberto error("read()", "", ""); 39254359Sroberto} 39354359Sroberto/*#endif*/ 39454359Sroberto 39554359Sroberto 39654359Sroberto/* 39754359Sroberto * error - print an error message 39854359Sroberto */ 39954359Srobertovoid 40054359Srobertoerror( 40154359Sroberto char *fmt, 40254359Sroberto char *s1, 40354359Sroberto char *s2 40454359Sroberto ) 40554359Sroberto{ 40654359Sroberto (void) fprintf(stderr, "%s: ", progname); 40754359Sroberto (void) fprintf(stderr, fmt, s1, s2); 40854359Sroberto (void) fprintf(stderr, ": "); 40954359Sroberto perror(""); 41054359Sroberto exit(1); 41154359Sroberto} 41254359Sroberto 41354359Sroberto/* 41454359Sroberto * Definitions 41554359Sroberto */ 41654359Sroberto#define MAXUNITS 4 /* maximum number of CHU units permitted */ 41754359Sroberto#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */ 41854359Sroberto#define NCHUCODES 9 /* expect 9 CHU codes per minute */ 41954359Sroberto 42054359Sroberto/* 42154359Sroberto * When CHU is operating optimally we want the primary clock distance 42254359Sroberto * to come out at 300 ms. Thus, peer.distance in the CHU peer structure 42354359Sroberto * is set to 290 ms and we compute delays which are at least 10 ms long. 42454359Sroberto * The following are 290 ms and 10 ms expressed in u_fp format 42554359Sroberto */ 42654359Sroberto#define CHUDISTANCE 0x00004a3d 42754359Sroberto#define CHUBASEDELAY 0x0000028f 42854359Sroberto 42954359Sroberto/* 43054359Sroberto * To compute a quality for the estimate (a pseudo delay) we add a 43154359Sroberto * fixed 10 ms for each missing code in the minute and add to this 43254359Sroberto * the sum of the differences between the remaining offsets and the 43354359Sroberto * estimated sample offset. 43454359Sroberto */ 43554359Sroberto#define CHUDELAYPENALTY 0x0000028f 43654359Sroberto 43754359Sroberto/* 43854359Sroberto * Other constant stuff 43954359Sroberto */ 44054359Sroberto#define CHUPRECISION (-9) /* what the heck */ 44154359Sroberto#define CHUREFID "CHU\0" 44254359Sroberto 44354359Sroberto/* 44454359Sroberto * Default fudge factors 44554359Sroberto */ 44654359Sroberto#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */ 44754359Sroberto#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */ 44854359Sroberto 44954359Sroberto/* 45054359Sroberto * Hacks to avoid excercising the multiplier. I have no pride. 45154359Sroberto */ 45254359Sroberto#define MULBY10(x) (((x)<<3) + ((x)<<1)) 45354359Sroberto#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */ 45454359Sroberto#define MULBY24(x) (((x)<<4) + ((x)<<3)) 45554359Sroberto 45654359Sroberto/* 45754359Sroberto * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1 45854359Sroberto * as an l_fp fraction, NZPOBITS is the number of significant bits 45954359Sroberto * in ZEROPTONE. 46054359Sroberto */ 46154359Sroberto#define ZEROPTONE 0x1999999a 46254359Sroberto#define NZPOBITS 29 46354359Sroberto 46454359Sroberto/* 46554359Sroberto * The CHU table. This gives the expected time of arrival of each 46654359Sroberto * character after the on-time second and is computed as follows: 46754359Sroberto * The CHU time code is sent at 300 bps. Your average UART will 46854359Sroberto * synchronize at the edge of the start bit and will consider the 46954359Sroberto * character complete at the center of the first stop bit, i.e. 47054359Sroberto * 0.031667 ms later. Thus the expected time of each interrupt 47154359Sroberto * is the start bit time plus 0.031667 seconds. These times are 47254359Sroberto * in chutable[]. To this we add such things as propagation delay 47354359Sroberto * and delay fudge factor. 47454359Sroberto */ 47554359Sroberto#define CHARDELAY 0x081b4e80 47654359Sroberto 47754359Srobertostatic u_long chutable[NCHUCHARS] = { 47854359Sroberto 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */ 47954359Sroberto 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */ 48054359Sroberto 0x34395810 + CHARDELAY, /* 0.204 (exactly) */ 48154359Sroberto 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */ 48254359Sroberto 0x472b020c + CHARDELAY, /* 0.278 (exactly) */ 48354359Sroberto 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */ 48454359Sroberto 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */ 48554359Sroberto 0x63958106 + CHARDELAY, /* 0.389 (exactly) */ 48654359Sroberto 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */ 48754359Sroberto 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */ 48854359Sroberto}; 48954359Sroberto 49054359Sroberto/* 49154359Sroberto * Keep the fudge factors separately so they can be set even 49254359Sroberto * when no clock is configured. 49354359Sroberto */ 49454359Srobertostatic l_fp propagation_delay; 49554359Srobertostatic l_fp fudgefactor; 49654359Srobertostatic l_fp offset_fudge; 49754359Sroberto 49854359Sroberto/* 49954359Sroberto * We keep track of the start of the year, watching for changes. 50054359Sroberto * We also keep track of whether the year is a leap year or not. 50154359Sroberto * All because stupid CHU doesn't include the year in the time code. 50254359Sroberto */ 50354359Srobertostatic u_long yearstart; 50454359Sroberto 50554359Sroberto/* 50654359Sroberto * Imported from the timer module 50754359Sroberto */ 50854359Srobertoextern u_long current_time; 50954359Srobertoextern struct event timerqueue[]; 51054359Sroberto 51154359Sroberto/* 51254359Sroberto * init_chu - initialize internal chu driver data 51354359Sroberto */ 51454359Srobertovoid 51554359Srobertoinit_chu(void) 51654359Sroberto{ 51754359Sroberto 51854359Sroberto /* 51954359Sroberto * Initialize fudge factors to default. 52054359Sroberto */ 52154359Sroberto propagation_delay.l_ui = 0; 52254359Sroberto propagation_delay.l_uf = DEFPROPDELAY; 52354359Sroberto fudgefactor.l_ui = 0; 52454359Sroberto fudgefactor.l_uf = DEFFILTFUDGE; 52554359Sroberto offset_fudge = propagation_delay; 52654359Sroberto L_ADD(&offset_fudge, &fudgefactor); 52754359Sroberto 52854359Sroberto yearstart = 0; 52954359Sroberto} 53054359Sroberto 53154359Sroberto 53254359Srobertovoid 53354359Srobertochufilter( 53454359Sroberto struct chucode *chuc, 53554359Sroberto l_fp *rtime 53654359Sroberto ) 53754359Sroberto{ 53854359Sroberto register int i; 53954359Sroberto register u_long date_ui; 54054359Sroberto register u_long tmp; 54154359Sroberto register u_char *code; 54254359Sroberto int isneg; 54354359Sroberto int imin; 54454359Sroberto int imax; 54554359Sroberto u_long reftime; 54654359Sroberto l_fp off[NCHUCHARS]; 54754359Sroberto l_fp ts; 54854359Sroberto int day, hour, minute, second; 54954359Sroberto static u_char lastcode[NCHUCHARS]; 55054359Sroberto 55154359Sroberto /* 55254359Sroberto * We'll skip the checks made in the kernel, but assume they've 55354359Sroberto * been done. This means that all characters are BCD and 55454359Sroberto * the intercharacter spacing isn't unreasonable. 55554359Sroberto */ 55654359Sroberto 55754359Sroberto /* 55854359Sroberto * print the code 55954359Sroberto */ 56054359Sroberto for (i = 0; i < NCHUCHARS; i++) 56154359Sroberto printf("%c%c", (chuc->codechars[i] & 0xf) + '0', 56254359Sroberto ((chuc->codechars[i]>>4) & 0xf) + '0'); 56354359Sroberto printf("\n"); 56454359Sroberto 56554359Sroberto /* 56654359Sroberto * Format check. Make sure the two halves match. 56754359Sroberto */ 56854359Sroberto for (i = 0; i < NCHUCHARS/2; i++) 56954359Sroberto if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) { 57054359Sroberto (void) printf("Bad format, halves don't match\n"); 57154359Sroberto return; 57254359Sroberto } 57354359Sroberto 57454359Sroberto /* 57554359Sroberto * Break out the code into the BCD nibbles. Only need to fiddle 57654359Sroberto * with the first half since both are identical. Note the first 57754359Sroberto * BCD character is the low order nibble, the second the high order. 57854359Sroberto */ 57954359Sroberto code = lastcode; 58054359Sroberto for (i = 0; i < NCHUCHARS/2; i++) { 58154359Sroberto *code++ = chuc->codechars[i] & 0xf; 58254359Sroberto *code++ = (chuc->codechars[i] >> 4) & 0xf; 58354359Sroberto } 58454359Sroberto 58554359Sroberto /* 58654359Sroberto * If the first nibble isn't a 6, we're up the creek 58754359Sroberto */ 58854359Sroberto code = lastcode; 58954359Sroberto if (*code++ != 6) { 59054359Sroberto (void) printf("Bad format, no 6 at start\n"); 59154359Sroberto return; 59254359Sroberto } 59354359Sroberto 59454359Sroberto /* 59554359Sroberto * Collect the day, the hour, the minute and the second. 59654359Sroberto */ 59754359Sroberto day = *code++; 59854359Sroberto day = MULBY10(day) + *code++; 59954359Sroberto day = MULBY10(day) + *code++; 60054359Sroberto hour = *code++; 60154359Sroberto hour = MULBY10(hour) + *code++; 60254359Sroberto minute = *code++; 60354359Sroberto minute = MULBY10(minute) + *code++; 60454359Sroberto second = *code++; 60554359Sroberto second = MULBY10(second) + *code++; 60654359Sroberto 60754359Sroberto /* 60854359Sroberto * Sanity check the day and time. Note that this 60954359Sroberto * only occurs on the 31st through the 39th second 61054359Sroberto * of the minute. 61154359Sroberto */ 61254359Sroberto if (day < 1 || day > 366 61354359Sroberto || hour > 23 || minute > 59 61454359Sroberto || second < 31 || second > 39) { 61554359Sroberto (void) printf("Failed date sanity check: %d %d %d %d\n", 61654359Sroberto day, hour, minute, second); 61754359Sroberto return; 61854359Sroberto } 61954359Sroberto 62054359Sroberto /* 62154359Sroberto * Compute seconds into the year. 62254359Sroberto */ 62354359Sroberto tmp = (u_long)(MULBY24((day-1)) + hour); /* hours */ 62454359Sroberto tmp = MULBY60(tmp) + (u_long)minute; /* minutes */ 62554359Sroberto tmp = MULBY60(tmp) + (u_long)second; /* seconds */ 62654359Sroberto 62754359Sroberto /* 62854359Sroberto * Now the fun begins. We demand that the received time code 62954359Sroberto * be within CLOCK_WAYTOOBIG of the receive timestamp, but 63054359Sroberto * there is uncertainty about the year the timestamp is in. 63154359Sroberto * Use the current year start for the first check, this should 63254359Sroberto * work most of the time. 63354359Sroberto */ 63454359Sroberto date_ui = tmp + yearstart; 635290001Sglebius#define CLOCK_WAYTOOBIG 1000 /* revived from ancient sources */ 63654359Sroberto if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG) 63754359Sroberto && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG)) 63854359Sroberto goto codeokay; /* looks good */ 63954359Sroberto 64054359Sroberto /* 64154359Sroberto * Trouble. Next check is to see if the year rolled over and, if 64254359Sroberto * so, try again with the new year's start. 64354359Sroberto */ 644290001Sglebius date_ui = calyearstart(rtime->l_ui, NULL); 64554359Sroberto if (date_ui != yearstart) { 64654359Sroberto yearstart = date_ui; 64754359Sroberto date_ui += tmp; 64854359Sroberto (void) printf("time %u, code %u, difference %d\n", 64954359Sroberto date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui); 65054359Sroberto if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG) 65154359Sroberto && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG)) 65254359Sroberto goto codeokay; /* okay this time */ 65354359Sroberto } 65454359Sroberto 65554359Sroberto ts.l_uf = 0; 65654359Sroberto ts.l_ui = yearstart; 65754359Sroberto printf("yearstart %s\n", prettydate(&ts)); 65854359Sroberto printf("received %s\n", prettydate(rtime)); 65954359Sroberto ts.l_ui = date_ui; 66054359Sroberto printf("date_ui %s\n", prettydate(&ts)); 66154359Sroberto 66254359Sroberto /* 66354359Sroberto * Here we know the year start matches the current system 66454359Sroberto * time. One remaining possibility is that the time code 66554359Sroberto * is in the year previous to that of the system time. This 66654359Sroberto * is only worth checking if the receive timestamp is less 66754359Sroberto * than CLOCK_WAYTOOBIG seconds into the new year. 66854359Sroberto */ 66954359Sroberto if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) { 670290001Sglebius date_ui = tmp; 671290001Sglebius date_ui += calyearstart(yearstart - CLOCK_WAYTOOBIG, 672290001Sglebius NULL); 67354359Sroberto if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG) 67454359Sroberto goto codeokay; 67554359Sroberto } 67654359Sroberto 67754359Sroberto /* 67854359Sroberto * One last possibility is that the time stamp is in the year 67954359Sroberto * following the year the system is in. Try this one before 68054359Sroberto * giving up. 68154359Sroberto */ 682290001Sglebius date_ui = tmp; 683290001Sglebius date_ui += calyearstart(yearstart + (400 * SECSPERDAY), 684290001Sglebius NULL); 68554359Sroberto if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) { 68654359Sroberto printf("Date hopelessly off\n"); 68754359Sroberto return; /* hopeless, let it sync to other peers */ 68854359Sroberto } 68954359Sroberto 69054359Sroberto codeokay: 69154359Sroberto reftime = date_ui; 69254359Sroberto /* 69354359Sroberto * We've now got the integral seconds part of the time code (we hope). 69454359Sroberto * The fractional part comes from the table. We next compute 69554359Sroberto * the offsets for each character. 69654359Sroberto */ 69754359Sroberto for (i = 0; i < NCHUCHARS; i++) { 69854359Sroberto register u_long tmp2; 69954359Sroberto 70054359Sroberto off[i].l_ui = date_ui; 70154359Sroberto off[i].l_uf = chutable[i]; 70254359Sroberto tmp = chuc->codetimes[i].tv_sec + JAN_1970; 70354359Sroberto TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2); 70454359Sroberto M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2); 70554359Sroberto } 70654359Sroberto 70754359Sroberto /* 70854359Sroberto * Here is a *big* problem. What one would normally 70954359Sroberto * do here on a machine with lots of clock bits (say 71054359Sroberto * a Vax or the gizmo board) is pick the most positive 71154359Sroberto * offset and the estimate, since this is the one that 71254359Sroberto * is most likely suffered the smallest interrupt delay. 71354359Sroberto * The trouble is that the low order clock bit on an IBM 71454359Sroberto * RT, which is the machine I had in mind when doing this, 71554359Sroberto * ticks at just under the millisecond mark. This isn't 71654359Sroberto * precise enough. What we can do to improve this is to 71754359Sroberto * average all 10 samples and rely on the second level 71854359Sroberto * filtering to pick the least delayed estimate. Trouble 71954359Sroberto * is, this means we have to divide a 64 bit fixed point 72054359Sroberto * number by 10, a procedure which really sucks. Oh, well. 72154359Sroberto * First compute the sum. 72254359Sroberto */ 72354359Sroberto date_ui = 0; 72454359Sroberto tmp = 0; 72554359Sroberto for (i = 0; i < NCHUCHARS; i++) 72654359Sroberto M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf); 72754359Sroberto if (M_ISNEG(date_ui, tmp)) 72854359Sroberto isneg = 1; 72954359Sroberto else 73054359Sroberto isneg = 0; 73154359Sroberto 73254359Sroberto /* 73354359Sroberto * Here is a multiply-by-0.1 optimization that should apply 73454359Sroberto * just about everywhere. If the magnitude of the sum 73554359Sroberto * is less than 9 we don't have to worry about overflow 73654359Sroberto * out of a 64 bit product, even after rounding. 73754359Sroberto */ 73854359Sroberto if (date_ui < 9 || date_ui > 0xfffffff7) { 73954359Sroberto register u_long prod_ui; 74054359Sroberto register u_long prod_uf; 74154359Sroberto 74254359Sroberto prod_ui = prod_uf = 0; 74354359Sroberto /* 74454359Sroberto * This code knows the low order bit in 0.1 is zero 74554359Sroberto */ 74654359Sroberto for (i = 1; i < NZPOBITS; i++) { 74754359Sroberto M_LSHIFT(date_ui, tmp); 74854359Sroberto if (ZEROPTONE & (1<<i)) 74954359Sroberto M_ADD(prod_ui, prod_uf, date_ui, tmp); 75054359Sroberto } 75154359Sroberto 75254359Sroberto /* 75354359Sroberto * Done, round it correctly. Prod_ui contains the 75454359Sroberto * fraction. 75554359Sroberto */ 75654359Sroberto if (prod_uf & 0x80000000) 75754359Sroberto prod_ui++; 75854359Sroberto if (isneg) 75954359Sroberto date_ui = 0xffffffff; 76054359Sroberto else 76154359Sroberto date_ui = 0; 76254359Sroberto tmp = prod_ui; 76354359Sroberto /* 76454359Sroberto * date_ui is integral part, tmp is fraction. 76554359Sroberto */ 76654359Sroberto } else { 76754359Sroberto register u_long prod_ovr; 76854359Sroberto register u_long prod_ui; 76954359Sroberto register u_long prod_uf; 77054359Sroberto register u_long highbits; 77154359Sroberto 77254359Sroberto prod_ovr = prod_ui = prod_uf = 0; 77354359Sroberto if (isneg) 77454359Sroberto highbits = 0xffffffff; /* sign extend */ 77554359Sroberto else 77654359Sroberto highbits = 0; 77754359Sroberto /* 77854359Sroberto * This code knows the low order bit in 0.1 is zero 77954359Sroberto */ 78054359Sroberto for (i = 1; i < NZPOBITS; i++) { 78154359Sroberto M_LSHIFT3(highbits, date_ui, tmp); 78254359Sroberto if (ZEROPTONE & (1<<i)) 78354359Sroberto M_ADD3(prod_ovr, prod_uf, prod_ui, 78454359Sroberto highbits, date_ui, tmp); 78554359Sroberto } 78654359Sroberto 78754359Sroberto if (prod_uf & 0x80000000) 78854359Sroberto M_ADDUF(prod_ovr, prod_ui, (u_long)1); 78954359Sroberto date_ui = prod_ovr; 79054359Sroberto tmp = prod_ui; 79154359Sroberto } 79254359Sroberto 79354359Sroberto /* 79454359Sroberto * At this point we have the mean offset, with the integral 79554359Sroberto * part in date_ui and the fractional part in tmp. Store 79654359Sroberto * it in the structure. 79754359Sroberto */ 79854359Sroberto /* 79954359Sroberto * Add in fudge factor. 80054359Sroberto */ 80154359Sroberto M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf); 80254359Sroberto 80354359Sroberto /* 80454359Sroberto * Find the minimun and maximum offset 80554359Sroberto */ 80654359Sroberto imin = imax = 0; 80754359Sroberto for (i = 1; i < NCHUCHARS; i++) { 80854359Sroberto if (L_ISGEQ(&off[i], &off[imax])) { 80954359Sroberto imax = i; 81054359Sroberto } else if (L_ISGEQ(&off[imin], &off[i])) { 81154359Sroberto imin = i; 81254359Sroberto } 81354359Sroberto } 81454359Sroberto 81554359Sroberto L_ADD(&off[imin], &offset_fudge); 81654359Sroberto if (imin != imax) 81754359Sroberto L_ADD(&off[imax], &offset_fudge); 81854359Sroberto (void) printf("mean %s, min %s, max %s\n", 81954359Sroberto mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8), 82054359Sroberto lfptoa(&off[imax], 8)); 82154359Sroberto} 822