1 /* $NetBSD: refclock_msfees.c,v 1.3 2006/06/11 19:34:12 kardel Exp $ */
3 /* refclock_ees - clock driver for the EES M201 receiver */
9 #if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
11 /* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
12 * were removed as the code was overly hairy, they weren't in use
13 * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
18 #include "ntp_refclock.h"
19 #include "ntp_unixtime.h"
20 #include "ntp_calendar.h"
23 #if defined(HAVE_BSD_TTYS)
25 #endif /* HAVE_BSD_TTYS */
26 #if defined(HAVE_SYSV_TTYS)
28 #endif /* HAVE_SYSV_TTYS */
29 #if defined(HAVE_TERMIOS)
36 #ifdef HAVE_SYS_TERMIOS_H
37 # include <sys/termios.h>
39 #ifdef HAVE_SYS_PPSCLOCK_H
40 # include <sys/ppsclock.h>
43 #include "ntp_stdlib.h"
47 fudgefactor = fudgetime1;
48 os_delay = fudgetime2;
49 offset_fudge = os_delay + fudgefactor + inherent_delay;
50 stratumtouse = fudgeval1 & 0xf
52 sloppyclockflag = flags & CLK_FLAG1;
53 1 log smoothing summary when processing sample
54 4 dump the buffer from the clock
55 8 EIOGETKD the last n uS time stamps
56 if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
57 ees->dump_vals = flags & CLK_FLAG3;
58 ees->usealldata = flags & CLK_FLAG4;
61 bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
62 bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
63 bug->values[2] = (u_long)ees->status;
64 bug->values[3] = (u_long)ees->lastevent;
65 bug->values[4] = (u_long)ees->reason;
66 bug->values[5] = (u_long)ees->nsamples;
67 bug->values[6] = (u_long)ees->codestate;
68 bug->values[7] = (u_long)ees->day;
69 bug->values[8] = (u_long)ees->hour;
70 bug->values[9] = (u_long)ees->minute;
71 bug->values[10] = (u_long)ees->second;
72 bug->values[11] = (u_long)ees->tz;
73 bug->values[12] = ees->yearstart;
74 bug->values[13] = (ees->leaphold > current_time) ?
75 ees->leaphold - current_time : 0;
76 bug->values[14] = inherent_delay[unit].l_uf;
77 bug->values[15] = offset_fudge[unit].l_uf;
79 bug->times[0] = ees->reftime;
80 bug->times[1] = ees->arrvtime;
81 bug->times[2] = ees->lastsampletime;
82 bug->times[3] = ees->offset;
83 bug->times[4] = ees->lowoffset;
84 bug->times[5] = ees->highoffset;
85 bug->times[6] = inherent_delay[unit];
86 bug->times[8] = os_delay[unit];
87 bug->times[7] = fudgefactor[unit];
88 bug->times[9] = offset_fudge[unit];
89 bug->times[10]= ees->yearstart, 0;
92 /* This should support the use of an EES M201 receiver with RS232
93 * output (modified to transmit time once per second).
95 * For the format of the message sent by the clock, see the EESM_
98 * It appears to run free for an integral number of minutes, until the error
99 * reaches 4mS, at which point it steps at second = 01.
100 * It appears that sometimes it steps 4mS (say at 7 min interval),
101 * then the next minute it decides that it was an error, so steps back.
102 * On the next minute it steps forward again :-(
103 * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
104 * or 9.5uS/S then 3990.5uS at a 7min re-sync,
105 * at which point it may lose the "00" second time stamp.
106 * I assume that the most accurate time is just AFTER the re-sync.
107 * Hence remember the last cycle interval,
109 * Can run in any one of:
111 * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
112 * (sun) *in the interupt code*, so as to avoid problems with
113 * the STREAMS scheduling.
115 * It appears that it goes 16.5 uS slow each second, then every 4 mins it
116 * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
121 #define MAXUNITS 4 /* maximum number of EES units permitted */
125 #define EES232 "/dev/ees%d" /* Device to open to read the data */
128 /* Other constant stuff */
130 #define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
133 #define EESREFID "MSF\0" /* String to identify the clock */
136 #define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
139 /* Description of clock */
140 #define EESDESCRIPTION "EES M201 MSF Receiver"
142 /* Speed we run the clock port at. If this is changed the UARTDELAY
143 * value should be recomputed to suit.
146 #define SPEED232 B9600 /* 9600 baud */
149 /* What is the inherent delay for this mode of working, i.e. when is the
152 #define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
153 #define BITS_TO_L_FP(bits, baud) \
154 (((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
155 #define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
156 #define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
159 #define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
162 #define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
165 /* Offsets of the bytes of the serial line code. The clock gives
166 * local time with a GMT/BST indication. The EESM_ definitions
167 * give offsets into ees->lastcode.
169 #define EESM_CSEC 0 /* centiseconds - always zero in our clock */
170 #define EESM_SEC 1 /* seconds in BCD */
171 #define EESM_MIN 2 /* minutes in BCD */
172 #define EESM_HOUR 3 /* hours in BCD */
173 #define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
174 #define EESM_DAY 5 /* day of month in BCD */
175 #define EESM_MON 6 /* month in BCD */
176 #define EESM_YEAR 7 /* year MOD 100 in BCD */
177 #define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
178 #define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
179 #define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
180 /* followed by a frame alignment byte (0xff) /
181 / which is not put into the lastcode buffer*/
183 /* Length of the serial time code, in characters. The first length
184 * is less the frame alignment byte.
186 #define LENEESPRT (EESM_MSFOK+1)
187 #define LENEESCODE (LENEESPRT+1)
190 #define EESCS_WAIT 0 /* waiting for start of timecode */
191 #define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
193 /* Default fudge factor and character to receive */
194 #define DEFFUDGETIME 0 /* Default user supplied fudge factor */
196 #define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
198 #define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
200 /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
201 * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
202 * as the estimated offset, otherwise average the remainder.
204 #define FULLSHIFT 6 /* NCODES root 2 */
205 #define NCODES (1<< FULLSHIFT) /* 64 */
206 #define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
208 /* Towards the high ( Why ?) end of half */
209 #define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
211 /* Leap hold time. After a leap second the clock will no longer be
212 * reliable until it resynchronizes. Hope 40 minutes is enough. */
213 #define EESLEAPHOLD (40 * 60)
215 #define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
216 #define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
217 #define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
218 #define EES_STEP_NOTES 50 /* Only do a limited number */
219 #define MAX_STEP 16 /* Max number of steps to remember */
221 /* debug is a bit mask of debugging that is wanted */
222 #define DB_SYSLOG_SMPLI 0x0001
223 #define DB_SYSLOG_SMPLE 0x0002
224 #define DB_SYSLOG_SMTHI 0x0004
225 #define DB_SYSLOG_NSMTHE 0x0008
226 #define DB_SYSLOG_NSMTHI 0x0010
227 #define DB_SYSLOG_SMTHE 0x0020
228 #define DB_PRINT_EV 0x0040
229 #define DB_PRINT_CDT 0x0080
230 #define DB_PRINT_CDTC 0x0100
231 #define DB_SYSLOG_KEEPD 0x0800
232 #define DB_SYSLOG_KEEPE 0x1000
233 #define DB_LOG_DELTAS 0x2000
234 #define DB_PRINT_DELTAS 0x4000
235 #define DB_LOG_AWAITMORE 0x8000
236 #define DB_LOG_SAMPLES 0x10000
237 #define DB_NO_PPS 0x20000
238 #define DB_INC_PPS 0x40000
239 #define DB_DUMP_DELTAS 0x80000
241 struct eesunit
{ /* EES unit control structure. */
242 struct peer
*peer
; /* associated peer structure */
243 struct refclockio io
; /* given to the I/O handler */
244 l_fp reftime
; /* reference time */
245 l_fp lastsampletime
; /* time as in txt from last EES msg */
246 l_fp arrvtime
; /* Time at which pkt arrived */
247 l_fp codeoffsets
[NCODES
]; /* the time of arrival of 232 codes */
248 l_fp offset
; /* chosen offset (for clkbug) */
249 l_fp lowoffset
; /* lowest sample offset (for clkbug) */
250 l_fp highoffset
; /* highest " " (for clkbug) */
251 char lastcode
[LENEESCODE
+6]; /* last time code we received */
252 u_long lasttime
; /* last time clock heard from */
253 u_long clocklastgood
; /* last time good radio seen */
254 u_char lencode
; /* length of code in buffer */
255 u_char nsamples
; /* number of samples we've collected */
256 u_char codestate
; /* state of 232 code reception */
257 u_char unit
; /* unit number for this guy */
258 u_char status
; /* clock status */
259 u_char lastevent
; /* last clock event */
260 u_char reason
; /* reason for last abort */
261 u_char hour
; /* hour of day */
262 u_char minute
; /* minute of hour */
263 u_char second
; /* seconds of minute */
264 char tz
; /* timezone from clock */
265 u_char ttytype
; /* method used */
266 u_char dump_vals
; /* Should clock values be dumped */
267 u_char usealldata
; /* Use ALL samples */
268 u_short day
; /* day of year from last code */
269 u_long yearstart
; /* start of current year */
270 u_long leaphold
; /* time of leap hold expiry */
271 u_long badformat
; /* number of bad format codes */
272 u_long baddata
; /* number of invalid time codes */
273 u_long timestarted
; /* time we started this */
274 long last_pps_no
; /* The serial # of the last PPS */
275 char fix_pending
; /* Is a "sync to time" pending ? */
276 /* Fine tuning - compensate for 4 mS ramping .... */
277 l_fp last_l
; /* last time stamp */
278 u_char last_steps
[MAX_STEP
]; /* Most recent n steps */
279 int best_av_step
; /* Best guess at average step */
280 char best_av_step_count
; /* # of steps over used above */
281 char this_step
; /* Current pos in buffer */
282 int last_step_late
; /* How late the last step was (0-59) */
283 long jump_fsecs
; /* # of fractions of a sec last jump */
284 u_long last_step
; /* time of last step */
285 int last_step_secs
; /* Number of seconds in last step */
286 int using_ramp
; /* 1 -> noemal, -1 -> over stepped */
288 #define last_sec last_l.l_ui
289 #define last_sfsec last_l.l_f
290 #define this_uisec ((ees->arrvtime).l_ui)
291 #define this_sfsec ((ees->arrvtime).l_f)
292 #define msec(x) ((x) / (1<<22))
293 #define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
294 #define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
296 /* Bitmask for what methods to try to use -- currently only PPS enabled */
299 /* macros to test above */
300 #define is_cbreak(x) ((x)->ttytype & T_CBREAK)
301 #define is_pps(x) ((x)->ttytype & T_PPS)
302 #define is_any(x) ((x)->ttytype)
304 #define CODEREASON 20 /* reason codes */
306 /* Data space for the unit structures. Note that we allocate these on
307 * the fly, but never give them back. */
308 static struct eesunit
*eesunits
[MAXUNITS
];
309 static u_char unitinuse
[MAXUNITS
];
311 /* Keep the fudge factors separately so they can be set even
312 * when no clock is configured. */
313 static l_fp inherent_delay
[MAXUNITS
]; /* when time stamp is taken */
314 static l_fp fudgefactor
[MAXUNITS
]; /* fudgetime1 */
315 static l_fp os_delay
[MAXUNITS
]; /* fudgetime2 */
316 static l_fp offset_fudge
[MAXUNITS
]; /* Sum of above */
317 static u_char stratumtouse
[MAXUNITS
];
318 static u_char sloppyclockflag
[MAXUNITS
];
320 static int deltas
[60];
322 static l_fp acceptable_slop
; /* = { 0, 1 << (FRACTION_PREC -2) }; */
323 static l_fp onesec
; /* = { 1, 0 }; */
325 #ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
326 #define DUMP_BUF_SIZE 10112
329 /* ees_reset - reset the count back to zero */
330 #define ees_reset(ees) (ees)->nsamples = 0; \
331 (ees)->codestate = EESCS_WAIT
333 /* ees_event - record and report an event */
334 #define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
335 ees_report_event((ees), (evcode))
337 /* Find the precision of the system clock by reading it */
338 #define USECS 1000000
339 #define MINSTEP 5 /* some systems increment uS on each call */
340 #define MAXLOOPS (USECS/9)
343 * Function prototypes
346 static int msfees_start
P((int unit
, struct peer
*peer
));
347 static void msfees_shutdown
P((int unit
, struct peer
*peer
));
348 static void msfees_poll
P((int unit
, struct peer
*peer
));
349 static void msfees_init
P((void));
350 static void dump_buf
P((l_fp
*coffs
, int from
, int to
, char *text
));
351 static void ees_report_event
P((struct eesunit
*ees
, int code
));
352 static void ees_receive
P((struct recvbuf
*rbufp
));
353 static void ees_process
P((struct eesunit
*ees
));
354 #ifdef QSORT_USES_VOID_P
355 static int offcompare
P((const void *va
, const void *vb
));
357 static int offcompare
P((const l_fp
*a
, const l_fp
*b
));
358 #endif /* QSORT_USES_VOID_P */
364 struct refclock refclock_msfees
= {
365 msfees_start
, /* start up driver */
366 msfees_shutdown
, /* shut down driver */
367 msfees_poll
, /* transmit poll message */
368 noentry
, /* not used */
369 msfees_init
, /* initialize driver */
370 noentry
, /* not used */
371 NOFLAGS
/* not used */
383 char buff
[DUMP_BUF_SIZE
+ 80];
385 register char *ptr
= buff
;
388 for (i
=from
; i
<to
; i
++)
389 { while (*ptr
) ptr
++;
390 if ((ptr
-buff
) > DUMP_BUF_SIZE
) msyslog(LOG_DEBUG
, "D: %s", ptr
=buff
);
391 sprintf(ptr
, " %06d", ((int)coffs
[i
].l_f
) / 4295);
393 msyslog(LOG_DEBUG
, "D: %s", buff
);
396 /* msfees_init - initialize internal ees driver data */
401 /* Just zero the data arrays */
402 memset((char *)eesunits
, 0, sizeof eesunits
);
403 memset((char *)unitinuse
, 0, sizeof unitinuse
);
405 acceptable_slop
.l_ui
= 0;
406 acceptable_slop
.l_uf
= 1 << (FRACTION_PREC
-2);
411 /* Initialize fudge factors to default. */
412 for (i
= 0; i
< MAXUNITS
; i
++) {
413 fudgefactor
[i
].l_ui
= 0;
414 fudgefactor
[i
].l_uf
= DEFFUDGETIME
;
415 os_delay
[i
].l_ui
= 0;
416 os_delay
[i
].l_uf
= DEFOSTIME
;
417 inherent_delay
[i
].l_ui
= 0;
418 inherent_delay
[i
].l_uf
= DEFINHTIME
;
419 offset_fudge
[i
] = os_delay
[i
];
420 L_ADD(&offset_fudge
[i
], &fudgefactor
[i
]);
421 L_ADD(&offset_fudge
[i
], &inherent_delay
[i
]);
423 sloppyclockflag
[i
] = 0;
428 /* msfees_start - open the EES devices and initialize data for processing */
435 register struct eesunit
*ees
;
439 struct termios ttyb
, *ttyp
;
440 struct refclockproc
*pp
;
443 if (unit
>= MAXUNITS
) {
444 msyslog(LOG_ERR
, "ees clock: unit number %d invalid (max %d)",
448 if (unitinuse
[unit
]) {
449 msyslog(LOG_ERR
, "ees clock: unit number %d in use", unit
);
453 /* Unit okay, attempt to open the devices. We do them both at
454 * once to make sure we can */
455 (void) sprintf(eesdev
, EES232
, unit
);
457 fd232
= open(eesdev
, O_RDWR
, 0777);
459 msyslog(LOG_ERR
, "ees clock: open of %s failed: %m", eesdev
);
464 /* Set for exclusive use */
465 if (ioctl(fd232
, TIOCEXCL
, (char *)0) < 0) {
466 msyslog(LOG_ERR
, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev
);
471 /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
473 /* Set port characteristics. If we don't have a STREAMS module or
474 * a clock line discipline, cooked mode is just usable, even though it
475 * strips the top bit. The only EES byte which uses the top
476 * bit is the year, and we don't use that anyway. If we do
477 * have the line discipline, we choose raw mode, and the
478 * line discipline code will block up the messages.
481 /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
484 if (tcgetattr(fd232
, ttyp
) < 0) {
485 msyslog(LOG_ERR
, "msfees_start: tcgetattr(%s): %m", eesdev
);
489 ttyp
->c_iflag
= IGNBRK
|IGNPAR
|ICRNL
;
490 ttyp
->c_cflag
= SPEED232
|CS8
|CLOCAL
|CREAD
;
492 ttyp
->c_lflag
= ICANON
;
493 ttyp
->c_cc
[VERASE
] = ttyp
->c_cc
[VKILL
] = '\0';
494 if (tcsetattr(fd232
, TCSANOW
, ttyp
) < 0) {
495 msyslog(LOG_ERR
, "msfees_start: tcsetattr(%s): %m", eesdev
);
499 if (tcflush(fd232
, TCIOFLUSH
) < 0) {
500 msyslog(LOG_ERR
, "msfees_start: tcflush(%s): %m", eesdev
);
504 inherent_delay
[unit
].l_uf
= INH_DELAY_PPS
;
506 /* offset fudge (how *late* the timestamp is) = fudge + os delays */
507 offset_fudge
[unit
] = os_delay
[unit
];
508 L_ADD(&offset_fudge
[unit
], &fudgefactor
[unit
]);
509 L_ADD(&offset_fudge
[unit
], &inherent_delay
[unit
]);
511 /* Looks like this might succeed. Find memory for the structure.
512 * Look to see if there are any unused ones, if not we malloc() one.
514 if (eesunits
[unit
] != 0) /* The one we want is okay */
515 ees
= eesunits
[unit
];
517 /* Look for an unused, but allocated struct */
518 for (i
= 0; i
< MAXUNITS
; i
++) {
519 if (!unitinuse
[i
] && eesunits
[i
] != 0)
523 if (i
< MAXUNITS
) { /* Reclaim this one */
526 } /* no spare -- make a new one */
527 else ees
= (struct eesunit
*) emalloc(sizeof(struct eesunit
));
529 memset((char *)ees
, 0, sizeof(struct eesunit
));
530 eesunits
[unit
] = ees
;
532 /* Set up the structures */
534 ees
->unit
= (u_char
)unit
;
535 ees
->timestarted
= current_time
;
537 ees
->io
.clock_recv
= ees_receive
;
538 ees
->io
.srcclock
= (caddr_t
)ees
;
542 /* Okay. Push one of the two (linked into the kernel, or dynamically
543 * loaded) STREAMS module, and give it to the I/O code to start
550 /* Pop any existing onews first ... */
551 while (ioctl(fd232
, I_POP
, 0 ) >= 0) ;
553 /* Now try pushing either of the possible modules */
554 if ((rc1
=ioctl(fd232
, I_PUSH
, STREAM_PP1
)) < 0 &&
555 ioctl(fd232
, I_PUSH
, STREAM_PP2
) < 0) {
557 "ees clock: Push of `%s' and `%s' to %s failed %m",
558 STREAM_PP1
, STREAM_PP2
, eesdev
);
562 NLOG(NLOG_CLOCKINFO
) /* conditional if clause for conditional syslog */
563 msyslog(LOG_INFO
, "I: ees clock: PUSHed %s on %s",
564 (rc1
>= 0) ? STREAM_PP1
: STREAM_PP2
, eesdev
);
565 ees
->ttytype
|= T_PPS
;
571 if (!io_addclock(&ees
->io
)) {
572 /* Oh shit. Just close and return. */
573 msyslog(LOG_ERR
, "ees clock: io_addclock(%s): %m", eesdev
);
578 /* All done. Initialize a few random peer variables, then
580 peer
->precision
= sys_precision
;
581 peer
->stratum
= stratumtouse
[unit
];
582 if (stratumtouse
[unit
] <= 1) {
583 memcpy((char *)&pp
->refid
, EESREFID
, 4);
584 if (unit
> 0 && unit
< 10)
585 ((char *)&pp
->refid
)[3] = '0' + unit
;
587 peer
->refid
= htonl(EESHSREFID
);
590 pp
->unitptr
= (caddr_t
) &eesunits
[unit
];
591 pp
->clockdesc
= EESDESCRIPTION
;
592 msyslog(LOG_ERR
, "ees clock: %s OK on %d", eesdev
, unit
);
602 /* msfees_shutdown - shut down a EES clock */
609 register struct eesunit
*ees
;
611 if (unit
>= MAXUNITS
) {
613 "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
617 if (!unitinuse
[unit
]) {
619 "ees clock: INTERNAL ERROR, unit number %d not in use", unit
);
623 /* Tell the I/O module to turn us off. We're history. */
624 ees
= eesunits
[unit
];
625 io_closeclock(&ees
->io
);
630 /* ees_report_event - note the occurance of an event */
637 if (ees
->status
!= (u_char
)code
) {
638 ees
->status
= (u_char
)code
;
639 if (code
!= CEVNT_NOMINAL
)
640 ees
->lastevent
= (u_char
)code
;
641 /* Should report event to trap handler in here.
648 /* ees_receive - receive data from the serial interface on an EES clock */
651 struct recvbuf
*rbufp
654 register int n_sample
;
656 register struct eesunit
*ees
;
657 register u_char
*dpt
; /* Data PoinTeR: move along ... */
658 register u_char
*dpend
; /* Points just *after* last data char */
661 int call_pps_sample
= 0;
665 int suspect_4ms_step
= 0;
666 struct ppsclockev ppsclockev
;
667 long *ptr
= (long *) &ppsclockev
;
673 #ifdef HAVE_TIOCGPPSEV
674 request
= TIOCGPPSEV
;
677 /* Get the clock this applies to and a pointer to the data */
678 ees
= (struct eesunit
*)rbufp
->recv_srcclock
;
679 dpt
= (u_char
*)&rbufp
->recv_space
;
680 dpend
= dpt
+ rbufp
->recv_length
;
681 if ((dbg
& DB_LOG_AWAITMORE
) && (rbufp
->recv_length
!= LENEESCODE
))
682 printf("[%d] ", rbufp
->recv_length
);
684 /* Check out our state and process appropriately */
685 switch (ees
->codestate
) {
687 /* Set an initial guess at the timestamp as the recv time.
688 * If just running in CBREAK mode, we can't improve this.
689 * If we have the CLOCK Line Discipline, PPSCD, or sime such,
690 * then we will do better later ....
692 ees
->arrvtime
= rbufp
->recv_time
;
693 ees
->codestate
= EESCS_GOTSOME
;
698 cp
= &(ees
->lastcode
[ees
->lencode
]);
700 /* Gobble the bytes until the final (possibly stripped) 0xff */
701 while (dpt
< dpend
&& (*dpt
& 0x7f) != 0x7f) {
702 *cp
++ = (char)*dpt
++;
704 /* Oh dear -- too many bytes .. */
705 if (ees
->lencode
> LENEESPRT
) {
706 NLOG(NLOG_CLOCKINFO
) /* conditional if clause for conditional syslog */
708 "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
709 ees
->lencode
, dpend
- dpt
, LENEESPRT
,
710 #define D(x) (ees->lastcode[x])
711 D(0), D(1), D(2), D(3), D(4), D(5), D(6),
712 D(7), D(8), D(9), D(10), D(11), D(12));
715 ees
->reason
= CODEREASON
+ 1;
716 ees_event(ees
, CEVNT_BADREPLY
);
721 /* Gave up because it was end of the buffer, rather than ff */
723 /* Incomplete. Wait for more. */
724 if (dbg
& DB_LOG_AWAITMORE
)
726 "I: ees clock %d: %p == %p: await more",
727 ees
->unit
, dpt
, dpend
);
731 /* This shouldn't happen ... ! */
732 if ((*dpt
& 0x7f) != 0x7f) {
733 msyslog(LOG_INFO
, "I: ees clock: %0x & 0x7f != 0x7f", *dpt
);
735 ees
->reason
= CODEREASON
+ 2;
736 ees_event(ees
, CEVNT_BADREPLY
);
744 /* Finally, got a complete buffer. Mainline code will
750 msyslog(LOG_ERR
, "ees clock: INTERNAL ERROR: %d state %d",
751 ees
->unit
, ees
->codestate
);
752 ees
->reason
= CODEREASON
+ 5;
753 ees_event(ees
, CEVNT_FAULT
);
758 /* Boy! After all that crap, the lastcode buffer now contains
759 * something we hope will be a valid time code. Do length
760 * checks and sanity checks on constant data.
762 ees
->codestate
= EESCS_WAIT
;
763 ees
->lasttime
= current_time
;
764 if (ees
->lencode
!= LENEESPRT
) {
766 ees
->reason
= CODEREASON
+ 6;
767 ees_event(ees
, CEVNT_BADREPLY
);
774 /* Check that centisecond is zero */
775 if (cp
[EESM_CSEC
] != 0) {
777 ees
->reason
= CODEREASON
+ 7;
778 ees_event(ees
, CEVNT_BADREPLY
);
783 /* Check flag formats */
784 if (cp
[EESM_LEAP
] != 0 && cp
[EESM_LEAP
] != 0x0f) {
786 ees
->reason
= CODEREASON
+ 8;
787 ees_event(ees
, CEVNT_BADREPLY
);
792 if (cp
[EESM_BST
] != 0 && cp
[EESM_BST
] != 0x03) {
794 ees
->reason
= CODEREASON
+ 9;
795 ees_event(ees
, CEVNT_BADREPLY
);
800 if (cp
[EESM_MSFOK
] != 0 && cp
[EESM_MSFOK
] != 0x3f) {
802 ees
->reason
= CODEREASON
+ 10;
803 ees_event(ees
, CEVNT_BADREPLY
);
808 /* So far, so good. Compute day, hours, minutes, seconds,
809 * time zone. Do range checks on these.
812 #define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
813 #define istrue(x) ((x)?1:0)
815 ees
->second
= bcdunpack(cp
[EESM_SEC
]); /* second */
816 ees
->minute
= bcdunpack(cp
[EESM_MIN
]); /* minute */
817 ees
->hour
= bcdunpack(cp
[EESM_HOUR
]); /* hour */
819 day
= bcdunpack(cp
[EESM_DAY
]); /* day of month */
821 switch (bcdunpack(cp
[EESM_MON
])) { /* month */
823 /* Add in lengths of all previous months. Add one more
824 if it is a leap year and after February.
826 case 12: day
+= NOV
; /*FALLSTHROUGH*/
827 case 11: day
+= OCT
; /*FALLSTHROUGH*/
828 case 10: day
+= SEP
; /*FALLSTHROUGH*/
829 case 9: day
+= AUG
; /*FALLSTHROUGH*/
830 case 8: day
+= JUL
; /*FALLSTHROUGH*/
831 case 7: day
+= JUN
; /*FALLSTHROUGH*/
832 case 6: day
+= MAY
; /*FALLSTHROUGH*/
833 case 5: day
+= APR
; /*FALLSTHROUGH*/
834 case 4: day
+= MAR
; /*FALLSTHROUGH*/
836 if (istrue(cp
[EESM_LEAP
])) day
++; /*FALLSTHROUGH*/
837 case 2: day
+= JAN
; /*FALLSTHROUGH*/
839 default: ees
->baddata
++;
840 ees
->reason
= CODEREASON
+ 11;
841 ees_event(ees
, CEVNT_BADDATE
);
848 /* Get timezone. The clocktime routine wants the number
849 * of hours to add to the delivered time to get UT.
850 * Currently -1 if BST flag set, 0 otherwise. This
851 * is the place to tweak things if double summer time
854 ees
->tz
= istrue(cp
[EESM_BST
]) ? -1 : 0;
856 if (ees
->day
> 366 || ees
->day
< 1 ||
857 ees
->hour
> 23 || ees
->minute
> 59 || ees
->second
> 59) {
859 ees
->reason
= CODEREASON
+ 12;
860 ees_event(ees
, CEVNT_BADDATE
);
865 n_sample
= ees
->nsamples
;
867 /* Now, compute the reference time value: text -> tmp.l_ui */
868 if (!clocktime(ees
->day
, ees
->hour
, ees
->minute
, ees
->second
,
869 ees
->tz
, rbufp
->recv_time
.l_ui
, &ees
->yearstart
,
872 ees
->reason
= CODEREASON
+ 13;
873 ees_event(ees
, CEVNT_BADDATE
);
879 /* DON'T use ees->arrvtime -- it may be < reftime */
880 ees
->lastsampletime
= tmp
;
882 /* If we are synchronised to the radio, update the reference time.
883 * Also keep a note of when clock was last good.
885 if (istrue(cp
[EESM_MSFOK
])) {
887 ees
->clocklastgood
= current_time
;
891 /* Compute the offset. For the fractional part of the
892 * offset we use the expected delay for the message.
894 ees
->codeoffsets
[n_sample
].l_ui
= tmp
.l_ui
;
895 ees
->codeoffsets
[n_sample
].l_uf
= 0;
897 /* Number of seconds since the last step */
898 sincelast
= this_uisec
- ees
->last_step
;
900 memset((char *) &ppsclockev
, 0, sizeof ppsclockev
);
902 rc
= ioctl(ees
->io
.fd
, request
, (char *) &ppsclockev
);
903 if (dbg
& DB_PRINT_EV
) fprintf(stderr
,
904 "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
905 DB_PRINT_EV
, ees
->unit
, ees
->io
.fd
, request
, is_pps(ees
),
906 rc
, errno
, ptr
[0], ptr
[1], ptr
[2]);
908 /* If we managed to get the time of arrival, process the info */
911 pps_step
= ppsclockev
.serial
- ees
->last_pps_no
;
913 /* Possible that PPS triggered, but text message didn't */
914 if (pps_step
== 2) msyslog(LOG_ERR
, "pps step = 2 @ %02d", ees
->second
);
915 if (pps_step
== 2 && ees
->second
== 1) suspect_4ms_step
|= 1;
916 if (pps_step
== 2 && ees
->second
== 2) suspect_4ms_step
|= 4;
918 /* allow for single loss of PPS only */
919 if (pps_step
!= 1 && pps_step
!= 2)
920 fprintf(stderr
, "PPS step: %d too far off %ld (%d)\n",
921 ppsclockev
.serial
, ees
->last_pps_no
, pps_step
);
922 else if (!buftvtots((char *) &(ppsclockev
.tv
), &pps_arrvstamp
))
923 fprintf(stderr
, "buftvtots failed\n");
924 else { /* if ((ABS(time difference) - 0.25) < 0)
925 * then believe it ...
928 diff
= pps_arrvstamp
;
930 L_SUB(&diff
, &ees
->arrvtime
);
931 if (dbg
& DB_PRINT_CDT
)
932 printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
933 DB_PRINT_CDT
, (long)ees
->arrvtime
.l_ui
, (long)ees
->arrvtime
.l_uf
,
934 (long)pps_arrvstamp
.l_ui
, (long)pps_arrvstamp
.l_uf
,
935 (long)diff
.l_ui
, (long)diff
.l_uf
,
936 ctime(&(ppsclockev
.tv
.tv_sec
)));
937 if (L_ISNEG(&diff
)) M_NEG(diff
.l_ui
, diff
.l_uf
);
938 L_SUB(&diff
, &acceptable_slop
);
939 if (L_ISNEG(&diff
)) { /* AOK -- pps_sample */
940 ees
->arrvtime
= pps_arrvstamp
;
944 /* Some loss of some signals around sec = 1 */
945 else if (ees
->second
== 1) {
946 diff
= pps_arrvstamp
;
947 L_ADD(&diff
, &onesec
);
948 L_SUB(&diff
, &ees
->arrvtime
);
949 if (L_ISNEG(&diff
)) M_NEG(diff
.l_ui
, diff
.l_uf
);
950 L_SUB(&diff
, &acceptable_slop
);
951 msyslog(LOG_ERR
, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
952 pps_arrvstamp
.l_ui
- ees
->arrvtime
.l_ui
,
955 diff
.l_ui
, diff
.l_uf
,
956 (int)ppsclockev
.tv
.tv_usec
,
957 ctime(&(ppsclockev
.tv
.tv_sec
)));
958 if (L_ISNEG(&diff
)) { /* AOK -- pps_sample */
959 suspect_4ms_step
|= 2;
960 ees
->arrvtime
= pps_arrvstamp
;
961 L_ADD(&ees
->arrvtime
, &onesec
);
967 ees
->last_pps_no
= ppsclockev
.serial
;
968 if (dbg
& DB_PRINT_CDTC
)
970 "[%x] %08lx %08lx %d u%d (%d %d)\n",
971 DB_PRINT_CDTC
, (long)pps_arrvstamp
.l_ui
,
972 (long)pps_arrvstamp
.l_uf
, conv
, ees
->unit
,
973 call_pps_sample
, pps_step
);
976 /* See if there has been a 4ms jump at a minute boundry */
978 #define delta_isec delta.l_ui
979 #define delta_ssec delta.l_i
980 #define delta_sfsec delta.l_f
983 delta
.l_i
= ees
->arrvtime
.l_i
;
984 delta
.l_f
= ees
->arrvtime
.l_f
;
986 L_SUB(&delta
, &ees
->last_l
);
987 delta_f_abs
= delta_sfsec
;
988 if (delta_f_abs
< 0) delta_f_abs
= -delta_f_abs
;
990 /* Dump the deltas each minute */
991 if (dbg
& DB_DUMP_DELTAS
)
992 { if (/*0 <= ees->second && */
993 ees
->second
< ((sizeof deltas
) / (sizeof deltas
[0]))) deltas
[ees
->second
] = delta_sfsec
;
994 /* Dump on second 1, as second 0 sometimes missed */
995 if (ees
->second
== 1) {
996 char text
[16 * ((sizeof deltas
) / (sizeof deltas
[0]))];
999 for (i
=0; i
<((sizeof deltas
) / (sizeof deltas
[0])); i
++) {
1000 sprintf(cptr
, " %d.%04d",
1001 msec(deltas
[i
]), subms(deltas
[i
]));
1002 while (*cptr
) cptr
++;
1004 msyslog(LOG_ERR
, "Deltas: %d.%04d<->%d.%04d: %s",
1005 msec(EES_STEP_F
- EES_STEP_F_GRACE
), subms(EES_STEP_F
- EES_STEP_F_GRACE
),
1006 msec(EES_STEP_F
+ EES_STEP_F_GRACE
), subms(EES_STEP_F
+ EES_STEP_F_GRACE
),
1008 for (i
=0; i
<((sizeof deltas
) / (sizeof deltas
[0])); i
++) deltas
[i
] = 0;
1012 /* Lets see if we have a 4 mS step at a minute boundaary */
1013 if ( ((EES_STEP_F
- EES_STEP_F_GRACE
) < delta_f_abs
) &&
1014 (delta_f_abs
< (EES_STEP_F
+ EES_STEP_F_GRACE
)) &&
1015 (ees
->second
== 0 || ees
->second
== 1 || ees
->second
== 2) &&
1016 (sincelast
< 0 || sincelast
> 122)
1017 ) { /* 4ms jump at min boundry */
1021 /* Yes -- so compute the ramp time */
1022 if (ees
->last_step
== 0) sincelast
= 0;
1023 old_sincelast
= sincelast
;
1025 /* First time in, just set "ees->last_step" */
1026 if(ees
->last_step
) {
1029 int this_step
= (sincelast
+ (60 /2)) / 60;
1030 int p_step
= ees
->this_step
;
1032 ees
->last_steps
[p_step
] = this_step
;
1035 if (p_step
>= LAST_STEPS
) p_step
= 0;
1036 ees
->this_step
= p_step
;
1037 /* Find the "average" interval */
1038 while (p
!= p_step
) {
1039 int this = ees
->last_steps
[p
];
1040 if (this == 0) break;
1041 if (this != this_step
) {
1042 if (other_step
== 0 && (
1043 this== (this_step
+2) ||
1044 this== (this_step
-2) ||
1045 this== (this_step
+1) ||
1046 this== (this_step
-1)))
1048 if (other_step
!= this) {
1049 int idelta
= (this_step
- other_step
);
1050 if (idelta
< 0) idelta
= - idelta
;
1051 if (third_step
== 0 && (
1053 this == (other_step
+1) ||
1054 this == (other_step
-1) ||
1055 this == (this_step
+1) ||
1056 this == (this_step
-1))
1059 this == (this_step
+ other_step
)/2
1061 )) third_step
= this;
1062 if (third_step
!= this) break;
1067 if (p
< 0) p
+= LAST_STEPS
;
1070 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
);
1071 if (count
!= 0) sum
= ((sum
* 60) + (count
/2)) / count
;
1072 #define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
1073 msyslog(LOG_ERR
, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
1074 ees
->unit
, suspect_4ms_step
, p_step
, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1075 SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1076 printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
1077 ees
->unit
, p_step
, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1078 SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1080 ees
->jump_fsecs
= delta_sfsec
;
1081 ees
->using_ramp
= 1;
1082 if (sincelast
> 170)
1083 ees
->last_step_late
+= sincelast
- ((sum
) ? sum
: ees
->last_step_secs
);
1084 else ees
->last_step_late
= 30;
1085 if (ees
->last_step_late
< -60 || ees
->last_step_late
> 120) ees
->last_step_late
= 30;
1086 if (ees
->last_step_late
< 0) ees
->last_step_late
= 0;
1087 if (ees
->last_step_late
>= 60) ees
->last_step_late
= 59;
1090 else { /* First time in -- just save info */
1091 ees
->last_step_late
= 30;
1092 ees
->jump_fsecs
= delta_sfsec
;
1093 ees
->using_ramp
= 1;
1096 ees
->last_step
= this_uisec
;
1097 printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
1098 ees
->unit
, (long)msec(delta_sfsec
), (long)subms(delta_sfsec
),
1099 ees
->second
, old_sincelast
, ees
->last_step_late
, count
, sum
,
1100 ees
->last_step_secs
);
1101 msyslog(LOG_ERR
, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
1102 ees
->unit
, msec(delta_sfsec
), subms(delta_sfsec
), ees
->second
,
1103 old_sincelast
, ees
->last_step_late
, count
, sum
, ees
->last_step_secs
);
1104 if (sum
) ees
->last_step_secs
= sum
;
1106 /* OK, so not a 4ms step at a minute boundry */
1108 if (suspect_4ms_step
) msyslog(LOG_ERR
,
1109 "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
1110 ees
->unit
, suspect_4ms_step
, msec(delta_sfsec
), subms(delta_sfsec
),
1111 msec(EES_STEP_F
- EES_STEP_F_GRACE
),
1112 subms(EES_STEP_F
- EES_STEP_F_GRACE
),
1113 (int)msec(delta_f_abs
),
1114 (int)subms(delta_f_abs
),
1115 msec(EES_STEP_F
+ EES_STEP_F_GRACE
),
1116 subms(EES_STEP_F
+ EES_STEP_F_GRACE
),
1119 if ((delta_f_abs
> EES_STEP_NOTE
) && ees
->last_l
.l_i
) {
1120 static int ees_step_notes
= EES_STEP_NOTES
;
1121 if (ees_step_notes
> 0) {
1123 printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
1124 ees
->unit
, (long)msec(delta_sfsec
), (long)subms(delta_sfsec
),
1125 ees
->second
, sincelast
, ees_step_notes
? "" : " -- NO MORE !");
1126 msyslog(LOG_ERR
, "MSF%d: D=%3d.%04d@%02d :%d%s",
1127 ees
->unit
, msec(delta_sfsec
), subms(delta_sfsec
), ees
->second
, (ees
->last_step
) ? sincelast
: -1, ees_step_notes
? "" : " -- NO MORE !");
1132 ees
->last_l
= ees
->arrvtime
;
1134 /* IF we have found that it's ramping
1135 * && it's within twice the expected ramp period
1136 * && there is a non zero step size (avoid /0 !)
1137 * THEN we twiddle things
1139 if (ees
->using_ramp
&&
1140 sincelast
< (ees
->last_step_secs
)*2 &&
1141 ees
->last_step_secs
)
1142 { long sec_of_ramp
= sincelast
+ ees
->last_step_late
;
1146 /* Ramp time may vary, so may ramp for longer than last time */
1147 if (sec_of_ramp
> (ees
->last_step_secs
+ 120))
1148 sec_of_ramp
= ees
->last_step_secs
;
1150 /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
1151 fsecs
= sec_of_ramp
* (ees
->jump_fsecs
/ ees
->last_step_secs
);
1153 if (dbg
& DB_LOG_DELTAS
) msyslog(LOG_ERR
,
1154 "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
1156 ees
->unit
, sec_of_ramp
, ees
->last_step_secs
, fsecs
,
1157 pps_arrvstamp
.l_f
, pps_arrvstamp
.l_f
+ fsecs
);
1158 if (dbg
& DB_PRINT_DELTAS
) printf(
1159 "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
1160 ees
->unit
, sec_of_ramp
, ees
->last_step_secs
, fsecs
,
1161 (long)pps_arrvstamp
.l_f
, pps_arrvstamp
.l_f
+ fsecs
);
1163 /* Must sign extend the result */
1164 inc
.l_i
= (fsecs
< 0) ? -1 : 0;
1166 if (dbg
& DB_INC_PPS
)
1167 { L_SUB(&pps_arrvstamp
, &inc
);
1168 L_SUB(&ees
->arrvtime
, &inc
);
1171 { L_ADD(&pps_arrvstamp
, &inc
);
1172 L_ADD(&ees
->arrvtime
, &inc
);
1176 if (dbg
& DB_LOG_DELTAS
) msyslog(LOG_ERR
,
1177 "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
1179 ees
->unit
, ees
->using_ramp
,
1181 (ees
->last_step_secs
)*2,
1182 ees
->last_step_secs
);
1183 if (dbg
& DB_PRINT_DELTAS
) printf(
1184 "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
1186 ees
->unit
, ees
->using_ramp
,
1188 (ees
->last_step_secs
)*2,
1189 ees
->last_step_secs
);
1192 L_SUB(&ees
->arrvtime
, &offset_fudge
[ees
->unit
]);
1193 L_SUB(&pps_arrvstamp
, &offset_fudge
[ees
->unit
]);
1195 if (call_pps_sample
&& !(dbg
& DB_NO_PPS
)) {
1196 /* Sigh -- it expects its args negated */
1197 L_NEG(&pps_arrvstamp
);
1199 * I had to disable this here, since it appears there is no pointer to the
1202 (void) pps_sample(peer, &pps_arrvstamp);
1206 /* Subtract off the local clock time stamp */
1207 L_SUB(&ees
->codeoffsets
[n_sample
], &ees
->arrvtime
);
1208 if (dbg
& DB_LOG_SAMPLES
) msyslog(LOG_ERR
,
1209 "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
1210 ees
->unit
, DB_LOG_DELTAS
, n_sample
,
1211 ees
->codeoffsets
[n_sample
].l_f
,
1212 ees
->codeoffsets
[n_sample
].l_f
/ 4295,
1214 pps_arrvstamp
.l_f
/4295,
1215 (dbg
& DB_NO_PPS
) ? " [no PPS]" : "");
1217 if (ees
->nsamples
++ == NCODES
-1) ees_process(ees
);
1223 /* offcompare - auxiliary comparison routine for offset sort */
1225 #ifdef QSORT_USES_VOID_P
1232 const l_fp
*a
= (const l_fp
*)va
;
1233 const l_fp
*b
= (const l_fp
*)vb
;
1234 return(L_ISGEQ(a
, b
) ? (L_ISEQU(a
, b
) ? 0 : 1) : -1);
1243 return(L_ISGEQ(a
, b
) ? (L_ISEQU(a
, b
) ? 0 : 1) : -1);
1245 #endif /* QSORT_USES_VOID_P */
1248 /* ees_process - process a pile of samples from the clock */
1254 static int last_samples
= -1;
1257 register l_fp
*coffs
= ees
->codeoffsets
;
1259 double dispersion
; /* ++++ */
1260 int lostsync
, isinsync
;
1261 int samples
= ees
->nsamples
;
1262 int samplelog
= 0; /* keep "gcc -Wall" happy ! */
1263 int samplereduce
= (samples
+ 1) / 2;
1266 /* Reset things to zero so we don't have to worry later */
1269 if (sloppyclockflag
[ees
->unit
]) {
1270 samplelog
= (samples
< 2) ? 0 :
1273 (samples
< 17) ? 3 :
1274 (samples
< 33) ? 4 : 5;
1275 samplereduce
= (1 << samplelog
);
1278 if (samples
!= last_samples
&&
1279 ((samples
!= (last_samples
-1)) || samples
< 3)) {
1280 msyslog(LOG_ERR
, "Samples=%d (%d), samplereduce=%d ....",
1281 samples
, last_samples
, samplereduce
);
1282 last_samples
= samples
;
1284 if (samples
< 1) return;
1286 /* If requested, dump the raw data we have in the buffer */
1287 if (ees
->dump_vals
) dump_buf(coffs
, 0, samples
, "Raw data is:");
1289 /* Sort the offsets, trim off the extremes, then choose one. */
1291 #ifdef QSORT_USES_VOID_P
1296 coffs
, (size_t)samples
, sizeof(l_fp
), offcompare
);
1300 while ((noff
- i
) > samplereduce
) {
1301 /* Trim off the sample which is further away
1302 * from the median. We work this out by doubling
1303 * the median, subtracting off the end samples, and
1304 * looking at the sign of the answer, using the
1305 * identity (c-b)-(b-a) == 2*b-a-c
1307 tmp
= coffs
[(noff
+ i
)/2];
1309 L_SUB(&tmp
, &coffs
[i
]);
1310 L_SUB(&tmp
, &coffs
[noff
-1]);
1311 if (L_ISNEG(&tmp
)) noff
--; else i
++;
1314 /* If requested, dump the reduce data we have in the buffer */
1315 if (ees
->dump_vals
) dump_buf(coffs
, i
, noff
, "Reduced to:");
1317 /* What we do next depends on the setting of the sloppy clock flag.
1318 * If it is on, average the remainder to derive our estimate.
1319 * Otherwise, just pick a representative value from the remaining stuff
1321 if (sloppyclockflag
[ees
->unit
]) {
1322 offset
.l_ui
= offset
.l_uf
= 0;
1323 for (j
= i
; j
< noff
; j
++)
1324 L_ADD(&offset
, &coffs
[j
]);
1325 for (j
= samplelog
; j
> 0; j
--)
1328 else offset
= coffs
[i
+BESTSAMPLE
];
1330 /* Compute the dispersion as the difference between the
1331 * lowest and highest offsets that remain in the
1332 * consideration list.
1334 * It looks like MOST clocks have MOD (max error), so halve it !
1336 tmp
= coffs
[noff
-1];
1337 L_SUB(&tmp
, &coffs
[i
]);
1338 #define FRACT_SEC(n) ((1 << 30) / (n/2))
1339 dispersion
= LFPTOFP(&tmp
) / 2; /* ++++ */
1340 if (dbg
& (DB_SYSLOG_SMPLI
| DB_SYSLOG_SMPLE
)) msyslog(
1341 (dbg
& DB_SYSLOG_SMPLE
) ? LOG_ERR
: LOG_INFO
,
1342 "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
1343 dbg
& (DB_SYSLOG_SMPLI
| DB_SYSLOG_SMPLE
),
1344 offset
.l_f
/ 4295, offset
.l_f
,
1345 (dispersion
* 1526) / 100,
1346 (sloppyclockflag
[ees
->unit
]) ? " by averaging" : "",
1347 FRACT_SEC(10) / 4295,
1348 (coffs
[0].l_f
) / 4295,
1350 (coffs
[i
].l_f
) / 4295,
1351 (coffs
[samples
/2].l_f
) / 4295,
1352 (coffs
[i
+BESTSAMPLE
].l_f
) / 4295,
1354 (coffs
[noff
-1].l_f
) / 4295,
1355 (coffs
[samples
-1].l_f
) / 4295);
1357 /* Are we playing silly wotsits ?
1358 * If we are using all data, see if there is a "small" delta,
1359 * and if so, blurr this with 3/4 of the delta from the last value
1361 if (ees
->usealldata
&& ees
->offset
.l_uf
) {
1362 long diff
= (long) (ees
->offset
.l_uf
- offset
.l_uf
);
1364 /* is the delta small enough ? */
1365 if ((- FRACT_SEC(100)) < diff
&& diff
< FRACT_SEC(100)) {
1366 int samd
= (64 * 4) / samples
;
1368 if (samd
< 2) samd
= 2;
1369 new = offset
.l_uf
+ ((diff
* (samd
-1)) / samd
);
1371 /* Sign change -> need to fix up int part */
1372 if ((new & 0x80000000) !=
1373 (((long) offset
.l_uf
) & 0x80000000))
1374 { NLOG(NLOG_CLOCKINFO
) /* conditional if clause for conditional syslog */
1375 msyslog(LOG_INFO
, "I: %lx != %lx (%lx %lx), so add %d",
1377 ((long) offset
.l_uf
) & 0x80000000,
1378 new, (long) offset
.l_uf
,
1379 (new < 0) ? -1 : 1);
1380 offset
.l_ui
+= (new < 0) ? -1 : 1;
1383 if (dbg
& (DB_SYSLOG_SMTHI
| DB_SYSLOG_SMTHE
)) msyslog(
1384 (dbg
& DB_SYSLOG_SMTHE
) ? LOG_ERR
: LOG_INFO
,
1385 "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
1386 dbg
& (DB_SYSLOG_SMTHI
| DB_SYSLOG_SMTHE
),
1387 ((long) offset
.l_uf
) / 4295, new / 4295,
1388 (dispersion
* 1526) / 100);
1389 offset
.l_uf
= (unsigned long) new;
1391 else if (dbg
& (DB_SYSLOG_NSMTHI
| DB_SYSLOG_NSMTHE
)) msyslog(
1392 (dbg
& DB_SYSLOG_NSMTHE
) ? LOG_ERR
: LOG_INFO
,
1393 "[%x] No smooth as delta not %d < %ld < %d",
1394 dbg
& (DB_SYSLOG_NSMTHI
| DB_SYSLOG_NSMTHE
),
1395 - FRACT_SEC(100), diff
, FRACT_SEC(100));
1397 else if (dbg
& (DB_SYSLOG_NSMTHI
| DB_SYSLOG_NSMTHE
)) msyslog(
1398 (dbg
& DB_SYSLOG_NSMTHE
) ? LOG_ERR
: LOG_INFO
,
1399 "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
1400 dbg
& (DB_SYSLOG_NSMTHI
| DB_SYSLOG_NSMTHE
),
1401 ees
->usealldata
, ees
->offset
.l_f
, ees
->offset
.l_uf
,
1402 offset
.l_f
, ees
->offset
.l_f
- offset
.l_f
);
1404 /* Collect offset info for debugging info */
1405 ees
->offset
= offset
;
1406 ees
->lowoffset
= coffs
[i
];
1407 ees
->highoffset
= coffs
[noff
-1];
1409 /* Determine synchronization status. Can be unsync'd either
1410 * by a report from the clock or by a leap hold.
1412 * Loss of the radio signal for a short time does not cause
1413 * us to go unsynchronised, since the receiver keeps quite
1414 * good time on its own. The spec says 20ms in 4 hours; the
1415 * observed drift in our clock (Cambridge) is about a second
1416 * a day, but even that keeps us within the inherent tolerance
1417 * of the clock for about 15 minutes. Observation shows that
1418 * the typical "short" outage is 3 minutes, so to allow us
1419 * to ride out those, we will give it 5 minutes.
1421 lostsync
= current_time
- ees
->clocklastgood
> 300 ? 1 : 0;
1422 isinsync
= (lostsync
|| ees
->leaphold
> current_time
) ? 0 : 1;
1424 /* Done. Use time of last good, synchronised code as the
1425 * reference time, and lastsampletime as the receive time.
1427 if (ees
->fix_pending
) {
1428 msyslog(LOG_ERR
, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
1429 ees
->fix_pending
, ees
->unit
, offset
.l_i
, offset
.l_f
);
1430 ees
->fix_pending
= 0;
1432 LFPTOD(&offset
, doffset
);
1433 refclock_receive(ees
->peer
);
1434 ees_event(ees
, lostsync
? CEVNT_PROP
: CEVNT_NOMINAL
);
1437 /* msfees_poll - called by the transmit procedure */
1444 if (unit
>= MAXUNITS
) {
1445 msyslog(LOG_ERR
, "ees clock poll: INTERNAL: unit %d invalid",
1449 if (!unitinuse
[unit
]) {
1450 msyslog(LOG_ERR
, "ees clock poll: INTERNAL: unit %d unused",
1455 ees_process(eesunits
[unit
]);
1457 if ((current_time
- eesunits
[unit
]->lasttime
) > 150)
1458 ees_event(eesunits
[unit
], CEVNT_FAULT
);
1463 int refclock_msfees_bs
;
1464 #endif /* REFCLOCK */