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