154359Sroberto/*
256746Sroberto * refclock_chu - clock driver for Canadian CHU time/frequency station
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
554359Sroberto#include <config.h>
654359Sroberto#endif
754359Sroberto
8290001Sglebius#include "ntp_types.h"
9290001Sglebius
1054359Sroberto#if defined(REFCLOCK) && defined(CLOCK_CHU)
1154359Sroberto
1254359Sroberto#include "ntpd.h"
1354359Sroberto#include "ntp_io.h"
1454359Sroberto#include "ntp_refclock.h"
1554359Sroberto#include "ntp_calendar.h"
1654359Sroberto#include "ntp_stdlib.h"
1782498Sroberto
1882498Sroberto#include <stdio.h>
1982498Sroberto#include <ctype.h>
2082498Sroberto#include <math.h>
2182498Sroberto
2282498Sroberto#ifdef HAVE_AUDIO
2356746Sroberto#include "audio.h"
2482498Sroberto#endif /* HAVE_AUDIO */
2554359Sroberto
2656746Sroberto#define ICOM 	1		/* undefine to suppress ICOM code */
2756746Sroberto
2856746Sroberto#ifdef ICOM
2956746Sroberto#include "icom.h"
3056746Sroberto#endif /* ICOM */
3154359Sroberto/*
3256746Sroberto * Audio CHU demodulator/decoder
3354359Sroberto *
3454359Sroberto * This driver synchronizes the computer time using data encoded in
3554359Sroberto * radio transmissions from Canadian time/frequency station CHU in
3654359Sroberto * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
37290001Sglebius * 7850 kHz and 14670 kHz in upper sideband, compatible AM mode. An
3854359Sroberto * ordinary shortwave receiver can be tuned manually to one of these
3954359Sroberto * frequencies or, in the case of ICOM receivers, the receiver can be
40290001Sglebius * tuned automatically as propagation conditions change throughout the
41290001Sglebius * day and season.
4254359Sroberto *
43290001Sglebius * The driver requires an audio codec or sound card with sampling rate 8
44290001Sglebius * kHz and mu-law companding. This is the same standard as used by the
45290001Sglebius * telephone industry and is supported by most hardware and operating
46290001Sglebius * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this
47290001Sglebius * implementation, only one audio driver and codec can be supported on a
48290001Sglebius * single machine.
4956746Sroberto *
5054359Sroberto * The driver can be compiled to use a Bell 103 compatible modem or
5154359Sroberto * modem chip to receive the radio signal and demodulate the data.
5254359Sroberto * Alternatively, the driver can be compiled to use the audio codec of
53290001Sglebius * the workstation or another with compatible audio drivers. In the
5454359Sroberto * latter case, the driver implements the modem using DSP routines, so
5554359Sroberto * the radio can be connected directly to either the microphone on line
5654359Sroberto * input port. In either case, the driver decodes the data using a
57290001Sglebius * maximum-likelihood technique which exploits the considerable degree
5854359Sroberto * of redundancy available to maximize accuracy and minimize errors.
5954359Sroberto *
6054359Sroberto * The CHU time broadcast includes an audio signal compatible with the
61290001Sglebius * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). The signal
62290001Sglebius * consists of nine, ten-character bursts transmitted at 300 bps between
63290001Sglebius * seconds 31 and 39 of each minute. Each character consists of eight
64290001Sglebius * data bits plus one start bit and two stop bits to encode two hex
65290001Sglebius * digits. The burst data consist of five characters (ten hex digits)
66290001Sglebius * followed by a repeat of these characters. In format A, the characters
67290001Sglebius * are repeated in the same polarity; in format B, the characters are
68290001Sglebius * repeated in the opposite polarity.
6954359Sroberto *
7054359Sroberto * Format A bursts are sent at seconds 32 through 39 of the minute in
71290001Sglebius * hex digits (nibble swapped)
7254359Sroberto *
7354359Sroberto *	6dddhhmmss6dddhhmmss
7454359Sroberto *
7554359Sroberto * The first ten digits encode a frame marker (6) followed by the day
7654359Sroberto * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
7754359Sroberto * format A bursts are sent during the third decade of seconds the tens
7854359Sroberto * digit of ss is always 3. The driver uses this to determine correct
7954359Sroberto * burst synchronization. These digits are then repeated with the same
8054359Sroberto * polarity.
8154359Sroberto *
8254359Sroberto * Format B bursts are sent at second 31 of the minute in hex digits
8354359Sroberto *
8454359Sroberto *	xdyyyyttaaxdyyyyttaa
8554359Sroberto *
8654359Sroberto * The first ten digits encode a code (x described below) followed by
8754359Sroberto * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
8854359Sroberto * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
8954359Sroberto * digits are then repeated with inverted polarity.
9054359Sroberto *
9154359Sroberto * The x is coded
9254359Sroberto *
9354359Sroberto * 1 Sign of DUT (0 = +)
9454359Sroberto * 2 Leap second warning. One second will be added.
9554359Sroberto * 4 Leap second warning. One second will be subtracted.
9654359Sroberto * 8 Even parity bit for this nibble.
9754359Sroberto *
9854359Sroberto * By design, the last stop bit of the last character in the burst
9954359Sroberto * coincides with 0.5 second. Since characters have 11 bits and are
10054359Sroberto * transmitted at 300 bps, the last stop bit of the first character
101290001Sglebius * coincides with 0.5 - 9 * 11/300 = 0.170 second. Depending on the
102290001Sglebius * UART, character interrupts can vary somewhere between the end of bit
103290001Sglebius * 9 and end of bit 11. These eccentricities can be corrected along with
104290001Sglebius * the radio propagation delay using fudge time 1.
10554359Sroberto *
10654359Sroberto * Debugging aids
10754359Sroberto *
10854359Sroberto * The timecode format used for debugging and data recording includes
10954359Sroberto * data helpful in diagnosing problems with the radio signal and serial
110132451Sroberto * connections. With debugging enabled (-d on the ntpd command line),
111132451Sroberto * the driver produces one line for each burst in two formats
112290001Sglebius * corresponding to format A and B.Each line begins with the format code
113290001Sglebius * chuA or chuB followed by the status code and signal level (0-9999).
114290001Sglebius * The remainder of the line is as follows.
11554359Sroberto *
116290001Sglebius * Following is format A:
117290001Sglebius *
11854359Sroberto *	n b f s m code
11954359Sroberto *
120290001Sglebius * where n is the number of characters in the burst (0-10), b the burst
12154359Sroberto * distance (0-40), f the field alignment (-1, 0, 1), s the
12254359Sroberto * synchronization distance (0-16), m the burst number (2-9) and code
12354359Sroberto * the burst characters as received. Note that the hex digits in each
12454359Sroberto * character are reversed, so the burst
12554359Sroberto *
12654359Sroberto *	10 38 0 16 9 06851292930685129293
12754359Sroberto *
128290001Sglebius * is interpreted as containing 10 characters with burst distance 38,
12954359Sroberto * field alignment 0, synchronization distance 16 and burst number 9.
13054359Sroberto * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
13154359Sroberto * second 39.
13254359Sroberto *
13354359Sroberto * Following is format B:
13454359Sroberto *
13554359Sroberto *	n b s code
13654359Sroberto *
137290001Sglebius * where n is the number of characters in the burst (0-10), b the burst
13854359Sroberto * distance (0-40), s the synchronization distance (0-40) and code the
13954359Sroberto * burst characters as received. Note that the hex digits in each
14054359Sroberto * character are reversed and the last ten digits inverted, so the burst
14154359Sroberto *
142290001Sglebius *	10 40 1091891300ef6e76ec
14354359Sroberto *
144290001Sglebius * is interpreted as containing 10 characters with burst distance 40.
14554359Sroberto * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
14654359Sroberto * - UTC 31 seconds.
14754359Sroberto *
148290001Sglebius * Each line is preceeded by the code chuA or chuB, as appropriate. If
149290001Sglebius * the audio driver is compiled, the current gain (0-255) and relative
150290001Sglebius * signal level (0-9999) follow the code. The receiver volume control
151290001Sglebius * should be set so that the gain is somewhere near the middle of the
152290001Sglebius * range 0-255, which results in a signal level near 1000.
153290001Sglebius *
15454359Sroberto * In addition to the above, the reference timecode is updated and
15554359Sroberto * written to the clockstats file and debug score after the last burst
15654359Sroberto * received in the minute. The format is
15754359Sroberto *
158290001Sglebius *	sq yyyy ddd hh:mm:ss l s dd t agc ident m b
15954359Sroberto *
160290001Sglebius * s	'?' before first synchronized and ' ' after that
161290001Sglebius * q	status code (see below)
162290001Sglebius * yyyy	year
163290001Sglebius * ddd	day of year
164290001Sglebius * hh:mm:ss time of day
165290001Sglebius * l	leap second indicator (space, L or D)
166290001Sglebius * dst	Canadian daylight code (opaque)
167290001Sglebius * t	number of minutes since last synchronized
168290001Sglebius * agc	audio gain (0 - 255)
169290001Sglebius * ident identifier (CHU0 3330 kHz, CHU1 7850 kHz, CHU2 14670 kHz)
170290001Sglebius * m	signal metric (0 - 100)
171290001Sglebius * b	number of timecodes for the previous minute (0 - 59)
17254359Sroberto *
17354359Sroberto * Fudge factors
17454359Sroberto *
17554359Sroberto * For accuracies better than the low millisceconds, fudge time1 can be
17654359Sroberto * set to the radio propagation delay from CHU to the receiver. This can
177132451Sroberto * be done conviently using the minimuf program.
17854359Sroberto *
179132451Sroberto * Fudge flag4 causes the dubugging output described above to be
180132451Sroberto * recorded in the clockstats file. When the audio driver is compiled,
181132451Sroberto * fudge flag2 selects the audio input port, where 0 is the mike port
182132451Sroberto * (default) and 1 is the line-in port. It does not seem useful to
183132451Sroberto * select the compact disc player port. Fudge flag3 enables audio
184132451Sroberto * monitoring of the input signal. For this purpose, the monitor gain is
185132451Sroberto * set to a default value.
18656746Sroberto *
18782498Sroberto * The audio codec code is normally compiled in the driver if the
188132451Sroberto * architecture supports it (HAVE_AUDIO defined), but is used only if
189132451Sroberto * the link /dev/chu_audio is defined and valid. The serial port code is
190132451Sroberto * always compiled in the driver, but is used only if the autdio codec
191132451Sroberto * is not available and the link /dev/chu%d is defined and valid.
192132451Sroberto *
19382498Sroberto * The ICOM code is normally compiled in the driver if selected (ICOM
19482498Sroberto * defined), but is used only if the link /dev/icom%d is defined and
19582498Sroberto * valid and the mode keyword on the server configuration command
19682498Sroberto * specifies a nonzero mode (ICOM ID select code). The C-IV speed is
19782498Sroberto * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps
19882498Sroberto * if one. The C-IV trace is turned on if the debug level is greater
19982498Sroberto * than one.
200290001Sglebius *
201290001Sglebius * Alarm codes
202290001Sglebius *
203290001Sglebius * CEVNT_BADTIME	invalid date or time
204290001Sglebius * CEVNT_PROP		propagation failure - no stations heard
20554359Sroberto */
20654359Sroberto/*
20754359Sroberto * Interface definitions
20854359Sroberto */
20954359Sroberto#define	SPEED232	B300	/* uart speed (300 baud) */
21054359Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
21154359Sroberto#define	REFID		"CHU"	/* reference ID */
21282498Sroberto#define	DEVICE		"/dev/chu%d" /* device name and unit */
21382498Sroberto#define	SPEED232	B300	/* UART speed (300 baud) */
21456746Sroberto#ifdef ICOM
215290001Sglebius#define TUNE		.001	/* offset for narrow filter (MHz) */
216290001Sglebius#define DWELL		5	/* minutes in a dwell */
21756746Sroberto#define NCHAN		3	/* number of channels */
218132451Sroberto#define ISTAGE		3	/* number of integrator stages */
21956746Sroberto#endif /* ICOM */
220132451Sroberto
22182498Sroberto#ifdef HAVE_AUDIO
22254359Sroberto/*
22354359Sroberto * Audio demodulator definitions
22454359Sroberto */
22556746Sroberto#define SECOND		8000	/* nominal sample rate (Hz) */
22654359Sroberto#define BAUD		300	/* modulation rate (bps) */
22754359Sroberto#define OFFSET		128	/* companded sample offset */
22854359Sroberto#define SIZE		256	/* decompanding table size */
229182007Sroberto#define	MAXAMP		6000.	/* maximum signal level */
230132451Sroberto#define	MAXCLP		100	/* max clips above reference per s */
231290001Sglebius#define	SPAN		800.	/* min envelope span */
23254359Sroberto#define LIMIT		1000.	/* soft limiter threshold */
23354359Sroberto#define AGAIN		6.	/* baseband gain */
23454359Sroberto#define LAG		10	/* discriminator lag */
235182007Sroberto#define	DEVICE_AUDIO	"/dev/audio" /* device name */
23682498Sroberto#define	DESCRIPTION	"CHU Audio/Modem Receiver" /* WRU */
237132451Sroberto#define	AUDIO_BUFSIZ	240	/* audio buffer size (30 ms) */
23854359Sroberto#else
23982498Sroberto#define	DESCRIPTION	"CHU Modem Receiver" /* WRU */
24082498Sroberto#endif /* HAVE_AUDIO */
24154359Sroberto
24254359Sroberto/*
24354359Sroberto * Decoder definitions
24454359Sroberto */
24554359Sroberto#define CHAR		(11. / 300.) /* character time (s) */
24654359Sroberto#define BURST		11	/* max characters per burst */
247290001Sglebius#define MINCHARS		9	/* min characters per burst */
24854359Sroberto#define MINDIST		28	/* min burst distance (of 40)  */
24954359Sroberto#define MINSYNC		8	/* min sync distance (of 16) */
25054359Sroberto#define MINSTAMP	20	/* min timestamps (of 60) */
251290001Sglebius#define MINMETRIC	50	/* min channel metric (of 160) */
25254359Sroberto
25354359Sroberto/*
254290001Sglebius * The on-time synchronization point for the driver is the last stop bit
255290001Sglebius * of the first character 170 ms. The modem delay is 0.8 ms, while the
256290001Sglebius * receiver delay is approxmately 4.7 ms at 2125 Hz. The fudge value 1.3
257290001Sglebius * ms due to the codec and other causes was determined by calibrating to
258290001Sglebius * a PPS signal from a GPS receiver. The additional propagation delay
259290001Sglebius * specific to each receiver location can be programmed in the fudge
260290001Sglebius * time1.
261290001Sglebius *
262290001Sglebius * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are
263290001Sglebius * generally within 0.5 ms short term with 0.3 ms jitter. The long-term
264290001Sglebius * offsets vary up to 0.3 ms due to ionospheric layer height variations.
265290001Sglebius * The processor load due to the driver is 0.4 percent.
26654359Sroberto */
267290001Sglebius#define	PDELAY	((170 + .8 + 4.7 + 1.3) / 1000)	/* system delay (s) */
26854359Sroberto
26954359Sroberto/*
27056746Sroberto * Status bits (status)
27154359Sroberto */
27256746Sroberto#define RUNT		0x0001	/* runt burst */
27356746Sroberto#define NOISE		0x0002	/* noise burst */
27456746Sroberto#define BFRAME		0x0004	/* invalid format B frame sync */
27556746Sroberto#define BFORMAT		0x0008	/* invalid format B data */
27656746Sroberto#define AFRAME		0x0010	/* invalid format A frame sync */
27756746Sroberto#define AFORMAT		0x0020	/* invalid format A data */
27856746Sroberto#define DECODE		0x0040	/* invalid data decode */
27956746Sroberto#define STAMP		0x0080	/* too few timestamps */
280132451Sroberto#define AVALID		0x0100	/* valid A frame */
281132451Sroberto#define BVALID		0x0200	/* valid B frame */
282132451Sroberto#define INSYNC		0x0400	/* clock synchronized */
283290001Sglebius#define	METRIC		0x0800	/* one or more stations heard */
28454359Sroberto
28556746Sroberto/*
28656746Sroberto * Alarm status bits (alarm)
28756746Sroberto *
28856746Sroberto * These alarms are set at the end of a minute in which at least one
28956746Sroberto * burst was received. SYNERR is raised if the AFRAME or BFRAME status
29056746Sroberto * bits are set during the minute, FMTERR is raised if the AFORMAT or
29156746Sroberto * BFORMAT status bits are set, DECERR is raised if the DECODE status
29256746Sroberto * bit is set and TSPERR is raised if the STAMP status bit is set.
29356746Sroberto */
29456746Sroberto#define SYNERR		0x01	/* frame sync error */
29556746Sroberto#define FMTERR		0x02	/* data format error */
29656746Sroberto#define DECERR		0x04	/* data decoding error */
29756746Sroberto#define TSPERR		0x08	/* insufficient data */
29856746Sroberto
29982498Sroberto#ifdef HAVE_AUDIO
300132451Sroberto/*
301290001Sglebius * Maximum-likelihood UART structure. There are eight of these
302132451Sroberto * corresponding to the number of phases.
303132451Sroberto */
30454359Srobertostruct surv {
305290001Sglebius	l_fp	cstamp;		/* last bit timestamp */
306290001Sglebius	double	shift[12];	/* sample shift register */
307290001Sglebius	double	span;		/* shift register envelope span */
30854359Sroberto	double	dist;		/* sample distance */
30954359Sroberto	int	uart;		/* decoded character */
31054359Sroberto};
31182498Sroberto#endif /* HAVE_AUDIO */
31254359Sroberto
313132451Sroberto#ifdef ICOM
31454359Sroberto/*
315132451Sroberto * CHU station structure. There are three of these corresponding to the
316132451Sroberto * three frequencies.
317132451Sroberto */
318132451Srobertostruct xmtr {
319132451Sroberto	double	integ[ISTAGE];	/* circular integrator */
320132451Sroberto	double	metric;		/* integrator sum */
321132451Sroberto	int	iptr;		/* integrator pointer */
322132451Sroberto	int	probe;		/* dwells since last probe */
323132451Sroberto};
324132451Sroberto#endif /* ICOM */
325132451Sroberto
326132451Sroberto/*
32754359Sroberto * CHU unit control structure
32854359Sroberto */
32954359Srobertostruct chuunit {
330290001Sglebius	u_char	decode[20][16];	/* maximum-likelihood decoding matrix */
33154359Sroberto	l_fp	cstamp[BURST];	/* character timestamps */
33254359Sroberto	l_fp	tstamp[MAXSTAGE]; /* timestamp samples */
33354359Sroberto	l_fp	timestamp;	/* current buffer timestamp */
33454359Sroberto	l_fp	laststamp;	/* last buffer timestamp */
33554359Sroberto	l_fp	charstamp;	/* character time as a l_fp */
336290001Sglebius	int	second;		/* counts the seconds of the minute */
33754359Sroberto	int	errflg;		/* error flags */
33856746Sroberto	int	status;		/* status bits */
339132451Sroberto	char	ident[5];	/* station ID and channel */
34056746Sroberto#ifdef ICOM
34182498Sroberto	int	fd_icom;	/* ICOM file descriptor */
342290001Sglebius	int	chan;		/* radio channel */
343132451Sroberto	int	dwell;		/* dwell cycle */
344132451Sroberto	struct xmtr xmtr[NCHAN]; /* station metric */
34556746Sroberto#endif /* ICOM */
34654359Sroberto
34754359Sroberto	/*
34854359Sroberto	 * Character burst variables
34954359Sroberto	 */
35054359Sroberto	int	cbuf[BURST];	/* character buffer */
35154359Sroberto	int	ntstamp;	/* number of timestamp samples */
35254359Sroberto	int	ndx;		/* buffer start index */
35354359Sroberto	int	prevsec;	/* previous burst second */
35454359Sroberto	int	burdist;	/* burst distance */
35554359Sroberto	int	syndist;	/* sync distance */
35654359Sroberto	int	burstcnt;	/* format A bursts this minute */
357290001Sglebius	double	maxsignal;	/* signal level (modem only) */
358290001Sglebius	int	gain;		/* codec gain (modem only) */
35954359Sroberto
36056746Sroberto	/*
36156746Sroberto	 * Format particulars
36256746Sroberto	 */
36356746Sroberto	int	leap;		/* leap/dut code */
36456746Sroberto	int	dut;		/* UTC1 correction */
36556746Sroberto	int	tai;		/* TAI - UTC correction */
36656746Sroberto	int	dst;		/* Canadian DST code */
36756746Sroberto
36882498Sroberto#ifdef HAVE_AUDIO
36954359Sroberto	/*
37054359Sroberto	 * Audio codec variables
37154359Sroberto	 */
37282498Sroberto	int	fd_audio;	/* audio port file descriptor */
37354359Sroberto	double	comp[SIZE];	/* decompanding table */
37454359Sroberto	int	port;		/* codec port */
375132451Sroberto	int	mongain;	/* codec monitor gain */
37654359Sroberto	int	clipcnt;	/* sample clip count */
37754359Sroberto	int	seccnt;		/* second interval counter */
37854359Sroberto
37954359Sroberto	/*
38054359Sroberto	 * Modem variables
38154359Sroberto	 */
38254359Sroberto	l_fp	tick;		/* audio sample increment */
38354359Sroberto	double	bpf[9];		/* IIR bandpass filter */
38454359Sroberto	double	disc[LAG];	/* discriminator shift register */
38554359Sroberto	double	lpf[27];	/* FIR lowpass filter */
38654359Sroberto	double	monitor;	/* audio monitor */
38754359Sroberto	int	discptr;	/* discriminator pointer */
38854359Sroberto
38954359Sroberto	/*
390290001Sglebius	 * Maximum-likelihood UART variables
39154359Sroberto	 */
39254359Sroberto	double	baud;		/* baud interval */
39354359Sroberto	struct surv surv[8];	/* UART survivor structures */
39454359Sroberto	int	decptr;		/* decode pointer */
395290001Sglebius	int	decpha;		/* decode phase */
39654359Sroberto	int	dbrk;		/* holdoff counter */
39782498Sroberto#endif /* HAVE_AUDIO */
39854359Sroberto};
39954359Sroberto
40054359Sroberto/*
40154359Sroberto * Function prototypes
40254359Sroberto */
403290001Sglebiusstatic	int	chu_start	(int, struct peer *);
404290001Sglebiusstatic	void	chu_shutdown	(int, struct peer *);
405290001Sglebiusstatic	void	chu_receive	(struct recvbuf *);
406290001Sglebiusstatic	void	chu_second	(int, struct peer *);
407290001Sglebiusstatic	void	chu_poll	(int, struct peer *);
40854359Sroberto
40954359Sroberto/*
41054359Sroberto * More function prototypes
41154359Sroberto */
412290001Sglebiusstatic	void	chu_decode	(struct peer *, int, l_fp);
413290001Sglebiusstatic	void	chu_burst	(struct peer *);
414290001Sglebiusstatic	void	chu_clear	(struct peer *);
415290001Sglebiusstatic	void	chu_a		(struct peer *, int);
416290001Sglebiusstatic	void	chu_b		(struct peer *, int);
417290001Sglebiusstatic	int	chu_dist	(int, int);
418290001Sglebiusstatic	double	chu_major	(struct peer *);
41982498Sroberto#ifdef HAVE_AUDIO
420290001Sglebiusstatic	void	chu_uart	(struct surv *, double);
421290001Sglebiusstatic	void	chu_rf		(struct peer *, double);
422290001Sglebiusstatic	void	chu_gain	(struct peer *);
423290001Sglebiusstatic	void	chu_audio_receive (struct recvbuf *rbufp);
42482498Sroberto#endif /* HAVE_AUDIO */
425132451Sroberto#ifdef ICOM
426290001Sglebiusstatic	int	chu_newchan	(struct peer *, double);
427132451Sroberto#endif /* ICOM */
428290001Sglebiusstatic	void	chu_serial_receive (struct recvbuf *rbufp);
42954359Sroberto
43054359Sroberto/*
43154359Sroberto * Global variables
43254359Sroberto */
433132451Srobertostatic char hexchar[] = "0123456789abcdef_*=";
434132451Sroberto
43556746Sroberto#ifdef ICOM
436132451Sroberto/*
437132451Sroberto * Note the tuned frequencies are 1 kHz higher than the carrier. CHU
438132451Sroberto * transmits on USB with carrier so we can use AM and the narrow SSB
439132451Sroberto * filter.
440132451Sroberto */
441290001Sglebiusstatic double qsy[NCHAN] = {3.330, 7.850, 14.670}; /* freq (MHz) */
44256746Sroberto#endif /* ICOM */
44354359Sroberto
44454359Sroberto/*
44554359Sroberto * Transfer vector
44654359Sroberto */
44754359Srobertostruct	refclock refclock_chu = {
44854359Sroberto	chu_start,		/* start up driver */
44954359Sroberto	chu_shutdown,		/* shut down driver */
45054359Sroberto	chu_poll,		/* transmit poll message */
45154359Sroberto	noentry,		/* not used (old chu_control) */
45254359Sroberto	noentry,		/* initialize driver (not used) */
45354359Sroberto	noentry,		/* not used (old chu_buginfo) */
454290001Sglebius	chu_second		/* housekeeping timer */
45554359Sroberto};
45654359Sroberto
45754359Sroberto
45854359Sroberto/*
45954359Sroberto * chu_start - open the devices and initialize data for processing
46054359Sroberto */
46154359Srobertostatic int
46254359Srobertochu_start(
46354359Sroberto	int	unit,		/* instance number (not used) */
46454359Sroberto	struct peer *peer	/* peer structure pointer */
46554359Sroberto	)
46654359Sroberto{
46754359Sroberto	struct chuunit *up;
46854359Sroberto	struct refclockproc *pp;
46982498Sroberto	char device[20];	/* device name */
47054359Sroberto	int	fd;		/* file descriptor */
47156746Sroberto#ifdef ICOM
47256746Sroberto	int	temp;
47356746Sroberto#endif /* ICOM */
47482498Sroberto#ifdef HAVE_AUDIO
47582498Sroberto	int	fd_audio;	/* audio port file descriptor */
47654359Sroberto	int	i;		/* index */
47754359Sroberto	double	step;		/* codec adjustment */
47854359Sroberto
47954359Sroberto	/*
480290001Sglebius	 * Open audio device. Don't complain if not there.
48154359Sroberto	 */
482132451Sroberto	fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
483290001Sglebius
48456746Sroberto#ifdef DEBUG
485290001Sglebius	if (fd_audio >= 0 && debug)
48656746Sroberto		audio_show();
48756746Sroberto#endif
48854359Sroberto
48954359Sroberto	/*
490290001Sglebius	 * If audio is unavailable, Open serial port in raw mode.
49154359Sroberto	 */
492290001Sglebius	if (fd_audio >= 0) {
49382498Sroberto		fd = fd_audio;
49482498Sroberto	} else {
495290001Sglebius		snprintf(device, sizeof(device), DEVICE, unit);
49682498Sroberto		fd = refclock_open(device, SPEED232, LDISC_RAW);
49754359Sroberto	}
49882498Sroberto#else /* HAVE_AUDIO */
49954359Sroberto
50054359Sroberto	/*
50182498Sroberto	 * Open serial port in raw mode.
50282498Sroberto	 */
503290001Sglebius	snprintf(device, sizeof(device), DEVICE, unit);
50482498Sroberto	fd = refclock_open(device, SPEED232, LDISC_RAW);
50582498Sroberto#endif /* HAVE_AUDIO */
506290001Sglebius
507290001Sglebius	if (fd < 0)
50882498Sroberto		return (0);
50982498Sroberto
51082498Sroberto	/*
51154359Sroberto	 * Allocate and initialize unit structure
51254359Sroberto	 */
513290001Sglebius	up = emalloc_zero(sizeof(*up));
51454359Sroberto	pp = peer->procptr;
515290001Sglebius	pp->unitptr = up;
51654359Sroberto	pp->io.clock_recv = chu_receive;
517290001Sglebius	pp->io.srcclock = peer;
51854359Sroberto	pp->io.datalen = 0;
51954359Sroberto	pp->io.fd = fd;
52054359Sroberto	if (!io_addclock(&pp->io)) {
52182498Sroberto		close(fd);
522290001Sglebius		pp->io.fd = -1;
52354359Sroberto		free(up);
524290001Sglebius		pp->unitptr = NULL;
52554359Sroberto		return (0);
52654359Sroberto	}
52754359Sroberto
52854359Sroberto	/*
52954359Sroberto	 * Initialize miscellaneous variables
53054359Sroberto	 */
53154359Sroberto	peer->precision = PRECISION;
53254359Sroberto	pp->clockdesc = DESCRIPTION;
533290001Sglebius	strlcpy(up->ident, "CHU", sizeof(up->ident));
534290001Sglebius	memcpy(&pp->refid, up->ident, 4);
53554359Sroberto	DTOLFP(CHAR, &up->charstamp);
53682498Sroberto#ifdef HAVE_AUDIO
53754359Sroberto
53854359Sroberto	/*
53954359Sroberto	 * The companded samples are encoded sign-magnitude. The table
54082498Sroberto	 * contains all the 256 values in the interest of speed. We do
54182498Sroberto	 * this even if the audio codec is not available. C'est la lazy.
54254359Sroberto	 */
54382498Sroberto	up->fd_audio = fd_audio;
54482498Sroberto	up->gain = 127;
54554359Sroberto	up->comp[0] = up->comp[OFFSET] = 0.;
54654359Sroberto	up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
54754359Sroberto	up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
54854359Sroberto	step = 2.;
54954359Sroberto	for (i = 3; i < OFFSET; i++) {
55054359Sroberto		up->comp[i] = up->comp[i - 1] + step;
55154359Sroberto		up->comp[OFFSET + i] = -up->comp[i];
55254359Sroberto                if (i % 16 == 0)
55354359Sroberto                	step *= 2.;
55454359Sroberto	}
55556746Sroberto	DTOLFP(1. / SECOND, &up->tick);
55682498Sroberto#endif /* HAVE_AUDIO */
55756746Sroberto#ifdef ICOM
55856746Sroberto	temp = 0;
55956746Sroberto#ifdef DEBUG
56056746Sroberto	if (debug > 1)
56156746Sroberto		temp = P_TRACE;
56256746Sroberto#endif
563132451Sroberto	if (peer->ttl > 0) {
564132451Sroberto		if (peer->ttl & 0x80)
56556746Sroberto			up->fd_icom = icom_init("/dev/icom", B1200,
56656746Sroberto			    temp);
56756746Sroberto		else
56856746Sroberto			up->fd_icom = icom_init("/dev/icom", B9600,
56956746Sroberto			    temp);
57056746Sroberto	}
57156746Sroberto	if (up->fd_icom > 0) {
572132451Sroberto		if (chu_newchan(peer, 0) != 0) {
573290001Sglebius			msyslog(LOG_NOTICE, "icom: radio not found");
57456746Sroberto			close(up->fd_icom);
57556746Sroberto			up->fd_icom = 0;
57656746Sroberto		} else {
577290001Sglebius			msyslog(LOG_NOTICE, "icom: autotune enabled");
57856746Sroberto		}
57956746Sroberto	}
58056746Sroberto#endif /* ICOM */
58154359Sroberto	return (1);
58254359Sroberto}
58354359Sroberto
58454359Sroberto
58554359Sroberto/*
58654359Sroberto * chu_shutdown - shut down the clock
58754359Sroberto */
58854359Srobertostatic void
58954359Srobertochu_shutdown(
59054359Sroberto	int	unit,		/* instance number (not used) */
59154359Sroberto	struct peer *peer	/* peer structure pointer */
59254359Sroberto	)
59354359Sroberto{
59454359Sroberto	struct chuunit *up;
59554359Sroberto	struct refclockproc *pp;
59654359Sroberto
59754359Sroberto	pp = peer->procptr;
598290001Sglebius	up = pp->unitptr;
59982498Sroberto	if (up == NULL)
60082498Sroberto		return;
601132451Sroberto
60254359Sroberto	io_closeclock(&pp->io);
603132451Sroberto#ifdef ICOM
60456746Sroberto	if (up->fd_icom > 0)
60556746Sroberto		close(up->fd_icom);
606132451Sroberto#endif /* ICOM */
60754359Sroberto	free(up);
60854359Sroberto}
60954359Sroberto
610132451Sroberto
61154359Sroberto/*
61282498Sroberto * chu_receive - receive data from the audio or serial device
61354359Sroberto */
61454359Srobertostatic void
61554359Srobertochu_receive(
61654359Sroberto	struct recvbuf *rbufp	/* receive buffer structure pointer */
61754359Sroberto	)
61854359Sroberto{
61982498Sroberto#ifdef HAVE_AUDIO
62054359Sroberto	struct chuunit *up;
62154359Sroberto	struct refclockproc *pp;
62254359Sroberto	struct peer *peer;
62354359Sroberto
624290001Sglebius	peer = rbufp->recv_peer;
62582498Sroberto	pp = peer->procptr;
626290001Sglebius	up = pp->unitptr;
62782498Sroberto
62882498Sroberto	/*
62982498Sroberto	 * If the audio codec is warmed up, the buffer contains codec
63082498Sroberto	 * samples which need to be demodulated and decoded into CHU
63182498Sroberto	 * characters using the software UART. Otherwise, the buffer
63282498Sroberto	 * contains CHU characters from the serial port, so the software
63382498Sroberto	 * UART is bypassed. In this case the CPU will probably run a
63482498Sroberto	 * few degrees cooler.
63582498Sroberto	 */
63682498Sroberto	if (up->fd_audio > 0)
63782498Sroberto		chu_audio_receive(rbufp);
63882498Sroberto	else
63982498Sroberto		chu_serial_receive(rbufp);
64082498Sroberto#else
64182498Sroberto	chu_serial_receive(rbufp);
64282498Sroberto#endif /* HAVE_AUDIO */
64382498Sroberto}
64482498Sroberto
645132451Sroberto
64682498Sroberto#ifdef HAVE_AUDIO
64782498Sroberto/*
64882498Sroberto * chu_audio_receive - receive data from the audio device
64982498Sroberto */
65082498Srobertostatic void
65182498Srobertochu_audio_receive(
65282498Sroberto	struct recvbuf *rbufp	/* receive buffer structure pointer */
65382498Sroberto	)
65482498Sroberto{
65582498Sroberto	struct chuunit *up;
65682498Sroberto	struct refclockproc *pp;
65782498Sroberto	struct peer *peer;
65882498Sroberto
65954359Sroberto	double	sample;		/* codec sample */
66054359Sroberto	u_char	*dpt;		/* buffer pointer */
661132451Sroberto	int	bufcnt;		/* buffer counter */
66254359Sroberto	l_fp	ltemp;		/* l_fp temp */
66354359Sroberto
664290001Sglebius	peer = rbufp->recv_peer;
66554359Sroberto	pp = peer->procptr;
666290001Sglebius	up = pp->unitptr;
66754359Sroberto
66854359Sroberto	/*
66954359Sroberto	 * Main loop - read until there ain't no more. Note codec
67054359Sroberto	 * samples are bit-inverted.
67154359Sroberto	 */
672132451Sroberto	DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
673132451Sroberto	L_SUB(&rbufp->recv_time, &ltemp);
67454359Sroberto	up->timestamp = rbufp->recv_time;
675132451Sroberto	dpt = rbufp->recv_buffer;
676132451Sroberto	for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
677132451Sroberto		sample = up->comp[~*dpt++ & 0xff];
67854359Sroberto
67954359Sroberto		/*
680182007Sroberto		 * Clip noise spikes greater than MAXAMP. If no clips,
68154359Sroberto		 * increase the gain a tad; if the clips are too high,
68254359Sroberto		 * decrease a tad.
68354359Sroberto		 */
684182007Sroberto		if (sample > MAXAMP) {
685182007Sroberto			sample = MAXAMP;
68654359Sroberto			up->clipcnt++;
687182007Sroberto		} else if (sample < -MAXAMP) {
688182007Sroberto			sample = -MAXAMP;
68954359Sroberto			up->clipcnt++;
69054359Sroberto		}
69154359Sroberto		chu_rf(peer, sample);
692132451Sroberto		L_ADD(&up->timestamp, &up->tick);
69354359Sroberto
69454359Sroberto		/*
695132451Sroberto		 * Once each second ride gain.
69654359Sroberto		 */
697132451Sroberto		up->seccnt = (up->seccnt + 1) % SECOND;
698132451Sroberto		if (up->seccnt == 0) {
699132451Sroberto			chu_gain(peer);
70054359Sroberto		}
70154359Sroberto	}
702132451Sroberto
70354359Sroberto	/*
704132451Sroberto	 * Set the input port and monitor gain for the next buffer.
70554359Sroberto	 */
706132451Sroberto	if (pp->sloppyclockflag & CLK_FLAG2)
707132451Sroberto		up->port = 2;
708132451Sroberto	else
709132451Sroberto		up->port = 1;
71054359Sroberto	if (pp->sloppyclockflag & CLK_FLAG3)
711132451Sroberto		up->mongain = MONGAIN;
712132451Sroberto	else
713132451Sroberto		up->mongain = 0;
71454359Sroberto}
71554359Sroberto
71654359Sroberto
71754359Sroberto/*
71854359Sroberto * chu_rf - filter and demodulate the FSK signal
71954359Sroberto *
72054359Sroberto * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
72154359Sroberto * and space 2025 Hz. It uses a bandpass filter followed by a soft
722290001Sglebius * limiter, FM discriminator and lowpass filter. A maximum-likelihood
72354359Sroberto * decoder samples the baseband signal at eight times the baud rate and
72454359Sroberto * detects the start bit of each character.
72554359Sroberto *
72654359Sroberto * The filters are built for speed, which explains the rather clumsy
72754359Sroberto * code. Hopefully, the compiler will efficiently implement the move-
72854359Sroberto * and-muiltiply-and-add operations.
72954359Sroberto */
73056746Srobertostatic void
73154359Srobertochu_rf(
73254359Sroberto	struct peer *peer,	/* peer structure pointer */
73354359Sroberto	double	sample		/* analog sample */
73454359Sroberto	)
73554359Sroberto{
73654359Sroberto	struct refclockproc *pp;
73754359Sroberto	struct chuunit *up;
73854359Sroberto	struct surv *sp;
73954359Sroberto
74054359Sroberto	/*
74154359Sroberto	 * Local variables
74254359Sroberto	 */
74354359Sroberto	double	signal;		/* bandpass signal */
74454359Sroberto	double	limit;		/* limiter signal */
74554359Sroberto	double	disc;		/* discriminator signal */
74654359Sroberto	double	lpf;		/* lowpass signal */
74754359Sroberto	double	dist;		/* UART signal distance */
74856746Sroberto	int	i, j;
74954359Sroberto
75054359Sroberto	pp = peer->procptr;
751290001Sglebius	up = pp->unitptr;
75256746Sroberto
75354359Sroberto	/*
75454359Sroberto	 * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
755290001Sglebius	 * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB,
756290001Sglebius	 * phase delay 0.24 ms.
75754359Sroberto	 */
75854359Sroberto	signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
75954359Sroberto	signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
76054359Sroberto	signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
76154359Sroberto	signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
76254359Sroberto	signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
76354359Sroberto	signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
76454359Sroberto	signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
76554359Sroberto	signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
76654359Sroberto	up->bpf[0] = sample - signal;
76754359Sroberto	signal = up->bpf[0] * 6.176213e-03
76854359Sroberto	    + up->bpf[1] * 3.156599e-03
76954359Sroberto	    + up->bpf[2] * 7.567487e-03
77054359Sroberto	    + up->bpf[3] * 4.344580e-03
77154359Sroberto	    + up->bpf[4] * 1.190128e-02
77254359Sroberto	    + up->bpf[5] * 4.344580e-03
77354359Sroberto	    + up->bpf[6] * 7.567487e-03
77454359Sroberto	    + up->bpf[7] * 3.156599e-03
77554359Sroberto	    + up->bpf[8] * 6.176213e-03;
77654359Sroberto
77754359Sroberto	up->monitor = signal / 4.;	/* note monitor after filter */
77854359Sroberto
77954359Sroberto	/*
78054359Sroberto	 * Soft limiter/discriminator. The 11-sample discriminator lag
78154359Sroberto	 * interval corresponds to three cycles of 2125 Hz, which
78254359Sroberto	 * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
78354359Sroberto	 * Hz. The discriminator output varies +-0.5 interval for input
78454359Sroberto	 * frequency 2025-2225 Hz. However, we don't get to sample at
78554359Sroberto	 * this frequency, so the discriminator output is biased. Life
78654359Sroberto	 * at 8000 Hz sucks.
78754359Sroberto	 */
78854359Sroberto	limit = signal;
78954359Sroberto	if (limit > LIMIT)
79054359Sroberto		limit = LIMIT;
79154359Sroberto	else if (limit < -LIMIT)
79254359Sroberto		limit = -LIMIT;
79354359Sroberto	disc = up->disc[up->discptr] * -limit;
79454359Sroberto	up->disc[up->discptr] = limit;
79554359Sroberto	up->discptr = (up->discptr + 1 ) % LAG;
79654359Sroberto	if (disc >= 0)
79782498Sroberto		disc = SQRT(disc);
79854359Sroberto	else
79982498Sroberto		disc = -SQRT(-disc);
80054359Sroberto
80154359Sroberto	/*
802290001Sglebius	 * Lowpass filter. Raised cosine FIR, Ts = 1 / 300, beta = 0.1.
80354359Sroberto	 */
80454359Sroberto	lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
80554359Sroberto	lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
80654359Sroberto	lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
80754359Sroberto	lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
80854359Sroberto	lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
80954359Sroberto	lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
81054359Sroberto	lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
81154359Sroberto	lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
81254359Sroberto	lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
81354359Sroberto	lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
81454359Sroberto	lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
81554359Sroberto	lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
81654359Sroberto	lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
81754359Sroberto	lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
81854359Sroberto	lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
81954359Sroberto	lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
82054359Sroberto	lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
82154359Sroberto	lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
82254359Sroberto	lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
82354359Sroberto	lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
82454359Sroberto	lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
82554359Sroberto	lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
82654359Sroberto	lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
82754359Sroberto	lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
82854359Sroberto	lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
82954359Sroberto	lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
83054359Sroberto	lpf += up->lpf[0] = disc * 2.538771e-02;
83156746Sroberto
83254359Sroberto	/*
833290001Sglebius	 * Maximum-likelihood decoder. The UART updates each of the
83454359Sroberto	 * eight survivors and determines the span, slice level and
83554359Sroberto	 * tentative decoded character. Valid 11-bit characters are
836290001Sglebius	 * framed so that bit 10 and bit 11 (stop bits) are mark and bit
837290001Sglebius	 * 1 (start bit) is space. When a valid character is found, the
83854359Sroberto	 * survivor with maximum distance determines the final decoded
83954359Sroberto	 * character.
84054359Sroberto	 */
84156746Sroberto	up->baud += 1. / SECOND;
84254359Sroberto	if (up->baud > 1. / (BAUD * 8.)) {
84354359Sroberto		up->baud -= 1. / (BAUD * 8.);
844290001Sglebius		up->decptr = (up->decptr + 1) % 8;
84554359Sroberto		sp = &up->surv[up->decptr];
846290001Sglebius		sp->cstamp = up->timestamp;
847290001Sglebius		chu_uart(sp, -lpf * AGAIN);
84854359Sroberto		if (up->dbrk > 0) {
84954359Sroberto			up->dbrk--;
850290001Sglebius			if (up->dbrk > 0)
851290001Sglebius				return;
852290001Sglebius
853290001Sglebius			up->decpha = up->decptr;
854290001Sglebius		}
855290001Sglebius		if (up->decptr != up->decpha)
856290001Sglebius			return;
857290001Sglebius
858290001Sglebius		dist = 0;
859290001Sglebius		j = -1;
860290001Sglebius		for (i = 0; i < 8; i++) {
861290001Sglebius
862290001Sglebius			/*
863290001Sglebius			 * The timestamp is taken at the last bit, so
864290001Sglebius			 * for correct decoding we reqire sufficient
865290001Sglebius			 * span and correct start bit and two stop bits.
866290001Sglebius			 */
867290001Sglebius			if ((up->surv[i].uart & 0x601) != 0x600 ||
868290001Sglebius			    up->surv[i].span < SPAN)
869290001Sglebius				continue;
870290001Sglebius
871290001Sglebius			if (up->surv[i].dist > dist) {
872290001Sglebius				dist = up->surv[i].dist;
873290001Sglebius				j = i;
87454359Sroberto			}
87554359Sroberto		}
876290001Sglebius		if (j < 0)
877290001Sglebius			return;
878290001Sglebius
879290001Sglebius		/*
880290001Sglebius		 * Process the character, then blank the decoder until
881290001Sglebius		 * the end of the next character.This sets the decoding
882290001Sglebius		 * phase of the entire burst from the phase of the first
883290001Sglebius		 * character.
884290001Sglebius		 */
885290001Sglebius		up->maxsignal = up->surv[j].span;
886290001Sglebius		chu_decode(peer, (up->surv[j].uart >> 1) & 0xff,
887290001Sglebius		    up->surv[j].cstamp);
888290001Sglebius		up->dbrk = 88;
88954359Sroberto	}
89054359Sroberto}
89154359Sroberto
89254359Sroberto
89354359Sroberto/*
894290001Sglebius * chu_uart - maximum-likelihood UART
89554359Sroberto *
89654359Sroberto * This routine updates a shift register holding the last 11 envelope
89754359Sroberto * samples. It then computes the slice level and span over these samples
89854359Sroberto * and determines the tentative data bits and distance. The calling
89954359Sroberto * program selects over the last eight survivors the one with maximum
90054359Sroberto * distance to determine the decoded character.
90154359Sroberto */
90256746Srobertostatic void
90354359Srobertochu_uart(
90454359Sroberto	struct surv *sp,	/* survivor structure pointer */
90554359Sroberto	double	sample		/* baseband signal */
90654359Sroberto	)
90754359Sroberto{
90856746Sroberto	double	es_max, es_min;	/* max/min envelope */
90954359Sroberto	double	slice;		/* slice level */
91054359Sroberto	double	dist;		/* distance */
91156746Sroberto	double	dtemp;
91256746Sroberto	int	i;
91354359Sroberto
91454359Sroberto	/*
91554359Sroberto	 * Save the sample and shift right. At the same time, measure
91654359Sroberto	 * the maximum and minimum over all eleven samples.
91754359Sroberto	 */
91856746Sroberto	es_max = -1e6;
91956746Sroberto	es_min = 1e6;
92054359Sroberto	sp->shift[0] = sample;
92154359Sroberto	for (i = 11; i > 0; i--) {
92254359Sroberto		sp->shift[i] = sp->shift[i - 1];
92356746Sroberto		if (sp->shift[i] > es_max)
92456746Sroberto			es_max = sp->shift[i];
92556746Sroberto		if (sp->shift[i] < es_min)
92656746Sroberto			es_min = sp->shift[i];
92754359Sroberto	}
92854359Sroberto
92954359Sroberto	/*
930290001Sglebius	 * Determine the span as the maximum less the minimum and the
931290001Sglebius	 * slice level as the minimum plus a fraction of the span. Note
932290001Sglebius	 * the slight bias toward mark to correct for the modem tendency
933290001Sglebius	 * to make more mark than space errors. Compute the distance on
934290001Sglebius	 * the assumption the last two bits must be mark, the first
935290001Sglebius	 * space and the rest either mark or space.
93654359Sroberto	 */
937290001Sglebius	sp->span = es_max - es_min;
938290001Sglebius	slice = es_min + .45 * sp->span;
93954359Sroberto	dist = 0;
94054359Sroberto	sp->uart = 0;
94154359Sroberto	for (i = 1; i < 12; i++) {
94254359Sroberto		sp->uart <<= 1;
94354359Sroberto		dtemp = sp->shift[i];
94454359Sroberto		if (dtemp > slice)
94554359Sroberto			sp->uart |= 0x1;
946290001Sglebius		if (i == 1 || i == 2) {
94756746Sroberto			dist += dtemp - es_min;
948290001Sglebius		} else if (i == 11) {
94956746Sroberto			dist += es_max - dtemp;
95054359Sroberto		} else {
95154359Sroberto			if (dtemp > slice)
95256746Sroberto				dist += dtemp - es_min;
95354359Sroberto			else
95456746Sroberto				dist += es_max - dtemp;
95554359Sroberto		}
95654359Sroberto	}
957290001Sglebius	sp->dist = dist / (11 * sp->span);
95854359Sroberto}
95982498Sroberto#endif /* HAVE_AUDIO */
96054359Sroberto
96154359Sroberto
96254359Sroberto/*
96382498Sroberto * chu_serial_receive - receive data from the serial device
96454359Sroberto */
96554359Srobertostatic void
96682498Srobertochu_serial_receive(
96754359Sroberto	struct recvbuf *rbufp	/* receive buffer structure pointer */
96854359Sroberto	)
96954359Sroberto{
97054359Sroberto	struct peer *peer;
97154359Sroberto
97254359Sroberto	u_char	*dpt;		/* receive buffer pointer */
97354359Sroberto
974290001Sglebius	peer = rbufp->recv_peer;
97554359Sroberto
97654359Sroberto	dpt = (u_char *)&rbufp->recv_space;
977290001Sglebius	chu_decode(peer, *dpt, rbufp->recv_time);
97854359Sroberto}
97954359Sroberto
98054359Sroberto
98154359Sroberto/*
98282498Sroberto * chu_decode - decode the character data
98354359Sroberto */
98454359Srobertostatic void
98554359Srobertochu_decode(
98654359Sroberto	struct peer *peer,	/* peer structure pointer */
987290001Sglebius	int	hexhex,		/* data character */
988290001Sglebius	l_fp	cstamp		/* data character timestamp */
98954359Sroberto	)
99054359Sroberto{
99154359Sroberto	struct refclockproc *pp;
99254359Sroberto	struct chuunit *up;
99354359Sroberto
99454359Sroberto	l_fp	tstmp;		/* timestamp temp */
99556746Sroberto	double	dtemp;
99654359Sroberto
99754359Sroberto	pp = peer->procptr;
998290001Sglebius	up = pp->unitptr;
99954359Sroberto
100054359Sroberto	/*
100154359Sroberto	 * If the interval since the last character is greater than the
100254359Sroberto	 * longest burst, process the last burst and start a new one. If
100354359Sroberto	 * the interval is less than this but greater than two
100454359Sroberto	 * characters, consider this a noise burst and reject it.
100554359Sroberto	 */
100654359Sroberto	tstmp = up->timestamp;
100754359Sroberto	if (L_ISZERO(&up->laststamp))
100854359Sroberto		up->laststamp = up->timestamp;
100954359Sroberto	L_SUB(&tstmp, &up->laststamp);
101054359Sroberto	up->laststamp = up->timestamp;
101154359Sroberto	LFPTOD(&tstmp, dtemp);
101254359Sroberto	if (dtemp > BURST * CHAR) {
101354359Sroberto		chu_burst(peer);
101454359Sroberto		up->ndx = 0;
101554359Sroberto	} else if (dtemp > 2.5 * CHAR) {
101654359Sroberto		up->ndx = 0;
101754359Sroberto	}
101854359Sroberto
101954359Sroberto	/*
102054359Sroberto	 * Append the character to the current burst and append the
1021290001Sglebius	 * character timestamp to the timestamp list.
102254359Sroberto	 */
102354359Sroberto	if (up->ndx < BURST) {
102454359Sroberto		up->cbuf[up->ndx] = hexhex & 0xff;
1025290001Sglebius		up->cstamp[up->ndx] = cstamp;
102654359Sroberto		up->ndx++;
102754359Sroberto
102854359Sroberto	}
102954359Sroberto}
103054359Sroberto
103154359Sroberto
103254359Sroberto/*
103354359Sroberto * chu_burst - search for valid burst format
103454359Sroberto */
103554359Srobertostatic void
103654359Srobertochu_burst(
103754359Sroberto	struct peer *peer
103854359Sroberto	)
103954359Sroberto{
104054359Sroberto	struct chuunit *up;
104154359Sroberto	struct refclockproc *pp;
104254359Sroberto
104356746Sroberto	int	i;
104454359Sroberto
104554359Sroberto	pp = peer->procptr;
1046290001Sglebius	up = pp->unitptr;
104754359Sroberto
104854359Sroberto	/*
104954359Sroberto	 * Correlate a block of five characters with the next block of
105054359Sroberto	 * five characters. The burst distance is defined as the number
105154359Sroberto	 * of bits that match in the two blocks for format A and that
105254359Sroberto	 * match the inverse for format B.
105354359Sroberto	 */
1054290001Sglebius	if (up->ndx < MINCHARS) {
105556746Sroberto		up->status |= RUNT;
105654359Sroberto		return;
105754359Sroberto	}
105854359Sroberto	up->burdist = 0;
105954359Sroberto	for (i = 0; i < 5 && i < up->ndx - 5; i++)
106054359Sroberto		up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
106154359Sroberto
106254359Sroberto	/*
106354359Sroberto	 * If the burst distance is at least MINDIST, this must be a
106454359Sroberto	 * format A burst; if the value is not greater than -MINDIST, it
106556746Sroberto	 * must be a format B burst. If the B burst is perfect, we
106656746Sroberto	 * believe it; otherwise, it is a noise burst and of no use to
106756746Sroberto	 * anybody.
106854359Sroberto	 */
106954359Sroberto	if (up->burdist >= MINDIST) {
107056746Sroberto		chu_a(peer, up->ndx);
107154359Sroberto	} else if (up->burdist <= -MINDIST) {
107256746Sroberto		chu_b(peer, up->ndx);
107354359Sroberto	} else {
107456746Sroberto		up->status |= NOISE;
107554359Sroberto		return;
107654359Sroberto	}
107754359Sroberto
107854359Sroberto	/*
107954359Sroberto	 * If this is a valid burst, wait a guard time of ten seconds to
108054359Sroberto	 * allow for more bursts, then arm the poll update routine to
108154359Sroberto	 * process the minute. Don't do this if this is called from the
108254359Sroberto	 * timer interrupt routine.
108354359Sroberto	 */
108456746Sroberto	if (peer->outdate != current_time)
108554359Sroberto		peer->nextdate = current_time + 10;
108654359Sroberto}
108754359Sroberto
108854359Sroberto
108954359Sroberto/*
109056746Sroberto * chu_b - decode format B burst
109154359Sroberto */
109254359Srobertostatic void
109356746Srobertochu_b(
109454359Sroberto	struct peer *peer,
109554359Sroberto	int	nchar
109654359Sroberto	)
109754359Sroberto{
109854359Sroberto	struct	refclockproc *pp;
109954359Sroberto	struct	chuunit *up;
110054359Sroberto
110154359Sroberto	u_char	code[11];	/* decoded timecode */
110256746Sroberto	char	tbuf[80];	/* trace buffer */
1103290001Sglebius	char *	p;
1104290001Sglebius	size_t	chars;
1105290001Sglebius	size_t	cb;
110656746Sroberto	int	i;
110754359Sroberto
110854359Sroberto	pp = peer->procptr;
1109290001Sglebius	up = pp->unitptr;
111054359Sroberto
111154359Sroberto	/*
111254359Sroberto	 * In a format B burst, a character is considered valid only if
1113290001Sglebius	 * the first occurence matches the last occurence. The burst is
1114290001Sglebius	 * considered valid only if all characters are valid; that is,
1115290001Sglebius	 * only if the distance is 40. Note that once a valid frame has
1116290001Sglebius	 * been found errors are ignored.
111754359Sroberto	 */
1118290001Sglebius	snprintf(tbuf, sizeof(tbuf), "chuB %04x %4.0f %2d %2d ",
1119290001Sglebius		 up->status, up->maxsignal, nchar, -up->burdist);
1120290001Sglebius	cb = sizeof(tbuf);
1121290001Sglebius	p = tbuf;
1122290001Sglebius	for (i = 0; i < nchar; i++) {
1123290001Sglebius		chars = strlen(p);
1124290001Sglebius		if (cb < chars + 1) {
1125290001Sglebius			msyslog(LOG_ERR, "chu_b() fatal out buffer");
1126290001Sglebius			exit(1);
1127290001Sglebius		}
1128290001Sglebius		cb -= chars;
1129290001Sglebius		p += chars;
1130290001Sglebius		snprintf(p, cb, "%02x", up->cbuf[i]);
1131290001Sglebius	}
113254359Sroberto	if (pp->sloppyclockflag & CLK_FLAG4)
113356746Sroberto		record_clock_stats(&peer->srcadr, tbuf);
113454359Sroberto#ifdef DEBUG
113556746Sroberto	if (debug)
113656746Sroberto		printf("%s\n", tbuf);
113754359Sroberto#endif
113856746Sroberto	if (up->burdist > -40) {
113956746Sroberto		up->status |= BFRAME;
114054359Sroberto		return;
114154359Sroberto	}
114254359Sroberto
114354359Sroberto	/*
1144290001Sglebius	 * Convert the burst data to internal format. Don't bother with
1145290001Sglebius	 * the timestamps.
114654359Sroberto	 */
114754359Sroberto	for (i = 0; i < 5; i++) {
114854359Sroberto		code[2 * i] = hexchar[up->cbuf[i] & 0xf];
114954359Sroberto		code[2 * i + 1] = hexchar[(up->cbuf[i] >>
115054359Sroberto		    4) & 0xf];
115154359Sroberto	}
115256746Sroberto	if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
115356746Sroberto	    &pp->year, &up->tai, &up->dst) != 5) {
115456746Sroberto		up->status |= BFORMAT;
115554359Sroberto		return;
115654359Sroberto	}
1157290001Sglebius	up->status |= BVALID;
115856746Sroberto	if (up->leap & 0x8)
115956746Sroberto		up->dut = -up->dut;
116054359Sroberto}
116154359Sroberto
116254359Sroberto
116354359Sroberto/*
116456746Sroberto * chu_a - decode format A burst
116554359Sroberto */
116654359Srobertostatic void
116756746Srobertochu_a(
116854359Sroberto	struct peer *peer,
116954359Sroberto	int nchar
117054359Sroberto	)
117154359Sroberto{
117254359Sroberto	struct refclockproc *pp;
117354359Sroberto	struct chuunit *up;
117454359Sroberto
117556746Sroberto	char	tbuf[80];	/* trace buffer */
1176290001Sglebius	char *	p;
1177290001Sglebius	size_t	chars;
1178290001Sglebius	size_t	cb;
117954359Sroberto	l_fp	offset;		/* timestamp offset */
118054359Sroberto	int	val;		/* distance */
118156746Sroberto	int	temp;
118256746Sroberto	int	i, j, k;
118354359Sroberto
118454359Sroberto	pp = peer->procptr;
1185290001Sglebius	up = pp->unitptr;
118654359Sroberto
118754359Sroberto	/*
118854359Sroberto	 * Determine correct burst phase. There are three cases
118954359Sroberto	 * corresponding to in-phase, one character early or one
119054359Sroberto	 * character late. These cases are distinguished by the position
1191290001Sglebius	 * of the framing digits 0x6 at positions 0 and 5 and 0x3 at
119254359Sroberto	 * positions 4 and 9. The correct phase is when the distance
119354359Sroberto	 * relative to the framing digits is maximum. The burst is valid
119454359Sroberto	 * only if the maximum distance is at least MINSYNC.
119554359Sroberto	 */
119654359Sroberto	up->syndist = k = 0;
1197290001Sglebius	// val = -16;
119854359Sroberto	for (i = -1; i < 2; i++) {
119954359Sroberto		temp = up->cbuf[i + 4] & 0xf;
120054359Sroberto		if (i >= 0)
120154359Sroberto			temp |= (up->cbuf[i] & 0xf) << 4;
120254359Sroberto		val = chu_dist(temp, 0x63);
120354359Sroberto		temp = (up->cbuf[i + 5] & 0xf) << 4;
120454359Sroberto		if (i + 9 < nchar)
120554359Sroberto			temp |= up->cbuf[i + 9] & 0xf;
120654359Sroberto		val += chu_dist(temp, 0x63);
120754359Sroberto		if (val > up->syndist) {
120854359Sroberto			up->syndist = val;
120954359Sroberto			k = i;
121054359Sroberto		}
121154359Sroberto	}
1212290001Sglebius
1213290001Sglebius	/*
1214290001Sglebius	 * Extract the second number; it must be in the range 2 through
1215290001Sglebius	 * 9 and the two repititions must be the same.
1216290001Sglebius	 */
121754359Sroberto	temp = (up->cbuf[k + 4] >> 4) & 0xf;
1218290001Sglebius	if (temp < 2 || temp > 9 || k + 9 >= nchar || temp !=
1219290001Sglebius	    ((up->cbuf[k + 9] >> 4) & 0xf))
122054359Sroberto		temp = 0;
1221290001Sglebius	snprintf(tbuf, sizeof(tbuf),
1222290001Sglebius		 "chuA %04x %4.0f %2d %2d %2d %2d %1d ", up->status,
1223290001Sglebius		 up->maxsignal, nchar, up->burdist, k, up->syndist,
1224290001Sglebius		 temp);
1225290001Sglebius	cb = sizeof(tbuf);
1226290001Sglebius	p = tbuf;
1227290001Sglebius	for (i = 0; i < nchar; i++) {
1228290001Sglebius		chars = strlen(p);
1229290001Sglebius		if (cb < chars + 1) {
1230290001Sglebius			msyslog(LOG_ERR, "chu_a() fatal out buffer");
1231290001Sglebius			exit(1);
1232290001Sglebius		}
1233290001Sglebius		cb -= chars;
1234290001Sglebius		p += chars;
1235290001Sglebius		snprintf(p, cb, "%02x", up->cbuf[i]);
1236290001Sglebius	}
123754359Sroberto	if (pp->sloppyclockflag & CLK_FLAG4)
123856746Sroberto		record_clock_stats(&peer->srcadr, tbuf);
123954359Sroberto#ifdef DEBUG
124056746Sroberto	if (debug)
124156746Sroberto		printf("%s\n", tbuf);
124254359Sroberto#endif
124354359Sroberto	if (up->syndist < MINSYNC) {
124456746Sroberto		up->status |= AFRAME;
124554359Sroberto		return;
124654359Sroberto	}
124754359Sroberto
124854359Sroberto	/*
124954359Sroberto	 * A valid burst requires the first seconds number to match the
125054359Sroberto	 * last seconds number. If so, the burst timestamps are
125154359Sroberto	 * corrected to the current minute and saved for later
125254359Sroberto	 * processing. In addition, the seconds decode is advanced from
125354359Sroberto	 * the previous burst to the current one.
125454359Sroberto	 */
1255290001Sglebius	if (temp == 0) {
1256290001Sglebius		up->status |= AFORMAT;
1257290001Sglebius	} else {
1258290001Sglebius		up->status |= AVALID;
1259290001Sglebius		up->second = pp->second = 30 + temp;
126054359Sroberto		offset.l_ui = 30 + temp;
1261290001Sglebius		offset.l_uf = 0;
126254359Sroberto		i = 0;
126354359Sroberto		if (k < 0)
126454359Sroberto			offset = up->charstamp;
126554359Sroberto		else if (k > 0)
126654359Sroberto			i = 1;
1267294905Sdelphij		for (; i < nchar && (i - 10) < k; i++) {
126854359Sroberto			up->tstamp[up->ntstamp] = up->cstamp[i];
126954359Sroberto			L_SUB(&up->tstamp[up->ntstamp], &offset);
127054359Sroberto			L_ADD(&offset, &up->charstamp);
1271290001Sglebius			if (up->ntstamp < MAXSTAGE - 1)
127254359Sroberto				up->ntstamp++;
127354359Sroberto		}
127454359Sroberto		while (temp > up->prevsec) {
127554359Sroberto			for (j = 15; j > 0; j--) {
127654359Sroberto				up->decode[9][j] = up->decode[9][j - 1];
127754359Sroberto				up->decode[19][j] =
127854359Sroberto				    up->decode[19][j - 1];
127954359Sroberto			}
128054359Sroberto			up->decode[9][j] = up->decode[19][j] = 0;
128154359Sroberto			up->prevsec++;
128254359Sroberto		}
128354359Sroberto	}
1284290001Sglebius
1285290001Sglebius	/*
1286290001Sglebius	 * Stash the data in the decoding matrix.
1287290001Sglebius	 */
128854359Sroberto	i = -(2 * k);
128954359Sroberto	for (j = 0; j < nchar; j++) {
1290182007Sroberto		if (i < 0 || i > 18) {
129154359Sroberto			i += 2;
129254359Sroberto			continue;
129354359Sroberto		}
129456746Sroberto		up->decode[i][up->cbuf[j] & 0xf]++;
129556746Sroberto		i++;
129656746Sroberto		up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
129756746Sroberto		i++;
129854359Sroberto	}
129954359Sroberto	up->burstcnt++;
130054359Sroberto}
130154359Sroberto
130254359Sroberto
130354359Sroberto/*
130454359Sroberto * chu_poll - called by the transmit procedure
130554359Sroberto */
130654359Srobertostatic void
130754359Srobertochu_poll(
130854359Sroberto	int unit,
130956746Sroberto	struct peer *peer	/* peer structure pointer */
131054359Sroberto	)
131154359Sroberto{
131254359Sroberto	struct refclockproc *pp;
1313290001Sglebius
1314290001Sglebius	pp = peer->procptr;
1315290001Sglebius	pp->polls++;
1316290001Sglebius}
1317290001Sglebius
1318290001Sglebius
1319290001Sglebius/*
1320290001Sglebius * chu_second - process minute data
1321290001Sglebius */
1322290001Sglebiusstatic void
1323290001Sglebiuschu_second(
1324290001Sglebius	int unit,
1325290001Sglebius	struct peer *peer	/* peer structure pointer */
1326290001Sglebius	)
1327290001Sglebius{
1328290001Sglebius	struct refclockproc *pp;
132954359Sroberto	struct chuunit *up;
1330132451Sroberto	l_fp	offset;
133156746Sroberto	char	synchar, qual, leapchar;
1332132451Sroberto	int	minset, i;
1333132451Sroberto	double	dtemp;
1334132451Sroberto
133554359Sroberto	pp = peer->procptr;
1336290001Sglebius	up = pp->unitptr;
1337132451Sroberto
1338132451Sroberto	/*
1339290001Sglebius	 * This routine is called once per minute to process the
1340290001Sglebius	 * accumulated burst data. We do a bit of fancy footwork so that
1341290001Sglebius	 * this doesn't run while burst data are being accumulated.
1342132451Sroberto	 */
1343290001Sglebius	up->second = (up->second + 1) % 60;
1344290001Sglebius	if (up->second != 0)
1345290001Sglebius		return;
134654359Sroberto
134754359Sroberto	/*
134854359Sroberto	 * Process the last burst, if still in the burst buffer.
1349290001Sglebius	 * If the minute contains a valid B frame with sufficient A
1350290001Sglebius	 * frame metric, it is considered valid. However, the timecode
1351290001Sglebius	 * is sent to clockstats even if invalid.
135254359Sroberto	 */
135354359Sroberto	chu_burst(peer);
1354290001Sglebius	minset = ((current_time - peer->update) + 30) / 60;
1355132451Sroberto	dtemp = chu_major(peer);
135656746Sroberto	qual = 0;
135756746Sroberto	if (up->status & (BFRAME | AFRAME))
135856746Sroberto		qual |= SYNERR;
135956746Sroberto	if (up->status & (BFORMAT | AFORMAT))
136056746Sroberto		qual |= FMTERR;
136156746Sroberto	if (up->status & DECODE)
136256746Sroberto		qual |= DECERR;
136356746Sroberto	if (up->status & STAMP)
136456746Sroberto		qual |= TSPERR;
1365290001Sglebius	if (up->status & BVALID && dtemp >= MINMETRIC)
1366132451Sroberto		up->status |= INSYNC;
136756746Sroberto	synchar = leapchar = ' ';
136856746Sroberto	if (!(up->status & INSYNC)) {
136956746Sroberto		pp->leap = LEAP_NOTINSYNC;
137056746Sroberto		synchar = '?';
137156746Sroberto	} else if (up->leap & 0x2) {
137256746Sroberto		pp->leap = LEAP_ADDSECOND;
137356746Sroberto		leapchar = 'L';
1374106163Sroberto	} else if (up->leap & 0x4) {
1375106163Sroberto		pp->leap = LEAP_DELSECOND;
1376106163Sroberto		leapchar = 'l';
137756746Sroberto	} else {
137856746Sroberto		pp->leap = LEAP_NOWARNING;
137954359Sroberto	}
1380290001Sglebius	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
1381290001Sglebius	    "%c%1X %04d %03d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
138256746Sroberto	    synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
1383290001Sglebius	    pp->second, leapchar, up->dst, up->dut, minset, up->gain,
1384290001Sglebius	    up->ident, dtemp, up->ntstamp);
138556746Sroberto	pp->lencode = strlen(pp->a_lastcode);
138654359Sroberto
138754359Sroberto	/*
1388132451Sroberto	 * If in sync and the signal metric is above threshold, the
1389132451Sroberto	 * timecode is ipso fatso valid and can be selected to
1390290001Sglebius	 * discipline the clock.
139156746Sroberto	 */
1392290001Sglebius	if (up->status & INSYNC && !(up->status & (DECODE | STAMP)) &&
1393290001Sglebius	    dtemp > MINMETRIC) {
1394132451Sroberto		if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
1395132451Sroberto		    up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
1396132451Sroberto			up->errflg = CEVNT_BADTIME;
1397132451Sroberto		} else {
1398132451Sroberto			offset.l_uf = 0;
1399132451Sroberto			for (i = 0; i < up->ntstamp; i++)
1400132451Sroberto				refclock_process_offset(pp, offset,
1401290001Sglebius				up->tstamp[i], PDELAY +
1402132451Sroberto				    pp->fudgetime1);
1403132451Sroberto			pp->lastref = up->timestamp;
1404132451Sroberto			refclock_receive(peer);
1405132451Sroberto		}
1406290001Sglebius	}
1407290001Sglebius	if (dtemp > 0)
140856746Sroberto		record_clock_stats(&peer->srcadr, pp->a_lastcode);
140956746Sroberto#ifdef DEBUG
141056746Sroberto	if (debug)
141156746Sroberto		printf("chu: timecode %d %s\n", pp->lencode,
141256746Sroberto		    pp->a_lastcode);
141356746Sroberto#endif
1414132451Sroberto#ifdef ICOM
1415132451Sroberto	chu_newchan(peer, dtemp);
1416132451Sroberto#endif /* ICOM */
141756746Sroberto	chu_clear(peer);
141856746Sroberto	if (up->errflg)
141956746Sroberto		refclock_report(peer, up->errflg);
142056746Sroberto	up->errflg = 0;
142156746Sroberto}
142256746Sroberto
142356746Sroberto
142456746Sroberto/*
142556746Sroberto * chu_major - majority decoder
142656746Sroberto */
1427132451Srobertostatic double
142856746Srobertochu_major(
142956746Sroberto	struct peer *peer	/* peer structure pointer */
143056746Sroberto	)
143156746Sroberto{
143256746Sroberto	struct refclockproc *pp;
143356746Sroberto	struct chuunit *up;
143456746Sroberto
143556746Sroberto	u_char	code[11];	/* decoded timecode */
1436290001Sglebius	int	metric;		/* distance metric */
1437290001Sglebius	int	val1;		/* maximum distance */
143856746Sroberto	int	synchar;	/* stray cat */
143956746Sroberto	int	temp;
144056746Sroberto	int	i, j, k;
144156746Sroberto
144256746Sroberto	pp = peer->procptr;
1443290001Sglebius	up = pp->unitptr;
144456746Sroberto
144556746Sroberto	/*
144656746Sroberto	 * Majority decoder. Each burst encodes two replications at each
144756746Sroberto	 * digit position in the timecode. Each row of the decoding
1448290001Sglebius	 * matrix encodes the number of occurences of each digit found
144956746Sroberto	 * at the corresponding position. The maximum over all
1450132451Sroberto	 * occurrences at each position is the distance for this
1451290001Sglebius	 * position and the corresponding digit is the maximum-
1452290001Sglebius	 * likelihood candidate. If the distance is not more than half
1453290001Sglebius	 * the total number of occurences, a majority has not been found
1454290001Sglebius	 * and the data are discarded. The decoding distance is defined
1455290001Sglebius	 * as the sum of the distances over the first nine digits. The
1456290001Sglebius	 * tenth digit varies over the seconds, so we don't count it.
145754359Sroberto	 */
1458290001Sglebius	metric = 0;
145956746Sroberto	for (i = 0; i < 9; i++) {
1460290001Sglebius		val1 = 0;
146154359Sroberto		k = 0;
146254359Sroberto		for (j = 0; j < 16; j++) {
146354359Sroberto			temp = up->decode[i][j] + up->decode[i + 10][j];
146454359Sroberto			if (temp > val1) {
146554359Sroberto				val1 = temp;
146654359Sroberto				k = j;
146754359Sroberto			}
146854359Sroberto		}
1469290001Sglebius		if (val1 <= up->burstcnt)
1470290001Sglebius			up->status |= DECODE;
1471290001Sglebius		metric += val1;
1472290001Sglebius		code[i] = hexchar[k];
147354359Sroberto	}
147456746Sroberto
147556746Sroberto	/*
147654359Sroberto	 * Compute the timecode timestamp from the days, hours and
147754359Sroberto	 * minutes of the timecode. Use clocktime() for the aggregate
147854359Sroberto	 * minutes and the minute offset computed from the burst
147954359Sroberto	 * seconds. Note that this code relies on the filesystem time
148054359Sroberto	 * for the years and does not use the years of the timecode.
148154359Sroberto	 */
148256746Sroberto	if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
1483290001Sglebius	    &pp->hour, &pp->minute) != 4)
1484290001Sglebius		up->status |= DECODE;
1485290001Sglebius	if (up->ntstamp < MINSTAMP)
1486290001Sglebius		up->status |= STAMP;
1487290001Sglebius	return (metric);
148854359Sroberto}
148954359Sroberto
149054359Sroberto
149154359Sroberto/*
149254359Sroberto * chu_clear - clear decoding matrix
149354359Sroberto */
149454359Srobertostatic void
149554359Srobertochu_clear(
149656746Sroberto	struct peer *peer	/* peer structure pointer */
149754359Sroberto	)
149854359Sroberto{
149954359Sroberto	struct refclockproc *pp;
150054359Sroberto	struct chuunit *up;
150156746Sroberto	int	i, j;
150254359Sroberto
150354359Sroberto	pp = peer->procptr;
1504290001Sglebius	up = pp->unitptr;
150554359Sroberto
150654359Sroberto	/*
150756746Sroberto	 * Clear stuff for the minute.
150854359Sroberto	 */
150956746Sroberto	up->ndx = up->prevsec = 0;
1510132451Sroberto	up->burstcnt = up->ntstamp = 0;
1511290001Sglebius	up->status &= INSYNC | METRIC;
151254359Sroberto	for (i = 0; i < 20; i++) {
151354359Sroberto		for (j = 0; j < 16; j++)
151454359Sroberto			up->decode[i][j] = 0;
151554359Sroberto	}
151654359Sroberto}
151754359Sroberto
1518132451Sroberto#ifdef ICOM
1519132451Sroberto/*
1520132451Sroberto * chu_newchan - called once per minute to find the best channel;
1521132451Sroberto * returns zero on success, nonzero if ICOM error.
1522132451Sroberto */
1523132451Srobertostatic int
1524132451Srobertochu_newchan(
1525132451Sroberto	struct peer *peer,
1526132451Sroberto	double	met
1527132451Sroberto	)
1528132451Sroberto{
1529132451Sroberto	struct chuunit *up;
1530132451Sroberto	struct refclockproc *pp;
1531132451Sroberto	struct xmtr *sp;
1532132451Sroberto	int	rval;
1533132451Sroberto	double	metric;
1534290001Sglebius	int	i;
153554359Sroberto
1536132451Sroberto	pp = peer->procptr;
1537290001Sglebius	up = pp->unitptr;
1538132451Sroberto
1539132451Sroberto	/*
1540132451Sroberto	 * The radio can be tuned to three channels: 0 (3330 kHz), 1
1541290001Sglebius	 * (7850 kHz) and 2 (14670 kHz). There are five one-minute
1542132451Sroberto	 * dwells in each cycle. During the first dwell the radio is
1543290001Sglebius	 * tuned to one of the three channels to measure the channel
1544290001Sglebius	 * metric. The channel is selected as the one least recently
1545290001Sglebius	 * measured. During the remaining four dwells the radio is tuned
1546290001Sglebius	 * to the channel with the highest channel metric.
1547132451Sroberto	 */
1548132451Sroberto	if (up->fd_icom <= 0)
1549132451Sroberto		return (0);
1550132451Sroberto
1551290001Sglebius	/*
1552290001Sglebius	 * Update the current channel metric and age of all channels.
1553290001Sglebius	 * Scan all channels for the highest metric.
1554290001Sglebius	 */
1555290001Sglebius	sp = &up->xmtr[up->chan];
1556132451Sroberto	sp->metric -= sp->integ[sp->iptr];
1557132451Sroberto	sp->integ[sp->iptr] = met;
1558132451Sroberto	sp->metric += sp->integ[sp->iptr];
1559290001Sglebius	sp->probe = 0;
1560132451Sroberto	sp->iptr = (sp->iptr + 1) % ISTAGE;
1561132451Sroberto	metric = 0;
1562132451Sroberto	for (i = 0; i < NCHAN; i++) {
1563132451Sroberto		up->xmtr[i].probe++;
1564290001Sglebius		if (up->xmtr[i].metric > metric) {
1565290001Sglebius			up->status |= METRIC;
1566290001Sglebius			metric = up->xmtr[i].metric;
1567290001Sglebius			up->chan = i;
1568290001Sglebius		}
1569132451Sroberto	}
1570132451Sroberto
1571132451Sroberto	/*
1572290001Sglebius	 * Start the next dwell. If the first dwell or no stations have
1573290001Sglebius	 * been heard, continue round-robin scan.
1574132451Sroberto	 */
1575290001Sglebius	up->dwell = (up->dwell + 1) % DWELL;
1576290001Sglebius	if (up->dwell == 0 || metric == 0) {
1577132451Sroberto		rval = 0;
1578132451Sroberto		for (i = 0; i < NCHAN; i++) {
1579290001Sglebius			if (up->xmtr[i].probe > rval) {
1580290001Sglebius				rval = up->xmtr[i].probe;
1581290001Sglebius				up->chan = i;
1582290001Sglebius			}
1583132451Sroberto		}
1584132451Sroberto	}
1585290001Sglebius
1586290001Sglebius	/* Retune the radio at each dwell in case somebody nudges the
1587290001Sglebius	 * tuning knob.
1588290001Sglebius	 */
1589290001Sglebius	rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan] +
1590290001Sglebius	    TUNE);
1591290001Sglebius	snprintf(up->ident, sizeof(up->ident), "CHU%d", up->chan);
1592290001Sglebius	memcpy(&pp->refid, up->ident, 4);
1593290001Sglebius	memcpy(&peer->refid, up->ident, 4);
1594290001Sglebius	if (metric == 0 && up->status & METRIC) {
1595290001Sglebius		up->status &= ~METRIC;
1596290001Sglebius		refclock_report(peer, CEVNT_PROP);
1597290001Sglebius	}
1598132451Sroberto	return (rval);
1599132451Sroberto}
1600132451Sroberto#endif /* ICOM */
1601132451Sroberto
1602290001Sglebius
160354359Sroberto/*
160454359Sroberto * chu_dist - determine the distance of two octet arguments
160554359Sroberto */
160654359Srobertostatic int
160754359Srobertochu_dist(
160854359Sroberto	int	x,		/* an octet of bits */
160954359Sroberto	int	y		/* another octet of bits */
161054359Sroberto	)
161154359Sroberto{
161254359Sroberto	int	val;		/* bit count */
161356746Sroberto	int	temp;
161456746Sroberto	int	i;
161554359Sroberto
161654359Sroberto	/*
161754359Sroberto	 * The distance is determined as the weight of the exclusive OR
161854359Sroberto	 * of the two arguments. The weight is determined by the number
161954359Sroberto	 * of one bits in the result. Each one bit increases the weight,
162054359Sroberto	 * while each zero bit decreases it.
162154359Sroberto	 */
162254359Sroberto	temp = x ^ y;
162354359Sroberto	val = 0;
162454359Sroberto	for (i = 0; i < 8; i++) {
162554359Sroberto		if ((temp & 0x1) == 0)
162654359Sroberto			val++;
162754359Sroberto		else
162854359Sroberto			val--;
162954359Sroberto		temp >>= 1;
163054359Sroberto	}
163154359Sroberto	return (val);
163254359Sroberto}
163354359Sroberto
163454359Sroberto
163582498Sroberto#ifdef HAVE_AUDIO
163654359Sroberto/*
163754359Sroberto * chu_gain - adjust codec gain
163854359Sroberto *
1639290001Sglebius * This routine is called at the end of each second. During the second
1640290001Sglebius * the number of signal clips above the MAXAMP threshold (6000). If
1641290001Sglebius * there are no clips, the gain is bumped up; if there are more than
1642290001Sglebius * MAXCLP clips (100), it is bumped down. The decoder is relatively
1643290001Sglebius * insensitive to amplitude, so this crudity works just peachy. The
1644290001Sglebius * routine also jiggles the input port and selectively mutes the
164554359Sroberto */
164654359Srobertostatic void
164754359Srobertochu_gain(
164854359Sroberto	struct peer *peer	/* peer structure pointer */
164954359Sroberto	)
165054359Sroberto{
165154359Sroberto	struct refclockproc *pp;
165254359Sroberto	struct chuunit *up;
165354359Sroberto
165454359Sroberto	pp = peer->procptr;
1655290001Sglebius	up = pp->unitptr;
165654359Sroberto
165754359Sroberto	/*
165854359Sroberto	 * Apparently, the codec uses only the high order bits of the
165954359Sroberto	 * gain control field. Thus, it may take awhile for changes to
166056746Sroberto	 * wiggle the hardware bits.
166154359Sroberto	 */
166254359Sroberto	if (up->clipcnt == 0) {
166354359Sroberto		up->gain += 4;
1664132451Sroberto		if (up->gain > MAXGAIN)
1665132451Sroberto			up->gain = MAXGAIN;
1666132451Sroberto	} else if (up->clipcnt > MAXCLP) {
166754359Sroberto		up->gain -= 4;
166856746Sroberto		if (up->gain < 0)
166956746Sroberto			up->gain = 0;
167054359Sroberto	}
1671132451Sroberto	audio_gain(up->gain, up->mongain, up->port);
167256746Sroberto	up->clipcnt = 0;
167354359Sroberto}
167482498Sroberto#endif /* HAVE_AUDIO */
167554359Sroberto
167654359Sroberto
167754359Sroberto#else
1678290001SglebiusNONEMPTY_TRANSLATION_UNIT
167954359Sroberto#endif /* REFCLOCK */
1680