1 1.6 christos /* $NetBSD: chutest.c,v 1.6 2020/05/25 20:47:19 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.4 christos char const *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