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