summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/clockstuff/chutest.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/clockstuff/chutest.c')
-rw-r--r--contrib/ntp/clockstuff/chutest.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/contrib/ntp/clockstuff/chutest.c b/contrib/ntp/clockstuff/chutest.c
new file mode 100644
index 0000000..785c253
--- /dev/null
+++ b/contrib/ntp/clockstuff/chutest.c
@@ -0,0 +1,816 @@
+/* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
+ * chutest - test the CHU clock
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sgtty.h>
+
+#include "../include/ntp_fp.h"
+#include "../include/ntp.h"
+#include "../include/ntp_unixtime.h"
+
+#ifdef CHULDISC
+#ifdef STREAM
+# ifdef HAVE_SYS_CHUDEFS_H
+#include <sys/chudefs.h>
+#endif
+#include <stropts.h>
+#endif
+#endif
+
+#ifdef CHULDISC
+# ifdef HAVE_SYS_CHUDEFS_H
+#include <sys/chudefs.h>
+#endif
+#endif
+
+#ifndef CHULDISC
+#ifndef STREAM
+#define NCHUCHARS (10)
+
+struct chucode {
+ u_char codechars[NCHUCHARS]; /* code characters */
+ u_char ncodechars; /* number of code characters */
+ u_char chustatus; /* not used currently */
+ struct timeval codetimes[NCHUCHARS]; /* arrival times */
+};
+#endif
+#endif
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+int dofilter = 0; /* set to 1 when we should run filter algorithm */
+int showtimes = 0; /* set to 1 when we should show char arrival times */
+int doprocess = 0; /* set to 1 when we do processing analogous to driver */
+#ifdef CHULDISC
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+#ifdef STREAM
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+
+struct timeval lasttv;
+struct chucode chudata;
+
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+/*
+ * main - parse arguments and handle options
+ */
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ void init_chu();
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
+ switch (c) {
+ case 'c':
+#ifdef STREAM
+ usechuldisc = 1;
+ break;
+#endif
+#ifdef CHULDISC
+ usechuldisc = 1;
+ break;
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr,
+ "%s: CHU line discipline not available on this machine\n",
+ progname);
+ exit(2);
+#endif
+#endif
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ dofilter = 1;
+ break;
+ case 'p':
+ doprocess = 1;
+ case 't':
+ showtimes = 1;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind+1 != argc) {
+#ifdef STREAM
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifdef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
+ progname);
+#endif
+#endif
+ exit(2);
+ }
+
+ (void) gettimeofday(&lasttv, (struct timezone *)0);
+ c = openterm(argv[ntp_optind]);
+ init_chu();
+#ifdef STREAM
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+#ifdef CHULDISC
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+ process_raw(c);
+ /*NOTREACHED*/
+}
+
+
+/*
+ * openterm - open a port to the CHU clock
+ */
+int
+openterm(
+ char *dev
+ )
+{
+ int s;
+ struct sgttyb ttyb;
+
+ if (debug)
+ (void) fprintf(stderr, "Doing open...");
+ if ((s = open(dev, O_RDONLY, 0777)) < 0)
+ error("open(%s)", dev, "");
+ if (debug)
+ (void) fprintf(stderr, "open okay\n");
+
+ if (debug)
+ (void) fprintf(stderr, "Setting exclusive use...");
+ if (ioctl(s, TIOCEXCL, (char *)0) < 0)
+ error("ioctl(TIOCEXCL)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+ ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
+ ttyb.sg_erase = ttyb.sg_kill = 0;
+ ttyb.sg_flags = EVENP|ODDP|RAW;
+ if (debug)
+ (void) fprintf(stderr, "Setting baud rate et al...");
+ if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
+ error("ioctl(TIOCSETP, raw)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+#ifdef CHULDISC
+ if (usechuldisc) {
+ int ldisc;
+
+ if (debug)
+ (void) fprintf(stderr, "Switching to CHU ldisc...");
+ ldisc = CHULDISC;
+ if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
+ error("ioctl(TIOCSETD, CHULDISC)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+#ifdef STREAM
+ if (usechuldisc) {
+
+ if (debug)
+ (void) fprintf(stderr, "Poping off streams...");
+ while (ioctl(s, I_POP, 0) >=0) ;
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ if (debug)
+ (void) fprintf(stderr, "Pushing CHU stream...");
+ if (ioctl(s, I_PUSH, "chu") < 0)
+ error("ioctl(I_PUSH, \"chu\")", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+ return s;
+}
+
+
+/*
+ * process_raw - process characters in raw mode
+ */
+int
+process_raw(
+ int s
+ )
+{
+ u_char c;
+ int n;
+ struct timeval tv;
+ struct timeval difftv;
+
+ while ((n = read(s, &c, sizeof(char))) > 0) {
+ (void) gettimeofday(&tv, (struct timezone *)0);
+ if (dofilter)
+ raw_filter((unsigned int)c, &tv);
+ else {
+ difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
+ difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
+ if (difftv.tv_usec < 0) {
+ difftv.tv_sec--;
+ difftv.tv_usec += 1000000;
+ }
+ (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
+ c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
+ difftv.tv_usec);
+ lasttv = tv;
+ }
+ }
+
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+
+
+/*
+ * raw_filter - run the line discipline filter over raw data
+ */
+int
+raw_filter(
+ unsigned int c,
+ struct timeval *tv
+ )
+{
+ static struct timeval diffs[10] = { 0 };
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed BCD test\n");
+ chudata.ncodechars = 0;
+ return;
+ }
+
+ if (chudata.ncodechars > 0) {
+ diff.tv_sec = tv->tv_sec
+ - chudata.codetimes[chudata.ncodechars].tv_sec;
+ diff.tv_usec = tv->tv_usec
+ - chudata.codetimes[chudata.ncodechars].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ } /*
+ if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed time test\n");
+ chudata.ncodechars = 0;
+ return;
+ } */
+ }
+
+ chudata.codechars[chudata.ncodechars] = c;
+ chudata.codetimes[chudata.ncodechars] = *tv;
+ if (chudata.ncodechars > 0)
+ diffs[chudata.ncodechars] = diff;
+ if (++chudata.ncodechars == 10) {
+ if (doprocess) {
+ TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
+ } else {
+ register int i;
+
+ for (i = 0; i < chudata.ncodechars; i++) {
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chudata.codechars[i] & 0xf,
+ (chudata.codechars[i] >>4 ) & 0xf,
+ chudata.codetimes[i].tv_sec,
+ chudata.codetimes[i].tv_usec,
+ diffs[i].tv_sec, diffs[i].tv_usec);
+ }
+ }
+ chudata.ncodechars = 0;
+ }
+}
+
+
+/* #ifdef CHULDISC*/
+/*
+ * process_ldisc - process line discipline
+ */
+int
+process_ldisc(
+ int s
+ )
+{
+ struct chucode chu;
+ int n;
+ register int i;
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
+ if (n != sizeof chu) {
+ (void) fprintf(stderr, "Expected %d, got %d\n",
+ sizeof chu, n);
+ continue;
+ }
+
+ if (doprocess) {
+ TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chu, &ts);
+ } else {
+ for (i = 0; i < NCHUCHARS; i++) {
+ if (i == 0)
+ diff.tv_sec = diff.tv_usec = 0;
+ else {
+ diff.tv_sec = chu.codetimes[i].tv_sec
+ - chu.codetimes[i-1].tv_sec;
+ diff.tv_usec = chu.codetimes[i].tv_usec
+ - chu.codetimes[i-1].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ }
+ }
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
+ chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
+ diff.tv_sec, diff.tv_usec);
+ }
+ }
+ }
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+/*#endif*/
+
+
+/*
+ * error - print an error message
+ */
+void
+error(
+ char *fmt,
+ char *s1,
+ char *s2
+ )
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, s1, s2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+ exit(1);
+}
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of CHU units permitted */
+#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
+#define NCHUCODES 9 /* expect 9 CHU codes per minute */
+
+/*
+ * When CHU is operating optimally we want the primary clock distance
+ * to come out at 300 ms. Thus, peer.distance in the CHU peer structure
+ * is set to 290 ms and we compute delays which are at least 10 ms long.
+ * The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define CHUDISTANCE 0x00004a3d
+#define CHUBASEDELAY 0x0000028f
+
+/*
+ * To compute a quality for the estimate (a pseudo delay) we add a
+ * fixed 10 ms for each missing code in the minute and add to this
+ * the sum of the differences between the remaining offsets and the
+ * estimated sample offset.
+ */
+#define CHUDELAYPENALTY 0x0000028f
+
+/*
+ * Other constant stuff
+ */
+#define CHUPRECISION (-9) /* what the heck */
+#define CHUREFID "CHU\0"
+
+/*
+ * Default fudge factors
+ */
+#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
+#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
+ * as an l_fp fraction, NZPOBITS is the number of significant bits
+ * in ZEROPTONE.
+ */
+#define ZEROPTONE 0x1999999a
+#define NZPOBITS 29
+
+/*
+ * The CHU table. This gives the expected time of arrival of each
+ * character after the on-time second and is computed as follows:
+ * The CHU time code is sent at 300 bps. Your average UART will
+ * synchronize at the edge of the start bit and will consider the
+ * character complete at the center of the first stop bit, i.e.
+ * 0.031667 ms later. Thus the expected time of each interrupt
+ * is the start bit time plus 0.031667 seconds. These times are
+ * in chutable[]. To this we add such things as propagation delay
+ * and delay fudge factor.
+ */
+#define CHARDELAY 0x081b4e80
+
+static u_long chutable[NCHUCHARS] = {
+ 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */
+ 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */
+ 0x34395810 + CHARDELAY, /* 0.204 (exactly) */
+ 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */
+ 0x472b020c + CHARDELAY, /* 0.278 (exactly) */
+ 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */
+ 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */
+ 0x63958106 + CHARDELAY, /* 0.389 (exactly) */
+ 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */
+ 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */
+};
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp propagation_delay;
+static l_fp fudgefactor;
+static l_fp offset_fudge;
+
+/*
+ * We keep track of the start of the year, watching for changes.
+ * We also keep track of whether the year is a leap year or not.
+ * All because stupid CHU doesn't include the year in the time code.
+ */
+static u_long yearstart;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Time conversion tables imported from the library
+ */
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+
+/*
+ * init_chu - initialize internal chu driver data
+ */
+void
+init_chu(void)
+{
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ propagation_delay.l_ui = 0;
+ propagation_delay.l_uf = DEFPROPDELAY;
+ fudgefactor.l_ui = 0;
+ fudgefactor.l_uf = DEFFILTFUDGE;
+ offset_fudge = propagation_delay;
+ L_ADD(&offset_fudge, &fudgefactor);
+
+ yearstart = 0;
+}
+
+
+void
+chufilter(
+ struct chucode *chuc,
+ l_fp *rtime
+ )
+{
+ register int i;
+ register u_long date_ui;
+ register u_long tmp;
+ register u_char *code;
+ int isneg;
+ int imin;
+ int imax;
+ u_long reftime;
+ l_fp off[NCHUCHARS];
+ l_fp ts;
+ int day, hour, minute, second;
+ static u_char lastcode[NCHUCHARS];
+ extern u_long calyearstart();
+ extern char *mfptoa();
+ void chu_process();
+ extern char *prettydate();
+
+ /*
+ * We'll skip the checks made in the kernel, but assume they've
+ * been done. This means that all characters are BCD and
+ * the intercharacter spacing isn't unreasonable.
+ */
+
+ /*
+ * print the code
+ */
+ for (i = 0; i < NCHUCHARS; i++)
+ printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
+ ((chuc->codechars[i]>>4) & 0xf) + '0');
+ printf("\n");
+
+ /*
+ * Format check. Make sure the two halves match.
+ */
+ for (i = 0; i < NCHUCHARS/2; i++)
+ if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
+ (void) printf("Bad format, halves don't match\n");
+ return;
+ }
+
+ /*
+ * Break out the code into the BCD nibbles. Only need to fiddle
+ * with the first half since both are identical. Note the first
+ * BCD character is the low order nibble, the second the high order.
+ */
+ code = lastcode;
+ for (i = 0; i < NCHUCHARS/2; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ /*
+ * If the first nibble isn't a 6, we're up the creek
+ */
+ code = lastcode;
+ if (*code++ != 6) {
+ (void) printf("Bad format, no 6 at start\n");
+ return;
+ }
+
+ /*
+ * Collect the day, the hour, the minute and the second.
+ */
+ day = *code++;
+ day = MULBY10(day) + *code++;
+ day = MULBY10(day) + *code++;
+ hour = *code++;
+ hour = MULBY10(hour) + *code++;
+ minute = *code++;
+ minute = MULBY10(minute) + *code++;
+ second = *code++;
+ second = MULBY10(second) + *code++;
+
+ /*
+ * Sanity check the day and time. Note that this
+ * only occurs on the 31st through the 39th second
+ * of the minute.
+ */
+ if (day < 1 || day > 366
+ || hour > 23 || minute > 59
+ || second < 31 || second > 39) {
+ (void) printf("Failed date sanity check: %d %d %d %d\n",
+ day, hour, minute, second);
+ return;
+ }
+
+ /*
+ * Compute seconds into the year.
+ */
+ tmp = (u_long)(MULBY24((day-1)) + hour); /* hours */
+ tmp = MULBY60(tmp) + (u_long)minute; /* minutes */
+ tmp = MULBY60(tmp) + (u_long)second; /* seconds */
+
+ /*
+ * Now the fun begins. We demand that the received time code
+ * be within CLOCK_WAYTOOBIG of the receive timestamp, but
+ * there is uncertainty about the year the timestamp is in.
+ * Use the current year start for the first check, this should
+ * work most of the time.
+ */
+ date_ui = tmp + yearstart;
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* looks good */
+
+ /*
+ * Trouble. Next check is to see if the year rolled over and, if
+ * so, try again with the new year's start.
+ */
+ date_ui = calyearstart(rtime->l_ui);
+ if (date_ui != yearstart) {
+ yearstart = date_ui;
+ date_ui += tmp;
+ (void) printf("time %u, code %u, difference %d\n",
+ date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* okay this time */
+ }
+
+ ts.l_uf = 0;
+ ts.l_ui = yearstart;
+ printf("yearstart %s\n", prettydate(&ts));
+ printf("received %s\n", prettydate(rtime));
+ ts.l_ui = date_ui;
+ printf("date_ui %s\n", prettydate(&ts));
+
+ /*
+ * Here we know the year start matches the current system
+ * time. One remaining possibility is that the time code
+ * is in the year previous to that of the system time. This
+ * is only worth checking if the receive timestamp is less
+ * than CLOCK_WAYTOOBIG seconds into the new year.
+ */
+ if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
+ date_ui = tmp + calyearstart(yearstart - CLOCK_WAYTOOBIG);
+ if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
+ goto codeokay;
+ }
+
+ /*
+ * One last possibility is that the time stamp is in the year
+ * following the year the system is in. Try this one before
+ * giving up.
+ */
+ date_ui = tmp + calyearstart(yearstart + (400*24*60*60)); /* 400 days */
+ if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
+ printf("Date hopelessly off\n");
+ return; /* hopeless, let it sync to other peers */
+ }
+
+ codeokay:
+ reftime = date_ui;
+ /*
+ * We've now got the integral seconds part of the time code (we hope).
+ * The fractional part comes from the table. We next compute
+ * the offsets for each character.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ register u_long tmp2;
+
+ off[i].l_ui = date_ui;
+ off[i].l_uf = chutable[i];
+ tmp = chuc->codetimes[i].tv_sec + JAN_1970;
+ TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
+ M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
+ }
+
+ /*
+ * Here is a *big* problem. What one would normally
+ * do here on a machine with lots of clock bits (say
+ * a Vax or the gizmo board) is pick the most positive
+ * offset and the estimate, since this is the one that
+ * is most likely suffered the smallest interrupt delay.
+ * The trouble is that the low order clock bit on an IBM
+ * RT, which is the machine I had in mind when doing this,
+ * ticks at just under the millisecond mark. This isn't
+ * precise enough. What we can do to improve this is to
+ * average all 10 samples and rely on the second level
+ * filtering to pick the least delayed estimate. Trouble
+ * is, this means we have to divide a 64 bit fixed point
+ * number by 10, a procedure which really sucks. Oh, well.
+ * First compute the sum.
+ */
+ date_ui = 0;
+ tmp = 0;
+ for (i = 0; i < NCHUCHARS; i++)
+ M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
+ if (M_ISNEG(date_ui, tmp))
+ isneg = 1;
+ else
+ isneg = 0;
+
+ /*
+ * Here is a multiply-by-0.1 optimization that should apply
+ * just about everywhere. If the magnitude of the sum
+ * is less than 9 we don't have to worry about overflow
+ * out of a 64 bit product, even after rounding.
+ */
+ if (date_ui < 9 || date_ui > 0xfffffff7) {
+ register u_long prod_ui;
+ register u_long prod_uf;
+
+ prod_ui = prod_uf = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT(date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD(prod_ui, prod_uf, date_ui, tmp);
+ }
+
+ /*
+ * Done, round it correctly. Prod_ui contains the
+ * fraction.
+ */
+ if (prod_uf & 0x80000000)
+ prod_ui++;
+ if (isneg)
+ date_ui = 0xffffffff;
+ else
+ date_ui = 0;
+ tmp = prod_ui;
+ /*
+ * date_ui is integral part, tmp is fraction.
+ */
+ } else {
+ register u_long prod_ovr;
+ register u_long prod_ui;
+ register u_long prod_uf;
+ register u_long highbits;
+
+ prod_ovr = prod_ui = prod_uf = 0;
+ if (isneg)
+ highbits = 0xffffffff; /* sign extend */
+ else
+ highbits = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT3(highbits, date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD3(prod_ovr, prod_uf, prod_ui,
+ highbits, date_ui, tmp);
+ }
+
+ if (prod_uf & 0x80000000)
+ M_ADDUF(prod_ovr, prod_ui, (u_long)1);
+ date_ui = prod_ovr;
+ tmp = prod_ui;
+ }
+
+ /*
+ * At this point we have the mean offset, with the integral
+ * part in date_ui and the fractional part in tmp. Store
+ * it in the structure.
+ */
+ /*
+ * Add in fudge factor.
+ */
+ M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
+
+ /*
+ * Find the minimun and maximum offset
+ */
+ imin = imax = 0;
+ for (i = 1; i < NCHUCHARS; i++) {
+ if (L_ISGEQ(&off[i], &off[imax])) {
+ imax = i;
+ } else if (L_ISGEQ(&off[imin], &off[i])) {
+ imin = i;
+ }
+ }
+
+ L_ADD(&off[imin], &offset_fudge);
+ if (imin != imax)
+ L_ADD(&off[imax], &offset_fudge);
+ (void) printf("mean %s, min %s, max %s\n",
+ mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
+ lfptoa(&off[imax], 8));
+}
OpenPOWER on IntegriCloud