/*
 * refclock_chu - clock driver for the CHU time code
 */
#if defined(REFCLOCK) && (defined(CHU) || defined(CHUCLK) || defined(CHUPPS))

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <fcntl.h>

#include "ntpd.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"


#if defined(BSD_TTYS)
#include <sgtty.h>
#endif /* BSD_TTYS */

#if defined(SYSV_TTYS)
#include <termio.h>
#endif /* SYSV_TTYS */

#if defined(STREAM)
#include <termios.h>
#include <stropts.h>
#endif /* STREAM */

#if defined (CHUPPS)
#include <sys/ppsclock.h>
#endif /* CHUPPS */

#include <sys/chudefs.h>

/*
 * The CHU time signal includes a time code which is modulated at the
 * standard Bell 103 frequencies (i.e. mark=2225Hz, space=2025Hz).
 * and formatted into 8 bit characters with one start bit and two
 * stop bits. The time code is composed of 10 8-bit characters.
 * The second 5 bytes of the timecode are a redundancy check, and
 * are a copy of the first 5 bytes.
 *
 * It is assumed that you have built or modified a Bell 103 standard
 * modem, attached the input to the output of a radio and cabled the
 * output to a serial port on your computer, i.e. what you are receiving
 * is essentially the output of your radio.  It is also assumed you have
 * installed a special CHU line discipline to condition the output from
 * the terminal driver and take accurate time stamps.
 *
 * There are two types of timecodes. One is sent in the 32nd
 * through 39th second of the minute.
 *
 *     6dddhhmmss6dddhhmmss
 *
 * where ddd is the day of the year, hh is the hour (in UTC), mm is
 * the minute and ss the second.  The 6 is a constant.  Note that
 * the code is sent twice.
 *
 * The second sort of timecode is sent only during the 31st second
 * past the minute.
 *
 *     xdyyyyttabXDYYYYTTAB
 *
 * In this case, the second part of the code is the one's complement
 * of the code. This differentiates it from the other timecode
 * format.
 *
 * d is the absolute value of DUT (in tenths of a second). yyyy
 * is the year. tt is the difference between UTC and TAI. a is
 * a canadian daylight time flag and b is a serial number.
 * x is a bitwise field. The least significant bit of x is
 * one if DUT is negative. The 2nd bit is set if a leap second
 * will be added at the next opportunity. The 3rd bit is set if
 * a leap second will be deleted at the next opportunity.
 * The 4th bit is an even parity bit for the other three bits
 * in this nibble.
 *
 * The start bit in each character has a precise relationship to
 * the on-time second.  Most often UART's synchronize themselves to the
 * start bit and will post an interrupt at the center of the first stop
 * bit.  Thus each character's interrupt should occur at a fixed offset
 * from the on-time second.  This means that a timestamp taken at the
 * arrival of each character in the code will provide an independent
 * estimate of the offset.  Since there are 10 characters in the time
 * code and the code is sent 9 times per minute, this means you potentially
 * get 90 offset samples per minute.  Much of the code in here is dedicated
 * to producing a single offset estimate from these samples.
 *
 * A note about the line discipline.  It is possible to receive the
 * CHU time code in raw mode, but this has disadvantages.  In particular,
 * this puts a lot of code between the interrupt and the time you freeze
 * a time stamp, decreasing precision.  It is also expensive in terms of
 * context switches, and made even more expensive by the way I do I/O.
 * Worse, since you are listening directly to the output of your radio,
 * CHU is noisy and will make you spend a lot of time receiving noise.
 *
 * The line discipline fixes a lot of this.  It knows that the CHU time
 * code consists of 10 bytes which arrive with an intercharacter
 * spacing of about 37 ms, and that the data is BCD, and filters on this
 * basis.  It delivers block of ten characters plus their associated time
 * stamps all at once.  The time stamps are hence about as accurate as
 * a Unix machine can get them, and much of the noise disappears in the
 * kernel with no context switching cost.
 *
 * The kernel module also will insure that the packets that are
 * delivered have the correct redundancy bytes, and will return
 * a flag in chutype to differentiate one sort of packet from
 * the other.
 */

/*
 * Definitions
 */
#define	MAXUNITS	4	/* maximum number of CHU units permitted */
#define	CHUDEV	"/dev/chu%d"	/* device we open.  %d is unit number */
#define SPEED232 B300		/* uart speed (300 baud) */
#define	NCHUCODES	8	/* expect 8 CHU codes per minute */
#define CHULDISC	10	/* XXX temp CHU line discipline */

/*
 * To compute a quality for the estimate (a pseudo dispersion) we add a
 * fixed 10 ms for each missing code in the minute and add to this
 * the sum of the differences between the remaining offsets and the
 * estimated sample offset.
 */
#define	CHUDELAYPENALTY	0x0000028f

/*
 * Other constant stuff
 */
#define	CHUPRECISION	(-9)	/* what the heck */
#define	CHUREFID	"CHU\0"
#define	CHUDESCRIPTION	"Direct synchronized to CHU timecode"
#define	CHUHSREFID	0x7f7f070a /* 127.127.7.10 refid for hi strata */

/*
 * Default fudge factors
 */
#define	DEFPROPDELAY	0x00624dd3	/* 0.0015 seconds, 1.5 ms */
#define	DEFFILTFUDGE	0x000d1b71	/* 0.0002 seconds, 200 us */

/*
 * Hacks to avoid excercising the multiplier.  I have no pride.
 */
#define	MULBY10(x)	(((x)<<3) + ((x)<<1))
#define	MULBY60(x)	(((x)<<6) - ((x)<<2))	/* watch overflow */
#define	MULBY24(x)	(((x)<<4) + ((x)<<3))

/*
 * Constants for use when multiplying by 0.1.  ZEROPTONE is 0.1
 * as an l_fp fraction, NZPOBITS is the number of significant bits
 * in ZEROPTONE.
 */
#define	ZEROPTONE	0x1999999a
#define	NZPOBITS	29

static char hexstring[]="0123456789abcdef";

/*
 * CHU unit control structure.
 */
struct chuunit {
	struct peer *peer;		/* associated peer structure */
	struct event chutimer;		/* timeout timer structure */
	struct refclockio chuio;	/* given to the I/O handler */
	l_fp offsets[NCHUCODES];	/* offsets computed from each code */
	l_fp rectimes[NCHUCODES];	/* times we received this stuff */
	U_LONG reftimes[NCHUCODES];	/* time of last code received */
	u_char lastcode[4*NCHUCHARS];	/* last code we received */
	u_char expect;			/* the next offset expected */
	u_char unit;			/* unit number for this guy */
	u_short haveoffset;		/* flag word indicating valid offsets */
	u_short flags;			/* operational flags */
	u_char status;			/* clock status */
	u_char lastevent;		/* last clock event */
	u_char unused[2];
	U_LONG lastupdate;		/* last time data received */
	U_LONG responses;		/* number of responses */
	U_LONG badformat;		/* number of bad format responses */
	U_LONG baddata;			/* number of invalid time codes */
	U_LONG timestarted;		/* time we started this */
	u_char leap;			/* leap status */
};

#define	CHUTIMERSET	0x1		/* timer is set to fire */


/*
 * The CHU table.  This gives the expected time of arrival of each
 * character after the on-time second and is computed as follows:
 * The CHU time code is sent at 300 bps.  Your average UART will
 * synchronize at the edge of the start bit and will consider the
 * character complete at the middle of the first stop bit, i.e.
 * 0.031667 ms later (some UARTS may complete the character at the
 * end of the stop bit instead of the middle, but you can fudge this).
 * Thus the expected time of each interrupt is the start bit time plus
 * 0.031667 seconds.  These times are in chutable[].  To this we add
 * such things as propagation delay and delay fudge factor.
 */
#define	CHARDELAY	0x081b4e82

static U_LONG chutable[NCHUCHARS] = {
	0x22222222 + CHARDELAY,		/* 0.1333333333 */
	0x2b851eb8 + CHARDELAY,		/* 0.170 (exactly) */
	0x34e81b4e + CHARDELAY,		/* 0.2066666667 */
	0x3f92c5f9 + CHARDELAY,		/* 0.2483333333 */
	0x47ae147b + CHARDELAY,		/* 0.280 (exactly) */
	0x51111111 + CHARDELAY,		/* 0.3166666667 */
	0x5a740da7 + CHARDELAY,		/* 0.3533333333 */
	0x63d70a3d + CHARDELAY,		/* 0.390 (exactly) */
	0x6d3a06d4 + CHARDELAY,		/* 0.4266666667 */
	0x769d0370 + CHARDELAY,		/* 0.4633333333 */
};

/*
 * Data space for the unit structures.  Note that we allocate these on
 * the fly, but never give them back.
 */
static struct chuunit *chuunits[MAXUNITS];
static u_char unitinuse[MAXUNITS];

/*
 * Keep the fudge factors separately so they can be set even
 * when no clock is configured.
 */
static l_fp propagation_delay[MAXUNITS];
static l_fp fudgefactor[MAXUNITS];
static l_fp offset_fudge[MAXUNITS];
static u_char stratumtouse[MAXUNITS];
static u_char sloppyclockflag[MAXUNITS];

/*
 * We keep track of the start of the year, watching for changes.
 * We also keep track of whether the year is a leap year or not.
 * All because stupid CHU doesn't include the year in the time code.
 */
static U_LONG yearstart;

/*
 * Imported from the timer module
 */
extern U_LONG current_time;
extern struct event timerqueue[];

/*
 * Imported from ntp_loopfilter module
 */
extern int fdpps;		/* pps file descriptor */

/*
 * Imported from ntpd module
 */
extern int debug;		/* global debug flag */

/*
 * Event reporting.  This optimizes things a little.
 */
#define	chu_event(chu, evcode) \
	do { \
		if ((chu)->status != (u_char)(evcode)) \
			chu_report_event((chu), (evcode)); \
	} while (0)

static	void	chu_init	P((void));
static	int	chu_start	P((u_int, struct peer *));
static	void	chu_shutdown	P((int));
static	void	chu_report_event	P((struct chuunit *, int));
static	void	chu_receive	P((struct recvbuf *));
static	void	chu_process	P((struct chuunit *));
static	void	chu_poll	P((int, struct peer *));
static	void	chu_control	P((u_int, struct refclockstat *, struct refclockstat *));
static	void	chu_timeout	P((struct peer *));

/*
 * Transfer vector
 */
struct	refclock refclock_chu = {
	chu_start, chu_shutdown, chu_poll,
	chu_control, chu_init, noentry, NOFLAGS
};

/*
 * chu_init - initialize internal chu driver data
 */
static void
chu_init()
{
	register int i;
	/*
	 * Just zero the data arrays
	 */
	bzero((char *)chuunits, sizeof chuunits);
	bzero((char *)unitinuse, sizeof unitinuse);

	/*
	 * Initialize fudge factors to default.
	 */
	for (i = 0; i < MAXUNITS; i++) {
		propagation_delay[i].l_ui = 0;
		propagation_delay[i].l_uf = DEFPROPDELAY;
		fudgefactor[i].l_ui = 0;
		fudgefactor[i].l_uf = DEFFILTFUDGE;
		offset_fudge[i] = propagation_delay[i];
		L_ADD(&offset_fudge[i], &fudgefactor[i]);
		stratumtouse[i] = 0;
		sloppyclockflag[i] = 0;
	}
}


/*
 * chu_start - open the CHU device and initialize data for processing
 */
static int
chu_start(unit, peer)
	u_int unit;
	struct peer *peer;
{
	register struct chuunit *chu;
	register int i;
	int fd232;
	char chudev[20];
	l_fp ts;

	/*
	 * Check configuration info
	 */
	if (unit >= MAXUNITS) {
		syslog(LOG_ERR, "chu_start: unit %d invalid", unit);
		return 0;
	}
	if (unitinuse[unit]) {
		syslog(LOG_ERR, "chu_start: unit %d in use", unit);
		return 0;
	}

	/*
	 * Open serial port
	 */
	(void) sprintf(chudev, CHUDEV, unit);
	fd232 = open(chudev, O_RDONLY, 0777);
	if (fd232 == -1) {
		syslog(LOG_ERR, "chu_start: open of %s: %m", chudev);
		return 0;
	}

#if defined(SYSV_TTYS)
	/*
	 * System V serial line parameters (termio interface)
	 *
	 * The CHUCLK support uses a 300-baud modem and level converter
	 * (gadget box). It requires the chu_clk line discipline and
	 * SysV v7 (?) or later.
	 */
    {	struct termio ttyb;
	if (ioctl(fd232, TCGETA, (char *)&ttyb) < 0) {
                syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TCGETA): %m", chudev);
                goto screwed;
        }
        ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
        ttyb.c_oflag = 0;
        ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
        ttyb.c_lflag = 0;
	ttyb.c_line = CHULDISC;
	ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
	ttyb.c_cc[VMIN] = 1;
	ttyb.c_cc[VTIME] = 0;
        if (ioctl(fd232, TCSETA, (char *)&ttyb) < 0) {
                syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TCSETA): %m", chudev);
                goto screwed;
        }
    }
#endif /* SYSV_TTYS */
#if defined(STREAM)
	/*
	 * System V STREAM serial line parameters (termios interface)
	 *
	 * The CHUCLK support uses a 300-baud modem and level converter
	 * (gadget box). It requires the chu_clk streams module and
	 * SunOS 4.1.1 or later.
	 */
    {	struct termios ttyb;
	if (ioctl(fd232, TCGETS, (char *)&ttyb) < 0) {
                syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TCGETS): %m", chudev);
                goto screwed;
        }
        ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
        ttyb.c_oflag = 0;
        ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
        ttyb.c_lflag = 0;
	ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
	ttyb.c_cc[VMIN] = 1;
	ttyb.c_cc[VTIME] = 0;
        if (ioctl(fd232, TCSETS, (char *)&ttyb) < 0) {
                syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TCSETS): %m", chudev);
                goto screwed;
        }
	while (ioctl(fd232, I_POP, 0 ) >= 0) ;
	if (ioctl(fd232, I_PUSH, "chu" ) < 0) {
		syslog(LOG_ERR,
		    "chu_start: ioctl(%s, I_PUSH, chu): %m", chudev);
		goto screwed;
	}
#if defined(CHUPPS)
	/*
	 * The following code is used only with the 1-pps signal and
	 * gadget box. It requires the ppsclock streams module and SunOS
	 * 4.1.1 or later.
	 */
	if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
		syslog(LOG_ERR,
		    "chu_start: ioctl(%s, I_PUSH, ppsclock): %m", chudev);
	else
		fdpps = fd232;
#endif /* CHUPPS */
    }
#endif /* STREAM */
#if defined(BSD_TTYS)
	/*
	 * 4.3bsd serial line parameters (sgttyb interface)
	 *
	 * The CHUCLK support uses a 300-baud modem and level converter
	 * (gadget box). It requires the chu_clk streams module and
	 * 4.3bsd or later.
	 */
    {	struct sgttyb ttyb;

	if (ioctl(fd232, TIOCGETP, (char *)&ttyb) < 0) {
		syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TIOCGETP): %m", chudev);
		goto screwed;
	}
	ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
	ttyb.sg_erase = ttyb.sg_kill = '\r';
	ttyb.sg_flags = EVENP|ODDP|RAW|CRMOD;
	if (ioctl(fd232, TIOCSETP, (char *)&ttyb) < 0) {
		syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TIOCSETP): %m", chudev);
		goto screwed;
	}
	if (ioctl(fd232, TIOCSETD, CHULDISC) < 0) {
		syslog(LOG_ERR,
		    "chu_start: ioctl(%s, TIOCSETD): %m",chudev);
		goto screwed;
	}
    }
#endif /* BSD_TTYS */

	/*
	 * Allocate unit structure
	 */
	if (chuunits[unit] != 0) {
		chu = chuunits[unit];	/* The one we want is okay */
	} else {
		for (i = 0; i < MAXUNITS; i++) {
			if (!unitinuse[i] && chuunits[i] != 0)
				break;
		}
		if (i < MAXUNITS) {
			/*
			 * Reclaim this one
			 */
			chu = chuunits[i];
			chuunits[i] = 0;
		} else {
			chu = (struct chuunit *)emalloc(sizeof(struct chuunit));
		}
	}
	bzero((char *)chu, sizeof(struct chuunit));
	chuunits[unit] = chu;

	/*
	 * Set up the structure
	 */
	chu->peer = peer;
	chu->unit = (u_char)unit;
	chu->timestarted = current_time;

	chu->chutimer.peer = (struct peer *)chu;
	chu->chutimer.event_handler = chu_timeout;

	chu->chuio.clock_recv = chu_receive;
	chu->chuio.srcclock = (caddr_t)chu;
	chu->chuio.datalen = sizeof(struct chucode);
	chu->chuio.fd = fd232;

	/*
	 * Initialize the year from the system time in case this is the
	 * first open.
	 */
	get_systime(&ts);
	yearstart = calyearstart(ts.l_ui);
	if (!io_addclock(&chu->chuio)) {
		goto screwed;
	}

	/*
	 * All done.  Initialize a few random peer variables, then
	 * return success.
	 */
	peer->precision = CHUPRECISION;
	peer->rootdelay = 0;
	peer->rootdispersion = 0;
	peer->stratum = stratumtouse[unit];
	if (stratumtouse[unit] <= 1)
		bcopy(CHUREFID, (char *)&peer->refid, 4);
	else
		peer->refid = htonl(CHUHSREFID);
	unitinuse[unit] = 1;
	return 1;

	/*
	 * Something broke; abandon ship.
	 */
screwed:
	(void) close(fd232);
	return (0);
}


/*
 * chu_shutdown - shut down a CHU clock
 */
static void
chu_shutdown(unit)
	int unit;
{
	register struct chuunit *chu;

	if (unit >= MAXUNITS) {
		syslog(LOG_ERR, "chu_shutdown: unit %d invalid", unit);
		return;
	}
	if (!unitinuse[unit]) {
		syslog(LOG_ERR, "chu_shutdown: unit %d not in use", unit);
		return;
	}

	/*
	 * Tell the I/O module to turn us off, and dequeue timer
	 * if any.  We're history.
	 */
	chu = chuunits[unit];
	if (chu->flags & CHUTIMERSET)
		TIMER_DEQUEUE(&chu->chutimer);
	io_closeclock(&chu->chuio);
	unitinuse[unit] = 0;
}

/*
 * chu_report_event - record an event and report it
 */
static void
chu_report_event(chu, code)
	struct chuunit *chu;
	int code;
{
	/*
	 * Trap support isn't up to handling this, so just
	 * record it.
	 */
	if (chu->status != (u_char)code) {
		chu->status = (u_char)code;
		if (code != CEVNT_NOMINAL)
			chu->lastevent = (u_char)code;
	}
}


/*
 * chu_receive - receive data from a CHU clock, do format checks and compute
 *		 an estimate from the sample data
 */
static void
chu_receive(rbufp)
	struct recvbuf *rbufp;
{
	register int i;
	register U_LONG date_ui;
	register U_LONG tmp;
	register u_char *code;
	register struct chuunit *chu;
	register struct chucode *chuc;
	int isneg;
	U_LONG reftime;
	l_fp off[NCHUCHARS];
	int day, hour, minute, second;

	/*
	 * Do a length check on the data.  Should be what we asked for.
	 */
	if (rbufp->recv_length != sizeof(struct chucode)) {
		syslog(LOG_ERR, "chu_receive: received %d bytes, expected %d",
		    rbufp->recv_length, sizeof(struct chucode));
		return;
	}

	/*
	 * Get the clock this applies to and a pointer to the data
	 */
	chu = (struct chuunit *)rbufp->recv_srcclock;
	chuc = (struct chucode *)&rbufp->recv_space;
	chu->responses++;
	chu->lastupdate = current_time;

	/*
	 * At this point we're assured that both halves of the
	 * data match because of what the kernel has done.
	 * But there's more than one data format. We need to
	 * check chutype to see what to do now. If it's a
	 * year packet, then we fiddle with it specially.
	 */

	if (chuc->chutype == CHU_YEAR)
	{
		u_char leapbits,parity;

		/*
	 	 * Break out the code into the BCD nibbles.
		 * Put it in the half of lastcode.
		 */
		code = chu->lastcode;
		code += 2*NCHUCHARS;
		for (i = 0; i < NCHUCHARS; i++) {
			*code++ = chuc->codechars[i] & 0xf;
			*code++ = (chuc->codechars[i] >> 4) & 0xf;
		}

		leapbits = chuc->codechars[0]&0xf;

		/*
		 * Now make sure that the leap nibble
		 * is even parity.
		 */

		parity = (leapbits ^ (leapbits >> 2))&0x3;
		parity = (parity ^ (parity>>1))&0x1;
		if (parity)
		{
			chu->badformat++;
			chu_event(chu, CEVNT_BADREPLY);
			return;
		}

		/*
		 * This just happens to work. :-)
		 */

		chu->leap = (leapbits >> 1) & 0x3;

		return;
	}

	if (chuc->chutype != CHU_TIME)
	{
		chu->badformat++;
		chu_event(chu, CEVNT_BADREPLY);
		return;
	}

	/*
	 * Break out the code into the BCD nibbles.  Only need to fiddle
	 * with the first half since both are identical.  Note the first
	 * BCD character is the low order nibble, the second the high order.
	 */
	code = chu->lastcode;
	for (i = 0; i < NCHUCHARS; i++) {
		*code++ = chuc->codechars[i] & 0xf;
		*code++ = (chuc->codechars[i] >> 4) & 0xf;
	}

	/*
	 * Format check.  Make sure the two halves match.
	 * There's really no need for this, but it can't hurt.
	 */
	for (i = 0; i < NCHUCHARS/2; i++)
		if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
			chu->badformat++;
			chu_event(chu, CEVNT_BADREPLY);
			return;
		}

	/*
	 * If the first nibble isn't a 6, we're up the creek
	 */
	code = chu->lastcode;
	if (*code++ != 6) {
		chu->badformat++;
		chu_event(chu, CEVNT_BADREPLY);
		return;
	}

	/*
	 * Collect the day, the hour, the minute and the second.
	 */
	day = *code++;
	day = MULBY10(day) + *code++;
	day = MULBY10(day) + *code++;
	hour = *code++;
	hour = MULBY10(hour) + *code++;
	minute = *code++;
	minute = MULBY10(minute) + *code++;
	second = *code++;
	second = MULBY10(second) + *code++;

	/*
	 * Sanity check the day and time.  Note that this
	 * only occurs on the 32st through the 39th second
	 * of the minute.
	 */
	if (day < 1 || day > 366
	    || hour > 23 || minute > 59
	    || second < 32 || second > 39) {
		chu->baddata++;
		if (day < 1 || day > 366) {
			chu_event(chu, CEVNT_BADDATE);
		} else {
			chu_event(chu, CEVNT_BADTIME);
		}
		return;
	}

	/*
	 * Compute the NTP date from the input data and the
	 * receive timestamp.  If this doesn't work, mark the
	 * date as bad and forget it.
	 */
	if (!clocktime(day, hour, minute, second, 0,
	    rbufp->recv_time.l_ui, &yearstart, &reftime)) {
		chu_event(chu, CEVNT_BADDATE);
		return;
	}
	date_ui = reftime;;

	/*
	 * We've now got the integral seconds part of the time code (we hope).
	 * The fractional part comes from the table.  We next compute
	 * the offsets for each character.
	 */
	for (i = 0; i < NCHUCHARS; i++) {
		register U_LONG tmp2;

		off[i].l_ui = date_ui;
		off[i].l_uf = chutable[i];
		tmp = chuc->codetimes[i].tv_sec + JAN_1970;
		TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
		M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
	}

	if (!sloppyclockflag[chu->unit]) {
		u_short ord[NCHUCHARS];
		/*
		 * In here we assume the clock has adequate bits
		 * to take timestamps with reasonable accuracy.
		 * Note that the time stamps may contain errors
		 * for a couple of reasons.  Timing is actually
		 * referenced to the start bit in each character
		 * in the time code.  If this is obscured by static
		 * you can still get a valid character but have the
		 * timestamp offset by +-1.5 ms.  Also, we may suffer
		 * from interrupt delays if the interrupt is being
		 * held off when the character arrives.  Note the
		 * latter error is always in the form of a delay.
		 *
		 * After fiddling I arrived at the following scheme.
		 * We sort the times into order by offset.  We then
		 * drop the most positive 2 offset values (which may
		 * correspond to a character arriving early due to
		 * static) and the most negative 4 (which may correspond
		 * to delayed characters, either from static or from
		 * interrupt latency).  We then take the mean of the
		 * remaining 4 offsets as our estimate.
		 */
		
		/*
		 * Set up the order array.
		 */
		for (i = 0; i < NCHUCHARS; i++)
			ord[i] = (u_short)i;
		
		/*
		 * Sort them into order.  Reuse variables with abandon.
		 */
		for (tmp = 0; tmp < (NCHUCHARS-1); tmp++) {
			for (i = (int)tmp+1; i < NCHUCHARS; i++) {
				if (!L_ISGEQ(&off[ord[i]], &off[ord[tmp]])) {
					date_ui = (U_LONG)ord[i];
					ord[i] = ord[tmp];
					ord[tmp] = (u_short)date_ui;
				}
			}
		}

		/*
		 * Done the sort.  We drop 0, 1, 2 and 3 at the negative
		 * end, and 8 and 9 at the positive.  Take the sum of
		 * 4, 5, 6 and 7.
		 */
		date_ui = off[ord[4]].l_ui;
		tmp = off[ord[4]].l_uf;
		for (i = 5; i <= 7; i++)
			M_ADD(date_ui, tmp, off[ord[i]].l_ui, off[ord[i]].l_uf);
		
		/*
		 * Round properly, then right shift two bits for the
		 * divide by four.
		 */
		if (tmp & 0x2)
			M_ADDUF(date_ui, tmp, 0x4);
		M_RSHIFT(date_ui, tmp);
		M_RSHIFT(date_ui, tmp);
	} else {
		/*
		 * Here is a *big* problem.  On a machine where the
		 * low order bit in the clock is on the order of half
		 * a millisecond or more we don't really have enough
		 * precision to make intelligent choices about which
		 * samples might be in error and which aren't.  More
		 * than this, in the case of error free data we can
		 * pick up a few bits of precision by taking the mean
		 * of the whole bunch.  This is what we do.  The problem
		 * comes when it comes time to divide the 64 bit sum of
		 * the 10 samples by 10, a procedure which really sucks.
		 * Oh, well, grin and bear it.  Compute the sum first.
		 */
		date_ui = 0;
		tmp = 0;
		for (i = 0; i < NCHUCHARS; i++)
			M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
		if (M_ISNEG(date_ui, tmp))
			isneg = 1;
		else
			isneg = 0;

		/*
		 * Here is a multiply-by-0.1 optimization that should apply
		 * just about everywhere.  If the magnitude of the sum
		 * is less than 9 we don't have to worry about overflow
		 * out of a 64 bit product, even after rounding.
		 */
		if (date_ui < 9 || date_ui > 0xfffffff7) {
			register U_LONG prod_ui;
			register U_LONG prod_uf;
	
			prod_ui = prod_uf = 0;
			/*
			 * This code knows the low order bit in 0.1 is zero
			 */
			for (i = 1; i < NZPOBITS; i++) {
				M_LSHIFT(date_ui, tmp);
				if (ZEROPTONE & (1<<i))
					M_ADD(prod_ui, prod_uf, date_ui, tmp);
			}

			/*
			 * Done, round it correctly.  Prod_ui contains the
			 * fraction.
			 */
			if (prod_uf & 0x80000000)
				prod_ui++;
			if (isneg)
				date_ui = 0xffffffff;
			else
				date_ui = 0;
			tmp = prod_ui;
			/*
			 * date_ui is integral part, tmp is fraction.
			 */
		} else {
			register U_LONG prod_ovr;
			register U_LONG prod_ui;
			register U_LONG prod_uf;
			register U_LONG highbits;

			prod_ovr = prod_ui = prod_uf = 0;
			if (isneg)
				highbits = 0xffffffff;	/* sign extend */
			else
				highbits = 0;
			/*
			 * This code knows the low order bit in 0.1 is zero
			 */
			for (i = 1; i < NZPOBITS; i++) {
				M_LSHIFT3(highbits, date_ui, tmp);
				if (ZEROPTONE & (1<<i))
					M_ADD3(prod_ovr, prod_uf, prod_ui,
					    highbits, date_ui, tmp);
			}

			if (prod_uf & 0x80000000)
				M_ADDUF(prod_ovr, prod_ui, (U_LONG)1);
			date_ui = prod_ovr;
			tmp = prod_ui;
		}
	}
	
	/*
	 * At this point we have the mean offset, with the integral
	 * part in date_ui and the fractional part in tmp.  Store
	 * it in the structure.
	 */
	i = second - 32;	/* gives a value 0 through 8 */
	if (i < chu->expect) {
		/*
		 * This shouldn't actually happen, but might if a single
		 * bit error occurred in the code which fooled us.
		 * Throw away all previous data.
		 */
		chu->expect = 0;
		chu->haveoffset = 0;
		if (chu->flags & CHUTIMERSET) {
			TIMER_DEQUEUE(&chu->chutimer);
			chu->flags &= ~CHUTIMERSET;
		}
	}

	/*
	 * Add in fudge factor.
	 */
	M_ADD(date_ui, tmp, offset_fudge[chu->unit].l_ui,
	    offset_fudge[chu->unit].l_uf);

	chu->offsets[i].l_ui = date_ui;
	chu->offsets[i].l_uf = tmp;
	chu->rectimes[i] = rbufp->recv_time;
	chu->reftimes[i] = reftime;

	chu->expect = i + 1;
	chu->haveoffset |= (1<<i);

	if (chu->expect >= NCHUCODES) {
		/*
		 * Got a full second's worth.  Dequeue timer and
		 * process this.
		 */
		if (chu->flags & CHUTIMERSET) {
			TIMER_DEQUEUE(&chu->chutimer);
			chu->flags &= ~CHUTIMERSET;
		}
		chu_process(chu);
	} else if (!(chu->flags & CHUTIMERSET)) {
		/*
		 * Try to take an interrupt sometime after the
		 * 42 second mark (leaves an extra 2 seconds for
		 * slop).  Round it up to an even multiple of
		 * 4 seconds.
		 */
		chu->chutimer.event_time =
		    current_time + (U_LONG)(10 - i) + (1<<EVENT_TIMEOUT);
		chu->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1);
		TIMER_INSERT(timerqueue, &chu->chutimer);
		chu->flags |= CHUTIMERSET;
	}
}


/*
 * chu_timeout - process a timeout event
 */
static void
chu_timeout(fakepeer)
	struct peer *fakepeer;
{
	/*
	 * If we got here it means we received some time codes
	 * but didn't get the one which should have arrived on
	 * the 39th second.  Process what we have.
	 */
	((struct chuunit *)fakepeer)->flags &= ~CHUTIMERSET;
	chu_process((struct chuunit *)fakepeer);
}


/*
 * chu_process - process the raw offset estimates we have and pass
 *		 the results on to the NTP clock filters.
 */
static void
chu_process(chu)
	register struct chuunit *chu;
{
	register int i;
	register s_fp bestoff;
	register s_fp tmpoff;
	u_fp dispersion;
	int imax;
	l_fp ts;

	/*
	 * The most positive offset.
	 */
	imax = NCHUCODES;
	for (i = 0; i < NCHUCODES; i++)
		if (chu->haveoffset & (1<<i))
			if (i < imax || L_ISGEQ(&chu->offsets[i],
			    &chu->offsets[imax]))
				imax = i;

	/*
	 * The most positive estimate is our best bet.  Go through
	 * the list again computing the dispersion.
	 */
	bestoff = LFPTOFP(&chu->offsets[imax]);
	dispersion = 0;
	for (i = 0; i < NCHUCODES; i++) {
		if (chu->haveoffset & (1<<i)) {
			tmpoff = LFPTOFP(&chu->offsets[i]);
			dispersion += (bestoff - tmpoff);
		} else {
			dispersion += CHUDELAYPENALTY;
		}
	}

	/*
	 * Make up a reference time stamp, then give it to the
	 * reference clock support code for further processing.
	 */
	ts.l_ui = chu->reftimes[imax];
	ts.l_uf = chutable[NCHUCHARS-1];

	refclock_receive(chu->peer, &chu->offsets[imax], 0,
	    dispersion, &ts, &chu->rectimes[imax], chu->leap);
	
	/*
	 * Zero out unit for next code series
	 */
	chu->haveoffset = 0;
	chu->expect = 0;
	chu_event(chu, CEVNT_NOMINAL);
}


/*
 * chu_poll - called by the transmit procedure
 */
static void
chu_poll(unit, peer)
	int unit;
	struct peer *peer;
{
	if (unit >= MAXUNITS) {
		syslog(LOG_ERR, "chu_poll: unit %d invalid", unit);
		return;
	}
	if (!unitinuse[unit]) {
		syslog(LOG_ERR, "chu_poll: unit %d not in use", unit);
		return;
	}

	if ((current_time - chuunits[unit]->lastupdate) > 150) {
		chu_event(chuunits[unit], CEVNT_PROP);
	}
}



/*
 * chu_control - set fudge factors, return statistics
 */
static void
chu_control(unit, in, out)
	u_int unit;
	struct refclockstat *in;
	struct refclockstat *out;
{
	register int i;
	register struct chuunit *chu;
	U_LONG npolls;
	static char asciicode[NCHUCHARS*4];

	if (unit >= MAXUNITS) {
		syslog(LOG_ERR, "chu_control: unit %d invalid", unit);
		return;
	}

	if (in != 0) {
		if (in->haveflags & CLK_HAVETIME1)
			propagation_delay[unit] = in->fudgetime1;
		if (in->haveflags & CLK_HAVETIME2)
			fudgefactor[unit] = in->fudgetime2;
		offset_fudge[unit] = propagation_delay[unit];
		L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
		if (in->haveflags & CLK_HAVEVAL1) {
			stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
			if (unitinuse[unit]) {
				struct peer *peer;

				/*
				 * Should actually reselect clock, but
				 * will wait for the next timecode
				 */
				peer = chuunits[unit]->peer;
				peer->stratum = stratumtouse[unit];
				if (stratumtouse[unit] <= 1)
					bcopy(CHUREFID, (char *)&peer->refid,4);
				else
					peer->refid = htonl(CHUHSREFID);
			}
		}
		if (in->haveflags & CLK_HAVEFLAG1) {
			sloppyclockflag[unit] = in->flags & CLK_FLAG1;
		}
	}

	if (out != 0) {
		out->type = REFCLK_CHU;
		out->flags = 0;
		out->haveflags
		    = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|
		      CLK_HAVEVAL2|CLK_HAVEFLAG1;
		out->clockdesc = CHUDESCRIPTION;
		out->fudgetime1 = propagation_delay[unit];
		out->fudgetime2 = fudgefactor[unit];
		out->fudgeval1 = (long)stratumtouse[unit];
		out->flags = sloppyclockflag[unit];
		if (unitinuse[unit]) {
			chu = chuunits[unit];
			out->lencode = NCHUCHARS*4;
			for (i = 0; i < NCHUCHARS*4; i++) {
				asciicode[i] = hexstring[chu->lastcode[i]];
			}
			out->fudgeval2 = chu->lastcode[2*NCHUCHARS+1];
			out->fudgeval2 *= (chu->lastcode[2*NCHUCHARS]&1)?-1:1;
			out->lastcode = asciicode;
			out->timereset = current_time - chu->timestarted;
			npolls = out->timereset / 6;	/* **divide** */
			out->polls = npolls;
			out->noresponse = (npolls - chu->responses);
			out->badformat = chu->badformat;
			out->baddata = chu->baddata;
			out->lastevent = chu->lastevent;
			out->currentstatus = chu->status;
		} else {
			out->fudgeval2 = 0;
			out->lencode = 0;
			out->lastcode = "";
			out->polls = out->noresponse = 0;
			out->badformat = out->baddata = 0;
			out->timereset = 0;
			out->currentstatus = out->lastevent = CEVNT_NOMINAL;
		}
	}
}
#endif
