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