refclock_chu.c revision 56746
199461Sobrien/*
2218822Sdim * refclock_chu - clock driver for Canadian CHU time/frequency station
3218822Sdim */
499461Sobrien#ifdef HAVE_CONFIG_H
599461Sobrien#include <config.h>
699461Sobrien#endif
799461Sobrien
899461Sobrien#if defined(REFCLOCK) && defined(CLOCK_CHU)
999461Sobrien
1099461Sobrien#include <stdio.h>
1199461Sobrien#include <ctype.h>
1299461Sobrien#include <sys/time.h>
1399461Sobrien#include <time.h>
1499461Sobrien#include <math.h>
1599461Sobrien
1699461Sobrien#include "ntpd.h"
1799461Sobrien#include "ntp_io.h"
1899461Sobrien#include "ntp_refclock.h"
1999461Sobrien#include "ntp_calendar.h"
20218822Sdim#include "ntp_stdlib.h"
21218822Sdim#ifdef AUDIO_CHU
2299461Sobrien#include "audio.h"
23218822Sdim#endif /* AUDIO_CHU */
2499461Sobrien
2599461Sobrien#define ICOM 	1		/* undefine to suppress ICOM code */
2699461Sobrien
2799461Sobrien#ifdef ICOM
2899461Sobrien#include "icom.h"
2999461Sobrien#endif /* ICOM */
3099461Sobrien
3199461Sobrien/*
3299461Sobrien * Audio CHU demodulator/decoder
33130561Sobrien *
3499461Sobrien * This driver synchronizes the computer time using data encoded in
3599461Sobrien * radio transmissions from Canadian time/frequency station CHU in
3699461Sobrien * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
3799461Sobrien * 7335 kHz and 14670 kHz in upper sideband, compatible AM mode. An
3899461Sobrien * ordinary shortwave receiver can be tuned manually to one of these
39130561Sobrien * frequencies or, in the case of ICOM receivers, the receiver can be
4099461Sobrien * tuned automatically using this program as propagation conditions
41130561Sobrien * change throughout the day and night.
4299461Sobrien *
4399461Sobrien * The driver receives, demodulates and decodes the radio signals when
44218822Sdim * connected to the audio codec of a Sun workstation running SunOS or
45107492Sobrien * Solaris, and with a little help, other workstations with similar
46130561Sobrien * codecs or sound cards. In this implementation, only one audio driver
4799461Sobrien * and codec can be supported on a single machine.
4899461Sobrien *
49130561Sobrien * The driver can be compiled to use a Bell 103 compatible modem or
50130561Sobrien * modem chip to receive the radio signal and demodulate the data.
51130561Sobrien * Alternatively, the driver can be compiled to use the audio codec of
52130561Sobrien * the Sun workstation or another with compatible audio drivers. In the
5399461Sobrien * latter case, the driver implements the modem using DSP routines, so
54130561Sobrien * the radio can be connected directly to either the microphone on line
5599461Sobrien * input port. In either case, the driver decodes the data using a
56130561Sobrien * maximum likelihood technique which exploits the considerable degree
5799461Sobrien * of redundancy available to maximize accuracy and minimize errors.
58130561Sobrien *
5999461Sobrien * The CHU time broadcast includes an audio signal compatible with the
60130561Sobrien * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). It consist
6199461Sobrien * of nine, ten-character bursts transmitted at 300 bps and beginning
6299461Sobrien * each second from second 31 to second 39 of the minute. Each character
63130561Sobrien * consists of eight data bits plus one start bit and two stop bits to
6499461Sobrien * encode two hex digits. The burst data consist of five characters (ten
6599461Sobrien * hex digits) followed by a repeat of these characters. In format A,
6699461Sobrien * the characters are repeated in the same polarity; in format B, the
6799461Sobrien * characters are repeated in the opposite polarity.
68130561Sobrien *
6999461Sobrien * Format A bursts are sent at seconds 32 through 39 of the minute in
70130561Sobrien * hex digits
71130561Sobrien *
72130561Sobrien *	6dddhhmmss6dddhhmmss
73130561Sobrien *
74130561Sobrien * The first ten digits encode a frame marker (6) followed by the day
75130561Sobrien * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
76130561Sobrien * format A bursts are sent during the third decade of seconds the tens
77130561Sobrien * digit of ss is always 3. The driver uses this to determine correct
78130561Sobrien * burst synchronization. These digits are then repeated with the same
79130561Sobrien * polarity.
80130561Sobrien *
81130561Sobrien * Format B bursts are sent at second 31 of the minute in hex digits
82130561Sobrien *
83130561Sobrien *	xdyyyyttaaxdyyyyttaa
84130561Sobrien *
85130561Sobrien * The first ten digits encode a code (x described below) followed by
8699461Sobrien * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
8799461Sobrien * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
8899461Sobrien * digits are then repeated with inverted polarity.
8999461Sobrien *
9099461Sobrien * The x is coded
9199461Sobrien *
9299461Sobrien * 1 Sign of DUT (0 = +)
9399461Sobrien * 2 Leap second warning. One second will be added.
9499461Sobrien * 4 Leap second warning. One second will be subtracted.
9599461Sobrien * 8 Even parity bit for this nibble.
9699461Sobrien *
97130561Sobrien * By design, the last stop bit of the last character in the burst
9899461Sobrien * coincides with 0.5 second. Since characters have 11 bits and are
9999461Sobrien * transmitted at 300 bps, the last stop bit of the first character
10099461Sobrien * coincides with 0.5 - 10 * 11/300 = 0.133 second. Depending on the
10199461Sobrien * UART, character interrupts can vary somewhere between the beginning
102130561Sobrien * of bit 9 and end of bit 11. These eccentricities can be corrected
10399461Sobrien * along with the radio propagation delay using fudge time 1.
10499461Sobrien *
105130561Sobrien * Debugging aids
10699461Sobrien *
107130561Sobrien * The timecode format used for debugging and data recording includes
108130561Sobrien * data helpful in diagnosing problems with the radio signal and serial
109130561Sobrien * connections. With debugging enabled (-d -d -d on the ntpd command
110130561Sobrien * line), the driver produces one line for each burst in two formats
111130561Sobrien * corresponding to format A and B. Following is format A:
112130561Sobrien *
113130561Sobrien *	n b f s m code
114130561Sobrien *
115130561Sobrien * where n is the number of characters in the burst (0-11), b the burst
116130561Sobrien * distance (0-40), f the field alignment (-1, 0, 1), s the
117130561Sobrien * synchronization distance (0-16), m the burst number (2-9) and code
118130561Sobrien * the burst characters as received. Note that the hex digits in each
119130561Sobrien * character are reversed, so the burst
120130561Sobrien *
121130561Sobrien *	10 38 0 16 9 06851292930685129293
122130561Sobrien *
123130561Sobrien * is interpreted as containing 11 characters with burst distance 38,
124130561Sobrien * field alignment 0, synchronization distance 16 and burst number 9.
125130561Sobrien * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
126130561Sobrien * second 39.
127130561Sobrien *
128130561Sobrien * When the audio driver is compiled, format A is preceded by
129130561Sobrien * the current gain (0-255) and relative signal level (0-9999). The
130130561Sobrien * receiver folume control should be set so that the gain is somewhere
131130561Sobrien * near the middle of the range 0-255, which results in a signal level
132130561Sobrien * near 1000.
133130561Sobrien *
134130561Sobrien * Following is format B:
135130561Sobrien *
136130561Sobrien *	n b s code
137130561Sobrien *
138130561Sobrien * where n is the number of characters in the burst (0-11), b the burst
139130561Sobrien * distance (0-40), s the synchronization distance (0-40) and code the
140130561Sobrien * burst characters as received. Note that the hex digits in each
141130561Sobrien * character are reversed and the last ten digits inverted, so the burst
142130561Sobrien *
143130561Sobrien *	11 40 1091891300ef6e76ecff
144130561Sobrien *
145130561Sobrien * is interpreted as containing 11 characters with burst distance 40.
146130561Sobrien * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
147130561Sobrien * - UTC 31 seconds.
148130561Sobrien *
149130561Sobrien * In addition to the above, the reference timecode is updated and
150130561Sobrien * written to the clockstats file and debug score after the last burst
151130561Sobrien * received in the minute. The format is
152130561Sobrien *
153130561Sobrien *	qq yyyy ddd hh:mm:ss nn dd tt
154130561Sobrien *
155130561Sobrien * where qq are the error flags, as described below, yyyy is the year,
156130561Sobrien * ddd the day, hh:mm:ss the time of day, nn the number of format A
157130561Sobrien * bursts received during the previous minute, dd the decoding distance
158130561Sobrien * and tt the number of timestamps. The error flags are cleared after
159130561Sobrien * every update.
160130561Sobrien *
161130561Sobrien * Fudge factors
162130561Sobrien *
163130561Sobrien * For accuracies better than the low millisceconds, fudge time1 can be
164130561Sobrien * set to the radio propagation delay from CHU to the receiver. This can
165130561Sobrien * be done conviently using the minimuf program. When the modem driver
166130561Sobrien * is compiled, fudge flag3 enables the ppsclock line discipline. Fudge
167130561Sobrien * flag4 causes the dubugging output described above to be recorded in
168130561Sobrien * the clockstats file.
169130561Sobrien *
170130561Sobrien * When the audio driver is compiled, fudge flag2 selects the audio
171130561Sobrien * input port, where 0 is the mike port (default) and 1 is the line-in
172130561Sobrien * port. It does not seem useful to select the compact disc player port.
173130561Sobrien * Fudge flag3 enables audio monitoring of the input signal. For this
174130561Sobrien * purpose, the speaker volume must be set before the driver is started.
175130561Sobrien *
176130561Sobrien * The ICOM code is normally compiled in the driver. It isn't used,
177130561Sobrien * unless the mode keyword on the server configuration command specifies
178130561Sobrien * a nonzero ICOM ID select code. The C-IV trace is turned on if the
179130561Sobrien * debug level is greater than one.
180130561Sobrien */
181130561Sobrien/*
182130561Sobrien * Interface definitions
183130561Sobrien */
184130561Sobrien#define	SPEED232	B300	/* uart speed (300 baud) */
185130561Sobrien#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
186130561Sobrien#define	REFID		"CHU"	/* reference ID */
187130561Sobrien#ifdef ICOM
188130561Sobrien#define DWELL		5	/* minutes before qsy */
189130561Sobrien#define NCHAN		3	/* number of channels */
190130561Sobrien#endif /* ICOM */
191130561Sobrien#ifdef AUDIO_CHU
192130561Sobrien#define	DESCRIPTION	"CHU Modem Receiver" /* WRU */
193130561Sobrien
194130561Sobrien/*
195130561Sobrien * Audio demodulator definitions
196130561Sobrien */
197130561Sobrien#define SECOND		8000	/* nominal sample rate (Hz) */
198130561Sobrien#define BAUD		300	/* modulation rate (bps) */
199130561Sobrien#define OFFSET		128	/* companded sample offset */
200130561Sobrien#define SIZE		256	/* decompanding table size */
201130561Sobrien#define	MAXSIG		6000.	/* maximum signal level */
202130561Sobrien#define LIMIT		1000.	/* soft limiter threshold */
203130561Sobrien#define AGAIN		6.	/* baseband gain */
204130561Sobrien#define LAG		10	/* discriminator lag */
205130561Sobrien#else
206130561Sobrien#define	DEVICE		"/dev/chu%d" /* device name and unit */
207130561Sobrien#define	SPEED232	B300	/* UART speed (300 baud) */
208130561Sobrien#define	DESCRIPTION	"CHU Audio Receiver" /* WRU */
209130561Sobrien#endif /* AUDIO_CHU */
210130561Sobrien
211130561Sobrien/*
212130561Sobrien * Decoder definitions
213130561Sobrien */
21499461Sobrien#define CHAR		(11. / 300.) /* character time (s) */
21599461Sobrien#define	FUDGE		.185	/* offset to first stop bit (s) */
21699461Sobrien#define BURST		11	/* max characters per burst */
21799461Sobrien#define MINCHAR		9	/* min characters per burst */
218130561Sobrien#define MINDIST		28	/* min burst distance (of 40)  */
21999461Sobrien#define MINSYNC		8	/* min sync distance (of 16) */
220130561Sobrien#define MINSTAMP	20	/* min timestamps (of 60) */
22199461Sobrien#define PANIC		(4 * 1440) /* panic restart */
22299461Sobrien
22399461Sobrien/*
22499461Sobrien * Hex extension codes (>= 16)
22599461Sobrien */
22699461Sobrien#define HEX_MISS	16	/* miss */
22799461Sobrien#define HEX_SOFT	17	/* soft error */
22899461Sobrien#define HEX_HARD	18	/* hard error */
22999461Sobrien
23099461Sobrien/*
23199461Sobrien * Status bits (status)
23299461Sobrien */
23399461Sobrien#define RUNT		0x0001	/* runt burst */
23499461Sobrien#define NOISE		0x0002	/* noise burst */
23599461Sobrien#define BFRAME		0x0004	/* invalid format B frame sync */
23699461Sobrien#define BFORMAT		0x0008	/* invalid format B data */
23799461Sobrien#define AFRAME		0x0010	/* invalid format A frame sync */
23899461Sobrien#define AFORMAT		0x0020	/* invalid format A data */
23999461Sobrien#define DECODE		0x0040	/* invalid data decode */
24099461Sobrien#define STAMP		0x0080	/* too few timestamps */
24199461Sobrien#define INYEAR		0x0100	/* valid B frame */
24299461Sobrien#define INSYNC		0x0200	/* clock synchronized */
24399461Sobrien
24499461Sobrien/*
24599461Sobrien * Alarm status bits (alarm)
24699461Sobrien *
24799461Sobrien * These alarms are set at the end of a minute in which at least one
24899461Sobrien * burst was received. SYNERR is raised if the AFRAME or BFRAME status
24999461Sobrien * bits are set during the minute, FMTERR is raised if the AFORMAT or
25099461Sobrien * BFORMAT status bits are set, DECERR is raised if the DECODE status
25199461Sobrien * bit is set and TSPERR is raised if the STAMP status bit is set.
25299461Sobrien */
25399461Sobrien#define SYNERR		0x01	/* frame sync error */
25499461Sobrien#define FMTERR		0x02	/* data format error */
25599461Sobrien#define DECERR		0x04	/* data decoding error */
25699461Sobrien#define TSPERR		0x08	/* insufficient data */
25799461Sobrien
258130561Sobrien#ifdef AUDIO_CHU
25999461Sobrienstruct surv {
26099461Sobrien	double	shift[12];	/* mark register */
26199461Sobrien	double	es_max, es_min;	/* max/min envelope signals */
26299461Sobrien	double	dist;		/* sample distance */
26399461Sobrien	int	uart;		/* decoded character */
26499461Sobrien};
26599461Sobrien#endif /* AUDIO_CHU */
26699461Sobrien
26799461Sobrien/*
26899461Sobrien * CHU unit control structure
26999461Sobrien */
27099461Sobrienstruct chuunit {
27199461Sobrien	u_char	decode[20][16];	/* maximum likelihood decoding matrix */
27299461Sobrien	l_fp	cstamp[BURST];	/* character timestamps */
27399461Sobrien	l_fp	tstamp[MAXSTAGE]; /* timestamp samples */
27499461Sobrien	l_fp	timestamp;	/* current buffer timestamp */
27599461Sobrien	l_fp	laststamp;	/* last buffer timestamp */
27699461Sobrien	l_fp	charstamp;	/* character time as a l_fp */
277130561Sobrien	int	errflg;		/* error flags */
278130561Sobrien	int	status;		/* status bits */
279130561Sobrien	int	bufptr;		/* buffer index pointer */
280130561Sobrien	char	ident[10];	/* transmitter frequency */
281130561Sobrien#ifdef ICOM
282130561Sobrien	int	chan;		/* frequency identifier */
283130561Sobrien	int	dwell;		/* dwell minutes at current frequency */
284130561Sobrien	int	fd_icom;	/* ICOM file descriptor */
285130561Sobrien#endif /* ICOM */
286130561Sobrien
287130561Sobrien	/*
288130561Sobrien	 * Character burst variables
289130561Sobrien	 */
290130561Sobrien	int	cbuf[BURST];	/* character buffer */
291130561Sobrien	int	ntstamp;	/* number of timestamp samples */
292130561Sobrien	int	ndx;		/* buffer start index */
293130561Sobrien	int	prevsec;	/* previous burst second */
294130561Sobrien	int	burdist;	/* burst distance */
295130561Sobrien	int	mindist;	/* minimum distance */
296130561Sobrien	int	syndist;	/* sync distance */
297130561Sobrien	int	burstcnt;	/* format A bursts this minute */
298130561Sobrien
299130561Sobrien	/*
300130561Sobrien	 * Format particulars
301130561Sobrien	 */
302130561Sobrien	int	leap;		/* leap/dut code */
303130561Sobrien	int	dut;		/* UTC1 correction */
304130561Sobrien	int	tai;		/* TAI - UTC correction */
305130561Sobrien	int	dst;		/* Canadian DST code */
306130561Sobrien
307130561Sobrien#ifdef AUDIO_CHU
308130561Sobrien	/*
309130561Sobrien	 * Audio codec variables
310130561Sobrien	 */
311130561Sobrien	double	comp[SIZE];	/* decompanding table */
312130561Sobrien	int	port;		/* codec port */
313130561Sobrien	int	gain;		/* codec gain */
314130561Sobrien	int	bufcnt;		/* samples in buffer */
315130561Sobrien	int	clipcnt;	/* sample clip count */
316130561Sobrien	int	seccnt;		/* second interval counter */
317130561Sobrien
318130561Sobrien	/*
319130561Sobrien	 * Modem variables
320130561Sobrien	 */
321130561Sobrien	l_fp	tick;		/* audio sample increment */
322130561Sobrien	double	bpf[9];		/* IIR bandpass filter */
323130561Sobrien	double	disc[LAG];	/* discriminator shift register */
324130561Sobrien	double	lpf[27];	/* FIR lowpass filter */
325130561Sobrien	double	monitor;	/* audio monitor */
326130561Sobrien	double	maxsignal;	/* signal level */
32799461Sobrien	int	discptr;	/* discriminator pointer */
32899461Sobrien
32999461Sobrien	/*
33099461Sobrien	 * Maximum likelihood UART variables
33199461Sobrien	 */
33299461Sobrien	double	baud;		/* baud interval */
33399461Sobrien	struct surv surv[8];	/* UART survivor structures */
33499461Sobrien	int	decptr;		/* decode pointer */
33599461Sobrien	int	dbrk;		/* holdoff counter */
33699461Sobrien#endif /* AUDIO_CHU */
337218822Sdim};
338218822Sdim
339218822Sdim/*
340218822Sdim * Function prototypes
341218822Sdim */
342218822Sdimstatic	int	chu_start	P((int, struct peer *));
343218822Sdimstatic	void	chu_shutdown	P((int, struct peer *));
344218822Sdimstatic	void	chu_receive	P((struct recvbuf *));
345218822Sdimstatic	void	chu_poll	P((int, struct peer *));
346218822Sdim
347218822Sdim/*
348218822Sdim * More function prototypes
349218822Sdim */
350218822Sdimstatic	void	chu_decode	P((struct peer *, int));
351218822Sdimstatic	void	chu_burst	P((struct peer *));
352218822Sdimstatic	void	chu_clear	P((struct peer *));
353218822Sdimstatic	void	chu_a		P((struct peer *, int));
354218822Sdimstatic	void	chu_b		P((struct peer *, int));
355218822Sdimstatic	int	chu_dist	P((int, int));
35699461Sobrienstatic	int	chu_major	P((struct peer *));
35799461Sobrien#ifdef AUDIO_CHU
35899461Sobrienstatic	void	chu_uart	P((struct surv *, double));
35999461Sobrienstatic	void	chu_rf		P((struct peer *, double));
36099461Sobrienstatic	void	chu_gain	P((struct peer *));
36199461Sobrien#endif /* AUDIO_CHU */
36299461Sobrien
36399461Sobrien/*
36499461Sobrien * Global variables
365218822Sdim */
366218822Sdimstatic char hexchar[] = "0123456789abcdef_-=";
36799461Sobrien#ifdef ICOM
36899461Sobrienstatic double qsy[NCHAN] = {3.33, 7.335, 14.67}; /* frequencies (MHz) */
36999461Sobrien#endif /* ICOM */
37099461Sobrien
37199461Sobrien/*
37299461Sobrien * Transfer vector
37399461Sobrien */
37499461Sobrienstruct	refclock refclock_chu = {
37599461Sobrien	chu_start,		/* start up driver */
37699461Sobrien	chu_shutdown,		/* shut down driver */
377218822Sdim	chu_poll,		/* transmit poll message */
378218822Sdim	noentry,		/* not used (old chu_control) */
379218822Sdim	noentry,		/* initialize driver (not used) */
380218822Sdim	noentry,		/* not used (old chu_buginfo) */
381218822Sdim	NOFLAGS			/* not used */
382218822Sdim};
383218822Sdim
38499461Sobrien
38599461Sobrien/*
38699461Sobrien * chu_start - open the devices and initialize data for processing
387130561Sobrien */
388130561Sobrienstatic int
389130561Sobrienchu_start(
390130561Sobrien	int	unit,		/* instance number (not used) */
391130561Sobrien	struct peer *peer	/* peer structure pointer */
392130561Sobrien	)
393130561Sobrien{
394130561Sobrien	struct chuunit *up;
395130561Sobrien	struct refclockproc *pp;
396130561Sobrien	int	fd;		/* file descriptor */
397130561Sobrien#ifdef ICOM
398130561Sobrien	char	tbuf[80];	/* trace buffer */
399130561Sobrien	int	temp;
400130561Sobrien#endif /* ICOM */
401130561Sobrien#ifdef AUDIO_CHU
402130561Sobrien	int	i;		/* index */
403130561Sobrien	double	step;		/* codec adjustment */
404130561Sobrien
405130561Sobrien	/*
406130561Sobrien	 * Open audio device
407130561Sobrien	 */
408130561Sobrien	fd = audio_init();
409130561Sobrien	if (fd < 0)
410130561Sobrien		return (0);
411130561Sobrien#ifdef DEBUG
412130561Sobrien	if (debug)
413130561Sobrien		audio_show();
414130561Sobrien#endif
415130561Sobrien#else
416130561Sobrien	char device[20];	/* device name */
417130561Sobrien
418130561Sobrien	/*
419130561Sobrien	 * Open serial port in raw mode.
420130561Sobrien	 */
421130561Sobrien	(void)sprintf(device, DEVICE, unit);
422130561Sobrien	if (!(fd = refclock_open(device, SPEED232, LDISC_RAW))) {
423130561Sobrien		return (0);
424130561Sobrien	}
425130561Sobrien#endif /* AUDIO_CHU */
426130561Sobrien
427130561Sobrien	/*
428130561Sobrien	 * Allocate and initialize unit structure
429130561Sobrien	 */
430130561Sobrien	if (!(up = (struct chuunit *)
431130561Sobrien	      emalloc(sizeof(struct chuunit)))) {
432218822Sdim		(void) close(fd);
433130561Sobrien		return (0);
434130561Sobrien	}
435130561Sobrien	memset((char *)up, 0, sizeof(struct chuunit));
436130561Sobrien	pp = peer->procptr;
437130561Sobrien	pp->unitptr = (caddr_t)up;
438130561Sobrien	pp->io.clock_recv = chu_receive;
439130561Sobrien	pp->io.srcclock = (caddr_t)peer;
440130561Sobrien	pp->io.datalen = 0;
441130561Sobrien	pp->io.fd = fd;
442130561Sobrien	if (!io_addclock(&pp->io)) {
443130561Sobrien		(void)close(fd);
444130561Sobrien		free(up);
445130561Sobrien		return (0);
446130561Sobrien	}
447130561Sobrien
448130561Sobrien	/*
449130561Sobrien	 * Initialize miscellaneous variables
450130561Sobrien	 */
451130561Sobrien	peer->precision = PRECISION;
452130561Sobrien	pp->clockdesc = DESCRIPTION;
453130561Sobrien	memcpy((char *)&pp->refid, REFID, 4);
454130561Sobrien	DTOLFP(CHAR, &up->charstamp);
455130561Sobrien#ifdef AUDIO_CHU
456130561Sobrien	up->gain = 127;
457130561Sobrien
45899461Sobrien	/*
45999461Sobrien	 * The companded samples are encoded sign-magnitude. The table
46099461Sobrien	 * contains all the 256 values in the interest of speed.
46199461Sobrien	 */
46299461Sobrien	up->comp[0] = up->comp[OFFSET] = 0.;
463130561Sobrien	up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
46499461Sobrien	up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
46599461Sobrien	step = 2.;
46699461Sobrien	for (i = 3; i < OFFSET; i++) {
46799461Sobrien		up->comp[i] = up->comp[i - 1] + step;
46899461Sobrien		up->comp[OFFSET + i] = -up->comp[i];
46999461Sobrien                if (i % 16 == 0)
47099461Sobrien                	step *= 2.;
47199461Sobrien	}
47299461Sobrien	DTOLFP(1. / SECOND, &up->tick);
47399461Sobrien#endif /* AUDIO_CHU */
47499461Sobrien	strcpy(up->ident, "X");
475130561Sobrien#ifdef ICOM
476130561Sobrien	temp = 0;
477130561Sobrien#ifdef DEBUG
478130561Sobrien	if (debug > 1)
479130561Sobrien		temp = P_TRACE;
480130561Sobrien#endif
48199461Sobrien	if (peer->ttl > 0) {
48299461Sobrien		if (peer->ttl & 0x80)
48399461Sobrien			up->fd_icom = icom_init("/dev/icom", B1200,
48499461Sobrien			    temp);
48599461Sobrien		else
48699461Sobrien			up->fd_icom = icom_init("/dev/icom", B9600,
48799461Sobrien			    temp);
48899461Sobrien	}
48999461Sobrien	if (up->fd_icom > 0) {
49099461Sobrien		if (icom_freq(up->fd_icom, peer->ttl & 0x7f,
49199461Sobrien		    qsy[up->chan]) < 0) {
49299461Sobrien			NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
49399461Sobrien			    msyslog(LOG_ERR,
49499461Sobrien			    "ICOM bus error; autotune disabled");
49599461Sobrien			up->errflg = CEVNT_FAULT;
49699461Sobrien			close(up->fd_icom);
49799461Sobrien			up->fd_icom = 0;
49899461Sobrien		} else {
49999461Sobrien			sprintf(up->ident, "%.1f", qsy[up->chan]);
50099461Sobrien			sprintf(tbuf, "chu: QSY to %s MHz", up->ident);
50199461Sobrien			record_clock_stats(&peer->srcadr, tbuf);
50299461Sobrien#ifdef DEBUG
50399461Sobrien			if (debug)
50499461Sobrien				printf("%s\n", tbuf);
505130561Sobrien#endif
50699461Sobrien		}
50799461Sobrien	}
50899461Sobrien#endif /* ICOM */
50999461Sobrien	return (1);
51099461Sobrien}
51199461Sobrien
51299461Sobrien
51399461Sobrien/*
51499461Sobrien * chu_shutdown - shut down the clock
51599461Sobrien */
51699461Sobrienstatic void
51799461Sobrienchu_shutdown(
51899461Sobrien	int	unit,		/* instance number (not used) */
51999461Sobrien	struct peer *peer	/* peer structure pointer */
52099461Sobrien	)
52199461Sobrien{
52299461Sobrien	struct chuunit *up;
52399461Sobrien	struct refclockproc *pp;
52499461Sobrien
52599461Sobrien	pp = peer->procptr;
52699461Sobrien	up = (struct chuunit *)pp->unitptr;
52799461Sobrien	io_closeclock(&pp->io);
52899461Sobrien	if (up->fd_icom > 0)
52999461Sobrien		close(up->fd_icom);
53099461Sobrien	free(up);
53199461Sobrien}
53299461Sobrien
53399461Sobrien#ifdef AUDIO_CHU
53499461Sobrien
53599461Sobrien/*
53699461Sobrien * chu_receive - receive data from the audio device
53799461Sobrien */
53899461Sobrienstatic void
53999461Sobrienchu_receive(
54099461Sobrien	struct recvbuf *rbufp	/* receive buffer structure pointer */
54199461Sobrien	)
54299461Sobrien{
54399461Sobrien	struct chuunit *up;
54499461Sobrien	struct refclockproc *pp;
54599461Sobrien	struct peer *peer;
54699461Sobrien
54799461Sobrien	double	sample;		/* codec sample */
54899461Sobrien	u_char	*dpt;		/* buffer pointer */
54999461Sobrien	l_fp	ltemp;		/* l_fp temp */
55099461Sobrien	int	isneg;		/* parity flag */
55199461Sobrien	double	dtemp;
55299461Sobrien	int	i, j;
55399461Sobrien
55499461Sobrien	peer = (struct peer *)rbufp->recv_srcclock;
55599461Sobrien	pp = peer->procptr;
55699461Sobrien	up = (struct chuunit *)pp->unitptr;
55799461Sobrien
55899461Sobrien	/*
55999461Sobrien	 * Main loop - read until there ain't no more. Note codec
56099461Sobrien	 * samples are bit-inverted.
56199461Sobrien	 */
56299461Sobrien	up->timestamp = rbufp->recv_time;
56399461Sobrien	up->bufcnt = rbufp->recv_length;
56499461Sobrien	DTOLFP(up->bufcnt * 1. / SECOND, &ltemp);
56599461Sobrien	L_SUB(&up->timestamp, &ltemp);
56699461Sobrien	dpt = (u_char *)&rbufp->recv_space;
56799461Sobrien	for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) {
56899461Sobrien		sample = up->comp[~*dpt & 0xff];
56999461Sobrien
57099461Sobrien		/*
57199461Sobrien		 * Clip noise spikes greater than MAXSIG. If no clips,
57299461Sobrien		 * increase the gain a tad; if the clips are too high,
57399461Sobrien		 * decrease a tad.
57499461Sobrien		 */
57599461Sobrien		if (sample > MAXSIG) {
57699461Sobrien			sample = MAXSIG;
57799461Sobrien			up->clipcnt++;
57899461Sobrien		} else if (sample < -MAXSIG) {
57999461Sobrien			sample = -MAXSIG;
58099461Sobrien			up->clipcnt++;
58199461Sobrien		}
58299461Sobrien		up->seccnt = (up->seccnt + 1) % SECOND;
58399461Sobrien		if (up->seccnt == 0) {
58499461Sobrien			if (pp->sloppyclockflag & CLK_FLAG2)
58599461Sobrien				up->port = 2;
58699461Sobrien			else
58799461Sobrien				up->port = 1;
58899461Sobrien			chu_gain(peer);
58999461Sobrien		}
59099461Sobrien		chu_rf(peer, sample);
59199461Sobrien
59299461Sobrien		/*
59399461Sobrien		 * During development, it is handy to have an audio
59499461Sobrien		 * monitor that can be switched to various signals. This
59599461Sobrien		 * code converts the linear signal left in up->monitor
59699461Sobrien		 * to codec format. If we can get the grass out of this
59799461Sobrien		 * thing and improve modem performance, this expensive
59899461Sobrien		 * code will be permanently nixed.
59999461Sobrien		 */
60099461Sobrien		isneg = 0;
60199461Sobrien		dtemp = up->monitor;
60299461Sobrien		if (sample < 0) {
60399461Sobrien			isneg = 1;
60499461Sobrien			dtemp-= dtemp;
60599461Sobrien		}
60699461Sobrien		i = 0;
60799461Sobrien		j = OFFSET >> 1;
60899461Sobrien		while (j != 0) {
60999461Sobrien			if (dtemp > up->comp[i])
61099461Sobrien				i += j;
61199461Sobrien			else if (dtemp < up->comp[i])
61299461Sobrien				i -= j;
61399461Sobrien			else
61499461Sobrien				break;
61599461Sobrien			j >>= 1;
61699461Sobrien		}
61799461Sobrien		if (isneg)
61899461Sobrien			*dpt = ~(i + OFFSET);
61999461Sobrien		else
62099461Sobrien			*dpt = ~i;
62199461Sobrien		dpt++;
62299461Sobrien		L_ADD(&up->timestamp, &up->tick);
62399461Sobrien	}
62499461Sobrien
62599461Sobrien	/*
62699461Sobrien	 * Squawk to the monitor speaker if enabled.
62799461Sobrien	 */
62899461Sobrien	if (pp->sloppyclockflag & CLK_FLAG3)
62999461Sobrien		if (write(pp->io.fd, (u_char *)&rbufp->recv_space,
63099461Sobrien		    (u_int)up->bufcnt) < 0)
63199461Sobrien			perror("chu:");
63299461Sobrien}
63399461Sobrien
63499461Sobrien
63599461Sobrien/*
63699461Sobrien * chu_rf - filter and demodulate the FSK signal
63799461Sobrien *
63899461Sobrien * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
63999461Sobrien * and space 2025 Hz. It uses a bandpass filter followed by a soft
64099461Sobrien * limiter, FM discriminator and lowpass filter. A maximum likelihood
64199461Sobrien * decoder samples the baseband signal at eight times the baud rate and
64299461Sobrien * detects the start bit of each character.
64399461Sobrien *
64499461Sobrien * The filters are built for speed, which explains the rather clumsy
64599461Sobrien * code. Hopefully, the compiler will efficiently implement the move-
64699461Sobrien * and-muiltiply-and-add operations.
64799461Sobrien */
64899461Sobrienstatic void
64999461Sobrienchu_rf(
65099461Sobrien	struct peer *peer,	/* peer structure pointer */
65199461Sobrien	double	sample		/* analog sample */
65299461Sobrien	)
65399461Sobrien{
65499461Sobrien	struct refclockproc *pp;
65599461Sobrien	struct chuunit *up;
65699461Sobrien	struct surv *sp;
65799461Sobrien
65899461Sobrien	/*
65999461Sobrien	 * Local variables
66099461Sobrien	 */
66199461Sobrien	double	signal;		/* bandpass signal */
662130561Sobrien	double	limit;		/* limiter signal */
663130561Sobrien	double	disc;		/* discriminator signal */
664130561Sobrien	double	lpf;		/* lowpass signal */
665130561Sobrien	double	span;		/* UART signal span */
666130561Sobrien	double	dist;		/* UART signal distance */
667130561Sobrien	int	i, j;
668130561Sobrien
669130561Sobrien	pp = peer->procptr;
670130561Sobrien	up = (struct chuunit *)pp->unitptr;
671130561Sobrien
67299461Sobrien	/*
67399461Sobrien	 * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
674130561Sobrien	 * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB.
675130561Sobrien	 */
676130561Sobrien	signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
677130561Sobrien	signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
678130561Sobrien	signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
679130561Sobrien	signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
680130561Sobrien	signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
681130561Sobrien	signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
682130561Sobrien	signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
683130561Sobrien	signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
684130561Sobrien	up->bpf[0] = sample - signal;
685130561Sobrien	signal = up->bpf[0] * 6.176213e-03
686130561Sobrien	    + up->bpf[1] * 3.156599e-03
687130561Sobrien	    + up->bpf[2] * 7.567487e-03
688130561Sobrien	    + up->bpf[3] * 4.344580e-03
689130561Sobrien	    + up->bpf[4] * 1.190128e-02
690130561Sobrien	    + up->bpf[5] * 4.344580e-03
691130561Sobrien	    + up->bpf[6] * 7.567487e-03
692218822Sdim	    + up->bpf[7] * 3.156599e-03
693130561Sobrien	    + up->bpf[8] * 6.176213e-03;
694130561Sobrien
695218822Sdim	up->monitor = signal / 4.;	/* note monitor after filter */
696218822Sdim
697218822Sdim	/*
698218822Sdim	 * Soft limiter/discriminator. The 11-sample discriminator lag
699218822Sdim	 * interval corresponds to three cycles of 2125 Hz, which
700218822Sdim	 * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
701218822Sdim	 * Hz. The discriminator output varies +-0.5 interval for input
702130561Sobrien	 * frequency 2025-2225 Hz. However, we don't get to sample at
703130561Sobrien	 * this frequency, so the discriminator output is biased. Life
704130561Sobrien	 * at 8000 Hz sucks.
705130561Sobrien	 */
706130561Sobrien	limit = signal;
707130561Sobrien	if (limit > LIMIT)
708130561Sobrien		limit = LIMIT;
709130561Sobrien	else if (limit < -LIMIT)
710130561Sobrien		limit = -LIMIT;
711130561Sobrien	disc = up->disc[up->discptr] * -limit;
71299461Sobrien	up->disc[up->discptr] = limit;
71399461Sobrien	up->discptr = (up->discptr + 1 ) % LAG;
71499461Sobrien	if (disc >= 0)
71599461Sobrien		disc = sqrt(disc);
71699461Sobrien	else
71799461Sobrien		disc = -sqrt(-disc);
71899461Sobrien
71999461Sobrien	/*
72099461Sobrien	 * Lowpass filter. Raised cosine, Ts = 1 / 300, beta = 0.1.
72199461Sobrien	 */
72299461Sobrien	lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
72399461Sobrien	lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
72499461Sobrien	lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
72599461Sobrien	lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
72699461Sobrien	lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
727130561Sobrien	lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
728130561Sobrien	lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
729130561Sobrien	lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
730130561Sobrien	lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
731130561Sobrien	lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
73299461Sobrien	lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
73399461Sobrien	lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
73499461Sobrien	lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
73599461Sobrien	lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
73699461Sobrien	lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
73799461Sobrien	lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
73899461Sobrien	lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
73999461Sobrien	lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
74099461Sobrien	lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
74199461Sobrien	lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
74299461Sobrien	lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
74399461Sobrien	lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
74499461Sobrien	lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
74599461Sobrien	lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
74699461Sobrien	lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
74799461Sobrien	lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
74899461Sobrien	lpf += up->lpf[0] = disc * 2.538771e-02;
74999461Sobrien
75099461Sobrien	/*
75199461Sobrien	 * Maximum likelihood decoder. The UART updates each of the
75299461Sobrien	 * eight survivors and determines the span, slice level and
75399461Sobrien	 * tentative decoded character. Valid 11-bit characters are
75499461Sobrien	 * framed so that bit 1 and bit 11 (stop bits) are mark and bit
75599461Sobrien	 * 2 (start bit) is space. When a valid character is found, the
75699461Sobrien	 * survivor with maximum distance determines the final decoded
75799461Sobrien	 * character.
75899461Sobrien	 */
75999461Sobrien	up->baud += 1. / SECOND;
76099461Sobrien	if (up->baud > 1. / (BAUD * 8.)) {
76199461Sobrien		up->baud -= 1. / (BAUD * 8.);
76299461Sobrien		sp = &up->surv[up->decptr];
76399461Sobrien		span = sp->es_max - sp->es_min;
76499461Sobrien		up->maxsignal += (span - up->maxsignal) / 80.;
76599461Sobrien		if (up->dbrk > 0) {
76699461Sobrien			up->dbrk--;
767130561Sobrien		} else if ((sp->uart & 0x403) == 0x401 && span > 1000.)
768130561Sobrien		    {
76999461Sobrien			dist = 0;
77099461Sobrien			j = 0;
77199461Sobrien			for (i = 0; i < 8; i++) {
77299461Sobrien				if (up->surv[i].dist > dist) {
77399461Sobrien					dist = up->surv[i].dist;
77499461Sobrien					j = i;
77599461Sobrien				}
77699461Sobrien			}
77799461Sobrien			chu_decode(peer, (up->surv[j].uart >> 2) &
77899461Sobrien			    0xff);
77999461Sobrien			up->dbrk = 80;
78099461Sobrien		}
78199461Sobrien		up->decptr = (up->decptr + 1) % 8;
78299461Sobrien		chu_uart(sp, -lpf * AGAIN);
783107492Sobrien	}
78499461Sobrien}
78599461Sobrien
78699461Sobrien
787218822Sdim/*
788218822Sdim * chu_uart - maximum likelihood UART
78999461Sobrien *
790107492Sobrien * This routine updates a shift register holding the last 11 envelope
79199461Sobrien * samples. It then computes the slice level and span over these samples
79299461Sobrien * and determines the tentative data bits and distance. The calling
79399461Sobrien * program selects over the last eight survivors the one with maximum
79499461Sobrien * distance to determine the decoded character.
79599461Sobrien */
79699461Sobrienstatic void
79799461Sobrienchu_uart(
79899461Sobrien	struct surv *sp,	/* survivor structure pointer */
79999461Sobrien	double	sample		/* baseband signal */
80099461Sobrien	)
801130561Sobrien{
80299461Sobrien	double	es_max, es_min;	/* max/min envelope */
80399461Sobrien	double	slice;		/* slice level */
80499461Sobrien	double	dist;		/* distance */
80599461Sobrien	double	dtemp;
80699461Sobrien	int	i;
80799461Sobrien
80899461Sobrien	/*
80999461Sobrien	 * Save the sample and shift right. At the same time, measure
810130561Sobrien	 * the maximum and minimum over all eleven samples.
81199461Sobrien	 */
81299461Sobrien	es_max = -1e6;
81399461Sobrien	es_min = 1e6;
81499461Sobrien	sp->shift[0] = sample;
81599461Sobrien	for (i = 11; i > 0; i--) {
81699461Sobrien		sp->shift[i] = sp->shift[i - 1];
81799461Sobrien		if (sp->shift[i] > es_max)
818130561Sobrien			es_max = sp->shift[i];
81999461Sobrien		if (sp->shift[i] < es_min)
82099461Sobrien			es_min = sp->shift[i];
82199461Sobrien	}
82299461Sobrien
82399461Sobrien	/*
82499461Sobrien	 * Determine the slice level midway beteen the maximum and
82599461Sobrien	 * minimum and the span as the maximum less the minimum. Compute
826218822Sdim	 * the distance on the assumption the first and last bits must
827218822Sdim	 * be mark, the second space and the rest either mark or space.
828218822Sdim	 */
829218822Sdim	slice = (es_max + es_min) / 2.;
830218822Sdim	dist = 0;
831218822Sdim	sp->uart = 0;
83299461Sobrien	for (i = 1; i < 12; i++) {
83399461Sobrien		sp->uart <<= 1;
834130561Sobrien		dtemp = sp->shift[i];
835130561Sobrien		if (dtemp > slice)
83699461Sobrien			sp->uart |= 0x1;
83799461Sobrien		if (i == 1 || i == 11) {
83899461Sobrien			dist += dtemp - es_min;
83999461Sobrien		} else if (i == 10) {
84099461Sobrien			dist += es_max - dtemp;
84199461Sobrien		} else {
842130561Sobrien			if (dtemp > slice)
84399461Sobrien				dist += dtemp - es_min;
84499461Sobrien			else
84599461Sobrien				dist += es_max - dtemp;
84699461Sobrien		}
84799461Sobrien	}
84899461Sobrien	sp->es_max = es_max;
84999461Sobrien	sp->es_min = es_min;
85099461Sobrien	sp->dist = dist / (11 * (es_max - es_min));
851130561Sobrien}
85299461Sobrien
85399461Sobrien
854130561Sobrien#else /* AUDIO_CHU */
85599461Sobrien/*
85699461Sobrien * chu_receive - receive data from the serial interface
85799461Sobrien */
85899461Sobrienstatic void
85999461Sobrienchu_receive(
86099461Sobrien	struct recvbuf *rbufp	/* receive buffer structure pointer */
86199461Sobrien	)
86299461Sobrien{
86399461Sobrien	struct chuunit *up;
86499461Sobrien	struct refclockproc *pp;
86599461Sobrien	struct peer *peer;
866130561Sobrien
86799461Sobrien	u_char	*dpt;		/* receive buffer pointer */
86899461Sobrien
86999461Sobrien	peer = (struct peer *)rbufp->recv_srcclock;
87099461Sobrien	pp = peer->procptr;
87199461Sobrien	up = (struct chuunit *)pp->unitptr;
872218822Sdim
873218822Sdim	/*
87499461Sobrien	 * Initialize pointers and read the timecode and timestamp.
87599461Sobrien	 */
87699461Sobrien	up->timestamp = rbufp->recv_time;
87799461Sobrien	dpt = (u_char *)&rbufp->recv_space;
87899461Sobrien	chu_decode(peer, *dpt);
87999461Sobrien}
88099461Sobrien#endif /* AUDIO_CHU */
88199461Sobrien
88299461Sobrien
88399461Sobrien/*
88499461Sobrien * chu_decode - decode the data
88599461Sobrien */
88699461Sobrienstatic void
88799461Sobrienchu_decode(
888218822Sdim	struct peer *peer,	/* peer structure pointer */
88999461Sobrien	int	hexhex		/* data character */
89099461Sobrien	)
89199461Sobrien{
89299461Sobrien	struct refclockproc *pp;
89399461Sobrien	struct chuunit *up;
89499461Sobrien
89599461Sobrien	l_fp	tstmp;		/* timestamp temp */
89699461Sobrien	double	dtemp;
89799461Sobrien
89899461Sobrien	pp = peer->procptr;
89999461Sobrien	up = (struct chuunit *)pp->unitptr;
90099461Sobrien
90199461Sobrien	/*
90299461Sobrien	 * If the interval since the last character is greater than the
90399461Sobrien	 * longest burst, process the last burst and start a new one. If
90499461Sobrien	 * the interval is less than this but greater than two
90599461Sobrien	 * characters, consider this a noise burst and reject it.
90699461Sobrien	 */
90799461Sobrien	tstmp = up->timestamp;
90899461Sobrien	if (L_ISZERO(&up->laststamp))
90999461Sobrien		up->laststamp = up->timestamp;
91099461Sobrien	L_SUB(&tstmp, &up->laststamp);
91199461Sobrien	up->laststamp = up->timestamp;
912130561Sobrien	LFPTOD(&tstmp, dtemp);
913130561Sobrien	if (dtemp > BURST * CHAR) {
914130561Sobrien		chu_burst(peer);
915130561Sobrien		up->ndx = 0;
916130561Sobrien	} else if (dtemp > 2.5 * CHAR) {
917130561Sobrien		up->ndx = 0;
918130561Sobrien	}
919130561Sobrien
920130561Sobrien	/*
921218822Sdim	 * Append the character to the current burst and append the
922218822Sdim	 * timestamp to the timestamp list.
923218822Sdim	 */
924218822Sdim	if (up->ndx < BURST) {
925218822Sdim		up->cbuf[up->ndx] = hexhex & 0xff;
926218822Sdim		up->cstamp[up->ndx] = up->timestamp;
927218822Sdim		up->ndx++;
928218822Sdim
929218822Sdim	}
930218822Sdim}
931130561Sobrien
932218822Sdim
93399461Sobrien/*
93499461Sobrien * chu_burst - search for valid burst format
935130561Sobrien */
936130561Sobrienstatic void
937130561Sobrienchu_burst(
938130561Sobrien	struct peer *peer
939130561Sobrien	)
940130561Sobrien{
941130561Sobrien	struct chuunit *up;
942130561Sobrien	struct refclockproc *pp;
943130561Sobrien
944130561Sobrien	int	i;
945130561Sobrien
946130561Sobrien	pp = peer->procptr;
947130561Sobrien	up = (struct chuunit *)pp->unitptr;
948130561Sobrien
949130561Sobrien	/*
950130561Sobrien	 * Correlate a block of five characters with the next block of
951130561Sobrien	 * five characters. The burst distance is defined as the number
952130561Sobrien	 * of bits that match in the two blocks for format A and that
953130561Sobrien	 * match the inverse for format B.
954130561Sobrien	 */
955130561Sobrien	if (up->ndx < MINCHAR) {
956130561Sobrien		up->status |= RUNT;
957130561Sobrien		return;
958130561Sobrien	}
959130561Sobrien	up->burdist = 0;
960130561Sobrien	for (i = 0; i < 5 && i < up->ndx - 5; i++)
961130561Sobrien		up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
96299461Sobrien
96399461Sobrien	/*
96499461Sobrien	 * If the burst distance is at least MINDIST, this must be a
96599461Sobrien	 * format A burst; if the value is not greater than -MINDIST, it
966130561Sobrien	 * must be a format B burst. If the B burst is perfect, we
96799461Sobrien	 * believe it; otherwise, it is a noise burst and of no use to
96899461Sobrien	 * anybody.
96999461Sobrien	 */
97099461Sobrien	if (up->burdist >= MINDIST) {
97199461Sobrien		chu_a(peer, up->ndx);
97299461Sobrien	} else if (up->burdist <= -MINDIST) {
97399461Sobrien		chu_b(peer, up->ndx);
97499461Sobrien	} else {
97599461Sobrien		up->status |= NOISE;
97699461Sobrien		return;
97799461Sobrien	}
97899461Sobrien
979130561Sobrien	/*
980130561Sobrien	 * If this is a valid burst, wait a guard time of ten seconds to
98199461Sobrien	 * allow for more bursts, then arm the poll update routine to
982130561Sobrien	 * process the minute. Don't do this if this is called from the
983130561Sobrien	 * timer interrupt routine.
98499461Sobrien	 */
98599461Sobrien	if (peer->outdate != current_time)
98699461Sobrien		peer->nextdate = current_time + 10;
98799461Sobrien}
988130561Sobrien
98999461Sobrien
99099461Sobrien/*
99199461Sobrien * chu_b - decode format B burst
99299461Sobrien */
99399461Sobrienstatic void
99499461Sobrienchu_b(
995130561Sobrien	struct peer *peer,
99699461Sobrien	int	nchar
99799461Sobrien	)
99899461Sobrien{
99999461Sobrien	struct	refclockproc *pp;
100099461Sobrien	struct	chuunit *up;
100199461Sobrien
100299461Sobrien	u_char	code[11];	/* decoded timecode */
1003218822Sdim	char	tbuf[80];	/* trace buffer */
1004218822Sdim	l_fp	offset;		/* timestamp offset */
1005130561Sobrien	int	i;
100699461Sobrien
100799461Sobrien	pp = peer->procptr;
100899461Sobrien	up = (struct chuunit *)pp->unitptr;
100999461Sobrien
101099461Sobrien	/*
1011218822Sdim	 * In a format B burst, a character is considered valid only if
1012218822Sdim	 * the first occurrence matches the last occurrence. The burst
1013218822Sdim	 * is considered valid only if all characters are valid; that
1014218822Sdim	 * is, only if the distance is 40.
1015218822Sdim	 */
1016218822Sdim	sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar,
101799461Sobrien	    -up->burdist);
1018130561Sobrien	for (i = 0; i < nchar; i++)
1019130561Sobrien		sprintf(&tbuf[strlen(tbuf)], "%02x",
1020130561Sobrien		    up->cbuf[i]);
1021130561Sobrien	if (pp->sloppyclockflag & CLK_FLAG4)
1022130561Sobrien		record_clock_stats(&peer->srcadr, tbuf);
1023130561Sobrien#ifdef DEBUG
102499461Sobrien	if (debug)
102599461Sobrien		printf("%s\n", tbuf);
1026130561Sobrien#endif
1027130561Sobrien	if (up->burdist > -40) {
102899461Sobrien		up->status |= BFRAME;
102999461Sobrien		return;
1030130561Sobrien	}
1031130561Sobrien	up->status |= INYEAR;
1032130561Sobrien
1033130561Sobrien	/*
1034130561Sobrien	 * Convert the burst data to internal format. If this succeeds,
1035130561Sobrien	 * save the timestamps for later.
1036130561Sobrien	 */
1037130561Sobrien	for (i = 0; i < 5; i++) {
1038130561Sobrien		code[2 * i] = hexchar[up->cbuf[i] & 0xf];
1039130561Sobrien		code[2 * i + 1] = hexchar[(up->cbuf[i] >>
1040130561Sobrien		    4) & 0xf];
1041130561Sobrien	}
1042130561Sobrien	if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
1043130561Sobrien	    &pp->year, &up->tai, &up->dst) != 5) {
104499461Sobrien		up->status |= BFORMAT;
1045130561Sobrien		return;
104699461Sobrien	}
1047130561Sobrien	if (up->leap & 0x8)
1048130561Sobrien		up->dut = -up->dut;
1049130561Sobrien	offset.l_ui = 31;
1050130561Sobrien	offset.l_f = 0;
105199461Sobrien	for (i = 0; i < nchar && i < 10; i++) {
1052130561Sobrien		up->tstamp[up->ntstamp] = up->cstamp[i];
1053130561Sobrien		L_SUB(&up->tstamp[up->ntstamp], &offset);
1054130561Sobrien		L_ADD(&offset, &up->charstamp);
1055130561Sobrien		if (up->ntstamp < MAXSTAGE)
105699461Sobrien			up->ntstamp++;
1057130561Sobrien	}
1058130561Sobrien}
1059130561Sobrien
106099461Sobrien
106199461Sobrien/*
106299461Sobrien * chu_a - decode format A burst
106399461Sobrien */
106499461Sobrienstatic void
106599461Sobrienchu_a(
106699461Sobrien	struct peer *peer,
1067130561Sobrien	int nchar
106899461Sobrien	)
1069130561Sobrien{
1070130561Sobrien	struct refclockproc *pp;
1071130561Sobrien	struct chuunit *up;
1072130561Sobrien
1073130561Sobrien	char	tbuf[80];	/* trace buffer */
1074130561Sobrien	l_fp	offset;		/* timestamp offset */
1075130561Sobrien	int	val;		/* distance */
1076130561Sobrien	int	temp;
1077130561Sobrien	int	i, j, k;
107899461Sobrien
107999461Sobrien	pp = peer->procptr;
1080130561Sobrien	up = (struct chuunit *)pp->unitptr;
1081130561Sobrien
108299461Sobrien	/*
1083130561Sobrien	 * Determine correct burst phase. There are three cases
1084130561Sobrien	 * corresponding to in-phase, one character early or one
108599461Sobrien	 * character late. These cases are distinguished by the position
1086130561Sobrien	 * of the framing digits x6 at positions 0 and 5 and x3 at
1087130561Sobrien	 * positions 4 and 9. The correct phase is when the distance
1088130561Sobrien	 * relative to the framing digits is maximum. The burst is valid
1089130561Sobrien	 * only if the maximum distance is at least MINSYNC.
1090130561Sobrien	 */
109199461Sobrien	up->syndist = k = 0;
109299461Sobrien	val = -16;
1093130561Sobrien	for (i = -1; i < 2; i++) {
1094130561Sobrien		temp = up->cbuf[i + 4] & 0xf;
1095130561Sobrien		if (i >= 0)
1096218822Sdim			temp |= (up->cbuf[i] & 0xf) << 4;
1097130561Sobrien		val = chu_dist(temp, 0x63);
1098130561Sobrien		temp = (up->cbuf[i + 5] & 0xf) << 4;
1099130561Sobrien		if (i + 9 < nchar)
110099461Sobrien			temp |= up->cbuf[i + 9] & 0xf;
1101130561Sobrien		val += chu_dist(temp, 0x63);
1102130561Sobrien		if (val > up->syndist) {
1103130561Sobrien			up->syndist = val;
1104130561Sobrien			k = i;
1105130561Sobrien		}
1106130561Sobrien	}
1107130561Sobrien	temp = (up->cbuf[k + 4] >> 4) & 0xf;
1108130561Sobrien	if (temp > 9 || k + 9 >= nchar || temp != ((up->cbuf[k + 9] >>
1109130561Sobrien	    4) & 0xf))
1110130561Sobrien		temp = 0;
1111130561Sobrien#ifdef AUDIO_CHU
1112130561Sobrien	sprintf(tbuf, "chuA %04x %4.0f %2d %2d %2d %2d %1d ",
1113130561Sobrien	    up->status, up->maxsignal, nchar, up->burdist, k,
1114130561Sobrien	    up->syndist, temp);
1115130561Sobrien#else
1116130561Sobrien	sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ", up->status,
1117218822Sdim	    nchar, up->burdist, k, up->syndist, temp);
1118130561Sobrien#endif /* AUDIO_CHU */
1119130561Sobrien	for (i = 0; i < nchar; i++)
1120130561Sobrien		sprintf(&tbuf[strlen(tbuf)], "%02x",
1121130561Sobrien		    up->cbuf[i]);
112299461Sobrien	if (pp->sloppyclockflag & CLK_FLAG4)
112399461Sobrien		record_clock_stats(&peer->srcadr, tbuf);
1124130561Sobrien#ifdef DEBUG
1125130561Sobrien	if (debug)
1126130561Sobrien		printf("%s\n", tbuf);
1127130561Sobrien#endif
1128130561Sobrien	if (up->syndist < MINSYNC) {
1129130561Sobrien		up->status |= AFRAME;
1130130561Sobrien		return;
1131130561Sobrien	}
1132130561Sobrien
1133130561Sobrien	/*
1134130561Sobrien	 * A valid burst requires the first seconds number to match the
1135130561Sobrien	 * last seconds number. If so, the burst timestamps are
1136130561Sobrien	 * corrected to the current minute and saved for later
1137130561Sobrien	 * processing. In addition, the seconds decode is advanced from
1138130561Sobrien	 * the previous burst to the current one.
1139130561Sobrien	 */
1140130561Sobrien	if (temp != 0) {
1141130561Sobrien		offset.l_ui = 30 + temp;
1142130561Sobrien		offset.l_f = 0;
1143130561Sobrien		i = 0;
1144130561Sobrien		if (k < 0)
1145130561Sobrien			offset = up->charstamp;
1146130561Sobrien		else if (k > 0)
1147130561Sobrien			i = 1;
1148130561Sobrien		for (; i < nchar && i < k + 10; i++) {
1149130561Sobrien			up->tstamp[up->ntstamp] = up->cstamp[i];
1150130561Sobrien			L_SUB(&up->tstamp[up->ntstamp], &offset);
1151130561Sobrien			L_ADD(&offset, &up->charstamp);
1152130561Sobrien			if (up->ntstamp < MAXSTAGE)
1153130561Sobrien				up->ntstamp++;
1154130561Sobrien		}
1155130561Sobrien		while (temp > up->prevsec) {
1156130561Sobrien			for (j = 15; j > 0; j--) {
1157130561Sobrien				up->decode[9][j] = up->decode[9][j - 1];
1158130561Sobrien				up->decode[19][j] =
1159130561Sobrien				    up->decode[19][j - 1];
1160130561Sobrien			}
1161130561Sobrien			up->decode[9][j] = up->decode[19][j] = 0;
1162130561Sobrien			up->prevsec++;
1163130561Sobrien		}
1164130561Sobrien	}
1165130561Sobrien	i = -(2 * k);
1166130561Sobrien	for (j = 0; j < nchar; j++) {
1167130561Sobrien		if (i < 0 || i > 19) {
1168130561Sobrien			i += 2;
1169130561Sobrien			continue;
1170130561Sobrien		}
1171130561Sobrien		up->decode[i][up->cbuf[j] & 0xf]++;
1172130561Sobrien		i++;
1173130561Sobrien		up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
1174130561Sobrien		i++;
1175130561Sobrien	}
1176130561Sobrien	up->burstcnt++;
1177130561Sobrien}
1178130561Sobrien
1179130561Sobrien
1180130561Sobrien/*
1181130561Sobrien * chu_poll - called by the transmit procedure
1182130561Sobrien */
1183130561Sobrienstatic void
1184130561Sobrienchu_poll(
1185218822Sdim	int unit,
1186218822Sdim	struct peer *peer	/* peer structure pointer */
1187130561Sobrien	)
1188130561Sobrien{
1189130561Sobrien	struct refclockproc *pp;
1190130561Sobrien	struct chuunit *up;
1191130561Sobrien	char	synchar, qual, leapchar;
1192130561Sobrien	int	minset;
1193130561Sobrien	int	temp;
1194130561Sobrien#ifdef ICOM
1195130561Sobrien	char	tbuf[80];	/* trace buffer */
1196130561Sobrien#endif /* ICOM */
1197130561Sobrien	pp = peer->procptr;
1198130561Sobrien	up = (struct chuunit *)pp->unitptr;
1199130561Sobrien	if (pp->coderecv == pp->codeproc)
1200130561Sobrien		up->errflg = CEVNT_TIMEOUT;
1201130561Sobrien	else
1202130561Sobrien		pp->polls++;
1203130561Sobrien	minset = ((current_time - peer->update) + 30) / 60;
1204130561Sobrien	if (up->status & INSYNC) {
1205130561Sobrien		if (minset > PANIC)
1206130561Sobrien			up->status = 0;
1207130561Sobrien		else
1208130561Sobrien			peer->reach |= 1;
1209130561Sobrien	}
1210130561Sobrien
1211130561Sobrien	/*
1212130561Sobrien	 * Process the last burst, if still in the burst buffer.
121399461Sobrien	 * Don't mess with anything if nothing has been heard.
1214130561Sobrien	 */
1215130561Sobrien	chu_burst(peer);
121699461Sobrien#ifdef ICOM
121799461Sobrien	if (up->burstcnt > 2) {
121899461Sobrien		up->dwell = 0;
121999461Sobrien	} else if (up->dwell < DWELL) {
122099461Sobrien		up->dwell++;
122199461Sobrien	} else if (up->fd_icom > 0) {
122299461Sobrien		up->dwell = 0;
122399461Sobrien		up->chan = (up->chan + 1) % NCHAN;
122499461Sobrien		icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan]);
122599461Sobrien		sprintf(up->ident, "%.3f", qsy[up->chan]);
1226218822Sdim		sprintf(tbuf, "chu: QSY to %s MHz", up->ident);
122799461Sobrien		record_clock_stats(&peer->srcadr, tbuf);
122899461Sobrien#ifdef DEBUG
122999461Sobrien		if (debug)
123099461Sobrien			printf("%s\n", tbuf);
123199461Sobrien#endif
123299461Sobrien	}
123399461Sobrien#endif /* ICOM */
123499461Sobrien	if (up->burstcnt == 0)
123599461Sobrien		return;
123699461Sobrien	temp = chu_major(peer);
123799461Sobrien	if (up->status & INYEAR)
123899461Sobrien		up->status |= INSYNC;
123999461Sobrien	qual = 0;
124099461Sobrien	if (up->status & (BFRAME | AFRAME))
124199461Sobrien		qual |= SYNERR;
124299461Sobrien	if (up->status & (BFORMAT | AFORMAT))
124399461Sobrien		qual |= FMTERR;
124499461Sobrien	if (up->status & DECODE)
124599461Sobrien		qual |= DECERR;
124699461Sobrien	if (up->status & STAMP)
124799461Sobrien		qual |= TSPERR;
124899461Sobrien	synchar = leapchar = ' ';
124999461Sobrien	if (!(up->status & INSYNC)) {
125099461Sobrien		pp->leap = LEAP_NOTINSYNC;
125199461Sobrien		synchar = '?';
125299461Sobrien	} else if (up->leap & 0x2) {
125399461Sobrien		pp->leap = LEAP_ADDSECOND;
125499461Sobrien		leapchar = 'L';
125599461Sobrien	} else {
125699461Sobrien		pp->leap = LEAP_NOWARNING;
125799461Sobrien	}
125899461Sobrien#ifdef AUDIO_CHU
125999461Sobrien	sprintf(pp->a_lastcode,
126099461Sobrien	    "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %d %s %d %d %d %d",
126199461Sobrien	    synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
126299461Sobrien	    pp->second, leapchar, up->dst, up->dut, minset, up->gain,
1263218822Sdim	    up->ident, up->tai, up->burstcnt, up->mindist, up->ntstamp);
1264130561Sobrien#else
1265130561Sobrien	sprintf(pp->a_lastcode,
126699461Sobrien	    "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %s %d %d %d %d",
126799461Sobrien	    synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
126899461Sobrien	    pp->second, leapchar, up->dst, up->dut, minset,
1269218822Sdim	    up->ident, up->tai, up->burstcnt, up->mindist, up->ntstamp);
127099461Sobrien#endif /* AUDIO_CHU */
127199461Sobrien	pp->lencode = strlen(pp->a_lastcode);
127299461Sobrien
127399461Sobrien	/*
127499461Sobrien	 * If timestamps have been stuffed, the timecode is ipso fatso
127599461Sobrien	 * correct and can be selected to discipline the clock.
127699461Sobrien	 */
127799461Sobrien	if (temp > 0) {
127899461Sobrien		record_clock_stats(&peer->srcadr, pp->a_lastcode);
127999461Sobrien		refclock_receive(peer);
128099461Sobrien	} else if (pp->sloppyclockflag & CLK_FLAG4) {
128199461Sobrien		record_clock_stats(&peer->srcadr, pp->a_lastcode);
128299461Sobrien	}
128399461Sobrien#ifdef DEBUG
128499461Sobrien	if (debug)
128599461Sobrien		printf("chu: timecode %d %s\n", pp->lencode,
128699461Sobrien		    pp->a_lastcode);
1287130561Sobrien#endif
128899461Sobrien	chu_clear(peer);
1289218822Sdim	if (up->errflg)
129099461Sobrien		refclock_report(peer, up->errflg);
129199461Sobrien	up->errflg = 0;
129299461Sobrien}
129399461Sobrien
1294218822Sdim
1295218822Sdim/*
129699461Sobrien * chu_major - majority decoder
129799461Sobrien */
129899461Sobrienstatic int
129999461Sobrienchu_major(
130099461Sobrien	struct peer *peer	/* peer structure pointer */
130199461Sobrien	)
130299461Sobrien{
130399461Sobrien	struct refclockproc *pp;
130499461Sobrien	struct chuunit *up;
130599461Sobrien
130699461Sobrien	u_char	code[11];	/* decoded timecode */
130799461Sobrien	l_fp	toffset, offset; /* l_fp temps */
130899461Sobrien	int	val1, val2;	/* maximum distance */
130999461Sobrien	int	synchar;	/* stray cat */
131099461Sobrien	double	dtemp;
1311218822Sdim	int	temp;
1312218822Sdim	int	i, j, k;
1313218822Sdim
131499461Sobrien	pp = peer->procptr;
131599461Sobrien	up = (struct chuunit *)pp->unitptr;
1316130561Sobrien
131799461Sobrien	/*
131899461Sobrien	 * Majority decoder. Each burst encodes two replications at each
131999461Sobrien	 * digit position in the timecode. Each row of the decoding
132099461Sobrien	 * matrix encodes the number of occurences of each digit found
132199461Sobrien	 * at the corresponding position. The maximum over all
132299461Sobrien	 * occurences at each position is the distance for this position
132399461Sobrien	 * and the corresponding digit is the maximumn likelihood
132499461Sobrien	 * candidate. If the distance is zero, assume a miss '_'; if the
132599461Sobrien	 * distance is not more than half the total number of
132699461Sobrien	 * occurences, assume a soft error '-'; if two different digits
132799461Sobrien	 * with the same distance are found, assume a hard error '='.
132899461Sobrien	 * These will later cause a format error when the timecode is
132999461Sobrien	 * interpreted. The decoding distance is defined as the minimum
133099461Sobrien	 * distance over the first nine digits. The tenth digit varies
133199461Sobrien	 * over the seconds, so we don't count it.
1332130561Sobrien	 */
1333218822Sdim	up->mindist = 16;
133499461Sobrien	for (i = 0; i < 9; i++) {
133599461Sobrien		val1 = val2 = 0;
133699461Sobrien		k = 0;
133799461Sobrien		for (j = 0; j < 16; j++) {
1338130561Sobrien			temp = up->decode[i][j] + up->decode[i + 10][j];
133999461Sobrien			if (temp > val1) {
1340218822Sdim				val2 = val1;
1341218822Sdim				val1 = temp;
134299461Sobrien				k = j;
134399461Sobrien			}
134499461Sobrien		}
134599461Sobrien		if (val1 == 0)
134699461Sobrien			code[i] = HEX_MISS;
134799461Sobrien		else if (val1 == val2)
1348130561Sobrien			code[i] = HEX_HARD;
134999461Sobrien		else if (val1 <= up->burstcnt)
135099461Sobrien			code[i] = HEX_SOFT;
135199461Sobrien		else
1352130561Sobrien			code[i] = k;
135399461Sobrien		if (val1 < up->mindist)
135499461Sobrien			up->mindist = val1;
135599461Sobrien		code[i] = hexchar[code[i]];
135699461Sobrien	}
135799461Sobrien	code[i] = 0;
135899461Sobrien
135999461Sobrien	/*
136099461Sobrien	 * A valid timecode requires at least three bursts and a
136199461Sobrien	 * decoding distance greater than half the total number of
136299461Sobrien	 * occurences. A valid timecode also requires at least 20 valid
136399461Sobrien	 * timestamps.
136499461Sobrien	 */
136599461Sobrien	if (up->burstcnt < 3 || up->mindist <= up->burstcnt)
136699461Sobrien		up->status |= DECODE;
136799461Sobrien	if (up->ntstamp < MINSTAMP)
136899461Sobrien		up->status |= STAMP;
136999461Sobrien
137099461Sobrien	/*
1371130561Sobrien	 * Compute the timecode timestamp from the days, hours and
1372130561Sobrien	 * minutes of the timecode. Use clocktime() for the aggregate
1373130561Sobrien	 * minutes and the minute offset computed from the burst
1374130561Sobrien	 * seconds. Note that this code relies on the filesystem time
137599461Sobrien	 * for the years and does not use the years of the timecode.
137699461Sobrien	 */
137799461Sobrien	if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
1378130561Sobrien	    &pp->hour, &pp->minute) != 4) {
1379130561Sobrien		up->status |= AFORMAT;
1380130561Sobrien		return (0);
1381130561Sobrien	}
138299461Sobrien	if (up->status & (DECODE | STAMP)) {
138399461Sobrien		up->errflg = CEVNT_BADREPLY;
138499461Sobrien		return (0);
138599461Sobrien	}
138699461Sobrien	L_CLR(&offset);
138799461Sobrien	if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
1388130561Sobrien	    up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
138999461Sobrien		up->errflg = CEVNT_BADTIME;
139099461Sobrien		return (0);
139199461Sobrien	}
139299461Sobrien	pp->lastref = offset;
139399461Sobrien	pp->variance = 0;
139499461Sobrien	for (i = 0; i < up->ntstamp; i++) {
1395218822Sdim		toffset = offset;
1396218822Sdim		L_SUB(&toffset, &up->tstamp[i]);
1397218822Sdim		LFPTOD(&toffset, dtemp);
1398218822Sdim		SAMPLE(dtemp + FUDGE + pp->fudgetime1);
1399218822Sdim	}
140099461Sobrien	return (i);
140199461Sobrien}
1402218822Sdim
1403218822Sdim
1404218822Sdim/*
1405218822Sdim * chu_clear - clear decoding matrix
1406218822Sdim */
1407218822Sdimstatic void
1408218822Sdimchu_clear(
140999461Sobrien	struct peer *peer	/* peer structure pointer */
141099461Sobrien	)
141199461Sobrien{
141299461Sobrien	struct refclockproc *pp;
141399461Sobrien	struct chuunit *up;
1414130561Sobrien	int	i, j;
1415218822Sdim
1416218822Sdim	pp = peer->procptr;
1417218822Sdim	up = (struct chuunit *)pp->unitptr;
1418218822Sdim
141999461Sobrien	/*
142099461Sobrien	 * Clear stuff for the minute.
142199461Sobrien	 */
142299461Sobrien	up->ndx = up->prevsec = 0;
142399461Sobrien	up->burstcnt = up->mindist = up->ntstamp = 0;
142499461Sobrien	up->status &= INSYNC | INYEAR;
142599461Sobrien	up->burstcnt = 0;
142699461Sobrien	for (i = 0; i < 20; i++) {
142799461Sobrien		for (j = 0; j < 16; j++)
142899461Sobrien			up->decode[i][j] = 0;
142999461Sobrien	}
143099461Sobrien}
143199461Sobrien
143299461Sobrien
1433130561Sobrien/*
1434130561Sobrien * chu_dist - determine the distance of two octet arguments
1435130561Sobrien */
1436130561Sobrienstatic int
143799461Sobrienchu_dist(
1438130561Sobrien	int	x,		/* an octet of bits */
1439130561Sobrien	int	y		/* another octet of bits */
1440130561Sobrien	)
1441130561Sobrien{
1442130561Sobrien	int	val;		/* bit count */
1443130561Sobrien	int	temp;
144499461Sobrien	int	i;
1445130561Sobrien
1446218822Sdim	/*
1447218822Sdim	 * The distance is determined as the weight of the exclusive OR
1448218822Sdim	 * of the two arguments. The weight is determined by the number
1449130561Sobrien	 * of one bits in the result. Each one bit increases the weight,
145099461Sobrien	 * while each zero bit decreases it.
1451130561Sobrien	 */
1452130561Sobrien	temp = x ^ y;
1453130561Sobrien	val = 0;
1454130561Sobrien	for (i = 0; i < 8; i++) {
1455130561Sobrien		if ((temp & 0x1) == 0)
1456130561Sobrien			val++;
1457130561Sobrien		else
1458130561Sobrien			val--;
145999461Sobrien		temp >>= 1;
1460130561Sobrien	}
1461130561Sobrien	return (val);
1462130561Sobrien}
1463130561Sobrien
1464130561Sobrien
1465130561Sobrien#ifdef AUDIO_CHU
1466130561Sobrien/*
1467130561Sobrien * chu_gain - adjust codec gain
146899461Sobrien *
1469130561Sobrien * This routine is called once each second. If the signal envelope
1470130561Sobrien * amplitude is too low, the codec gain is bumped up by four units; if
1471130561Sobrien * too high, it is bumped down. The decoder is relatively insensitive to
1472130561Sobrien * amplitude, so this crudity works just fine. The input port is set and
1473130561Sobrien * the error flag is cleared, mostly to be ornery.
1474130561Sobrien */
1475130561Sobrienstatic void
1476130561Sobrienchu_gain(
1477130561Sobrien	struct peer *peer	/* peer structure pointer */
1478130561Sobrien	)
1479130561Sobrien{
1480130561Sobrien	struct refclockproc *pp;
1481130561Sobrien	struct chuunit *up;
1482130561Sobrien
1483130561Sobrien	pp = peer->procptr;
1484130561Sobrien	up = (struct chuunit *)pp->unitptr;
1485130561Sobrien
1486130561Sobrien	/*
1487130561Sobrien	 * Apparently, the codec uses only the high order bits of the
1488130561Sobrien	 * gain control field. Thus, it may take awhile for changes to
1489130561Sobrien	 * wiggle the hardware bits.
1490130561Sobrien	 */
1491130561Sobrien	if (up->clipcnt == 0) {
1492130561Sobrien		up->gain += 4;
1493130561Sobrien		if (up->gain > 255)
1494130561Sobrien			up->gain = 255;
1495130561Sobrien	} else if (up->clipcnt > SECOND / 100) {
1496130561Sobrien		up->gain -= 4;
1497130561Sobrien		if (up->gain < 0)
1498130561Sobrien			up->gain = 0;
1499130561Sobrien	}
1500130561Sobrien	audio_gain(up->gain, up->port);
1501130561Sobrien	up->clipcnt = 0;
1502130561Sobrien}
1503130561Sobrien#endif /* AUDIO_CHU */
1504130561Sobrien
1505130561Sobrien
1506130561Sobrien#else
1507130561Sobrienint refclock_chu_bs;
1508130561Sobrien#endif /* REFCLOCK */
1509130561Sobrien