xref: /freebsd/contrib/ntp/ntpd/refclock_msfees.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /* refclock_ees - clock driver for the EES M201 receiver */
2 
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
6 
7 #if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
8 
9 /* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
10  * were removed as the code was overly hairy, they weren't in use
11  * (hence probably didn't work).  Still in RCS file at cl.cam.ac.uk
12  */
13 
14 #include <ctype.h>
15 #ifdef HAVE_SYS_TIME_H
16 # include <sys/time.h>
17 #endif
18 
19 #include "ntpd.h"
20 #include "ntp_io.h"
21 #include "ntp_refclock.h"
22 #include "ntp_unixtime.h"
23 #include "ntp_calendar.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 /*
47 	fudgefactor	= fudgetime1;
48 	os_delay	= fudgetime2;
49 	   offset_fudge	= os_delay + fudgefactor + inherent_delay;
50 	stratumtouse	= fudgeval1 & 0xf
51 	debug		= fudgeval2;
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;
59 
60 
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;
78 
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;
90 	*/
91 
92 /* This should support the use of an EES M201 receiver with RS232
93  * output (modified to transmit time once per second).
94  *
95  * For the format of the message sent by the clock, see the EESM_
96  * definitions below.
97  *
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 loose 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,
108  *
109  * Can run in any one of:
110  *
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.
114  *
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)
117  */
118 
119 /* Definitions */
120 #ifndef	MAXUNITS
121 #define	MAXUNITS	4	/* maximum number of EES units permitted */
122 #endif
123 
124 #ifndef	EES232
125 #define	EES232	"/dev/ees%d"	/* Device to open to read the data */
126 #endif
127 
128 /* Other constant stuff */
129 #ifndef	EESPRECISION
130 #define	EESPRECISION	(-10)		/* what the heck - 2**-10 = 1ms */
131 #endif
132 #ifndef	EESREFID
133 #define	EESREFID	"MSF\0"		/* String to identify the clock */
134 #endif
135 #ifndef	EESHSREFID
136 #define	EESHSREFID	(0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
137 #endif
138 
139 /* Description of clock */
140 #define	EESDESCRIPTION		"EES M201 MSF Receiver"
141 
142 /* Speed we run the clock port at. If this is changed the UARTDELAY
143  * value should be recomputed to suit.
144  */
145 #ifndef	SPEED232
146 #define	SPEED232	B9600	/* 9600 baud */
147 #endif
148 
149 /* What is the inherent delay for this mode of working, i.e. when is the
150  * data time stamped.
151  */
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)
157 
158 #ifndef	STREAM_PP1
159 #define	STREAM_PP1	"ppsclocd\0<-- patch space for module name1 -->"
160 #endif
161 #ifndef	STREAM_PP2
162 #define	STREAM_PP2	"ppsclock\0<-- patch space for module name2 -->"
163 #endif
164 
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.
168  */
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*/
182 
183 /* Length of the serial time code, in characters.  The first length
184  * is less the frame alignment byte.
185  */
186 #define	LENEESPRT	(EESM_MSFOK+1)
187 #define	LENEESCODE	(LENEESPRT+1)
188 
189      /* Code state. */
190 #define	EESCS_WAIT	0       /* waiting for start of timecode */
191 #define	EESCS_GOTSOME	1	/* have an incomplete time code buffered */
192 
193      /* Default fudge factor and character to receive */
194 #define	DEFFUDGETIME	0	/* Default user supplied fudge factor */
195 #ifndef	DEFOSTIME
196 #define	DEFOSTIME	0	/* Default OS delay -- passed by Make ? */
197 #endif
198 #define	DEFINHTIME	INH_DELAY_PPS /* inherent delay due to sample point*/
199 
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.
203  */
204 #define	FULLSHIFT	6			/* NCODES root 2 */
205 #define NCODES		(1<< FULLSHIFT)		/* 64 */
206 #define	REDUCESHIFT	(FULLSHIFT -1)		/* SAMPLEREDUCE root 2 */
207 
208      /* Towards the high ( Why ?) end of half */
209 #define	BESTSAMPLE	((samplereduce * 3) /4)	/* 24 */
210 
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)
214 
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 */
220 
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
240 
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 */
287      };
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)))
295 
296 /* Bitmask for what methods to try to use -- currently only PPS enabled */
297 #define	T_CBREAK	1
298 #define	T_PPS		8
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)
303 
304 #define	CODEREASON	20	/* reason codes */
305 
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];
310 
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];
319 
320 static int deltas[60];
321 
322 static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
323 static l_fp onesec; /* = { 1, 0 }; */
324 
325 #ifndef	DUMP_BUF_SIZE	/* Size of buffer to be used by dump_buf */
326 #define	DUMP_BUF_SIZE	10112
327 #endif
328 
329 /* ees_reset - reset the count back to zero */
330 #define	ees_reset(ees) (ees)->nsamples = 0; \
331 (ees)->codestate = EESCS_WAIT
332 
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))
336 
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)
341 
342 /*
343  * Function prototypes
344  */
345 
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));
356 #else
357 static	int	offcompare	P((const l_fp *a, const l_fp *b));
358 #endif /* QSORT_USES_VOID_P */
359 
360 
361 /*
362  * Transfer vector
363  */
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 */
372 };
373 
374 
375 static void
376 dump_buf(
377 	l_fp *coffs,
378 	int from,
379 	int to,
380 	char *text
381 	)
382 {
383 	char buff[DUMP_BUF_SIZE + 80];
384 	int i;
385 	register char *ptr = buff;
386 
387 	sprintf(ptr, text);
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);
392 	}
393 	msyslog(LOG_DEBUG, "D: %s", buff);
394 }
395 
396 /* msfees_init - initialize internal ees driver data */
397 static void
398 msfees_init(void)
399 {
400 	register int i;
401 	/* Just zero the data arrays */
402 	memset((char *)eesunits, 0, sizeof eesunits);
403 	memset((char *)unitinuse, 0, sizeof unitinuse);
404 
405 	acceptable_slop.l_ui = 0;
406 	acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
407 
408 	onesec.l_ui = 1;
409 	onesec.l_uf = 0;
410 
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]);
422 		stratumtouse[i]		= 0;
423 		sloppyclockflag[i]	= 0;
424 	}
425 }
426 
427 
428 /* msfees_start - open the EES devices and initialize data for processing */
429 static int
430 msfees_start(
431 	int unit,
432 	struct peer *peer
433 	)
434 {
435 	register struct eesunit *ees;
436 	register int i;
437 	int fd232 = -1;
438 	char eesdev[20];
439 	struct termios ttyb, *ttyp;
440 	struct refclockproc *pp;
441 	pp = peer->procptr;
442 
443 	if (unit >= MAXUNITS) {
444 		msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
445 			unit, MAXUNITS-1);
446 		return 0;
447 	}
448 	if (unitinuse[unit]) {
449 		msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
450 		return 0;
451 	}
452 
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);
456 
457 	fd232 = open(eesdev, O_RDWR, 0777);
458 	if (fd232 == -1) {
459 		msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
460 		return 0;
461 	}
462 
463 #ifdef	TIOCEXCL
464 	/* Set for exclusive use */
465 	if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
466 		msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
467 		goto screwed;
468 	}
469 #endif
470 
471 	/* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
472 
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.
479 	 */
480 
481 	/* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
482 
483 	ttyp = &ttyb;
484 	if (tcgetattr(fd232, ttyp) < 0) {
485 		msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
486 		goto screwed;
487 	}
488 
489 	ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
490 	ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
491 	ttyp->c_oflag = 0;
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);
496 		goto screwed;
497 	}
498 
499 	if (tcflush(fd232, TCIOFLUSH) < 0) {
500 		msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
501 		goto screwed;
502 	}
503 
504 	inherent_delay[unit].l_uf = INH_DELAY_PPS;
505 
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]);
510 
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.
513 	 */
514 	if (eesunits[unit] != 0) /* The one we want is okay */
515 	    ees = eesunits[unit];
516 	else {
517 		/* Look for an unused, but allocated struct */
518 		for (i = 0; i < MAXUNITS; i++) {
519 			if (!unitinuse[i] && eesunits[i] != 0)
520 			    break;
521 		}
522 
523 		if (i < MAXUNITS) {	/* Reclaim this one */
524 			ees = eesunits[i];
525 			eesunits[i] = 0;
526 		}			/* no spare -- make a new one */
527 		else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
528 	}
529 	memset((char *)ees, 0, sizeof(struct eesunit));
530 	eesunits[unit] = ees;
531 
532 	/* Set up the structures */
533 	ees->peer	= peer;
534 	ees->unit	= (u_char)unit;
535 	ees->timestarted= current_time;
536 	ees->ttytype	= 0;
537 	ees->io.clock_recv= ees_receive;
538 	ees->io.srcclock= (caddr_t)ees;
539 	ees->io.datalen	= 0;
540 	ees->io.fd	= fd232;
541 
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
544 	 * receiving stuff.
545 	 */
546 
547 #ifdef STREAM
548 	{
549 		int rc1;
550 		/* Pop any existing onews first ... */
551 		while (ioctl(fd232, I_POP, 0 ) >= 0) ;
552 
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) {
556 			msyslog(LOG_ERR,
557 				"ees clock: Push of `%s' and `%s' to %s failed %m",
558 				STREAM_PP1, STREAM_PP2, eesdev);
559 			goto screwed;
560 		}
561 		else {
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;
566 		}
567 	}
568 #endif /* STREAM */
569 
570 	/* Add the clock */
571 	if (!io_addclock(&ees->io)) {
572 		/* Oh shit.  Just close and return. */
573 		msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
574 		goto screwed;
575 	}
576 
577 
578 	/* All done.  Initialize a few random peer variables, then
579 	 * return success. */
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;
586 	} else {
587 		peer->refid = htonl(EESHSREFID);
588 	}
589 	unitinuse[unit] = 1;
590 	pp->unitptr = (caddr_t) &eesunits[unit];
591 	pp->clockdesc = EESDESCRIPTION;
592 	msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
593 	return (1);
594 
595     screwed:
596 	if (fd232 != -1)
597 	    (void) close(fd232);
598 	return (0);
599 }
600 
601 
602 /* msfees_shutdown - shut down a EES clock */
603 static void
604 msfees_shutdown(
605 	int unit,
606 	struct peer *peer
607 	)
608 {
609 	register struct eesunit *ees;
610 
611 	if (unit >= MAXUNITS) {
612 		msyslog(LOG_ERR,
613 			"ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
614 			unit, MAXUNITS);
615 		return;
616 	}
617 	if (!unitinuse[unit]) {
618 		msyslog(LOG_ERR,
619 			"ees clock: INTERNAL ERROR, unit number %d not in use", unit);
620 		return;
621 	}
622 
623 	/* Tell the I/O module to turn us off.  We're history. */
624 	ees = eesunits[unit];
625 	io_closeclock(&ees->io);
626 	unitinuse[unit] = 0;
627 }
628 
629 
630 /* ees_report_event - note the occurance of an event */
631 static void
632 ees_report_event(
633 	struct eesunit *ees,
634 	int code
635 	)
636 {
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.
642 		 * Soon...
643 		 */
644 	}
645 }
646 
647 
648 /* ees_receive - receive data from the serial interface on an EES clock */
649 static void
650 ees_receive(
651 	struct recvbuf *rbufp
652 	)
653 {
654 	register int n_sample;
655 	register int day;
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 */
659 	register char *cp;
660 	l_fp tmp;
661 	int call_pps_sample = 0;
662 	l_fp pps_arrvstamp;
663 	int	sincelast;
664 	int	pps_step = 0;
665 	int	suspect_4ms_step = 0;
666 	struct ppsclockev ppsclockev;
667 	long *ptr = (long *) &ppsclockev;
668 	int rc;
669 	int request;
670 #ifdef HAVE_CIOGETEV
671 	request = CIOGETEV;
672 #endif
673 #ifdef HAVE_TIOCGPPSEV
674 	request = TIOCGPPSEV;
675 #endif
676 
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 ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
682 	    printf("[%d] ", rbufp->recv_length);
683 
684 	/* Check out our state and process appropriately */
685 	switch (ees->codestate) {
686 	    case EESCS_WAIT:
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 ....
691 		 */
692 		ees->arrvtime = rbufp->recv_time;
693 		ees->codestate = EESCS_GOTSOME;
694 		ees->lencode = 0;
695 		/*FALLSTHROUGH*/
696 
697 	    case EESCS_GOTSOME:
698 		cp = &(ees->lastcode[ees->lencode]);
699 
700 		/* Gobble the bytes until the final (possibly stripped) 0xff */
701 		while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
702 			*cp++ = (char)*dpt++;
703 			ees->lencode++;
704 			/* Oh dear -- too many bytes .. */
705 			if (ees->lencode > LENEESPRT) {
706 				NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
707 					msyslog(LOG_INFO,
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));
713 #undef	D
714 				ees->badformat++;
715 				ees->reason = CODEREASON + 1;
716 				ees_event(ees, CEVNT_BADREPLY);
717 				ees_reset(ees);
718 				return;
719 			}
720 		}
721 		/* Gave up because it was end of the buffer, rather than ff */
722 		if (dpt == dpend) {
723 			/* Incomplete.  Wait for more. */
724 			if (debug & DB_LOG_AWAITMORE)
725 			    msyslog(LOG_INFO,
726 				    "I: ees clock %d: %x == %x: await more",
727 				    ees->unit, dpt, dpend);
728 			return;
729 		}
730 
731 		/* This shouldn't happen ... ! */
732 		if ((*dpt & 0x7f) != 0x7f) {
733 			msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
734 			ees->badformat++;
735 			ees->reason = CODEREASON + 2;
736 			ees_event(ees, CEVNT_BADREPLY);
737 			ees_reset(ees);
738 			return;
739 		}
740 
741 		/* Skip the 0xff */
742 		dpt++;
743 
744 		/* Finally, got a complete buffer.  Mainline code will
745 		 * continue on. */
746 		cp = ees->lastcode;
747 		break;
748 
749 	    default:
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);
754 		ees_reset(ees);
755 		return;
756 	}
757 
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.
761 	 */
762 	ees->codestate = EESCS_WAIT;
763 	ees->lasttime = current_time;
764 	if (ees->lencode != LENEESPRT) {
765 		ees->badformat++;
766 		ees->reason = CODEREASON + 6;
767 		ees_event(ees, CEVNT_BADREPLY);
768 		ees_reset(ees);
769 		return;
770 	}
771 
772 	cp = ees->lastcode;
773 
774 	/* Check that centisecond is zero */
775 	if (cp[EESM_CSEC] != 0) {
776 		ees->baddata++;
777 		ees->reason = CODEREASON + 7;
778 		ees_event(ees, CEVNT_BADREPLY);
779 		ees_reset(ees);
780 		return;
781 	}
782 
783 	/* Check flag formats */
784 	if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
785 		ees->badformat++;
786 		ees->reason = CODEREASON + 8;
787 		ees_event(ees, CEVNT_BADREPLY);
788 		ees_reset(ees);
789 		return;
790 	}
791 
792 	if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
793 		ees->badformat++;
794 		ees->reason = CODEREASON + 9;
795 		ees_event(ees, CEVNT_BADREPLY);
796 		ees_reset(ees);
797 		return;
798 	}
799 
800 	if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
801 		ees->badformat++;
802 		ees->reason = CODEREASON + 10;
803 		ees_event(ees, CEVNT_BADREPLY);
804 		ees_reset(ees);
805 		return;
806 	}
807 
808 	/* So far, so good.  Compute day, hours, minutes, seconds,
809 	 * time zone.  Do range checks on these.
810 	 */
811 
812 #define bcdunpack(val)	( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
813 #define istrue(x)	((x)?1:0)
814 
815 	ees->second  = bcdunpack(cp[EESM_SEC]);  /* second       */
816 	ees->minute  = bcdunpack(cp[EESM_MIN]);  /* minute       */
817 	ees->hour    = bcdunpack(cp[EESM_HOUR]); /* hour         */
818 
819 	day          = bcdunpack(cp[EESM_DAY]);  /* day of month */
820 
821 	switch (bcdunpack(cp[EESM_MON])) {       /* month        */
822 
823 		/*  Add in lengths of all previous months.  Add one more
824 		    if it is a leap year and after February.
825 		*/
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*/
835 	    case  3:	day += FEB;
836 		if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
837 	    case  2:	day += JAN;			  /*FALLSTHROUGH*/
838 	    case  1:	break;
839 	    default:	ees->baddata++;
840 		ees->reason = CODEREASON + 11;
841 		ees_event(ees, CEVNT_BADDATE);
842 		ees_reset(ees);
843 		return;
844 	}
845 
846 	ees->day     = day;
847 
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
852 	 * ever happens.
853 	 */
854 	ees->tz      = istrue(cp[EESM_BST]) ? -1 : 0;
855 
856 	if (ees->day > 366 || ees->day < 1 ||
857 	    ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
858 		ees->baddata++;
859 		ees->reason = CODEREASON + 12;
860 		ees_event(ees, CEVNT_BADDATE);
861 		ees_reset(ees);
862 		return;
863 	}
864 
865 	n_sample = ees->nsamples;
866 
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,
870 		       &tmp.l_ui)) {
871 		ees->baddata++;
872 		ees->reason = CODEREASON + 13;
873 		ees_event(ees, CEVNT_BADDATE);
874 		ees_reset(ees);
875 		return;
876 	}
877 	tmp.l_uf = 0;
878 
879 	/*  DON'T use ees->arrvtime -- it may be < reftime */
880 	ees->lastsampletime = tmp;
881 
882 	/* If we are synchronised to the radio, update the reference time.
883 	 * Also keep a note of when clock was last good.
884 	 */
885 	if (istrue(cp[EESM_MSFOK])) {
886 		ees->reftime = tmp;
887 		ees->clocklastgood = current_time;
888 	}
889 
890 
891 	/* Compute the offset.  For the fractional part of the
892 	 * offset we use the expected delay for the message.
893 	 */
894 	ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
895 	ees->codeoffsets[n_sample].l_uf = 0;
896 
897 	/* Number of seconds since the last step */
898 	sincelast = this_uisec - ees->last_step;
899 
900 	memset((char *) &ppsclockev, 0, sizeof ppsclockev);
901 
902 	rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
903 	if (debug & 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]);
907 
908 	/* If we managed to get the time of arrival, process the info */
909 	if (rc >= 0) {
910 		int conv = -1;
911 		pps_step = ppsclockev.serial - ees->last_pps_no;
912 
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;
917 
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 ...
926 			 */
927 			l_fp diff;
928 			diff = pps_arrvstamp;
929 			conv = 0;
930 			L_SUB(&diff, &ees->arrvtime);
931 			if (debug & 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;
941 				conv++;
942 				call_pps_sample++;
943 			}
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,
953 					pps_arrvstamp.l_uf,
954 					ees->arrvtime.l_uf,
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);
962 					conv++;
963 					call_pps_sample++;
964 				}
965 			}
966 		}
967 		ees->last_pps_no = ppsclockev.serial;
968 		if (debug & DB_PRINT_CDTC)
969 		    printf(
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);
974 	}
975 
976 	/* See if there has been a 4ms jump at a minute boundry */
977 	{	l_fp	delta;
978 #define	delta_isec	delta.l_ui
979 #define	delta_ssec	delta.l_i
980 #define	delta_sfsec	delta.l_f
981 	long	delta_f_abs;
982 
983 	delta.l_i = ees->arrvtime.l_i;
984 	delta.l_f = ees->arrvtime.l_f;
985 
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;
989 
990 	/* Dump the deltas each minute */
991 	if (debug & 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]))];
997 		char *cptr=text;
998 		int i;
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++;
1003 		}
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),
1007 			text+1);
1008 		for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
1009 	}
1010 	}
1011 
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 */
1018 		int old_sincelast;
1019 		int count=0;
1020 		int sum = 0;
1021 		/* Yes -- so compute the ramp time */
1022 		if (ees->last_step == 0) sincelast = 0;
1023 		old_sincelast = sincelast;
1024 
1025 		/* First time in, just set "ees->last_step" */
1026 		if(ees->last_step) {
1027 			int other_step = 0;
1028 			int third_step = 0;
1029 			int this_step = (sincelast + (60 /2)) / 60;
1030 			int p_step = ees->this_step;
1031 			int p;
1032 			ees->last_steps[p_step] = this_step;
1033 			p= p_step;
1034 			p_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)))
1047 					    other_step = this;
1048 					if (other_step != this) {
1049 						int idelta = (this_step - other_step);
1050 						if (idelta < 0) idelta = - idelta;
1051 						if (third_step == 0 && (
1052 							(idelta == 1) ? (
1053 								this == (other_step +1) ||
1054 								this == (other_step -1) ||
1055 								this == (this_step +1) ||
1056 								this == (this_step -1))
1057 							:
1058 							(
1059 								this == (this_step + other_step)/2
1060 								)
1061 							)) third_step = this;
1062 						if (third_step != this) break;
1063 					}
1064 				}
1065 				sum += this;
1066 				p--;
1067 				if (p < 0) p += LAST_STEPS;
1068 				count++;
1069 			}
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));
1079 #undef SV
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;
1088 			sincelast = 0;
1089 		}
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;
1094 			sum = 4 * 60;
1095 		}
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;
1105 	}
1106 	/* OK, so not a 4ms step at a minute boundry */
1107 	else {
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),
1117 					      ees->second,
1118 					      sincelast);
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) {
1122 				ees_step_notes--;
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 !");
1128 			}
1129 		}
1130 	}
1131 	}
1132 	ees->last_l = ees->arrvtime;
1133 
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
1138 	 */
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;
1143 	long	fsecs;
1144 	l_fp	inc;
1145 
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;
1149 
1150 	/* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
1151 	fsecs = sec_of_ramp * (ees->jump_fsecs /  ees->last_step_secs);
1152 
1153 	if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
1154 					   "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
1155 					   DB_LOG_DELTAS,
1156 					   ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
1157 					   pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1158 	if (debug & 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);
1162 
1163 	/* Must sign extend the result */
1164 	inc.l_i = (fsecs < 0) ? -1 : 0;
1165 	inc.l_f = fsecs;
1166 	if (debug & DB_INC_PPS)
1167 	{	L_SUB(&pps_arrvstamp, &inc);
1168 	L_SUB(&ees->arrvtime, &inc);
1169 	}
1170 	else
1171 	{	L_ADD(&pps_arrvstamp, &inc);
1172 	L_ADD(&ees->arrvtime, &inc);
1173 	}
1174 	}
1175 	else {
1176 		if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
1177 						   "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
1178 						   DB_LOG_DELTAS,
1179 						   ees->unit, ees->using_ramp,
1180 						   sincelast,
1181 						   (ees->last_step_secs)*2,
1182 						   ees->last_step_secs);
1183 		if (debug & DB_PRINT_DELTAS) printf(
1184 			"[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
1185 			DB_LOG_DELTAS,
1186 			ees->unit, ees->using_ramp,
1187 			sincelast,
1188 			(ees->last_step_secs)*2,
1189 			ees->last_step_secs);
1190 	}
1191 
1192 	L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
1193 	L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
1194 
1195 	if (call_pps_sample && !(debug & DB_NO_PPS)) {
1196 		/* Sigh -- it expects its args negated */
1197 		L_NEG(&pps_arrvstamp);
1198 		/*
1199 		 * I had to disable this here, since it appears there is no pointer to the
1200 		 * peer structure.
1201 		 *
1202 		 (void) pps_sample(peer, &pps_arrvstamp);
1203 		*/
1204 	}
1205 
1206 	/* Subtract off the local clock time stamp */
1207 	L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
1208 	if (debug & 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,
1213 					    pps_arrvstamp.l_f,
1214 					    pps_arrvstamp.l_f /4295,
1215 					    (debug & DB_NO_PPS) ? " [no PPS]" : "");
1216 
1217 	if (ees->nsamples++ == NCODES-1) ees_process(ees);
1218 
1219 	/* Done! */
1220 }
1221 
1222 
1223 /* offcompare - auxiliary comparison routine for offset sort */
1224 
1225 #ifdef QSORT_USES_VOID_P
1226 static int
1227 offcompare(
1228 	const void *va,
1229 	const void *vb
1230 	)
1231 {
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);
1235 }
1236 #else
1237 static int
1238 offcompare(
1239 	const l_fp *a,
1240 	const l_fp *b
1241 	)
1242 {
1243 	return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
1244 }
1245 #endif /* QSORT_USES_VOID_P */
1246 
1247 
1248 /* ees_process - process a pile of samples from the clock */
1249 static void
1250 ees_process(
1251 	struct eesunit *ees
1252 	)
1253 {
1254 	static int last_samples = -1;
1255 	register int i, j;
1256 	register int noff;
1257 	register l_fp *coffs = ees->codeoffsets;
1258 	l_fp offset, tmp;
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;
1264 	double doffset;
1265 
1266 	/* Reset things to zero so we don't have to worry later */
1267 	ees_reset(ees);
1268 
1269 	if (sloppyclockflag[ees->unit]) {
1270 		samplelog = (samples <  2) ? 0 :
1271 			(samples <  5) ? 1 :
1272 			(samples <  9) ? 2 :
1273 			(samples < 17) ? 3 :
1274 			(samples < 33) ? 4 : 5;
1275 		samplereduce = (1 << samplelog);
1276 	}
1277 
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;
1283 	}
1284 	if (samples < 1) return;
1285 
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:");
1288 
1289 	/* Sort the offsets, trim off the extremes, then choose one. */
1290 	qsort((char *) coffs, (u_int)samples, sizeof(l_fp), offcompare);
1291 
1292 	noff = samples;
1293 	i = 0;
1294 	while ((noff - i) > samplereduce) {
1295 		/* Trim off the sample which is further away
1296 		 * from the median.  We work this out by doubling
1297 		 * the median, subtracting off the end samples, and
1298 		 * looking at the sign of the answer, using the
1299 		 * identity (c-b)-(b-a) == 2*b-a-c
1300 		 */
1301 		tmp = coffs[(noff + i)/2];
1302 		L_ADD(&tmp, &tmp);
1303 		L_SUB(&tmp, &coffs[i]);
1304 		L_SUB(&tmp, &coffs[noff-1]);
1305 		if (L_ISNEG(&tmp)) noff--; else i++;
1306 	}
1307 
1308 	/* If requested, dump the reduce data we have in the buffer */
1309 	if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced    to:");
1310 
1311 	/* What we do next depends on the setting of the sloppy clock flag.
1312 	 * If it is on, average the remainder to derive our estimate.
1313 	 * Otherwise, just pick a representative value from the remaining stuff
1314 	 */
1315 	if (sloppyclockflag[ees->unit]) {
1316 		offset.l_ui = offset.l_uf = 0;
1317 		for (j = i; j < noff; j++)
1318 		    L_ADD(&offset, &coffs[j]);
1319 		for (j = samplelog; j > 0; j--)
1320 		    L_RSHIFTU(&offset);
1321 	}
1322 	else offset = coffs[i+BESTSAMPLE];
1323 
1324 	/* Compute the dispersion as the difference between the
1325 	 * lowest and highest offsets that remain in the
1326 	 * consideration list.
1327 	 *
1328 	 * It looks like MOST clocks have MOD (max error), so halve it !
1329 	 */
1330 	tmp = coffs[noff-1];
1331 	L_SUB(&tmp, &coffs[i]);
1332 #define	FRACT_SEC(n) ((1 << 30) / (n/2))
1333 	dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
1334 	if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
1335 		(debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
1336 		"I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
1337 		debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
1338 		offset.l_f / 4295, offset.l_f,
1339 		(dispersion * 1526) / 100,
1340 		(sloppyclockflag[ees->unit]) ? " by averaging" : "",
1341 		FRACT_SEC(10) / 4295,
1342 		(coffs[0].l_f) / 4295,
1343 		i,
1344 		(coffs[i].l_f) / 4295,
1345 		(coffs[samples/2].l_f) / 4295,
1346 		(coffs[i+BESTSAMPLE].l_f) / 4295,
1347 		noff-1,
1348 		(coffs[noff-1].l_f) / 4295,
1349 		(coffs[samples-1].l_f) / 4295);
1350 
1351 	/* Are we playing silly wotsits ?
1352 	 * If we are using all data, see if there is a "small" delta,
1353 	 * and if so, blurr this with 3/4 of the delta from the last value
1354 	 */
1355 	if (ees->usealldata && ees->offset.l_uf) {
1356 		long diff = (long) (ees->offset.l_uf - offset.l_uf);
1357 
1358 		/* is the delta small enough ? */
1359 		if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
1360 			int samd = (64 * 4) / samples;
1361 			long new;
1362 			if (samd < 2) samd = 2;
1363 			new = offset.l_uf + ((diff * (samd -1)) / samd);
1364 
1365 			/* Sign change -> need to fix up int part */
1366 			if ((new & 0x80000000) !=
1367 			    (((long) offset.l_uf) & 0x80000000))
1368 			{	NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
1369 					msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
1370 						new & 0x80000000,
1371 						((long) offset.l_uf) & 0x80000000,
1372 						new, (long) offset.l_uf,
1373 						(new < 0) ? -1 : 1);
1374 				offset.l_ui += (new < 0) ? -1 : 1;
1375 			}
1376 			dispersion /= 4;
1377 			if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
1378 				(debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
1379 				"I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
1380 				debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
1381 				((long) offset.l_uf) / 4295, new / 4295,
1382 				(dispersion * 1526) / 100);
1383 			offset.l_uf = (unsigned long) new;
1384 		}
1385 		else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1386 			(debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1387 			"[%x] No smooth as delta not %d < %ld < %d",
1388 			debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1389 			- FRACT_SEC(100), diff, FRACT_SEC(100));
1390 	}
1391 	else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1392 		(debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1393 		"I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
1394 		debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1395 		ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
1396 		offset.l_f, ees->offset.l_f - offset.l_f);
1397 
1398 	/* Collect offset info for debugging info */
1399 	ees->offset = offset;
1400 	ees->lowoffset = coffs[i];
1401 	ees->highoffset = coffs[noff-1];
1402 
1403 	/* Determine synchronization status.  Can be unsync'd either
1404 	 * by a report from the clock or by a leap hold.
1405 	 *
1406 	 * Loss of the radio signal for a short time does not cause
1407 	 * us to go unsynchronised, since the receiver keeps quite
1408 	 * good time on its own.  The spec says 20ms in 4 hours; the
1409 	 * observed drift in our clock (Cambridge) is about a second
1410 	 * a day, but even that keeps us within the inherent tolerance
1411 	 * of the clock for about 15 minutes. Observation shows that
1412 	 * the typical "short" outage is 3 minutes, so to allow us
1413 	 * to ride out those, we will give it 5 minutes.
1414 	 */
1415 	lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
1416 	isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
1417 
1418 	/* Done.  Use time of last good, synchronised code as the
1419 	 * reference time, and lastsampletime as the receive time.
1420 	 */
1421 	if (ees->fix_pending) {
1422 		msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
1423 			ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
1424 		ees->fix_pending = 0;
1425 	}
1426 	LFPTOD(&offset, doffset);
1427 	refclock_receive(ees->peer);
1428 	ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
1429 }
1430 
1431 /* msfees_poll - called by the transmit procedure */
1432 static void
1433 msfees_poll(
1434 	int unit,
1435 	struct peer *peer
1436 	)
1437 {
1438 	if (unit >= MAXUNITS) {
1439 		msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
1440 			unit);
1441 		return;
1442 	}
1443 	if (!unitinuse[unit]) {
1444 		msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
1445 			unit);
1446 		return;
1447 	}
1448 
1449 	ees_process(eesunits[unit]);
1450 
1451 	if ((current_time - eesunits[unit]->lasttime) > 150)
1452 	    ees_event(eesunits[unit], CEVNT_FAULT);
1453 }
1454 
1455 
1456 #else
1457 int refclock_msfees_bs;
1458 #endif /* REFCLOCK */
1459