refclock_msfees.c revision 182007
154359Sroberto/* refclock_ees - clock driver for the EES M201 receiver */
254359Sroberto
354359Sroberto#ifdef HAVE_CONFIG_H
454359Sroberto#include <config.h>
554359Sroberto#endif
654359Sroberto
754359Sroberto#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
854359Sroberto
954359Sroberto/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
1054359Sroberto * were removed as the code was overly hairy, they weren't in use
1154359Sroberto * (hence probably didn't work).  Still in RCS file at cl.cam.ac.uk
1254359Sroberto */
1354359Sroberto
1454359Sroberto#include "ntpd.h"
1554359Sroberto#include "ntp_io.h"
1654359Sroberto#include "ntp_refclock.h"
1754359Sroberto#include "ntp_unixtime.h"
1854359Sroberto#include "ntp_calendar.h"
1982498Sroberto
2082498Sroberto#include <ctype.h>
2154359Sroberto#if defined(HAVE_BSD_TTYS)
2254359Sroberto#include <sgtty.h>
2354359Sroberto#endif /* HAVE_BSD_TTYS */
2454359Sroberto#if defined(HAVE_SYSV_TTYS)
2554359Sroberto#include <termio.h>
2654359Sroberto#endif /* HAVE_SYSV_TTYS */
2754359Sroberto#if defined(HAVE_TERMIOS)
2854359Sroberto#include <termios.h>
2954359Sroberto#endif
3054359Sroberto#if defined(STREAM)
3154359Sroberto#include <stropts.h>
3254359Sroberto#endif
3354359Sroberto
3454359Sroberto#ifdef HAVE_SYS_TERMIOS_H
3554359Sroberto# include <sys/termios.h>
3654359Sroberto#endif
3754359Sroberto#ifdef HAVE_SYS_PPSCLOCK_H
3854359Sroberto# include <sys/ppsclock.h>
3954359Sroberto#endif
4054359Sroberto
4154359Sroberto#include "ntp_stdlib.h"
4254359Sroberto
43182007Srobertoint dbg = 0;
4454359Sroberto/*
4554359Sroberto	fudgefactor	= fudgetime1;
4654359Sroberto	os_delay	= fudgetime2;
4754359Sroberto	   offset_fudge	= os_delay + fudgefactor + inherent_delay;
4854359Sroberto	stratumtouse	= fudgeval1 & 0xf
49182007Sroberto	dbg		= fudgeval2;
5054359Sroberto	sloppyclockflag	= flags & CLK_FLAG1;
5154359Sroberto		1	  log smoothing summary when processing sample
5254359Sroberto		4	  dump the buffer from the clock
5354359Sroberto		8	  EIOGETKD the last n uS time stamps
5454359Sroberto	if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
5554359Sroberto	ees->dump_vals	= flags & CLK_FLAG3;
5654359Sroberto	ees->usealldata	= flags & CLK_FLAG4;
5754359Sroberto
5854359Sroberto
5954359Sroberto	bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
6054359Sroberto	bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
6154359Sroberto	bug->values[2] = (u_long)ees->status;
6254359Sroberto	bug->values[3] = (u_long)ees->lastevent;
6354359Sroberto	bug->values[4] = (u_long)ees->reason;
6454359Sroberto	bug->values[5] = (u_long)ees->nsamples;
6554359Sroberto	bug->values[6] = (u_long)ees->codestate;
6654359Sroberto	bug->values[7] = (u_long)ees->day;
6754359Sroberto	bug->values[8] = (u_long)ees->hour;
6854359Sroberto	bug->values[9] = (u_long)ees->minute;
6954359Sroberto	bug->values[10] = (u_long)ees->second;
7054359Sroberto	bug->values[11] = (u_long)ees->tz;
7154359Sroberto	bug->values[12] = ees->yearstart;
7254359Sroberto	bug->values[13] = (ees->leaphold > current_time) ?
7354359Sroberto				ees->leaphold - current_time : 0;
7454359Sroberto	bug->values[14] = inherent_delay[unit].l_uf;
7554359Sroberto	bug->values[15] = offset_fudge[unit].l_uf;
7654359Sroberto
7754359Sroberto	bug->times[0] = ees->reftime;
7854359Sroberto	bug->times[1] = ees->arrvtime;
7954359Sroberto	bug->times[2] = ees->lastsampletime;
8054359Sroberto	bug->times[3] = ees->offset;
8154359Sroberto	bug->times[4] = ees->lowoffset;
8254359Sroberto	bug->times[5] = ees->highoffset;
8354359Sroberto	bug->times[6] = inherent_delay[unit];
8454359Sroberto	bug->times[8] = os_delay[unit];
8554359Sroberto	bug->times[7] = fudgefactor[unit];
8654359Sroberto	bug->times[9] = offset_fudge[unit];
8754359Sroberto	bug->times[10]= ees->yearstart, 0;
8854359Sroberto	*/
8954359Sroberto
9054359Sroberto/* This should support the use of an EES M201 receiver with RS232
9154359Sroberto * output (modified to transmit time once per second).
9254359Sroberto *
9354359Sroberto * For the format of the message sent by the clock, see the EESM_
9454359Sroberto * definitions below.
9554359Sroberto *
9654359Sroberto * It appears to run free for an integral number of minutes, until the error
9754359Sroberto * reaches 4mS, at which point it steps at second = 01.
9854359Sroberto * It appears that sometimes it steps 4mS (say at 7 min interval),
9954359Sroberto * then the next minute it decides that it was an error, so steps back.
10054359Sroberto * On the next minute it steps forward again :-(
10154359Sroberto * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
10254359Sroberto * or 9.5uS/S then 3990.5uS at a 7min re-sync,
103132451Sroberto * at which point it may lose the "00" second time stamp.
10454359Sroberto * I assume that the most accurate time is just AFTER the re-sync.
10554359Sroberto * Hence remember the last cycle interval,
10654359Sroberto *
10754359Sroberto * Can run in any one of:
10854359Sroberto *
10954359Sroberto *	PPSCD	PPS signal sets CD which interupts, and grabs the current TOD
11054359Sroberto *	(sun)		*in the interupt code*, so as to avoid problems with
11154359Sroberto *			the STREAMS scheduling.
11254359Sroberto *
11354359Sroberto * It appears that it goes 16.5 uS slow each second, then every 4 mins it
11454359Sroberto * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
11554359Sroberto */
11654359Sroberto
11754359Sroberto/* Definitions */
11854359Sroberto#ifndef	MAXUNITS
11954359Sroberto#define	MAXUNITS	4	/* maximum number of EES units permitted */
12054359Sroberto#endif
12154359Sroberto
12254359Sroberto#ifndef	EES232
12354359Sroberto#define	EES232	"/dev/ees%d"	/* Device to open to read the data */
12454359Sroberto#endif
12554359Sroberto
12654359Sroberto/* Other constant stuff */
12754359Sroberto#ifndef	EESPRECISION
12854359Sroberto#define	EESPRECISION	(-10)		/* what the heck - 2**-10 = 1ms */
12954359Sroberto#endif
13054359Sroberto#ifndef	EESREFID
13154359Sroberto#define	EESREFID	"MSF\0"		/* String to identify the clock */
13254359Sroberto#endif
13354359Sroberto#ifndef	EESHSREFID
13454359Sroberto#define	EESHSREFID	(0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
13554359Sroberto#endif
13654359Sroberto
13754359Sroberto/* Description of clock */
13854359Sroberto#define	EESDESCRIPTION		"EES M201 MSF Receiver"
13954359Sroberto
14054359Sroberto/* Speed we run the clock port at. If this is changed the UARTDELAY
14154359Sroberto * value should be recomputed to suit.
14254359Sroberto */
14354359Sroberto#ifndef	SPEED232
14454359Sroberto#define	SPEED232	B9600	/* 9600 baud */
14554359Sroberto#endif
14654359Sroberto
14754359Sroberto/* What is the inherent delay for this mode of working, i.e. when is the
14854359Sroberto * data time stamped.
14954359Sroberto */
15054359Sroberto#define	SAFETY_SHIFT	10	/* Split the shift to avoid overflow */
15154359Sroberto#define	BITS_TO_L_FP(bits, baud) \
15254359Sroberto(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
15354359Sroberto#define	INH_DELAY_CBREAK	BITS_TO_L_FP(119, 9600)
15454359Sroberto#define	INH_DELAY_PPS		BITS_TO_L_FP(  0, 9600)
15554359Sroberto
15654359Sroberto#ifndef	STREAM_PP1
15754359Sroberto#define	STREAM_PP1	"ppsclocd\0<-- patch space for module name1 -->"
15854359Sroberto#endif
15954359Sroberto#ifndef	STREAM_PP2
16054359Sroberto#define	STREAM_PP2	"ppsclock\0<-- patch space for module name2 -->"
16154359Sroberto#endif
16254359Sroberto
16354359Sroberto     /* Offsets of the bytes of the serial line code.  The clock gives
16454359Sroberto * local time with a GMT/BST indication. The EESM_ definitions
16554359Sroberto * give offsets into ees->lastcode.
16654359Sroberto */
16754359Sroberto#define EESM_CSEC	 0	/* centiseconds - always zero in our clock  */
16854359Sroberto#define EESM_SEC	 1	/* seconds in BCD			    */
16954359Sroberto#define EESM_MIN	 2	/* minutes in BCD			    */
17054359Sroberto#define EESM_HOUR	 3	/* hours in BCD				    */
17154359Sroberto#define EESM_DAYWK	 4	/* day of week (Sun = 0 etc)		    */
17254359Sroberto#define EESM_DAY	 5	/* day of month in BCD			    */
17354359Sroberto#define EESM_MON	 6	/* month in BCD				    */
17454359Sroberto#define EESM_YEAR	 7	/* year MOD 100 in BCD			    */
17554359Sroberto#define EESM_LEAP	 8	/* 0x0f if leap year, otherwise zero        */
17654359Sroberto#define EESM_BST	 9	/* 0x03 if BST, 0x00 if GMT		    */
17754359Sroberto#define EESM_MSFOK	10	/* 0x3f if radio good, otherwise zero	    */
17854359Sroberto				/* followed by a frame alignment byte (0xff) /
17954359Sroberto				/  which is not put into the lastcode buffer*/
18054359Sroberto
18154359Sroberto/* Length of the serial time code, in characters.  The first length
18254359Sroberto * is less the frame alignment byte.
18354359Sroberto */
18454359Sroberto#define	LENEESPRT	(EESM_MSFOK+1)
18554359Sroberto#define	LENEESCODE	(LENEESPRT+1)
18654359Sroberto
18754359Sroberto     /* Code state. */
18854359Sroberto#define	EESCS_WAIT	0       /* waiting for start of timecode */
18954359Sroberto#define	EESCS_GOTSOME	1	/* have an incomplete time code buffered */
19054359Sroberto
19154359Sroberto     /* Default fudge factor and character to receive */
19254359Sroberto#define	DEFFUDGETIME	0	/* Default user supplied fudge factor */
19354359Sroberto#ifndef	DEFOSTIME
19454359Sroberto#define	DEFOSTIME	0	/* Default OS delay -- passed by Make ? */
19554359Sroberto#endif
19654359Sroberto#define	DEFINHTIME	INH_DELAY_PPS /* inherent delay due to sample point*/
19754359Sroberto
19854359Sroberto     /* Limits on things.  Reduce the number of samples to SAMPLEREDUCE by median
19954359Sroberto * elimination.  If we're running with an accurate clock, chose the BESTSAMPLE
20054359Sroberto * as the estimated offset, otherwise average the remainder.
20154359Sroberto */
20254359Sroberto#define	FULLSHIFT	6			/* NCODES root 2 */
20354359Sroberto#define NCODES		(1<< FULLSHIFT)		/* 64 */
20454359Sroberto#define	REDUCESHIFT	(FULLSHIFT -1)		/* SAMPLEREDUCE root 2 */
20554359Sroberto
20654359Sroberto     /* Towards the high ( Why ?) end of half */
20754359Sroberto#define	BESTSAMPLE	((samplereduce * 3) /4)	/* 24 */
20854359Sroberto
20954359Sroberto     /* Leap hold time.  After a leap second the clock will no longer be
21054359Sroberto * reliable until it resynchronizes.  Hope 40 minutes is enough. */
21154359Sroberto#define	EESLEAPHOLD	(40 * 60)
21254359Sroberto
21354359Sroberto#define	EES_STEP_F	(1 << 24) /* the receiver steps in units of about 4ms */
21454359Sroberto#define	EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
21554359Sroberto#define	EES_STEP_NOTE	(1 << 21)/* Log any unexpected jumps, say .5 ms .... */
21654359Sroberto#define	EES_STEP_NOTES	50	/* Only do a limited number */
21754359Sroberto#define	MAX_STEP	16	/* Max number of steps to remember */
21854359Sroberto
21954359Sroberto     /* debug is a bit mask of debugging that is wanted */
22054359Sroberto#define	DB_SYSLOG_SMPLI		0x0001
22154359Sroberto#define	DB_SYSLOG_SMPLE		0x0002
22254359Sroberto#define	DB_SYSLOG_SMTHI		0x0004
22354359Sroberto#define	DB_SYSLOG_NSMTHE	0x0008
22454359Sroberto#define	DB_SYSLOG_NSMTHI	0x0010
22554359Sroberto#define	DB_SYSLOG_SMTHE		0x0020
22654359Sroberto#define	DB_PRINT_EV		0x0040
22754359Sroberto#define	DB_PRINT_CDT		0x0080
22854359Sroberto#define	DB_PRINT_CDTC		0x0100
22954359Sroberto#define	DB_SYSLOG_KEEPD		0x0800
23054359Sroberto#define	DB_SYSLOG_KEEPE		0x1000
23154359Sroberto#define	DB_LOG_DELTAS		0x2000
23254359Sroberto#define	DB_PRINT_DELTAS		0x4000
23354359Sroberto#define	DB_LOG_AWAITMORE	0x8000
23454359Sroberto#define	DB_LOG_SAMPLES		0x10000
23554359Sroberto#define	DB_NO_PPS		0x20000
23654359Sroberto#define	DB_INC_PPS		0x40000
23754359Sroberto#define	DB_DUMP_DELTAS		0x80000
23854359Sroberto
23954359Sroberto     struct eesunit {			/* EES unit control structure. */
24054359Sroberto	     struct peer *peer;		/* associated peer structure */
24154359Sroberto	     struct refclockio io;		/* given to the I/O handler */
24254359Sroberto	     l_fp	reftime;		/* reference time */
24354359Sroberto	     l_fp	lastsampletime;		/* time as in txt from last EES msg */
24454359Sroberto	     l_fp	arrvtime;		/* Time at which pkt arrived */
24554359Sroberto	     l_fp	codeoffsets[NCODES];	/* the time of arrival of 232 codes */
24654359Sroberto	     l_fp	offset;			/* chosen offset        (for clkbug) */
24754359Sroberto	     l_fp	lowoffset;		/* lowest sample offset (for clkbug) */
24854359Sroberto	     l_fp	highoffset;		/* highest   "     "    (for clkbug) */
24954359Sroberto	     char	lastcode[LENEESCODE+6];	/* last time code we received */
25054359Sroberto	     u_long	lasttime;		/* last time clock heard from */
25154359Sroberto	     u_long	clocklastgood;		/* last time good radio seen */
25254359Sroberto	     u_char	lencode;		/* length of code in buffer */
25354359Sroberto	     u_char	nsamples;		/* number of samples we've collected */
25454359Sroberto	     u_char	codestate;		/* state of 232 code reception */
25554359Sroberto	     u_char	unit;			/* unit number for this guy */
25654359Sroberto	     u_char	status;			/* clock status */
25754359Sroberto	     u_char	lastevent;		/* last clock event */
25854359Sroberto	     u_char	reason;			/* reason for last abort */
25954359Sroberto	     u_char	hour;			/* hour of day */
26054359Sroberto	     u_char	minute;			/* minute of hour */
26154359Sroberto	     u_char	second;			/* seconds of minute */
26254359Sroberto	     char	tz;			/* timezone from clock */
26354359Sroberto	     u_char	ttytype;		/* method used */
26454359Sroberto	     u_char	dump_vals;		/* Should clock values be dumped */
26554359Sroberto	     u_char	usealldata;		/* Use ALL samples */
26654359Sroberto	     u_short	day;			/* day of year from last code */
26754359Sroberto	     u_long	yearstart;		/* start of current year */
26854359Sroberto	     u_long	leaphold;		/* time of leap hold expiry */
26954359Sroberto	     u_long	badformat;		/* number of bad format codes */
27054359Sroberto	     u_long	baddata;		/* number of invalid time codes */
27154359Sroberto	     u_long	timestarted;		/* time we started this */
27254359Sroberto	     long	last_pps_no;		/* The serial # of the last PPS */
27354359Sroberto	     char	fix_pending;		/* Is a "sync to time" pending ? */
27454359Sroberto	     /* Fine tuning - compensate for 4 mS ramping .... */
27554359Sroberto	     l_fp	last_l;			/* last time stamp */
27654359Sroberto	     u_char	last_steps[MAX_STEP];	/* Most recent n steps */
27754359Sroberto	     int	best_av_step;		/* Best guess at average step */
27854359Sroberto	     char	best_av_step_count;	/* # of steps over used above */
27954359Sroberto	     char	this_step;		/* Current pos in buffer */
28054359Sroberto	     int	last_step_late;		/* How late the last step was (0-59) */
28154359Sroberto	     long	jump_fsecs;		/* # of fractions of a sec last jump */
28254359Sroberto	     u_long	last_step;		/* time of last step */
28354359Sroberto	     int	last_step_secs;		/* Number of seconds in last step */
28454359Sroberto	     int	using_ramp;		/* 1 -> noemal, -1 -> over stepped */
28554359Sroberto     };
28654359Sroberto#define	last_sec	last_l.l_ui
28754359Sroberto#define	last_sfsec	last_l.l_f
28854359Sroberto#define	this_uisec	((ees->arrvtime).l_ui)
28954359Sroberto#define	this_sfsec	((ees->arrvtime).l_f)
29054359Sroberto#define	msec(x)		((x) / (1<<22))
29154359Sroberto#define	LAST_STEPS	(sizeof ees->last_steps / sizeof ees->last_steps[0])
29254359Sroberto#define	subms(x)	((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
29354359Sroberto
29454359Sroberto/* Bitmask for what methods to try to use -- currently only PPS enabled */
29554359Sroberto#define	T_CBREAK	1
29654359Sroberto#define	T_PPS		8
29754359Sroberto/* macros to test above */
29854359Sroberto#define	is_cbreak(x)	((x)->ttytype & T_CBREAK)
29954359Sroberto#define	is_pps(x)	((x)->ttytype & T_PPS)
30054359Sroberto#define	is_any(x)	((x)->ttytype)
30154359Sroberto
30254359Sroberto#define	CODEREASON	20	/* reason codes */
30354359Sroberto
30454359Sroberto/* Data space for the unit structures.  Note that we allocate these on
30554359Sroberto * the fly, but never give them back. */
30654359Srobertostatic struct eesunit *eesunits[MAXUNITS];
30754359Srobertostatic u_char unitinuse[MAXUNITS];
30854359Sroberto
30954359Sroberto/* Keep the fudge factors separately so they can be set even
31054359Sroberto * when no clock is configured. */
31154359Srobertostatic l_fp inherent_delay[MAXUNITS];		/* when time stamp is taken */
31254359Srobertostatic l_fp fudgefactor[MAXUNITS];		/* fudgetime1 */
31354359Srobertostatic l_fp os_delay[MAXUNITS];			/* fudgetime2 */
31454359Srobertostatic l_fp offset_fudge[MAXUNITS];		/* Sum of above */
31554359Srobertostatic u_char stratumtouse[MAXUNITS];
31654359Srobertostatic u_char sloppyclockflag[MAXUNITS];
31754359Sroberto
31854359Srobertostatic int deltas[60];
31954359Sroberto
32054359Srobertostatic l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
32154359Srobertostatic l_fp onesec; /* = { 1, 0 }; */
32254359Sroberto
32354359Sroberto#ifndef	DUMP_BUF_SIZE	/* Size of buffer to be used by dump_buf */
32454359Sroberto#define	DUMP_BUF_SIZE	10112
32554359Sroberto#endif
32654359Sroberto
32754359Sroberto/* ees_reset - reset the count back to zero */
32854359Sroberto#define	ees_reset(ees) (ees)->nsamples = 0; \
32954359Sroberto(ees)->codestate = EESCS_WAIT
33054359Sroberto
33154359Sroberto/* ees_event - record and report an event */
33254359Sroberto#define	ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
33354359Srobertoees_report_event((ees), (evcode))
33454359Sroberto
33554359Sroberto     /* Find the precision of the system clock by reading it */
33654359Sroberto#define	USECS	1000000
33754359Sroberto#define	MINSTEP	5	/* some systems increment uS on each call */
33854359Sroberto#define	MAXLOOPS (USECS/9)
33954359Sroberto
34054359Sroberto/*
34154359Sroberto * Function prototypes
34254359Sroberto */
34354359Sroberto
34454359Srobertostatic	int	msfees_start	P((int unit, struct peer *peer));
34554359Srobertostatic	void	msfees_shutdown	P((int unit, struct peer *peer));
34654359Srobertostatic	void	msfees_poll	P((int unit, struct peer *peer));
34754359Srobertostatic	void	msfees_init	P((void));
34854359Srobertostatic	void	dump_buf	P((l_fp *coffs, int from, int to, char *text));
34954359Srobertostatic	void	ees_report_event P((struct eesunit *ees, int code));
35054359Srobertostatic	void	ees_receive	P((struct recvbuf *rbufp));
35154359Srobertostatic	void	ees_process	P((struct eesunit *ees));
35256746Sroberto#ifdef QSORT_USES_VOID_P
35356746Srobertostatic	int	offcompare	P((const void *va, const void *vb));
35456746Sroberto#else
35556746Srobertostatic	int	offcompare	P((const l_fp *a, const l_fp *b));
35656746Sroberto#endif /* QSORT_USES_VOID_P */
35754359Sroberto
35856746Sroberto
35954359Sroberto/*
36054359Sroberto * Transfer vector
36154359Sroberto */
36254359Srobertostruct	refclock refclock_msfees = {
36354359Sroberto	msfees_start,		/* start up driver */
36454359Sroberto	msfees_shutdown,	/* shut down driver */
36554359Sroberto	msfees_poll,		/* transmit poll message */
36654359Sroberto	noentry,		/* not used */
36754359Sroberto	msfees_init,		/* initialize driver */
36854359Sroberto	noentry,		/* not used */
36954359Sroberto	NOFLAGS			/* not used */
37054359Sroberto};
37154359Sroberto
37254359Sroberto
37354359Srobertostatic void
37454359Srobertodump_buf(
37554359Sroberto	l_fp *coffs,
37654359Sroberto	int from,
37754359Sroberto	int to,
37854359Sroberto	char *text
37954359Sroberto	)
38054359Sroberto{
38154359Sroberto	char buff[DUMP_BUF_SIZE + 80];
38254359Sroberto	int i;
38354359Sroberto	register char *ptr = buff;
38454359Sroberto
38554359Sroberto	sprintf(ptr, text);
38654359Sroberto	for (i=from; i<to; i++)
38754359Sroberto	{	while (*ptr) ptr++;
38854359Sroberto	if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff);
38954359Sroberto	sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
39054359Sroberto	}
39154359Sroberto	msyslog(LOG_DEBUG, "D: %s", buff);
39254359Sroberto}
39354359Sroberto
39454359Sroberto/* msfees_init - initialize internal ees driver data */
39554359Srobertostatic void
39654359Srobertomsfees_init(void)
39754359Sroberto{
39854359Sroberto	register int i;
39954359Sroberto	/* Just zero the data arrays */
40054359Sroberto	memset((char *)eesunits, 0, sizeof eesunits);
40154359Sroberto	memset((char *)unitinuse, 0, sizeof unitinuse);
40254359Sroberto
40354359Sroberto	acceptable_slop.l_ui = 0;
40454359Sroberto	acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
40554359Sroberto
40654359Sroberto	onesec.l_ui = 1;
40754359Sroberto	onesec.l_uf = 0;
40854359Sroberto
40954359Sroberto	/* Initialize fudge factors to default. */
41054359Sroberto	for (i = 0; i < MAXUNITS; i++) {
41154359Sroberto		fudgefactor[i].l_ui	= 0;
41254359Sroberto		fudgefactor[i].l_uf	= DEFFUDGETIME;
41354359Sroberto		os_delay[i].l_ui	= 0;
41454359Sroberto		os_delay[i].l_uf	= DEFOSTIME;
41554359Sroberto		inherent_delay[i].l_ui	= 0;
41654359Sroberto		inherent_delay[i].l_uf	= DEFINHTIME;
41754359Sroberto		offset_fudge[i]		= os_delay[i];
41854359Sroberto		L_ADD(&offset_fudge[i], &fudgefactor[i]);
41954359Sroberto		L_ADD(&offset_fudge[i], &inherent_delay[i]);
42054359Sroberto		stratumtouse[i]		= 0;
42154359Sroberto		sloppyclockflag[i]	= 0;
42254359Sroberto	}
42354359Sroberto}
42454359Sroberto
42554359Sroberto
42654359Sroberto/* msfees_start - open the EES devices and initialize data for processing */
42754359Srobertostatic int
42854359Srobertomsfees_start(
42954359Sroberto	int unit,
43054359Sroberto	struct peer *peer
43154359Sroberto	)
43254359Sroberto{
43354359Sroberto	register struct eesunit *ees;
43454359Sroberto	register int i;
43554359Sroberto	int fd232 = -1;
43654359Sroberto	char eesdev[20];
43754359Sroberto	struct termios ttyb, *ttyp;
43854359Sroberto	struct refclockproc *pp;
43954359Sroberto	pp = peer->procptr;
44054359Sroberto
44154359Sroberto	if (unit >= MAXUNITS) {
44254359Sroberto		msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
44354359Sroberto			unit, MAXUNITS-1);
44454359Sroberto		return 0;
44554359Sroberto	}
44654359Sroberto	if (unitinuse[unit]) {
44754359Sroberto		msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
44854359Sroberto		return 0;
44954359Sroberto	}
45054359Sroberto
45154359Sroberto	/* Unit okay, attempt to open the devices.  We do them both at
45254359Sroberto	 * once to make sure we can */
45354359Sroberto	(void) sprintf(eesdev, EES232, unit);
45454359Sroberto
45554359Sroberto	fd232 = open(eesdev, O_RDWR, 0777);
45654359Sroberto	if (fd232 == -1) {
45754359Sroberto		msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
45854359Sroberto		return 0;
45954359Sroberto	}
46054359Sroberto
46154359Sroberto#ifdef	TIOCEXCL
46254359Sroberto	/* Set for exclusive use */
46354359Sroberto	if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
46454359Sroberto		msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
46554359Sroberto		goto screwed;
46654359Sroberto	}
46754359Sroberto#endif
46854359Sroberto
46954359Sroberto	/* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
47054359Sroberto
47154359Sroberto	/* Set port characteristics.  If we don't have a STREAMS module or
47254359Sroberto	 * a clock line discipline, cooked mode is just usable, even though it
47354359Sroberto	 * strips the top bit.  The only EES byte which uses the top
47454359Sroberto	 * bit is the year, and we don't use that anyway. If we do
47554359Sroberto	 * have the line discipline, we choose raw mode, and the
47654359Sroberto	 * line discipline code will block up the messages.
47754359Sroberto	 */
47854359Sroberto
47954359Sroberto	/* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
48054359Sroberto
48154359Sroberto	ttyp = &ttyb;
48254359Sroberto	if (tcgetattr(fd232, ttyp) < 0) {
48354359Sroberto		msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
48454359Sroberto		goto screwed;
48554359Sroberto	}
48654359Sroberto
48754359Sroberto	ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
48854359Sroberto	ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
48954359Sroberto	ttyp->c_oflag = 0;
49054359Sroberto	ttyp->c_lflag = ICANON;
49154359Sroberto	ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
49254359Sroberto	if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
49354359Sroberto		msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
49454359Sroberto		goto screwed;
49554359Sroberto	}
49654359Sroberto
49754359Sroberto	if (tcflush(fd232, TCIOFLUSH) < 0) {
49854359Sroberto		msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
49954359Sroberto		goto screwed;
50054359Sroberto	}
50154359Sroberto
50254359Sroberto	inherent_delay[unit].l_uf = INH_DELAY_PPS;
50354359Sroberto
50454359Sroberto	/* offset fudge (how *late* the timestamp is) = fudge + os delays */
50554359Sroberto	offset_fudge[unit] = os_delay[unit];
50654359Sroberto	L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
50754359Sroberto	L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
50854359Sroberto
50954359Sroberto	/* Looks like this might succeed.  Find memory for the structure.
51054359Sroberto	 * Look to see if there are any unused ones, if not we malloc() one.
51154359Sroberto	 */
51254359Sroberto	if (eesunits[unit] != 0) /* The one we want is okay */
51354359Sroberto	    ees = eesunits[unit];
51454359Sroberto	else {
51554359Sroberto		/* Look for an unused, but allocated struct */
51654359Sroberto		for (i = 0; i < MAXUNITS; i++) {
51754359Sroberto			if (!unitinuse[i] && eesunits[i] != 0)
51854359Sroberto			    break;
51954359Sroberto		}
52054359Sroberto
52154359Sroberto		if (i < MAXUNITS) {	/* Reclaim this one */
52254359Sroberto			ees = eesunits[i];
52354359Sroberto			eesunits[i] = 0;
52454359Sroberto		}			/* no spare -- make a new one */
52554359Sroberto		else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
52654359Sroberto	}
52754359Sroberto	memset((char *)ees, 0, sizeof(struct eesunit));
52854359Sroberto	eesunits[unit] = ees;
52954359Sroberto
53054359Sroberto	/* Set up the structures */
53154359Sroberto	ees->peer	= peer;
53254359Sroberto	ees->unit	= (u_char)unit;
53354359Sroberto	ees->timestarted= current_time;
53454359Sroberto	ees->ttytype	= 0;
53554359Sroberto	ees->io.clock_recv= ees_receive;
53654359Sroberto	ees->io.srcclock= (caddr_t)ees;
53754359Sroberto	ees->io.datalen	= 0;
53854359Sroberto	ees->io.fd	= fd232;
53954359Sroberto
54054359Sroberto	/* Okay.  Push one of the two (linked into the kernel, or dynamically
54154359Sroberto	 * loaded) STREAMS module, and give it to the I/O code to start
54254359Sroberto	 * receiving stuff.
54354359Sroberto	 */
54454359Sroberto
54554359Sroberto#ifdef STREAM
54654359Sroberto	{
54754359Sroberto		int rc1;
54854359Sroberto		/* Pop any existing onews first ... */
54954359Sroberto		while (ioctl(fd232, I_POP, 0 ) >= 0) ;
55054359Sroberto
55154359Sroberto		/* Now try pushing either of the possible modules */
55254359Sroberto		if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
55354359Sroberto		    ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
55454359Sroberto			msyslog(LOG_ERR,
55554359Sroberto				"ees clock: Push of `%s' and `%s' to %s failed %m",
55654359Sroberto				STREAM_PP1, STREAM_PP2, eesdev);
55754359Sroberto			goto screwed;
55854359Sroberto		}
55954359Sroberto		else {
56054359Sroberto			NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
56154359Sroberto				msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
56254359Sroberto					(rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
56354359Sroberto			ees->ttytype |= T_PPS;
56454359Sroberto		}
56554359Sroberto	}
56654359Sroberto#endif /* STREAM */
56754359Sroberto
56854359Sroberto	/* Add the clock */
56954359Sroberto	if (!io_addclock(&ees->io)) {
57054359Sroberto		/* Oh shit.  Just close and return. */
57154359Sroberto		msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
57254359Sroberto		goto screwed;
57354359Sroberto	}
57454359Sroberto
57554359Sroberto
57654359Sroberto	/* All done.  Initialize a few random peer variables, then
57754359Sroberto	 * return success. */
57854359Sroberto	peer->precision	= sys_precision;
57954359Sroberto	peer->stratum	= stratumtouse[unit];
58054359Sroberto	if (stratumtouse[unit] <= 1) {
58154359Sroberto		memcpy((char *)&pp->refid, EESREFID, 4);
58254359Sroberto		if (unit > 0 && unit < 10)
58354359Sroberto		    ((char *)&pp->refid)[3] = '0' + unit;
58454359Sroberto	} else {
58554359Sroberto		peer->refid = htonl(EESHSREFID);
58654359Sroberto	}
58754359Sroberto	unitinuse[unit] = 1;
58854359Sroberto	pp->unitptr = (caddr_t) &eesunits[unit];
58954359Sroberto	pp->clockdesc = EESDESCRIPTION;
59054359Sroberto	msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
59154359Sroberto	return (1);
59254359Sroberto
59354359Sroberto    screwed:
59454359Sroberto	if (fd232 != -1)
59554359Sroberto	    (void) close(fd232);
59654359Sroberto	return (0);
59754359Sroberto}
59854359Sroberto
59954359Sroberto
60054359Sroberto/* msfees_shutdown - shut down a EES clock */
60154359Srobertostatic void
60254359Srobertomsfees_shutdown(
60354359Sroberto	int unit,
60454359Sroberto	struct peer *peer
60554359Sroberto	)
60654359Sroberto{
60754359Sroberto	register struct eesunit *ees;
60854359Sroberto
60954359Sroberto	if (unit >= MAXUNITS) {
61054359Sroberto		msyslog(LOG_ERR,
61154359Sroberto			"ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
61254359Sroberto			unit, MAXUNITS);
61354359Sroberto		return;
61454359Sroberto	}
61554359Sroberto	if (!unitinuse[unit]) {
61654359Sroberto		msyslog(LOG_ERR,
61754359Sroberto			"ees clock: INTERNAL ERROR, unit number %d not in use", unit);
61854359Sroberto		return;
61954359Sroberto	}
62054359Sroberto
62154359Sroberto	/* Tell the I/O module to turn us off.  We're history. */
62254359Sroberto	ees = eesunits[unit];
62354359Sroberto	io_closeclock(&ees->io);
62454359Sroberto	unitinuse[unit] = 0;
62554359Sroberto}
62654359Sroberto
62754359Sroberto
62854359Sroberto/* ees_report_event - note the occurance of an event */
62954359Srobertostatic void
63054359Srobertoees_report_event(
63154359Sroberto	struct eesunit *ees,
63254359Sroberto	int code
63354359Sroberto	)
63454359Sroberto{
63554359Sroberto	if (ees->status != (u_char)code) {
63654359Sroberto		ees->status = (u_char)code;
63754359Sroberto		if (code != CEVNT_NOMINAL)
63854359Sroberto		    ees->lastevent = (u_char)code;
63954359Sroberto		/* Should report event to trap handler in here.
64054359Sroberto		 * Soon...
64154359Sroberto		 */
64254359Sroberto	}
64354359Sroberto}
64454359Sroberto
64554359Sroberto
64654359Sroberto/* ees_receive - receive data from the serial interface on an EES clock */
64754359Srobertostatic void
64854359Srobertoees_receive(
64954359Sroberto	struct recvbuf *rbufp
65054359Sroberto	)
65154359Sroberto{
65254359Sroberto	register int n_sample;
65354359Sroberto	register int day;
65454359Sroberto	register struct eesunit *ees;
65554359Sroberto	register u_char *dpt;		/* Data PoinTeR: move along ... */
65654359Sroberto	register u_char *dpend;		/* Points just *after* last data char */
65754359Sroberto	register char *cp;
65854359Sroberto	l_fp tmp;
65954359Sroberto	int call_pps_sample = 0;
66054359Sroberto	l_fp pps_arrvstamp;
66154359Sroberto	int	sincelast;
66254359Sroberto	int	pps_step = 0;
66354359Sroberto	int	suspect_4ms_step = 0;
66454359Sroberto	struct ppsclockev ppsclockev;
66554359Sroberto	long *ptr = (long *) &ppsclockev;
66654359Sroberto	int rc;
66754359Sroberto	int request;
66854359Sroberto#ifdef HAVE_CIOGETEV
66954359Sroberto	request = CIOGETEV;
67054359Sroberto#endif
67154359Sroberto#ifdef HAVE_TIOCGPPSEV
67254359Sroberto	request = TIOCGPPSEV;
67354359Sroberto#endif
67454359Sroberto
67554359Sroberto	/* Get the clock this applies to and a pointer to the data */
67654359Sroberto	ees = (struct eesunit *)rbufp->recv_srcclock;
67754359Sroberto	dpt = (u_char *)&rbufp->recv_space;
67854359Sroberto	dpend = dpt + rbufp->recv_length;
679182007Sroberto	if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
68054359Sroberto	    printf("[%d] ", rbufp->recv_length);
68154359Sroberto
68254359Sroberto	/* Check out our state and process appropriately */
68354359Sroberto	switch (ees->codestate) {
68454359Sroberto	    case EESCS_WAIT:
68554359Sroberto		/* Set an initial guess at the timestamp as the recv time.
68654359Sroberto		 * If just running in CBREAK mode, we can't improve this.
68754359Sroberto		 * If we have the CLOCK Line Discipline, PPSCD, or sime such,
68854359Sroberto		 * then we will do better later ....
68954359Sroberto		 */
69054359Sroberto		ees->arrvtime = rbufp->recv_time;
69154359Sroberto		ees->codestate = EESCS_GOTSOME;
69254359Sroberto		ees->lencode = 0;
69354359Sroberto		/*FALLSTHROUGH*/
69454359Sroberto
69554359Sroberto	    case EESCS_GOTSOME:
69654359Sroberto		cp = &(ees->lastcode[ees->lencode]);
69754359Sroberto
69854359Sroberto		/* Gobble the bytes until the final (possibly stripped) 0xff */
69954359Sroberto		while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
70054359Sroberto			*cp++ = (char)*dpt++;
70154359Sroberto			ees->lencode++;
70254359Sroberto			/* Oh dear -- too many bytes .. */
70354359Sroberto			if (ees->lencode > LENEESPRT) {
70454359Sroberto				NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
70554359Sroberto					msyslog(LOG_INFO,
70654359Sroberto						"I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
70754359Sroberto						ees->lencode, dpend - dpt, LENEESPRT,
70854359Sroberto#define D(x) (ees->lastcode[x])
70954359Sroberto						D(0), D(1), D(2), D(3), D(4), D(5), D(6),
71054359Sroberto						D(7), D(8), D(9), D(10), D(11), D(12));
71154359Sroberto#undef	D
71254359Sroberto				ees->badformat++;
71354359Sroberto				ees->reason = CODEREASON + 1;
71454359Sroberto				ees_event(ees, CEVNT_BADREPLY);
71554359Sroberto				ees_reset(ees);
71654359Sroberto				return;
71754359Sroberto			}
71854359Sroberto		}
71954359Sroberto		/* Gave up because it was end of the buffer, rather than ff */
72054359Sroberto		if (dpt == dpend) {
72154359Sroberto			/* Incomplete.  Wait for more. */
722182007Sroberto			if (dbg & DB_LOG_AWAITMORE)
72354359Sroberto			    msyslog(LOG_INFO,
724132451Sroberto				    "I: ees clock %d: %p == %p: await more",
72554359Sroberto				    ees->unit, dpt, dpend);
72654359Sroberto			return;
72754359Sroberto		}
72854359Sroberto
72954359Sroberto		/* This shouldn't happen ... ! */
73054359Sroberto		if ((*dpt & 0x7f) != 0x7f) {
73154359Sroberto			msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
73254359Sroberto			ees->badformat++;
73354359Sroberto			ees->reason = CODEREASON + 2;
73454359Sroberto			ees_event(ees, CEVNT_BADREPLY);
73554359Sroberto			ees_reset(ees);
73654359Sroberto			return;
73754359Sroberto		}
73854359Sroberto
73954359Sroberto		/* Skip the 0xff */
74054359Sroberto		dpt++;
74154359Sroberto
74254359Sroberto		/* Finally, got a complete buffer.  Mainline code will
74354359Sroberto		 * continue on. */
74454359Sroberto		cp = ees->lastcode;
74554359Sroberto		break;
74654359Sroberto
74754359Sroberto	    default:
74854359Sroberto		msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
74954359Sroberto			ees->unit, ees->codestate);
75054359Sroberto		ees->reason = CODEREASON + 5;
75154359Sroberto		ees_event(ees, CEVNT_FAULT);
75254359Sroberto		ees_reset(ees);
75354359Sroberto		return;
75454359Sroberto	}
75554359Sroberto
75654359Sroberto	/* Boy!  After all that crap, the lastcode buffer now contains
75754359Sroberto	 * something we hope will be a valid time code.  Do length
75854359Sroberto	 * checks and sanity checks on constant data.
75954359Sroberto	 */
76054359Sroberto	ees->codestate = EESCS_WAIT;
76154359Sroberto	ees->lasttime = current_time;
76254359Sroberto	if (ees->lencode != LENEESPRT) {
76354359Sroberto		ees->badformat++;
76454359Sroberto		ees->reason = CODEREASON + 6;
76554359Sroberto		ees_event(ees, CEVNT_BADREPLY);
76654359Sroberto		ees_reset(ees);
76754359Sroberto		return;
76854359Sroberto	}
76954359Sroberto
77054359Sroberto	cp = ees->lastcode;
77154359Sroberto
77254359Sroberto	/* Check that centisecond is zero */
77354359Sroberto	if (cp[EESM_CSEC] != 0) {
77454359Sroberto		ees->baddata++;
77554359Sroberto		ees->reason = CODEREASON + 7;
77654359Sroberto		ees_event(ees, CEVNT_BADREPLY);
77754359Sroberto		ees_reset(ees);
77854359Sroberto		return;
77954359Sroberto	}
78054359Sroberto
78154359Sroberto	/* Check flag formats */
78254359Sroberto	if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
78354359Sroberto		ees->badformat++;
78454359Sroberto		ees->reason = CODEREASON + 8;
78554359Sroberto		ees_event(ees, CEVNT_BADREPLY);
78654359Sroberto		ees_reset(ees);
78754359Sroberto		return;
78854359Sroberto	}
78954359Sroberto
79054359Sroberto	if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
79154359Sroberto		ees->badformat++;
79254359Sroberto		ees->reason = CODEREASON + 9;
79354359Sroberto		ees_event(ees, CEVNT_BADREPLY);
79454359Sroberto		ees_reset(ees);
79554359Sroberto		return;
79654359Sroberto	}
79754359Sroberto
79854359Sroberto	if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
79954359Sroberto		ees->badformat++;
80054359Sroberto		ees->reason = CODEREASON + 10;
80154359Sroberto		ees_event(ees, CEVNT_BADREPLY);
80254359Sroberto		ees_reset(ees);
80354359Sroberto		return;
80454359Sroberto	}
80554359Sroberto
80654359Sroberto	/* So far, so good.  Compute day, hours, minutes, seconds,
80754359Sroberto	 * time zone.  Do range checks on these.
80854359Sroberto	 */
80954359Sroberto
81054359Sroberto#define bcdunpack(val)	( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
81154359Sroberto#define istrue(x)	((x)?1:0)
81254359Sroberto
81354359Sroberto	ees->second  = bcdunpack(cp[EESM_SEC]);  /* second       */
81454359Sroberto	ees->minute  = bcdunpack(cp[EESM_MIN]);  /* minute       */
81554359Sroberto	ees->hour    = bcdunpack(cp[EESM_HOUR]); /* hour         */
81654359Sroberto
81754359Sroberto	day          = bcdunpack(cp[EESM_DAY]);  /* day of month */
81854359Sroberto
81954359Sroberto	switch (bcdunpack(cp[EESM_MON])) {       /* month        */
82054359Sroberto
82154359Sroberto		/*  Add in lengths of all previous months.  Add one more
82254359Sroberto		    if it is a leap year and after February.
82354359Sroberto		*/
82454359Sroberto	    case 12:	day += NOV;			  /*FALLSTHROUGH*/
82554359Sroberto	    case 11:	day += OCT;			  /*FALLSTHROUGH*/
82654359Sroberto	    case 10:	day += SEP;			  /*FALLSTHROUGH*/
82754359Sroberto	    case  9:	day += AUG;			  /*FALLSTHROUGH*/
82854359Sroberto	    case  8:	day += JUL;			  /*FALLSTHROUGH*/
82954359Sroberto	    case  7:	day += JUN;			  /*FALLSTHROUGH*/
83054359Sroberto	    case  6:	day += MAY;			  /*FALLSTHROUGH*/
83154359Sroberto	    case  5:	day += APR;			  /*FALLSTHROUGH*/
83254359Sroberto	    case  4:	day += MAR;			  /*FALLSTHROUGH*/
83354359Sroberto	    case  3:	day += FEB;
83454359Sroberto		if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
83554359Sroberto	    case  2:	day += JAN;			  /*FALLSTHROUGH*/
83654359Sroberto	    case  1:	break;
83754359Sroberto	    default:	ees->baddata++;
83854359Sroberto		ees->reason = CODEREASON + 11;
83954359Sroberto		ees_event(ees, CEVNT_BADDATE);
84054359Sroberto		ees_reset(ees);
84154359Sroberto		return;
84254359Sroberto	}
84354359Sroberto
84454359Sroberto	ees->day     = day;
84554359Sroberto
84654359Sroberto	/* Get timezone. The clocktime routine wants the number
84754359Sroberto	 * of hours to add to the delivered time to get UT.
84854359Sroberto	 * Currently -1 if BST flag set, 0 otherwise.  This
84954359Sroberto	 * is the place to tweak things if double summer time
85054359Sroberto	 * ever happens.
85154359Sroberto	 */
85254359Sroberto	ees->tz      = istrue(cp[EESM_BST]) ? -1 : 0;
85354359Sroberto
85454359Sroberto	if (ees->day > 366 || ees->day < 1 ||
85554359Sroberto	    ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
85654359Sroberto		ees->baddata++;
85754359Sroberto		ees->reason = CODEREASON + 12;
85854359Sroberto		ees_event(ees, CEVNT_BADDATE);
85954359Sroberto		ees_reset(ees);
86054359Sroberto		return;
86154359Sroberto	}
86254359Sroberto
86354359Sroberto	n_sample = ees->nsamples;
86454359Sroberto
86554359Sroberto	/* Now, compute the reference time value: text -> tmp.l_ui */
86654359Sroberto	if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
86754359Sroberto		       ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
86854359Sroberto		       &tmp.l_ui)) {
86954359Sroberto		ees->baddata++;
87054359Sroberto		ees->reason = CODEREASON + 13;
87154359Sroberto		ees_event(ees, CEVNT_BADDATE);
87254359Sroberto		ees_reset(ees);
87354359Sroberto		return;
87454359Sroberto	}
87554359Sroberto	tmp.l_uf = 0;
87654359Sroberto
87754359Sroberto	/*  DON'T use ees->arrvtime -- it may be < reftime */
87854359Sroberto	ees->lastsampletime = tmp;
87954359Sroberto
88054359Sroberto	/* If we are synchronised to the radio, update the reference time.
88154359Sroberto	 * Also keep a note of when clock was last good.
88254359Sroberto	 */
88354359Sroberto	if (istrue(cp[EESM_MSFOK])) {
88454359Sroberto		ees->reftime = tmp;
88554359Sroberto		ees->clocklastgood = current_time;
88654359Sroberto	}
88754359Sroberto
88854359Sroberto
88954359Sroberto	/* Compute the offset.  For the fractional part of the
89054359Sroberto	 * offset we use the expected delay for the message.
89154359Sroberto	 */
89254359Sroberto	ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
89354359Sroberto	ees->codeoffsets[n_sample].l_uf = 0;
89454359Sroberto
89554359Sroberto	/* Number of seconds since the last step */
89654359Sroberto	sincelast = this_uisec - ees->last_step;
89754359Sroberto
89854359Sroberto	memset((char *) &ppsclockev, 0, sizeof ppsclockev);
89954359Sroberto
90054359Sroberto	rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
901182007Sroberto	if (dbg & DB_PRINT_EV) fprintf(stderr,
90254359Sroberto					 "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
90354359Sroberto					 DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
90454359Sroberto					 rc, errno, ptr[0], ptr[1], ptr[2]);
90554359Sroberto
90654359Sroberto	/* If we managed to get the time of arrival, process the info */
90754359Sroberto	if (rc >= 0) {
90854359Sroberto		int conv = -1;
90954359Sroberto		pps_step = ppsclockev.serial - ees->last_pps_no;
91054359Sroberto
91154359Sroberto		/* Possible that PPS triggered, but text message didn't */
91254359Sroberto		if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
91354359Sroberto		if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
91454359Sroberto		if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
91554359Sroberto
91654359Sroberto		/* allow for single loss of PPS only */
91754359Sroberto		if (pps_step != 1 && pps_step != 2)
91854359Sroberto		    fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
91954359Sroberto			    ppsclockev.serial, ees->last_pps_no, pps_step);
92054359Sroberto		else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
92154359Sroberto		    fprintf(stderr, "buftvtots failed\n");
92254359Sroberto		else {	/* if ((ABS(time difference) - 0.25) < 0)
92354359Sroberto			 * then believe it ...
92454359Sroberto			 */
92554359Sroberto			l_fp diff;
92654359Sroberto			diff = pps_arrvstamp;
92754359Sroberto			conv = 0;
92854359Sroberto			L_SUB(&diff, &ees->arrvtime);
929182007Sroberto			if (dbg & DB_PRINT_CDT)
93054359Sroberto			    printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
93154359Sroberto				   DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
93254359Sroberto				   (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
93354359Sroberto				   (long)diff.l_ui, (long)diff.l_uf,
93454359Sroberto				   ctime(&(ppsclockev.tv.tv_sec)));
93554359Sroberto			if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
93654359Sroberto			L_SUB(&diff, &acceptable_slop);
93754359Sroberto			if (L_ISNEG(&diff)) {	/* AOK -- pps_sample */
93854359Sroberto				ees->arrvtime = pps_arrvstamp;
93954359Sroberto				conv++;
94054359Sroberto				call_pps_sample++;
94154359Sroberto			}
94254359Sroberto			/* Some loss of some signals around sec = 1 */
94354359Sroberto			else if (ees->second == 1) {
94454359Sroberto				diff = pps_arrvstamp;
94554359Sroberto				L_ADD(&diff, &onesec);
94654359Sroberto				L_SUB(&diff, &ees->arrvtime);
94754359Sroberto				if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
94854359Sroberto				L_SUB(&diff, &acceptable_slop);
94954359Sroberto				msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
95054359Sroberto					pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
95154359Sroberto					pps_arrvstamp.l_uf,
95254359Sroberto					ees->arrvtime.l_uf,
95354359Sroberto					diff.l_ui, diff.l_uf,
95454359Sroberto					(int)ppsclockev.tv.tv_usec,
95554359Sroberto					ctime(&(ppsclockev.tv.tv_sec)));
95654359Sroberto				if (L_ISNEG(&diff)) {	/* AOK -- pps_sample */
95754359Sroberto					suspect_4ms_step |= 2;
95854359Sroberto					ees->arrvtime = pps_arrvstamp;
95954359Sroberto					L_ADD(&ees->arrvtime, &onesec);
96054359Sroberto					conv++;
96154359Sroberto					call_pps_sample++;
96254359Sroberto				}
96354359Sroberto			}
96454359Sroberto		}
96554359Sroberto		ees->last_pps_no = ppsclockev.serial;
966182007Sroberto		if (dbg & DB_PRINT_CDTC)
96754359Sroberto		    printf(
96854359Sroberto			    "[%x] %08lx %08lx %d u%d (%d %d)\n",
96954359Sroberto			    DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
97054359Sroberto			    (long)pps_arrvstamp.l_uf, conv, ees->unit,
97154359Sroberto			    call_pps_sample, pps_step);
97254359Sroberto	}
97354359Sroberto
97454359Sroberto	/* See if there has been a 4ms jump at a minute boundry */
97554359Sroberto	{	l_fp	delta;
97654359Sroberto#define	delta_isec	delta.l_ui
97754359Sroberto#define	delta_ssec	delta.l_i
97854359Sroberto#define	delta_sfsec	delta.l_f
97954359Sroberto	long	delta_f_abs;
98054359Sroberto
98154359Sroberto	delta.l_i = ees->arrvtime.l_i;
98254359Sroberto	delta.l_f = ees->arrvtime.l_f;
98354359Sroberto
98454359Sroberto	L_SUB(&delta, &ees->last_l);
98554359Sroberto	delta_f_abs = delta_sfsec;
98654359Sroberto	if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
98754359Sroberto
98854359Sroberto	/* Dump the deltas each minute */
989182007Sroberto	if (dbg & DB_DUMP_DELTAS)
99054359Sroberto	{	if (/*0 <= ees->second && */
99154359Sroberto		ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
99254359Sroberto	/* Dump on second 1, as second 0 sometimes missed */
99354359Sroberto	if (ees->second == 1) {
99454359Sroberto		char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
99554359Sroberto		char *cptr=text;
99654359Sroberto		int i;
99754359Sroberto		for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
99854359Sroberto			sprintf(cptr, " %d.%04d",
99954359Sroberto				msec(deltas[i]), subms(deltas[i]));
100054359Sroberto			while (*cptr) cptr++;
100154359Sroberto		}
100254359Sroberto		msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
100354359Sroberto			msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
100454359Sroberto			msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
100554359Sroberto			text+1);
100654359Sroberto		for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
100754359Sroberto	}
100854359Sroberto	}
100954359Sroberto
101054359Sroberto	/* Lets see if we have a 4 mS step at a minute boundaary */
101154359Sroberto	if (	((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
101254359Sroberto		(delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
101354359Sroberto		(ees->second == 0 || ees->second == 1 || ees->second == 2) &&
101454359Sroberto		(sincelast < 0 || sincelast > 122)
101554359Sroberto		) {	/* 4ms jump at min boundry */
101654359Sroberto		int old_sincelast;
101754359Sroberto		int count=0;
101854359Sroberto		int sum = 0;
101954359Sroberto		/* Yes -- so compute the ramp time */
102054359Sroberto		if (ees->last_step == 0) sincelast = 0;
102154359Sroberto		old_sincelast = sincelast;
102254359Sroberto
102354359Sroberto		/* First time in, just set "ees->last_step" */
102454359Sroberto		if(ees->last_step) {
102554359Sroberto			int other_step = 0;
102654359Sroberto			int third_step = 0;
102754359Sroberto			int this_step = (sincelast + (60 /2)) / 60;
102854359Sroberto			int p_step = ees->this_step;
102954359Sroberto			int p;
103054359Sroberto			ees->last_steps[p_step] = this_step;
103154359Sroberto			p= p_step;
103254359Sroberto			p_step++;
103354359Sroberto			if (p_step >= LAST_STEPS) p_step = 0;
103454359Sroberto			ees->this_step = p_step;
103554359Sroberto				/* Find the "average" interval */
103654359Sroberto			while (p != p_step) {
103754359Sroberto				int this = ees->last_steps[p];
103854359Sroberto				if (this == 0) break;
103954359Sroberto				if (this != this_step) {
104054359Sroberto					if (other_step == 0 && (
104154359Sroberto						this== (this_step +2) ||
104254359Sroberto						this== (this_step -2) ||
104354359Sroberto						this== (this_step +1) ||
104454359Sroberto						this== (this_step -1)))
104554359Sroberto					    other_step = this;
104654359Sroberto					if (other_step != this) {
104754359Sroberto						int idelta = (this_step - other_step);
104854359Sroberto						if (idelta < 0) idelta = - idelta;
104954359Sroberto						if (third_step == 0 && (
105054359Sroberto							(idelta == 1) ? (
105154359Sroberto								this == (other_step +1) ||
105254359Sroberto								this == (other_step -1) ||
105354359Sroberto								this == (this_step +1) ||
105454359Sroberto								this == (this_step -1))
105554359Sroberto							:
105654359Sroberto							(
105754359Sroberto								this == (this_step + other_step)/2
105854359Sroberto								)
105954359Sroberto							)) third_step = this;
106054359Sroberto						if (third_step != this) break;
106154359Sroberto					}
106254359Sroberto				}
106354359Sroberto				sum += this;
106454359Sroberto				p--;
106554359Sroberto				if (p < 0) p += LAST_STEPS;
106654359Sroberto				count++;
106754359Sroberto			}
106854359Sroberto			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);
106954359Sroberto			if (count != 0) sum = ((sum * 60) + (count /2)) / count;
107054359Sroberto#define	SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
107154359Sroberto			msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
107254359Sroberto				ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
107354359Sroberto				SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
107454359Sroberto			printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
107554359Sroberto			       ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
107654359Sroberto			       SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
107754359Sroberto#undef SV
107854359Sroberto			ees->jump_fsecs = delta_sfsec;
107954359Sroberto			ees->using_ramp = 1;
108054359Sroberto			if (sincelast > 170)
108154359Sroberto			    ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
108254359Sroberto			else ees->last_step_late = 30;
108354359Sroberto			if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
108454359Sroberto			if (ees->last_step_late < 0) ees->last_step_late = 0;
108554359Sroberto			if (ees->last_step_late >= 60) ees->last_step_late = 59;
108654359Sroberto			sincelast = 0;
108754359Sroberto		}
108854359Sroberto		else {	/* First time in -- just save info */
108954359Sroberto			ees->last_step_late = 30;
109054359Sroberto			ees->jump_fsecs = delta_sfsec;
109154359Sroberto			ees->using_ramp = 1;
109254359Sroberto			sum = 4 * 60;
109354359Sroberto		}
109454359Sroberto		ees->last_step = this_uisec;
109554359Sroberto		printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
109654359Sroberto		       ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
109754359Sroberto		       ees->second, old_sincelast, ees->last_step_late, count, sum,
109854359Sroberto		       ees->last_step_secs);
109954359Sroberto		msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
110054359Sroberto			ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
110154359Sroberto			old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
110254359Sroberto		if (sum) ees->last_step_secs = sum;
110354359Sroberto	}
110454359Sroberto	/* OK, so not a 4ms step at a minute boundry */
110554359Sroberto	else {
110654359Sroberto		if (suspect_4ms_step) msyslog(LOG_ERR,
110754359Sroberto					      "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
110854359Sroberto					      ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
110954359Sroberto					      msec(EES_STEP_F - EES_STEP_F_GRACE),
111054359Sroberto					      subms(EES_STEP_F - EES_STEP_F_GRACE),
111154359Sroberto					      (int)msec(delta_f_abs),
111254359Sroberto					      (int)subms(delta_f_abs),
111354359Sroberto					      msec(EES_STEP_F + EES_STEP_F_GRACE),
111454359Sroberto					      subms(EES_STEP_F + EES_STEP_F_GRACE),
111554359Sroberto					      ees->second,
111654359Sroberto					      sincelast);
111754359Sroberto		if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
111854359Sroberto			static int ees_step_notes = EES_STEP_NOTES;
111954359Sroberto			if (ees_step_notes > 0) {
112054359Sroberto				ees_step_notes--;
112154359Sroberto				printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
112254359Sroberto				       ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
112354359Sroberto				       ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
112454359Sroberto				msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
112554359Sroberto					ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
112654359Sroberto			}
112754359Sroberto		}
112854359Sroberto	}
112954359Sroberto	}
113054359Sroberto	ees->last_l = ees->arrvtime;
113154359Sroberto
113254359Sroberto	/* IF we have found that it's ramping
113354359Sroberto	 * && it's within twice the expected ramp period
113454359Sroberto	 * && there is a non zero step size (avoid /0 !)
113554359Sroberto	 * THEN we twiddle things
113654359Sroberto	 */
113754359Sroberto	if (ees->using_ramp &&
113854359Sroberto	    sincelast < (ees->last_step_secs)*2 &&
113954359Sroberto	    ees->last_step_secs)
114054359Sroberto	{	long	sec_of_ramp = sincelast + ees->last_step_late;
114154359Sroberto	long	fsecs;
114254359Sroberto	l_fp	inc;
114354359Sroberto
114454359Sroberto	/* Ramp time may vary, so may ramp for longer than last time */
114554359Sroberto	if (sec_of_ramp > (ees->last_step_secs + 120))
114654359Sroberto	    sec_of_ramp =  ees->last_step_secs;
114754359Sroberto
114854359Sroberto	/* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
114954359Sroberto	fsecs = sec_of_ramp * (ees->jump_fsecs /  ees->last_step_secs);
115054359Sroberto
1151182007Sroberto	if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
115254359Sroberto					   "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
115354359Sroberto					   DB_LOG_DELTAS,
115454359Sroberto					   ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
115554359Sroberto					   pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1156182007Sroberto	if (dbg & DB_PRINT_DELTAS) printf(
115754359Sroberto		"MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
115854359Sroberto		ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
115954359Sroberto		(long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
116054359Sroberto
116154359Sroberto	/* Must sign extend the result */
116254359Sroberto	inc.l_i = (fsecs < 0) ? -1 : 0;
116354359Sroberto	inc.l_f = fsecs;
1164182007Sroberto	if (dbg & DB_INC_PPS)
116554359Sroberto	{	L_SUB(&pps_arrvstamp, &inc);
116654359Sroberto	L_SUB(&ees->arrvtime, &inc);
116754359Sroberto	}
116854359Sroberto	else
116954359Sroberto	{	L_ADD(&pps_arrvstamp, &inc);
117054359Sroberto	L_ADD(&ees->arrvtime, &inc);
117154359Sroberto	}
117254359Sroberto	}
117354359Sroberto	else {
1174182007Sroberto		if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
117554359Sroberto						   "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
117654359Sroberto						   DB_LOG_DELTAS,
117754359Sroberto						   ees->unit, ees->using_ramp,
117854359Sroberto						   sincelast,
117954359Sroberto						   (ees->last_step_secs)*2,
118054359Sroberto						   ees->last_step_secs);
1181182007Sroberto		if (dbg & DB_PRINT_DELTAS) printf(
118254359Sroberto			"[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
118354359Sroberto			DB_LOG_DELTAS,
118454359Sroberto			ees->unit, ees->using_ramp,
118554359Sroberto			sincelast,
118654359Sroberto			(ees->last_step_secs)*2,
118754359Sroberto			ees->last_step_secs);
118854359Sroberto	}
118954359Sroberto
119054359Sroberto	L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
119154359Sroberto	L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
119254359Sroberto
1193182007Sroberto	if (call_pps_sample && !(dbg & DB_NO_PPS)) {
119454359Sroberto		/* Sigh -- it expects its args negated */
119554359Sroberto		L_NEG(&pps_arrvstamp);
119654359Sroberto		/*
119754359Sroberto		 * I had to disable this here, since it appears there is no pointer to the
119854359Sroberto		 * peer structure.
119954359Sroberto		 *
120054359Sroberto		 (void) pps_sample(peer, &pps_arrvstamp);
120154359Sroberto		*/
120254359Sroberto	}
120354359Sroberto
120454359Sroberto	/* Subtract off the local clock time stamp */
120554359Sroberto	L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
1206182007Sroberto	if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR,
120754359Sroberto					    "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
120854359Sroberto					    ees->unit, DB_LOG_DELTAS, n_sample,
120954359Sroberto					    ees->codeoffsets[n_sample].l_f,
121054359Sroberto					    ees->codeoffsets[n_sample].l_f / 4295,
121154359Sroberto					    pps_arrvstamp.l_f,
121254359Sroberto					    pps_arrvstamp.l_f /4295,
1213182007Sroberto					    (dbg & DB_NO_PPS) ? " [no PPS]" : "");
121454359Sroberto
121554359Sroberto	if (ees->nsamples++ == NCODES-1) ees_process(ees);
121654359Sroberto
121754359Sroberto	/* Done! */
121854359Sroberto}
121954359Sroberto
122054359Sroberto
122154359Sroberto/* offcompare - auxiliary comparison routine for offset sort */
122254359Sroberto
122356746Sroberto#ifdef QSORT_USES_VOID_P
122454359Srobertostatic int
122554359Srobertooffcompare(
122656746Sroberto	const void *va,
122756746Sroberto	const void *vb
122854359Sroberto	)
122954359Sroberto{
123056746Sroberto	const l_fp *a = (const l_fp *)va;
123156746Sroberto	const l_fp *b = (const l_fp *)vb;
123254359Sroberto	return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
123354359Sroberto}
123456746Sroberto#else
123556746Srobertostatic int
123656746Srobertooffcompare(
123756746Sroberto	const l_fp *a,
123856746Sroberto	const l_fp *b
123956746Sroberto	)
124056746Sroberto{
124156746Sroberto	return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
124256746Sroberto}
124356746Sroberto#endif /* QSORT_USES_VOID_P */
124454359Sroberto
124554359Sroberto
124654359Sroberto/* ees_process - process a pile of samples from the clock */
124754359Srobertostatic void
124854359Srobertoees_process(
124954359Sroberto	struct eesunit *ees
125054359Sroberto	)
125154359Sroberto{
125254359Sroberto	static int last_samples = -1;
125354359Sroberto	register int i, j;
125454359Sroberto	register int noff;
125554359Sroberto	register l_fp *coffs = ees->codeoffsets;
125654359Sroberto	l_fp offset, tmp;
125754359Sroberto	double dispersion;	/* ++++ */
125854359Sroberto	int lostsync, isinsync;
125954359Sroberto	int samples = ees->nsamples;
126054359Sroberto	int samplelog = 0;	/* keep "gcc -Wall" happy ! */
126154359Sroberto	int samplereduce = (samples + 1) / 2;
126254359Sroberto	double doffset;
126354359Sroberto
126454359Sroberto	/* Reset things to zero so we don't have to worry later */
126554359Sroberto	ees_reset(ees);
126654359Sroberto
126754359Sroberto	if (sloppyclockflag[ees->unit]) {
126854359Sroberto		samplelog = (samples <  2) ? 0 :
126954359Sroberto			(samples <  5) ? 1 :
127054359Sroberto			(samples <  9) ? 2 :
127154359Sroberto			(samples < 17) ? 3 :
127254359Sroberto			(samples < 33) ? 4 : 5;
127354359Sroberto		samplereduce = (1 << samplelog);
127454359Sroberto	}
127554359Sroberto
127654359Sroberto	if (samples != last_samples &&
127754359Sroberto	    ((samples != (last_samples-1)) || samples < 3)) {
127854359Sroberto		msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
127954359Sroberto			samples, last_samples, samplereduce);
128054359Sroberto		last_samples = samples;
128154359Sroberto	}
128254359Sroberto	if (samples < 1) return;
128354359Sroberto
128454359Sroberto	/* If requested, dump the raw data we have in the buffer */
128554359Sroberto	if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw  data  is:");
128654359Sroberto
128754359Sroberto	/* Sort the offsets, trim off the extremes, then choose one. */
1288182007Sroberto	qsort(
1289182007Sroberto#ifdef QSORT_USES_VOID_P
1290182007Sroberto	    (void *)
1291182007Sroberto#else
1292182007Sroberto	    (char *)
1293182007Sroberto#endif
1294182007Sroberto	    coffs, (size_t)samples, sizeof(l_fp), offcompare);
129554359Sroberto
129654359Sroberto	noff = samples;
129754359Sroberto	i = 0;
129854359Sroberto	while ((noff - i) > samplereduce) {
129954359Sroberto		/* Trim off the sample which is further away
130054359Sroberto		 * from the median.  We work this out by doubling
130154359Sroberto		 * the median, subtracting off the end samples, and
130254359Sroberto		 * looking at the sign of the answer, using the
130354359Sroberto		 * identity (c-b)-(b-a) == 2*b-a-c
130454359Sroberto		 */
130554359Sroberto		tmp = coffs[(noff + i)/2];
130654359Sroberto		L_ADD(&tmp, &tmp);
130754359Sroberto		L_SUB(&tmp, &coffs[i]);
130854359Sroberto		L_SUB(&tmp, &coffs[noff-1]);
130954359Sroberto		if (L_ISNEG(&tmp)) noff--; else i++;
131054359Sroberto	}
131154359Sroberto
131254359Sroberto	/* If requested, dump the reduce data we have in the buffer */
131354359Sroberto	if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced    to:");
131454359Sroberto
131554359Sroberto	/* What we do next depends on the setting of the sloppy clock flag.
131654359Sroberto	 * If it is on, average the remainder to derive our estimate.
131754359Sroberto	 * Otherwise, just pick a representative value from the remaining stuff
131854359Sroberto	 */
131954359Sroberto	if (sloppyclockflag[ees->unit]) {
132054359Sroberto		offset.l_ui = offset.l_uf = 0;
132154359Sroberto		for (j = i; j < noff; j++)
132254359Sroberto		    L_ADD(&offset, &coffs[j]);
132354359Sroberto		for (j = samplelog; j > 0; j--)
132454359Sroberto		    L_RSHIFTU(&offset);
132554359Sroberto	}
132654359Sroberto	else offset = coffs[i+BESTSAMPLE];
132754359Sroberto
132854359Sroberto	/* Compute the dispersion as the difference between the
132954359Sroberto	 * lowest and highest offsets that remain in the
133054359Sroberto	 * consideration list.
133154359Sroberto	 *
133254359Sroberto	 * It looks like MOST clocks have MOD (max error), so halve it !
133354359Sroberto	 */
133454359Sroberto	tmp = coffs[noff-1];
133554359Sroberto	L_SUB(&tmp, &coffs[i]);
133654359Sroberto#define	FRACT_SEC(n) ((1 << 30) / (n/2))
133754359Sroberto	dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
1338182007Sroberto	if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
1339182007Sroberto		(dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
134054359Sroberto		"I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
1341182007Sroberto		dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
134254359Sroberto		offset.l_f / 4295, offset.l_f,
134354359Sroberto		(dispersion * 1526) / 100,
134454359Sroberto		(sloppyclockflag[ees->unit]) ? " by averaging" : "",
134554359Sroberto		FRACT_SEC(10) / 4295,
134654359Sroberto		(coffs[0].l_f) / 4295,
134754359Sroberto		i,
134854359Sroberto		(coffs[i].l_f) / 4295,
134954359Sroberto		(coffs[samples/2].l_f) / 4295,
135054359Sroberto		(coffs[i+BESTSAMPLE].l_f) / 4295,
135154359Sroberto		noff-1,
135254359Sroberto		(coffs[noff-1].l_f) / 4295,
135354359Sroberto		(coffs[samples-1].l_f) / 4295);
135454359Sroberto
135554359Sroberto	/* Are we playing silly wotsits ?
135654359Sroberto	 * If we are using all data, see if there is a "small" delta,
135754359Sroberto	 * and if so, blurr this with 3/4 of the delta from the last value
135854359Sroberto	 */
135954359Sroberto	if (ees->usealldata && ees->offset.l_uf) {
136054359Sroberto		long diff = (long) (ees->offset.l_uf - offset.l_uf);
136154359Sroberto
136254359Sroberto		/* is the delta small enough ? */
136354359Sroberto		if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
136454359Sroberto			int samd = (64 * 4) / samples;
136554359Sroberto			long new;
136654359Sroberto			if (samd < 2) samd = 2;
136754359Sroberto			new = offset.l_uf + ((diff * (samd -1)) / samd);
136854359Sroberto
136954359Sroberto			/* Sign change -> need to fix up int part */
137056746Sroberto			if ((new & 0x80000000) !=
137156746Sroberto			    (((long) offset.l_uf) & 0x80000000))
137254359Sroberto			{	NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
137354359Sroberto					msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
137456746Sroberto						new & 0x80000000,
137556746Sroberto						((long) offset.l_uf) & 0x80000000,
137654359Sroberto						new, (long) offset.l_uf,
137754359Sroberto						(new < 0) ? -1 : 1);
137854359Sroberto				offset.l_ui += (new < 0) ? -1 : 1;
137954359Sroberto			}
138054359Sroberto			dispersion /= 4;
1381182007Sroberto			if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
1382182007Sroberto				(dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
138354359Sroberto				"I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
1384182007Sroberto				dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
138554359Sroberto				((long) offset.l_uf) / 4295, new / 4295,
138654359Sroberto				(dispersion * 1526) / 100);
138754359Sroberto			offset.l_uf = (unsigned long) new;
138854359Sroberto		}
1389182007Sroberto		else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1390182007Sroberto			(dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
139154359Sroberto			"[%x] No smooth as delta not %d < %ld < %d",
1392182007Sroberto			dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
139354359Sroberto			- FRACT_SEC(100), diff, FRACT_SEC(100));
139454359Sroberto	}
1395182007Sroberto	else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1396182007Sroberto		(dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
139754359Sroberto		"I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
1398182007Sroberto		dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
139954359Sroberto		ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
140054359Sroberto		offset.l_f, ees->offset.l_f - offset.l_f);
140154359Sroberto
140254359Sroberto	/* Collect offset info for debugging info */
140354359Sroberto	ees->offset = offset;
140454359Sroberto	ees->lowoffset = coffs[i];
140554359Sroberto	ees->highoffset = coffs[noff-1];
140654359Sroberto
140754359Sroberto	/* Determine synchronization status.  Can be unsync'd either
140854359Sroberto	 * by a report from the clock or by a leap hold.
140954359Sroberto	 *
141054359Sroberto	 * Loss of the radio signal for a short time does not cause
141154359Sroberto	 * us to go unsynchronised, since the receiver keeps quite
141254359Sroberto	 * good time on its own.  The spec says 20ms in 4 hours; the
141354359Sroberto	 * observed drift in our clock (Cambridge) is about a second
141454359Sroberto	 * a day, but even that keeps us within the inherent tolerance
141554359Sroberto	 * of the clock for about 15 minutes. Observation shows that
141654359Sroberto	 * the typical "short" outage is 3 minutes, so to allow us
141754359Sroberto	 * to ride out those, we will give it 5 minutes.
141854359Sroberto	 */
141954359Sroberto	lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
142054359Sroberto	isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
142154359Sroberto
142254359Sroberto	/* Done.  Use time of last good, synchronised code as the
142354359Sroberto	 * reference time, and lastsampletime as the receive time.
142454359Sroberto	 */
142554359Sroberto	if (ees->fix_pending) {
142654359Sroberto		msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
142754359Sroberto			ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
142854359Sroberto		ees->fix_pending = 0;
142954359Sroberto	}
143054359Sroberto	LFPTOD(&offset, doffset);
143154359Sroberto	refclock_receive(ees->peer);
143254359Sroberto	ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
143354359Sroberto}
143454359Sroberto
143554359Sroberto/* msfees_poll - called by the transmit procedure */
143654359Srobertostatic void
143754359Srobertomsfees_poll(
143854359Sroberto	int unit,
143954359Sroberto	struct peer *peer
144054359Sroberto	)
144154359Sroberto{
144254359Sroberto	if (unit >= MAXUNITS) {
144354359Sroberto		msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
144454359Sroberto			unit);
144554359Sroberto		return;
144654359Sroberto	}
144754359Sroberto	if (!unitinuse[unit]) {
144854359Sroberto		msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
144954359Sroberto			unit);
145054359Sroberto		return;
145154359Sroberto	}
145254359Sroberto
145354359Sroberto	ees_process(eesunits[unit]);
145454359Sroberto
145554359Sroberto	if ((current_time - eesunits[unit]->lasttime) > 150)
145654359Sroberto	    ees_event(eesunits[unit], CEVNT_FAULT);
145754359Sroberto}
145854359Sroberto
145954359Sroberto
146054359Sroberto#else
146154359Srobertoint refclock_msfees_bs;
146254359Sroberto#endif /* REFCLOCK */
1463