]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_msfees.c
This commit was generated by cvs2svn to compensate for changes in r171366,
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_msfees.c
1 /* refclock_ees - clock driver for the EES M201 receiver */
2
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
6
7 #if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
8
9 /* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
10  * were removed as the code was overly hairy, they weren't in use
11  * (hence probably didn't work).  Still in RCS file at cl.cam.ac.uk
12  */
13
14 #include "ntpd.h"
15 #include "ntp_io.h"
16 #include "ntp_refclock.h"
17 #include "ntp_unixtime.h"
18 #include "ntp_calendar.h"
19
20 #include <ctype.h>
21 #if defined(HAVE_BSD_TTYS)
22 #include <sgtty.h>
23 #endif /* HAVE_BSD_TTYS */
24 #if defined(HAVE_SYSV_TTYS)
25 #include <termio.h>
26 #endif /* HAVE_SYSV_TTYS */
27 #if defined(HAVE_TERMIOS)
28 #include <termios.h>
29 #endif
30 #if defined(STREAM)
31 #include <stropts.h>
32 #endif
33
34 #ifdef HAVE_SYS_TERMIOS_H
35 # include <sys/termios.h>
36 #endif
37 #ifdef HAVE_SYS_PPSCLOCK_H
38 # include <sys/ppsclock.h>
39 #endif
40
41 #include "ntp_stdlib.h"
42
43 /*
44         fudgefactor     = fudgetime1;
45         os_delay        = fudgetime2;
46            offset_fudge = os_delay + fudgefactor + inherent_delay;
47         stratumtouse    = fudgeval1 & 0xf
48         debug           = fudgeval2;
49         sloppyclockflag = flags & CLK_FLAG1;
50                 1         log smoothing summary when processing sample
51                 4         dump the buffer from the clock
52                 8         EIOGETKD the last n uS time stamps
53         if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
54         ees->dump_vals  = flags & CLK_FLAG3;
55         ees->usealldata = flags & CLK_FLAG4;
56
57
58         bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
59         bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
60         bug->values[2] = (u_long)ees->status;
61         bug->values[3] = (u_long)ees->lastevent;
62         bug->values[4] = (u_long)ees->reason;
63         bug->values[5] = (u_long)ees->nsamples;
64         bug->values[6] = (u_long)ees->codestate;
65         bug->values[7] = (u_long)ees->day;
66         bug->values[8] = (u_long)ees->hour;
67         bug->values[9] = (u_long)ees->minute;
68         bug->values[10] = (u_long)ees->second;
69         bug->values[11] = (u_long)ees->tz;
70         bug->values[12] = ees->yearstart;
71         bug->values[13] = (ees->leaphold > current_time) ?
72                                 ees->leaphold - current_time : 0;
73         bug->values[14] = inherent_delay[unit].l_uf;
74         bug->values[15] = offset_fudge[unit].l_uf;
75
76         bug->times[0] = ees->reftime;
77         bug->times[1] = ees->arrvtime;
78         bug->times[2] = ees->lastsampletime;
79         bug->times[3] = ees->offset;
80         bug->times[4] = ees->lowoffset;
81         bug->times[5] = ees->highoffset;
82         bug->times[6] = inherent_delay[unit];
83         bug->times[8] = os_delay[unit];
84         bug->times[7] = fudgefactor[unit];
85         bug->times[9] = offset_fudge[unit];
86         bug->times[10]= ees->yearstart, 0;
87         */
88
89 /* This should support the use of an EES M201 receiver with RS232
90  * output (modified to transmit time once per second).
91  *
92  * For the format of the message sent by the clock, see the EESM_
93  * definitions below.
94  *
95  * It appears to run free for an integral number of minutes, until the error
96  * reaches 4mS, at which point it steps at second = 01.
97  * It appears that sometimes it steps 4mS (say at 7 min interval),
98  * then the next minute it decides that it was an error, so steps back.
99  * On the next minute it steps forward again :-(
100  * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
101  * or 9.5uS/S then 3990.5uS at a 7min re-sync,
102  * at which point it may lose the "00" second time stamp.
103  * I assume that the most accurate time is just AFTER the re-sync.
104  * Hence remember the last cycle interval,
105  *
106  * Can run in any one of:
107  *
108  *      PPSCD   PPS signal sets CD which interupts, and grabs the current TOD
109  *      (sun)           *in the interupt code*, so as to avoid problems with
110  *                      the STREAMS scheduling.
111  *
112  * It appears that it goes 16.5 uS slow each second, then every 4 mins it
113  * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
114  */
115
116 /* Definitions */
117 #ifndef MAXUNITS
118 #define MAXUNITS        4       /* maximum number of EES units permitted */
119 #endif
120
121 #ifndef EES232
122 #define EES232  "/dev/ees%d"    /* Device to open to read the data */
123 #endif
124
125 /* Other constant stuff */
126 #ifndef EESPRECISION
127 #define EESPRECISION    (-10)           /* what the heck - 2**-10 = 1ms */
128 #endif
129 #ifndef EESREFID
130 #define EESREFID        "MSF\0"         /* String to identify the clock */
131 #endif
132 #ifndef EESHSREFID
133 #define EESHSREFID      (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
134 #endif
135
136 /* Description of clock */
137 #define EESDESCRIPTION          "EES M201 MSF Receiver"
138
139 /* Speed we run the clock port at. If this is changed the UARTDELAY
140  * value should be recomputed to suit.
141  */
142 #ifndef SPEED232
143 #define SPEED232        B9600   /* 9600 baud */
144 #endif
145
146 /* What is the inherent delay for this mode of working, i.e. when is the
147  * data time stamped.
148  */
149 #define SAFETY_SHIFT    10      /* Split the shift to avoid overflow */
150 #define BITS_TO_L_FP(bits, baud) \
151 (((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
152 #define INH_DELAY_CBREAK        BITS_TO_L_FP(119, 9600)
153 #define INH_DELAY_PPS           BITS_TO_L_FP(  0, 9600)
154
155 #ifndef STREAM_PP1
156 #define STREAM_PP1      "ppsclocd\0<-- patch space for module name1 -->"
157 #endif
158 #ifndef STREAM_PP2
159 #define STREAM_PP2      "ppsclock\0<-- patch space for module name2 -->"
160 #endif
161
162      /* Offsets of the bytes of the serial line code.  The clock gives
163  * local time with a GMT/BST indication. The EESM_ definitions
164  * give offsets into ees->lastcode.
165  */
166 #define EESM_CSEC        0      /* centiseconds - always zero in our clock  */
167 #define EESM_SEC         1      /* seconds in BCD                           */
168 #define EESM_MIN         2      /* minutes in BCD                           */
169 #define EESM_HOUR        3      /* hours in BCD                             */
170 #define EESM_DAYWK       4      /* day of week (Sun = 0 etc)                */
171 #define EESM_DAY         5      /* day of month in BCD                      */
172 #define EESM_MON         6      /* month in BCD                             */
173 #define EESM_YEAR        7      /* year MOD 100 in BCD                      */
174 #define EESM_LEAP        8      /* 0x0f if leap year, otherwise zero        */
175 #define EESM_BST         9      /* 0x03 if BST, 0x00 if GMT                 */
176 #define EESM_MSFOK      10      /* 0x3f if radio good, otherwise zero       */
177                                 /* followed by a frame alignment byte (0xff) /
178                                 /  which is not put into the lastcode buffer*/
179
180 /* Length of the serial time code, in characters.  The first length
181  * is less the frame alignment byte.
182  */
183 #define LENEESPRT       (EESM_MSFOK+1)
184 #define LENEESCODE      (LENEESPRT+1)
185
186      /* Code state. */
187 #define EESCS_WAIT      0       /* waiting for start of timecode */
188 #define EESCS_GOTSOME   1       /* have an incomplete time code buffered */
189
190      /* Default fudge factor and character to receive */
191 #define DEFFUDGETIME    0       /* Default user supplied fudge factor */
192 #ifndef DEFOSTIME
193 #define DEFOSTIME       0       /* Default OS delay -- passed by Make ? */
194 #endif
195 #define DEFINHTIME      INH_DELAY_PPS /* inherent delay due to sample point*/
196
197      /* Limits on things.  Reduce the number of samples to SAMPLEREDUCE by median
198  * elimination.  If we're running with an accurate clock, chose the BESTSAMPLE
199  * as the estimated offset, otherwise average the remainder.
200  */
201 #define FULLSHIFT       6                       /* NCODES root 2 */
202 #define NCODES          (1<< FULLSHIFT)         /* 64 */
203 #define REDUCESHIFT     (FULLSHIFT -1)          /* SAMPLEREDUCE root 2 */
204
205      /* Towards the high ( Why ?) end of half */
206 #define BESTSAMPLE      ((samplereduce * 3) /4) /* 24 */
207
208      /* Leap hold time.  After a leap second the clock will no longer be
209  * reliable until it resynchronizes.  Hope 40 minutes is enough. */
210 #define EESLEAPHOLD     (40 * 60)
211
212 #define EES_STEP_F      (1 << 24) /* the receiver steps in units of about 4ms */
213 #define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
214 #define EES_STEP_NOTE   (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
215 #define EES_STEP_NOTES  50      /* Only do a limited number */
216 #define MAX_STEP        16      /* Max number of steps to remember */
217
218      /* debug is a bit mask of debugging that is wanted */
219 #define DB_SYSLOG_SMPLI         0x0001
220 #define DB_SYSLOG_SMPLE         0x0002
221 #define DB_SYSLOG_SMTHI         0x0004
222 #define DB_SYSLOG_NSMTHE        0x0008
223 #define DB_SYSLOG_NSMTHI        0x0010
224 #define DB_SYSLOG_SMTHE         0x0020
225 #define DB_PRINT_EV             0x0040
226 #define DB_PRINT_CDT            0x0080
227 #define DB_PRINT_CDTC           0x0100
228 #define DB_SYSLOG_KEEPD         0x0800
229 #define DB_SYSLOG_KEEPE         0x1000
230 #define DB_LOG_DELTAS           0x2000
231 #define DB_PRINT_DELTAS         0x4000
232 #define DB_LOG_AWAITMORE        0x8000
233 #define DB_LOG_SAMPLES          0x10000
234 #define DB_NO_PPS               0x20000
235 #define DB_INC_PPS              0x40000
236 #define DB_DUMP_DELTAS          0x80000
237
238      struct eesunit {                   /* EES unit control structure. */
239              struct peer *peer;         /* associated peer structure */
240              struct refclockio io;              /* given to the I/O handler */
241              l_fp       reftime;                /* reference time */
242              l_fp       lastsampletime;         /* time as in txt from last EES msg */
243              l_fp       arrvtime;               /* Time at which pkt arrived */
244              l_fp       codeoffsets[NCODES];    /* the time of arrival of 232 codes */
245              l_fp       offset;                 /* chosen offset        (for clkbug) */
246              l_fp       lowoffset;              /* lowest sample offset (for clkbug) */
247              l_fp       highoffset;             /* highest   "     "    (for clkbug) */
248              char       lastcode[LENEESCODE+6]; /* last time code we received */
249              u_long     lasttime;               /* last time clock heard from */
250              u_long     clocklastgood;          /* last time good radio seen */
251              u_char     lencode;                /* length of code in buffer */
252              u_char     nsamples;               /* number of samples we've collected */
253              u_char     codestate;              /* state of 232 code reception */
254              u_char     unit;                   /* unit number for this guy */
255              u_char     status;                 /* clock status */
256              u_char     lastevent;              /* last clock event */
257              u_char     reason;                 /* reason for last abort */
258              u_char     hour;                   /* hour of day */
259              u_char     minute;                 /* minute of hour */
260              u_char     second;                 /* seconds of minute */
261              char       tz;                     /* timezone from clock */
262              u_char     ttytype;                /* method used */
263              u_char     dump_vals;              /* Should clock values be dumped */
264              u_char     usealldata;             /* Use ALL samples */
265              u_short    day;                    /* day of year from last code */
266              u_long     yearstart;              /* start of current year */
267              u_long     leaphold;               /* time of leap hold expiry */
268              u_long     badformat;              /* number of bad format codes */
269              u_long     baddata;                /* number of invalid time codes */
270              u_long     timestarted;            /* time we started this */
271              long       last_pps_no;            /* The serial # of the last PPS */
272              char       fix_pending;            /* Is a "sync to time" pending ? */
273              /* Fine tuning - compensate for 4 mS ramping .... */
274              l_fp       last_l;                 /* last time stamp */
275              u_char     last_steps[MAX_STEP];   /* Most recent n steps */
276              int        best_av_step;           /* Best guess at average step */
277              char       best_av_step_count;     /* # of steps over used above */
278              char       this_step;              /* Current pos in buffer */
279              int        last_step_late;         /* How late the last step was (0-59) */
280              long       jump_fsecs;             /* # of fractions of a sec last jump */
281              u_long     last_step;              /* time of last step */
282              int        last_step_secs;         /* Number of seconds in last step */
283              int        using_ramp;             /* 1 -> noemal, -1 -> over stepped */
284      };
285 #define last_sec        last_l.l_ui
286 #define last_sfsec      last_l.l_f
287 #define this_uisec      ((ees->arrvtime).l_ui)
288 #define this_sfsec      ((ees->arrvtime).l_f)
289 #define msec(x)         ((x) / (1<<22))
290 #define LAST_STEPS      (sizeof ees->last_steps / sizeof ees->last_steps[0])
291 #define subms(x)        ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
292
293 /* Bitmask for what methods to try to use -- currently only PPS enabled */
294 #define T_CBREAK        1
295 #define T_PPS           8
296 /* macros to test above */
297 #define is_cbreak(x)    ((x)->ttytype & T_CBREAK)
298 #define is_pps(x)       ((x)->ttytype & T_PPS)
299 #define is_any(x)       ((x)->ttytype)
300
301 #define CODEREASON      20      /* reason codes */
302
303 /* Data space for the unit structures.  Note that we allocate these on
304  * the fly, but never give them back. */
305 static struct eesunit *eesunits[MAXUNITS];
306 static u_char unitinuse[MAXUNITS];
307
308 /* Keep the fudge factors separately so they can be set even
309  * when no clock is configured. */
310 static l_fp inherent_delay[MAXUNITS];           /* when time stamp is taken */
311 static l_fp fudgefactor[MAXUNITS];              /* fudgetime1 */
312 static l_fp os_delay[MAXUNITS];                 /* fudgetime2 */
313 static l_fp offset_fudge[MAXUNITS];             /* Sum of above */
314 static u_char stratumtouse[MAXUNITS];
315 static u_char sloppyclockflag[MAXUNITS];
316
317 static int deltas[60];
318
319 static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
320 static l_fp onesec; /* = { 1, 0 }; */
321
322 #ifndef DUMP_BUF_SIZE   /* Size of buffer to be used by dump_buf */
323 #define DUMP_BUF_SIZE   10112
324 #endif
325
326 /* ees_reset - reset the count back to zero */
327 #define ees_reset(ees) (ees)->nsamples = 0; \
328 (ees)->codestate = EESCS_WAIT
329
330 /* ees_event - record and report an event */
331 #define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
332 ees_report_event((ees), (evcode))
333
334      /* Find the precision of the system clock by reading it */
335 #define USECS   1000000
336 #define MINSTEP 5       /* some systems increment uS on each call */
337 #define MAXLOOPS (USECS/9)
338
339 /*
340  * Function prototypes
341  */
342
343 static  int     msfees_start    P((int unit, struct peer *peer));
344 static  void    msfees_shutdown P((int unit, struct peer *peer));
345 static  void    msfees_poll     P((int unit, struct peer *peer));
346 static  void    msfees_init     P((void));
347 static  void    dump_buf        P((l_fp *coffs, int from, int to, char *text));
348 static  void    ees_report_event P((struct eesunit *ees, int code));
349 static  void    ees_receive     P((struct recvbuf *rbufp));
350 static  void    ees_process     P((struct eesunit *ees));
351 #ifdef QSORT_USES_VOID_P
352 static  int     offcompare      P((const void *va, const void *vb));
353 #else
354 static  int     offcompare      P((const l_fp *a, const l_fp *b));
355 #endif /* QSORT_USES_VOID_P */
356
357
358 /*
359  * Transfer vector
360  */
361 struct  refclock refclock_msfees = {
362         msfees_start,           /* start up driver */
363         msfees_shutdown,        /* shut down driver */
364         msfees_poll,            /* transmit poll message */
365         noentry,                /* not used */
366         msfees_init,            /* initialize driver */
367         noentry,                /* not used */
368         NOFLAGS                 /* not used */
369 };
370
371
372 static void
373 dump_buf(
374         l_fp *coffs,
375         int from,
376         int to,
377         char *text
378         )
379 {
380         char buff[DUMP_BUF_SIZE + 80];
381         int i;
382         register char *ptr = buff;
383
384         sprintf(ptr, text);
385         for (i=from; i<to; i++)
386         {       while (*ptr) ptr++;
387         if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff);
388         sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
389         }
390         msyslog(LOG_DEBUG, "D: %s", buff);
391 }
392
393 /* msfees_init - initialize internal ees driver data */
394 static void
395 msfees_init(void)
396 {
397         register int i;
398         /* Just zero the data arrays */
399         memset((char *)eesunits, 0, sizeof eesunits);
400         memset((char *)unitinuse, 0, sizeof unitinuse);
401
402         acceptable_slop.l_ui = 0;
403         acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
404
405         onesec.l_ui = 1;
406         onesec.l_uf = 0;
407
408         /* Initialize fudge factors to default. */
409         for (i = 0; i < MAXUNITS; i++) {
410                 fudgefactor[i].l_ui     = 0;
411                 fudgefactor[i].l_uf     = DEFFUDGETIME;
412                 os_delay[i].l_ui        = 0;
413                 os_delay[i].l_uf        = DEFOSTIME;
414                 inherent_delay[i].l_ui  = 0;
415                 inherent_delay[i].l_uf  = DEFINHTIME;
416                 offset_fudge[i]         = os_delay[i];
417                 L_ADD(&offset_fudge[i], &fudgefactor[i]);
418                 L_ADD(&offset_fudge[i], &inherent_delay[i]);
419                 stratumtouse[i]         = 0;
420                 sloppyclockflag[i]      = 0;
421         }
422 }
423
424
425 /* msfees_start - open the EES devices and initialize data for processing */
426 static int
427 msfees_start(
428         int unit,
429         struct peer *peer
430         )
431 {
432         register struct eesunit *ees;
433         register int i;
434         int fd232 = -1;
435         char eesdev[20];
436         struct termios ttyb, *ttyp;
437         struct refclockproc *pp;
438         pp = peer->procptr;
439
440         if (unit >= MAXUNITS) {
441                 msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
442                         unit, MAXUNITS-1);
443                 return 0;
444         }
445         if (unitinuse[unit]) {
446                 msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
447                 return 0;
448         }
449
450         /* Unit okay, attempt to open the devices.  We do them both at
451          * once to make sure we can */
452         (void) sprintf(eesdev, EES232, unit);
453
454         fd232 = open(eesdev, O_RDWR, 0777);
455         if (fd232 == -1) {
456                 msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
457                 return 0;
458         }
459
460 #ifdef  TIOCEXCL
461         /* Set for exclusive use */
462         if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
463                 msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
464                 goto screwed;
465         }
466 #endif
467
468         /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
469
470         /* Set port characteristics.  If we don't have a STREAMS module or
471          * a clock line discipline, cooked mode is just usable, even though it
472          * strips the top bit.  The only EES byte which uses the top
473          * bit is the year, and we don't use that anyway. If we do
474          * have the line discipline, we choose raw mode, and the
475          * line discipline code will block up the messages.
476          */
477
478         /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
479
480         ttyp = &ttyb;
481         if (tcgetattr(fd232, ttyp) < 0) {
482                 msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
483                 goto screwed;
484         }
485
486         ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
487         ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
488         ttyp->c_oflag = 0;
489         ttyp->c_lflag = ICANON;
490         ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
491         if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
492                 msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
493                 goto screwed;
494         }
495
496         if (tcflush(fd232, TCIOFLUSH) < 0) {
497                 msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
498                 goto screwed;
499         }
500
501         inherent_delay[unit].l_uf = INH_DELAY_PPS;
502
503         /* offset fudge (how *late* the timestamp is) = fudge + os delays */
504         offset_fudge[unit] = os_delay[unit];
505         L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
506         L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
507
508         /* Looks like this might succeed.  Find memory for the structure.
509          * Look to see if there are any unused ones, if not we malloc() one.
510          */
511         if (eesunits[unit] != 0) /* The one we want is okay */
512             ees = eesunits[unit];
513         else {
514                 /* Look for an unused, but allocated struct */
515                 for (i = 0; i < MAXUNITS; i++) {
516                         if (!unitinuse[i] && eesunits[i] != 0)
517                             break;
518                 }
519
520                 if (i < MAXUNITS) {     /* Reclaim this one */
521                         ees = eesunits[i];
522                         eesunits[i] = 0;
523                 }                       /* no spare -- make a new one */
524                 else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
525         }
526         memset((char *)ees, 0, sizeof(struct eesunit));
527         eesunits[unit] = ees;
528
529         /* Set up the structures */
530         ees->peer       = peer;
531         ees->unit       = (u_char)unit;
532         ees->timestarted= current_time;
533         ees->ttytype    = 0;
534         ees->io.clock_recv= ees_receive;
535         ees->io.srcclock= (caddr_t)ees;
536         ees->io.datalen = 0;
537         ees->io.fd      = fd232;
538
539         /* Okay.  Push one of the two (linked into the kernel, or dynamically
540          * loaded) STREAMS module, and give it to the I/O code to start
541          * receiving stuff.
542          */
543
544 #ifdef STREAM
545         {
546                 int rc1;
547                 /* Pop any existing onews first ... */
548                 while (ioctl(fd232, I_POP, 0 ) >= 0) ;
549
550                 /* Now try pushing either of the possible modules */
551                 if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
552                     ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
553                         msyslog(LOG_ERR,
554                                 "ees clock: Push of `%s' and `%s' to %s failed %m",
555                                 STREAM_PP1, STREAM_PP2, eesdev);
556                         goto screwed;
557                 }
558                 else {
559                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
560                                 msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
561                                         (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
562                         ees->ttytype |= T_PPS;
563                 }
564         }
565 #endif /* STREAM */
566
567         /* Add the clock */
568         if (!io_addclock(&ees->io)) {
569                 /* Oh shit.  Just close and return. */
570                 msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
571                 goto screwed;
572         }
573
574
575         /* All done.  Initialize a few random peer variables, then
576          * return success. */
577         peer->precision = sys_precision;
578         peer->stratum   = stratumtouse[unit];
579         if (stratumtouse[unit] <= 1) {
580                 memcpy((char *)&pp->refid, EESREFID, 4);
581                 if (unit > 0 && unit < 10)
582                     ((char *)&pp->refid)[3] = '0' + unit;
583         } else {
584                 peer->refid = htonl(EESHSREFID);
585         }
586         unitinuse[unit] = 1;
587         pp->unitptr = (caddr_t) &eesunits[unit];
588         pp->clockdesc = EESDESCRIPTION;
589         msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
590         return (1);
591
592     screwed:
593         if (fd232 != -1)
594             (void) close(fd232);
595         return (0);
596 }
597
598
599 /* msfees_shutdown - shut down a EES clock */
600 static void
601 msfees_shutdown(
602         int unit,
603         struct peer *peer
604         )
605 {
606         register struct eesunit *ees;
607
608         if (unit >= MAXUNITS) {
609                 msyslog(LOG_ERR,
610                         "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
611                         unit, MAXUNITS);
612                 return;
613         }
614         if (!unitinuse[unit]) {
615                 msyslog(LOG_ERR,
616                         "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
617                 return;
618         }
619
620         /* Tell the I/O module to turn us off.  We're history. */
621         ees = eesunits[unit];
622         io_closeclock(&ees->io);
623         unitinuse[unit] = 0;
624 }
625
626
627 /* ees_report_event - note the occurance of an event */
628 static void
629 ees_report_event(
630         struct eesunit *ees,
631         int code
632         )
633 {
634         if (ees->status != (u_char)code) {
635                 ees->status = (u_char)code;
636                 if (code != CEVNT_NOMINAL)
637                     ees->lastevent = (u_char)code;
638                 /* Should report event to trap handler in here.
639                  * Soon...
640                  */
641         }
642 }
643
644
645 /* ees_receive - receive data from the serial interface on an EES clock */
646 static void
647 ees_receive(
648         struct recvbuf *rbufp
649         )
650 {
651         register int n_sample;
652         register int day;
653         register struct eesunit *ees;
654         register u_char *dpt;           /* Data PoinTeR: move along ... */
655         register u_char *dpend;         /* Points just *after* last data char */
656         register char *cp;
657         l_fp tmp;
658         int call_pps_sample = 0;
659         l_fp pps_arrvstamp;
660         int     sincelast;
661         int     pps_step = 0;
662         int     suspect_4ms_step = 0;
663         struct ppsclockev ppsclockev;
664         long *ptr = (long *) &ppsclockev;
665         int rc;
666         int request;
667 #ifdef HAVE_CIOGETEV
668         request = CIOGETEV;
669 #endif
670 #ifdef HAVE_TIOCGPPSEV
671         request = TIOCGPPSEV;
672 #endif
673
674         /* Get the clock this applies to and a pointer to the data */
675         ees = (struct eesunit *)rbufp->recv_srcclock;
676         dpt = (u_char *)&rbufp->recv_space;
677         dpend = dpt + rbufp->recv_length;
678         if ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
679             printf("[%d] ", rbufp->recv_length);
680
681         /* Check out our state and process appropriately */
682         switch (ees->codestate) {
683             case EESCS_WAIT:
684                 /* Set an initial guess at the timestamp as the recv time.
685                  * If just running in CBREAK mode, we can't improve this.
686                  * If we have the CLOCK Line Discipline, PPSCD, or sime such,
687                  * then we will do better later ....
688                  */
689                 ees->arrvtime = rbufp->recv_time;
690                 ees->codestate = EESCS_GOTSOME;
691                 ees->lencode = 0;
692                 /*FALLSTHROUGH*/
693
694             case EESCS_GOTSOME:
695                 cp = &(ees->lastcode[ees->lencode]);
696
697                 /* Gobble the bytes until the final (possibly stripped) 0xff */
698                 while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
699                         *cp++ = (char)*dpt++;
700                         ees->lencode++;
701                         /* Oh dear -- too many bytes .. */
702                         if (ees->lencode > LENEESPRT) {
703                                 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
704                                         msyslog(LOG_INFO,
705                                                 "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
706                                                 ees->lencode, dpend - dpt, LENEESPRT,
707 #define D(x) (ees->lastcode[x])
708                                                 D(0), D(1), D(2), D(3), D(4), D(5), D(6),
709                                                 D(7), D(8), D(9), D(10), D(11), D(12));
710 #undef  D
711                                 ees->badformat++;
712                                 ees->reason = CODEREASON + 1;
713                                 ees_event(ees, CEVNT_BADREPLY);
714                                 ees_reset(ees);
715                                 return;
716                         }
717                 }
718                 /* Gave up because it was end of the buffer, rather than ff */
719                 if (dpt == dpend) {
720                         /* Incomplete.  Wait for more. */
721                         if (debug & DB_LOG_AWAITMORE)
722                             msyslog(LOG_INFO,
723                                     "I: ees clock %d: %p == %p: await more",
724                                     ees->unit, dpt, dpend);
725                         return;
726                 }
727
728                 /* This shouldn't happen ... ! */
729                 if ((*dpt & 0x7f) != 0x7f) {
730                         msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
731                         ees->badformat++;
732                         ees->reason = CODEREASON + 2;
733                         ees_event(ees, CEVNT_BADREPLY);
734                         ees_reset(ees);
735                         return;
736                 }
737
738                 /* Skip the 0xff */
739                 dpt++;
740
741                 /* Finally, got a complete buffer.  Mainline code will
742                  * continue on. */
743                 cp = ees->lastcode;
744                 break;
745
746             default:
747                 msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
748                         ees->unit, ees->codestate);
749                 ees->reason = CODEREASON + 5;
750                 ees_event(ees, CEVNT_FAULT);
751                 ees_reset(ees);
752                 return;
753         }
754
755         /* Boy!  After all that crap, the lastcode buffer now contains
756          * something we hope will be a valid time code.  Do length
757          * checks and sanity checks on constant data.
758          */
759         ees->codestate = EESCS_WAIT;
760         ees->lasttime = current_time;
761         if (ees->lencode != LENEESPRT) {
762                 ees->badformat++;
763                 ees->reason = CODEREASON + 6;
764                 ees_event(ees, CEVNT_BADREPLY);
765                 ees_reset(ees);
766                 return;
767         }
768
769         cp = ees->lastcode;
770
771         /* Check that centisecond is zero */
772         if (cp[EESM_CSEC] != 0) {
773                 ees->baddata++;
774                 ees->reason = CODEREASON + 7;
775                 ees_event(ees, CEVNT_BADREPLY);
776                 ees_reset(ees);
777                 return;
778         }
779
780         /* Check flag formats */
781         if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
782                 ees->badformat++;
783                 ees->reason = CODEREASON + 8;
784                 ees_event(ees, CEVNT_BADREPLY);
785                 ees_reset(ees);
786                 return;
787         }
788
789         if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
790                 ees->badformat++;
791                 ees->reason = CODEREASON + 9;
792                 ees_event(ees, CEVNT_BADREPLY);
793                 ees_reset(ees);
794                 return;
795         }
796
797         if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
798                 ees->badformat++;
799                 ees->reason = CODEREASON + 10;
800                 ees_event(ees, CEVNT_BADREPLY);
801                 ees_reset(ees);
802                 return;
803         }
804
805         /* So far, so good.  Compute day, hours, minutes, seconds,
806          * time zone.  Do range checks on these.
807          */
808
809 #define bcdunpack(val)  ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
810 #define istrue(x)       ((x)?1:0)
811
812         ees->second  = bcdunpack(cp[EESM_SEC]);  /* second       */
813         ees->minute  = bcdunpack(cp[EESM_MIN]);  /* minute       */
814         ees->hour    = bcdunpack(cp[EESM_HOUR]); /* hour         */
815
816         day          = bcdunpack(cp[EESM_DAY]);  /* day of month */
817
818         switch (bcdunpack(cp[EESM_MON])) {       /* month        */
819
820                 /*  Add in lengths of all previous months.  Add one more
821                     if it is a leap year and after February.
822                 */
823             case 12:    day += NOV;                       /*FALLSTHROUGH*/
824             case 11:    day += OCT;                       /*FALLSTHROUGH*/
825             case 10:    day += SEP;                       /*FALLSTHROUGH*/
826             case  9:    day += AUG;                       /*FALLSTHROUGH*/
827             case  8:    day += JUL;                       /*FALLSTHROUGH*/
828             case  7:    day += JUN;                       /*FALLSTHROUGH*/
829             case  6:    day += MAY;                       /*FALLSTHROUGH*/
830             case  5:    day += APR;                       /*FALLSTHROUGH*/
831             case  4:    day += MAR;                       /*FALLSTHROUGH*/
832             case  3:    day += FEB;
833                 if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
834             case  2:    day += JAN;                       /*FALLSTHROUGH*/
835             case  1:    break;
836             default:    ees->baddata++;
837                 ees->reason = CODEREASON + 11;
838                 ees_event(ees, CEVNT_BADDATE);
839                 ees_reset(ees);
840                 return;
841         }
842
843         ees->day     = day;
844
845         /* Get timezone. The clocktime routine wants the number
846          * of hours to add to the delivered time to get UT.
847          * Currently -1 if BST flag set, 0 otherwise.  This
848          * is the place to tweak things if double summer time
849          * ever happens.
850          */
851         ees->tz      = istrue(cp[EESM_BST]) ? -1 : 0;
852
853         if (ees->day > 366 || ees->day < 1 ||
854             ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
855                 ees->baddata++;
856                 ees->reason = CODEREASON + 12;
857                 ees_event(ees, CEVNT_BADDATE);
858                 ees_reset(ees);
859                 return;
860         }
861
862         n_sample = ees->nsamples;
863
864         /* Now, compute the reference time value: text -> tmp.l_ui */
865         if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
866                        ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
867                        &tmp.l_ui)) {
868                 ees->baddata++;
869                 ees->reason = CODEREASON + 13;
870                 ees_event(ees, CEVNT_BADDATE);
871                 ees_reset(ees);
872                 return;
873         }
874         tmp.l_uf = 0;
875
876         /*  DON'T use ees->arrvtime -- it may be < reftime */
877         ees->lastsampletime = tmp;
878
879         /* If we are synchronised to the radio, update the reference time.
880          * Also keep a note of when clock was last good.
881          */
882         if (istrue(cp[EESM_MSFOK])) {
883                 ees->reftime = tmp;
884                 ees->clocklastgood = current_time;
885         }
886
887
888         /* Compute the offset.  For the fractional part of the
889          * offset we use the expected delay for the message.
890          */
891         ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
892         ees->codeoffsets[n_sample].l_uf = 0;
893
894         /* Number of seconds since the last step */
895         sincelast = this_uisec - ees->last_step;
896
897         memset((char *) &ppsclockev, 0, sizeof ppsclockev);
898
899         rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
900         if (debug & DB_PRINT_EV) fprintf(stderr,
901                                          "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
902                                          DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
903                                          rc, errno, ptr[0], ptr[1], ptr[2]);
904
905         /* If we managed to get the time of arrival, process the info */
906         if (rc >= 0) {
907                 int conv = -1;
908                 pps_step = ppsclockev.serial - ees->last_pps_no;
909
910                 /* Possible that PPS triggered, but text message didn't */
911                 if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
912                 if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
913                 if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
914
915                 /* allow for single loss of PPS only */
916                 if (pps_step != 1 && pps_step != 2)
917                     fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
918                             ppsclockev.serial, ees->last_pps_no, pps_step);
919                 else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
920                     fprintf(stderr, "buftvtots failed\n");
921                 else {  /* if ((ABS(time difference) - 0.25) < 0)
922                          * then believe it ...
923                          */
924                         l_fp diff;
925                         diff = pps_arrvstamp;
926                         conv = 0;
927                         L_SUB(&diff, &ees->arrvtime);
928                         if (debug & DB_PRINT_CDT)
929                             printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
930                                    DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
931                                    (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
932                                    (long)diff.l_ui, (long)diff.l_uf,
933                                    ctime(&(ppsclockev.tv.tv_sec)));
934                         if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
935                         L_SUB(&diff, &acceptable_slop);
936                         if (L_ISNEG(&diff)) {   /* AOK -- pps_sample */
937                                 ees->arrvtime = pps_arrvstamp;
938                                 conv++;
939                                 call_pps_sample++;
940                         }
941                         /* Some loss of some signals around sec = 1 */
942                         else if (ees->second == 1) {
943                                 diff = pps_arrvstamp;
944                                 L_ADD(&diff, &onesec);
945                                 L_SUB(&diff, &ees->arrvtime);
946                                 if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
947                                 L_SUB(&diff, &acceptable_slop);
948                                 msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
949                                         pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
950                                         pps_arrvstamp.l_uf,
951                                         ees->arrvtime.l_uf,
952                                         diff.l_ui, diff.l_uf,
953                                         (int)ppsclockev.tv.tv_usec,
954                                         ctime(&(ppsclockev.tv.tv_sec)));
955                                 if (L_ISNEG(&diff)) {   /* AOK -- pps_sample */
956                                         suspect_4ms_step |= 2;
957                                         ees->arrvtime = pps_arrvstamp;
958                                         L_ADD(&ees->arrvtime, &onesec);
959                                         conv++;
960                                         call_pps_sample++;
961                                 }
962                         }
963                 }
964                 ees->last_pps_no = ppsclockev.serial;
965                 if (debug & DB_PRINT_CDTC)
966                     printf(
967                             "[%x] %08lx %08lx %d u%d (%d %d)\n",
968                             DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
969                             (long)pps_arrvstamp.l_uf, conv, ees->unit,
970                             call_pps_sample, pps_step);
971         }
972
973         /* See if there has been a 4ms jump at a minute boundry */
974         {       l_fp    delta;
975 #define delta_isec      delta.l_ui
976 #define delta_ssec      delta.l_i
977 #define delta_sfsec     delta.l_f
978         long    delta_f_abs;
979
980         delta.l_i = ees->arrvtime.l_i;
981         delta.l_f = ees->arrvtime.l_f;
982
983         L_SUB(&delta, &ees->last_l);
984         delta_f_abs = delta_sfsec;
985         if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
986
987         /* Dump the deltas each minute */
988         if (debug & DB_DUMP_DELTAS)
989         {       if (/*0 <= ees->second && */
990                 ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
991         /* Dump on second 1, as second 0 sometimes missed */
992         if (ees->second == 1) {
993                 char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
994                 char *cptr=text;
995                 int i;
996                 for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
997                         sprintf(cptr, " %d.%04d",
998                                 msec(deltas[i]), subms(deltas[i]));
999                         while (*cptr) cptr++;
1000                 }
1001                 msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
1002                         msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
1003                         msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
1004                         text+1);
1005                 for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
1006         }
1007         }
1008
1009         /* Lets see if we have a 4 mS step at a minute boundaary */
1010         if (    ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
1011                 (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
1012                 (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
1013                 (sincelast < 0 || sincelast > 122)
1014                 ) {     /* 4ms jump at min boundry */
1015                 int old_sincelast;
1016                 int count=0;
1017                 int sum = 0;
1018                 /* Yes -- so compute the ramp time */
1019                 if (ees->last_step == 0) sincelast = 0;
1020                 old_sincelast = sincelast;
1021
1022                 /* First time in, just set "ees->last_step" */
1023                 if(ees->last_step) {
1024                         int other_step = 0;
1025                         int third_step = 0;
1026                         int this_step = (sincelast + (60 /2)) / 60;
1027                         int p_step = ees->this_step;
1028                         int p;
1029                         ees->last_steps[p_step] = this_step;
1030                         p= p_step;
1031                         p_step++;
1032                         if (p_step >= LAST_STEPS) p_step = 0;
1033                         ees->this_step = p_step;
1034                                 /* Find the "average" interval */
1035                         while (p != p_step) {
1036                                 int this = ees->last_steps[p];
1037                                 if (this == 0) break;
1038                                 if (this != this_step) {
1039                                         if (other_step == 0 && (
1040                                                 this== (this_step +2) ||
1041                                                 this== (this_step -2) ||
1042                                                 this== (this_step +1) ||
1043                                                 this== (this_step -1)))
1044                                             other_step = this;
1045                                         if (other_step != this) {
1046                                                 int idelta = (this_step - other_step);
1047                                                 if (idelta < 0) idelta = - idelta;
1048                                                 if (third_step == 0 && (
1049                                                         (idelta == 1) ? (
1050                                                                 this == (other_step +1) ||
1051                                                                 this == (other_step -1) ||
1052                                                                 this == (this_step +1) ||
1053                                                                 this == (this_step -1))
1054                                                         :
1055                                                         (
1056                                                                 this == (this_step + other_step)/2
1057                                                                 )
1058                                                         )) third_step = this;
1059                                                 if (third_step != this) break;
1060                                         }
1061                                 }
1062                                 sum += this;
1063                                 p--;
1064                                 if (p < 0) p += LAST_STEPS;
1065                                 count++;
1066                         }
1067                         msyslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
1068                         if (count != 0) sum = ((sum * 60) + (count /2)) / count;
1069 #define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
1070                         msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
1071                                 ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1072                                 SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1073                         printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
1074                                ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1075                                SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1076 #undef SV
1077                         ees->jump_fsecs = delta_sfsec;
1078                         ees->using_ramp = 1;
1079                         if (sincelast > 170)
1080                             ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
1081                         else ees->last_step_late = 30;
1082                         if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
1083                         if (ees->last_step_late < 0) ees->last_step_late = 0;
1084                         if (ees->last_step_late >= 60) ees->last_step_late = 59;
1085                         sincelast = 0;
1086                 }
1087                 else {  /* First time in -- just save info */
1088                         ees->last_step_late = 30;
1089                         ees->jump_fsecs = delta_sfsec;
1090                         ees->using_ramp = 1;
1091                         sum = 4 * 60;
1092                 }
1093                 ees->last_step = this_uisec;
1094                 printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
1095                        ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
1096                        ees->second, old_sincelast, ees->last_step_late, count, sum,
1097                        ees->last_step_secs);
1098                 msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
1099                         ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
1100                         old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
1101                 if (sum) ees->last_step_secs = sum;
1102         }
1103         /* OK, so not a 4ms step at a minute boundry */
1104         else {
1105                 if (suspect_4ms_step) msyslog(LOG_ERR,
1106                                               "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
1107                                               ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
1108                                               msec(EES_STEP_F - EES_STEP_F_GRACE),
1109                                               subms(EES_STEP_F - EES_STEP_F_GRACE),
1110                                               (int)msec(delta_f_abs),
1111                                               (int)subms(delta_f_abs),
1112                                               msec(EES_STEP_F + EES_STEP_F_GRACE),
1113                                               subms(EES_STEP_F + EES_STEP_F_GRACE),
1114                                               ees->second,
1115                                               sincelast);
1116                 if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
1117                         static int ees_step_notes = EES_STEP_NOTES;
1118                         if (ees_step_notes > 0) {
1119                                 ees_step_notes--;
1120                                 printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
1121                                        ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
1122                                        ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
1123                                 msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
1124                                         ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
1125                         }
1126                 }
1127         }
1128         }
1129         ees->last_l = ees->arrvtime;
1130
1131         /* IF we have found that it's ramping
1132          * && it's within twice the expected ramp period
1133          * && there is a non zero step size (avoid /0 !)
1134          * THEN we twiddle things
1135          */
1136         if (ees->using_ramp &&
1137             sincelast < (ees->last_step_secs)*2 &&
1138             ees->last_step_secs)
1139         {       long    sec_of_ramp = sincelast + ees->last_step_late;
1140         long    fsecs;
1141         l_fp    inc;
1142
1143         /* Ramp time may vary, so may ramp for longer than last time */
1144         if (sec_of_ramp > (ees->last_step_secs + 120))
1145             sec_of_ramp =  ees->last_step_secs;
1146
1147         /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
1148         fsecs = sec_of_ramp * (ees->jump_fsecs /  ees->last_step_secs);
1149
1150         if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
1151                                            "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
1152                                            DB_LOG_DELTAS,
1153                                            ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
1154                                            pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1155         if (debug & DB_PRINT_DELTAS) printf(
1156                 "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
1157                 ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
1158                 (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1159
1160         /* Must sign extend the result */
1161         inc.l_i = (fsecs < 0) ? -1 : 0;
1162         inc.l_f = fsecs;
1163         if (debug & DB_INC_PPS)
1164         {       L_SUB(&pps_arrvstamp, &inc);
1165         L_SUB(&ees->arrvtime, &inc);
1166         }
1167         else
1168         {       L_ADD(&pps_arrvstamp, &inc);
1169         L_ADD(&ees->arrvtime, &inc);
1170         }
1171         }
1172         else {
1173                 if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
1174                                                    "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
1175                                                    DB_LOG_DELTAS,
1176                                                    ees->unit, ees->using_ramp,
1177                                                    sincelast,
1178                                                    (ees->last_step_secs)*2,
1179                                                    ees->last_step_secs);
1180                 if (debug & DB_PRINT_DELTAS) printf(
1181                         "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
1182                         DB_LOG_DELTAS,
1183                         ees->unit, ees->using_ramp,
1184                         sincelast,
1185                         (ees->last_step_secs)*2,
1186                         ees->last_step_secs);
1187         }
1188
1189         L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
1190         L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
1191
1192         if (call_pps_sample && !(debug & DB_NO_PPS)) {
1193                 /* Sigh -- it expects its args negated */
1194                 L_NEG(&pps_arrvstamp);
1195                 /*
1196                  * I had to disable this here, since it appears there is no pointer to the
1197                  * peer structure.
1198                  *
1199                  (void) pps_sample(peer, &pps_arrvstamp);
1200                 */
1201         }
1202
1203         /* Subtract off the local clock time stamp */
1204         L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
1205         if (debug & DB_LOG_SAMPLES) msyslog(LOG_ERR,
1206                                             "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
1207                                             ees->unit, DB_LOG_DELTAS, n_sample,
1208                                             ees->codeoffsets[n_sample].l_f,
1209                                             ees->codeoffsets[n_sample].l_f / 4295,
1210                                             pps_arrvstamp.l_f,
1211                                             pps_arrvstamp.l_f /4295,
1212                                             (debug & DB_NO_PPS) ? " [no PPS]" : "");
1213
1214         if (ees->nsamples++ == NCODES-1) ees_process(ees);
1215
1216         /* Done! */
1217 }
1218
1219
1220 /* offcompare - auxiliary comparison routine for offset sort */
1221
1222 #ifdef QSORT_USES_VOID_P
1223 static int
1224 offcompare(
1225         const void *va,
1226         const void *vb
1227         )
1228 {
1229         const l_fp *a = (const l_fp *)va;
1230         const l_fp *b = (const l_fp *)vb;
1231         return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
1232 }
1233 #else
1234 static int
1235 offcompare(
1236         const l_fp *a,
1237         const l_fp *b
1238         )
1239 {
1240         return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
1241 }
1242 #endif /* QSORT_USES_VOID_P */
1243
1244
1245 /* ees_process - process a pile of samples from the clock */
1246 static void
1247 ees_process(
1248         struct eesunit *ees
1249         )
1250 {
1251         static int last_samples = -1;
1252         register int i, j;
1253         register int noff;
1254         register l_fp *coffs = ees->codeoffsets;
1255         l_fp offset, tmp;
1256         double dispersion;      /* ++++ */
1257         int lostsync, isinsync;
1258         int samples = ees->nsamples;
1259         int samplelog = 0;      /* keep "gcc -Wall" happy ! */
1260         int samplereduce = (samples + 1) / 2;
1261         double doffset;
1262
1263         /* Reset things to zero so we don't have to worry later */
1264         ees_reset(ees);
1265
1266         if (sloppyclockflag[ees->unit]) {
1267                 samplelog = (samples <  2) ? 0 :
1268                         (samples <  5) ? 1 :
1269                         (samples <  9) ? 2 :
1270                         (samples < 17) ? 3 :
1271                         (samples < 33) ? 4 : 5;
1272                 samplereduce = (1 << samplelog);
1273         }
1274
1275         if (samples != last_samples &&
1276             ((samples != (last_samples-1)) || samples < 3)) {
1277                 msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
1278                         samples, last_samples, samplereduce);
1279                 last_samples = samples;
1280         }
1281         if (samples < 1) return;
1282
1283         /* If requested, dump the raw data we have in the buffer */
1284         if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw  data  is:");
1285
1286         /* Sort the offsets, trim off the extremes, then choose one. */
1287         qsort((char *) coffs, (size_t)samples, sizeof(l_fp), offcompare);
1288
1289         noff = samples;
1290         i = 0;
1291         while ((noff - i) > samplereduce) {
1292                 /* Trim off the sample which is further away
1293                  * from the median.  We work this out by doubling
1294                  * the median, subtracting off the end samples, and
1295                  * looking at the sign of the answer, using the
1296                  * identity (c-b)-(b-a) == 2*b-a-c
1297                  */
1298                 tmp = coffs[(noff + i)/2];
1299                 L_ADD(&tmp, &tmp);
1300                 L_SUB(&tmp, &coffs[i]);
1301                 L_SUB(&tmp, &coffs[noff-1]);
1302                 if (L_ISNEG(&tmp)) noff--; else i++;
1303         }
1304
1305         /* If requested, dump the reduce data we have in the buffer */
1306         if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced    to:");
1307
1308         /* What we do next depends on the setting of the sloppy clock flag.
1309          * If it is on, average the remainder to derive our estimate.
1310          * Otherwise, just pick a representative value from the remaining stuff
1311          */
1312         if (sloppyclockflag[ees->unit]) {
1313                 offset.l_ui = offset.l_uf = 0;
1314                 for (j = i; j < noff; j++)
1315                     L_ADD(&offset, &coffs[j]);
1316                 for (j = samplelog; j > 0; j--)
1317                     L_RSHIFTU(&offset);
1318         }
1319         else offset = coffs[i+BESTSAMPLE];
1320
1321         /* Compute the dispersion as the difference between the
1322          * lowest and highest offsets that remain in the
1323          * consideration list.
1324          *
1325          * It looks like MOST clocks have MOD (max error), so halve it !
1326          */
1327         tmp = coffs[noff-1];
1328         L_SUB(&tmp, &coffs[i]);
1329 #define FRACT_SEC(n) ((1 << 30) / (n/2))
1330         dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
1331         if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
1332                 (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
1333                 "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
1334                 debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
1335                 offset.l_f / 4295, offset.l_f,
1336                 (dispersion * 1526) / 100,
1337                 (sloppyclockflag[ees->unit]) ? " by averaging" : "",
1338                 FRACT_SEC(10) / 4295,
1339                 (coffs[0].l_f) / 4295,
1340                 i,
1341                 (coffs[i].l_f) / 4295,
1342                 (coffs[samples/2].l_f) / 4295,
1343                 (coffs[i+BESTSAMPLE].l_f) / 4295,
1344                 noff-1,
1345                 (coffs[noff-1].l_f) / 4295,
1346                 (coffs[samples-1].l_f) / 4295);
1347
1348         /* Are we playing silly wotsits ?
1349          * If we are using all data, see if there is a "small" delta,
1350          * and if so, blurr this with 3/4 of the delta from the last value
1351          */
1352         if (ees->usealldata && ees->offset.l_uf) {
1353                 long diff = (long) (ees->offset.l_uf - offset.l_uf);
1354
1355                 /* is the delta small enough ? */
1356                 if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
1357                         int samd = (64 * 4) / samples;
1358                         long new;
1359                         if (samd < 2) samd = 2;
1360                         new = offset.l_uf + ((diff * (samd -1)) / samd);
1361
1362                         /* Sign change -> need to fix up int part */
1363                         if ((new & 0x80000000) !=
1364                             (((long) offset.l_uf) & 0x80000000))
1365                         {       NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
1366                                         msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
1367                                                 new & 0x80000000,
1368                                                 ((long) offset.l_uf) & 0x80000000,
1369                                                 new, (long) offset.l_uf,
1370                                                 (new < 0) ? -1 : 1);
1371                                 offset.l_ui += (new < 0) ? -1 : 1;
1372                         }
1373                         dispersion /= 4;
1374                         if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
1375                                 (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
1376                                 "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
1377                                 debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
1378                                 ((long) offset.l_uf) / 4295, new / 4295,
1379                                 (dispersion * 1526) / 100);
1380                         offset.l_uf = (unsigned long) new;
1381                 }
1382                 else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1383                         (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1384                         "[%x] No smooth as delta not %d < %ld < %d",
1385                         debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1386                         - FRACT_SEC(100), diff, FRACT_SEC(100));
1387         }
1388         else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1389                 (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1390                 "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
1391                 debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1392                 ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
1393                 offset.l_f, ees->offset.l_f - offset.l_f);
1394
1395         /* Collect offset info for debugging info */
1396         ees->offset = offset;
1397         ees->lowoffset = coffs[i];
1398         ees->highoffset = coffs[noff-1];
1399
1400         /* Determine synchronization status.  Can be unsync'd either
1401          * by a report from the clock or by a leap hold.
1402          *
1403          * Loss of the radio signal for a short time does not cause
1404          * us to go unsynchronised, since the receiver keeps quite
1405          * good time on its own.  The spec says 20ms in 4 hours; the
1406          * observed drift in our clock (Cambridge) is about a second
1407          * a day, but even that keeps us within the inherent tolerance
1408          * of the clock for about 15 minutes. Observation shows that
1409          * the typical "short" outage is 3 minutes, so to allow us
1410          * to ride out those, we will give it 5 minutes.
1411          */
1412         lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
1413         isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
1414
1415         /* Done.  Use time of last good, synchronised code as the
1416          * reference time, and lastsampletime as the receive time.
1417          */
1418         if (ees->fix_pending) {
1419                 msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
1420                         ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
1421                 ees->fix_pending = 0;
1422         }
1423         LFPTOD(&offset, doffset);
1424         refclock_receive(ees->peer);
1425         ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
1426 }
1427
1428 /* msfees_poll - called by the transmit procedure */
1429 static void
1430 msfees_poll(
1431         int unit,
1432         struct peer *peer
1433         )
1434 {
1435         if (unit >= MAXUNITS) {
1436                 msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
1437                         unit);
1438                 return;
1439         }
1440         if (!unitinuse[unit]) {
1441                 msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
1442                         unit);
1443                 return;
1444         }
1445
1446         ees_process(eesunits[unit]);
1447
1448         if ((current_time - eesunits[unit]->lasttime) > 150)
1449             ees_event(eesunits[unit], CEVNT_FAULT);
1450 }
1451
1452
1453 #else
1454 int refclock_msfees_bs;
1455 #endif /* REFCLOCK */