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