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