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