PageRenderTime 139ms CodeModel.GetById 63ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/ntp/ntpd/refclock_msfees.c

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