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