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