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