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