xref: /freebsd/contrib/ntp/ntpd/refclock_chu.c (revision 9c2daa00)
1c0b746e5SOllivier Robert /*
2a151a66cSOllivier Robert  * refclock_chu - clock driver for Canadian CHU time/frequency station
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert #include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_CHU)
9c0b746e5SOllivier Robert 
10c0b746e5SOllivier Robert #include "ntpd.h"
11c0b746e5SOllivier Robert #include "ntp_io.h"
12c0b746e5SOllivier Robert #include "ntp_refclock.h"
13c0b746e5SOllivier Robert #include "ntp_calendar.h"
14c0b746e5SOllivier Robert #include "ntp_stdlib.h"
15224ba2bdSOllivier Robert 
16224ba2bdSOllivier Robert #include <stdio.h>
17224ba2bdSOllivier Robert #include <ctype.h>
18224ba2bdSOllivier Robert #include <math.h>
19224ba2bdSOllivier Robert 
20224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
21a151a66cSOllivier Robert #include "audio.h"
22224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
23a151a66cSOllivier Robert 
24a151a66cSOllivier Robert #define ICOM 	1		/* undefine to suppress ICOM code */
25a151a66cSOllivier Robert 
26a151a66cSOllivier Robert #ifdef ICOM
27a151a66cSOllivier Robert #include "icom.h"
28a151a66cSOllivier Robert #endif /* ICOM */
29c0b746e5SOllivier Robert 
30c0b746e5SOllivier Robert /*
31a151a66cSOllivier Robert  * Audio CHU demodulator/decoder
32c0b746e5SOllivier Robert  *
33c0b746e5SOllivier Robert  * This driver synchronizes the computer time using data encoded in
34c0b746e5SOllivier Robert  * radio transmissions from Canadian time/frequency station CHU in
35c0b746e5SOllivier Robert  * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
36c0b746e5SOllivier Robert  * 7335 kHz and 14670 kHz in upper sideband, compatible AM mode. An
37c0b746e5SOllivier Robert  * ordinary shortwave receiver can be tuned manually to one of these
38c0b746e5SOllivier Robert  * frequencies or, in the case of ICOM receivers, the receiver can be
39a151a66cSOllivier Robert  * tuned automatically using this program as propagation conditions
40a151a66cSOllivier Robert  * change throughout the day and night.
41a151a66cSOllivier Robert  *
42a151a66cSOllivier Robert  * The driver receives, demodulates and decodes the radio signals when
439c2daa00SOllivier Robert  * connected to the audio codec of a suported workstation hardware and
449c2daa00SOllivier Robert  * operating system. These include Solaris, SunOS, FreeBSD, NetBSD and
459c2daa00SOllivier Robert  * Linux. In this implementation, only one audio driver and codec can be
469c2daa00SOllivier Robert  * supported on a single machine.
47c0b746e5SOllivier Robert  *
48c0b746e5SOllivier Robert  * The driver can be compiled to use a Bell 103 compatible modem or
49c0b746e5SOllivier Robert  * modem chip to receive the radio signal and demodulate the data.
50c0b746e5SOllivier Robert  * Alternatively, the driver can be compiled to use the audio codec of
51c0b746e5SOllivier Robert  * the Sun workstation or another with compatible audio drivers. In the
52c0b746e5SOllivier Robert  * latter case, the driver implements the modem using DSP routines, so
53c0b746e5SOllivier Robert  * the radio can be connected directly to either the microphone on line
54c0b746e5SOllivier Robert  * input port. In either case, the driver decodes the data using a
55c0b746e5SOllivier Robert  * maximum likelihood technique which exploits the considerable degree
56c0b746e5SOllivier Robert  * of redundancy available to maximize accuracy and minimize errors.
57c0b746e5SOllivier Robert  *
58c0b746e5SOllivier Robert  * The CHU time broadcast includes an audio signal compatible with the
59c0b746e5SOllivier Robert  * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). It consist
60c0b746e5SOllivier Robert  * of nine, ten-character bursts transmitted at 300 bps and beginning
61c0b746e5SOllivier Robert  * each second from second 31 to second 39 of the minute. Each character
62c0b746e5SOllivier Robert  * consists of eight data bits plus one start bit and two stop bits to
63c0b746e5SOllivier Robert  * encode two hex digits. The burst data consist of five characters (ten
64c0b746e5SOllivier Robert  * hex digits) followed by a repeat of these characters. In format A,
65c0b746e5SOllivier Robert  * the characters are repeated in the same polarity; in format B, the
66c0b746e5SOllivier Robert  * characters are repeated in the opposite polarity.
67c0b746e5SOllivier Robert  *
68c0b746e5SOllivier Robert  * Format A bursts are sent at seconds 32 through 39 of the minute in
69c0b746e5SOllivier Robert  * hex digits
70c0b746e5SOllivier Robert  *
71c0b746e5SOllivier Robert  *	6dddhhmmss6dddhhmmss
72c0b746e5SOllivier Robert  *
73c0b746e5SOllivier Robert  * The first ten digits encode a frame marker (6) followed by the day
74c0b746e5SOllivier Robert  * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
75c0b746e5SOllivier Robert  * format A bursts are sent during the third decade of seconds the tens
76c0b746e5SOllivier Robert  * digit of ss is always 3. The driver uses this to determine correct
77c0b746e5SOllivier Robert  * burst synchronization. These digits are then repeated with the same
78c0b746e5SOllivier Robert  * polarity.
79c0b746e5SOllivier Robert  *
80c0b746e5SOllivier Robert  * Format B bursts are sent at second 31 of the minute in hex digits
81c0b746e5SOllivier Robert  *
82c0b746e5SOllivier Robert  *	xdyyyyttaaxdyyyyttaa
83c0b746e5SOllivier Robert  *
84c0b746e5SOllivier Robert  * The first ten digits encode a code (x described below) followed by
85c0b746e5SOllivier Robert  * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
86c0b746e5SOllivier Robert  * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
87c0b746e5SOllivier Robert  * digits are then repeated with inverted polarity.
88c0b746e5SOllivier Robert  *
89c0b746e5SOllivier Robert  * The x is coded
90c0b746e5SOllivier Robert  *
91c0b746e5SOllivier Robert  * 1 Sign of DUT (0 = +)
92c0b746e5SOllivier Robert  * 2 Leap second warning. One second will be added.
93c0b746e5SOllivier Robert  * 4 Leap second warning. One second will be subtracted.
94c0b746e5SOllivier Robert  * 8 Even parity bit for this nibble.
95c0b746e5SOllivier Robert  *
96c0b746e5SOllivier Robert  * By design, the last stop bit of the last character in the burst
97c0b746e5SOllivier Robert  * coincides with 0.5 second. Since characters have 11 bits and are
98c0b746e5SOllivier Robert  * transmitted at 300 bps, the last stop bit of the first character
99c0b746e5SOllivier Robert  * coincides with 0.5 - 10 * 11/300 = 0.133 second. Depending on the
100c0b746e5SOllivier Robert  * UART, character interrupts can vary somewhere between the beginning
101c0b746e5SOllivier Robert  * of bit 9 and end of bit 11. These eccentricities can be corrected
102c0b746e5SOllivier Robert  * along with the radio propagation delay using fudge time 1.
103c0b746e5SOllivier Robert  *
104c0b746e5SOllivier Robert  * Debugging aids
105c0b746e5SOllivier Robert  *
106c0b746e5SOllivier Robert  * The timecode format used for debugging and data recording includes
107c0b746e5SOllivier Robert  * data helpful in diagnosing problems with the radio signal and serial
1089c2daa00SOllivier Robert  * connections. With debugging enabled (-d on the ntpd command line),
1099c2daa00SOllivier Robert  * the driver produces one line for each burst in two formats
110c0b746e5SOllivier Robert  * corresponding to format A and B. Following is format A:
111c0b746e5SOllivier Robert  *
112c0b746e5SOllivier Robert  *	n b f s m code
113c0b746e5SOllivier Robert  *
114c0b746e5SOllivier Robert  * where n is the number of characters in the burst (0-11), b the burst
115c0b746e5SOllivier Robert  * distance (0-40), f the field alignment (-1, 0, 1), s the
116c0b746e5SOllivier Robert  * synchronization distance (0-16), m the burst number (2-9) and code
117c0b746e5SOllivier Robert  * the burst characters as received. Note that the hex digits in each
118c0b746e5SOllivier Robert  * character are reversed, so the burst
119c0b746e5SOllivier Robert  *
120c0b746e5SOllivier Robert  *	10 38 0 16 9 06851292930685129293
121c0b746e5SOllivier Robert  *
122c0b746e5SOllivier Robert  * is interpreted as containing 11 characters with burst distance 38,
123c0b746e5SOllivier Robert  * field alignment 0, synchronization distance 16 and burst number 9.
124c0b746e5SOllivier Robert  * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
125c0b746e5SOllivier Robert  * second 39.
126c0b746e5SOllivier Robert  *
127c0b746e5SOllivier Robert  * When the audio driver is compiled, format A is preceded by
128c0b746e5SOllivier Robert  * the current gain (0-255) and relative signal level (0-9999). The
129c0b746e5SOllivier Robert  * receiver folume control should be set so that the gain is somewhere
130c0b746e5SOllivier Robert  * near the middle of the range 0-255, which results in a signal level
131c0b746e5SOllivier Robert  * near 1000.
132c0b746e5SOllivier Robert  *
133c0b746e5SOllivier Robert  * Following is format B:
134c0b746e5SOllivier Robert  *
135c0b746e5SOllivier Robert  *	n b s code
136c0b746e5SOllivier Robert  *
137c0b746e5SOllivier Robert  * where n is the number of characters in the burst (0-11), b the burst
138c0b746e5SOllivier Robert  * distance (0-40), s the synchronization distance (0-40) and code the
139c0b746e5SOllivier Robert  * burst characters as received. Note that the hex digits in each
140c0b746e5SOllivier Robert  * character are reversed and the last ten digits inverted, so the burst
141c0b746e5SOllivier Robert  *
142c0b746e5SOllivier Robert  *	11 40 1091891300ef6e76ecff
143c0b746e5SOllivier Robert  *
144c0b746e5SOllivier Robert  * is interpreted as containing 11 characters with burst distance 40.
145c0b746e5SOllivier Robert  * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
146c0b746e5SOllivier Robert  * - UTC 31 seconds.
147c0b746e5SOllivier Robert  *
148c0b746e5SOllivier Robert  * In addition to the above, the reference timecode is updated and
149c0b746e5SOllivier Robert  * written to the clockstats file and debug score after the last burst
150c0b746e5SOllivier Robert  * received in the minute. The format is
151c0b746e5SOllivier Robert  *
152c0b746e5SOllivier Robert  *	qq yyyy ddd hh:mm:ss nn dd tt
153c0b746e5SOllivier Robert  *
154c0b746e5SOllivier Robert  * where qq are the error flags, as described below, yyyy is the year,
155c0b746e5SOllivier Robert  * ddd the day, hh:mm:ss the time of day, nn the number of format A
156c0b746e5SOllivier Robert  * bursts received during the previous minute, dd the decoding distance
157c0b746e5SOllivier Robert  * and tt the number of timestamps. The error flags are cleared after
158c0b746e5SOllivier Robert  * every update.
159c0b746e5SOllivier Robert  *
160c0b746e5SOllivier Robert  * Fudge factors
161c0b746e5SOllivier Robert  *
162c0b746e5SOllivier Robert  * For accuracies better than the low millisceconds, fudge time1 can be
163c0b746e5SOllivier Robert  * set to the radio propagation delay from CHU to the receiver. This can
1649c2daa00SOllivier Robert  * be done conviently using the minimuf program.
165c0b746e5SOllivier Robert  *
1669c2daa00SOllivier Robert  * Fudge flag4 causes the dubugging output described above to be
1679c2daa00SOllivier Robert  * recorded in the clockstats file. When the audio driver is compiled,
1689c2daa00SOllivier Robert  * fudge flag2 selects the audio input port, where 0 is the mike port
1699c2daa00SOllivier Robert  * (default) and 1 is the line-in port. It does not seem useful to
1709c2daa00SOllivier Robert  * select the compact disc player port. Fudge flag3 enables audio
1719c2daa00SOllivier Robert  * monitoring of the input signal. For this purpose, the monitor gain is
1729c2daa00SOllivier Robert  * set to a default value.
173a151a66cSOllivier Robert  *
174224ba2bdSOllivier Robert  * The audio codec code is normally compiled in the driver if the
1759c2daa00SOllivier Robert  * architecture supports it (HAVE_AUDIO defined), but is used only if
1769c2daa00SOllivier Robert  * the link /dev/chu_audio is defined and valid. The serial port code is
1779c2daa00SOllivier Robert  * always compiled in the driver, but is used only if the autdio codec
1789c2daa00SOllivier Robert  * is not available and the link /dev/chu%d is defined and valid.
1799c2daa00SOllivier Robert  *
180224ba2bdSOllivier Robert  * The ICOM code is normally compiled in the driver if selected (ICOM
181224ba2bdSOllivier Robert  * defined), but is used only if the link /dev/icom%d is defined and
182224ba2bdSOllivier Robert  * valid and the mode keyword on the server configuration command
183224ba2bdSOllivier Robert  * specifies a nonzero mode (ICOM ID select code). The C-IV speed is
184224ba2bdSOllivier Robert  * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps
185224ba2bdSOllivier Robert  * if one. The C-IV trace is turned on if the debug level is greater
186224ba2bdSOllivier Robert  * than one.
187c0b746e5SOllivier Robert  */
188c0b746e5SOllivier Robert /*
189c0b746e5SOllivier Robert  * Interface definitions
190c0b746e5SOllivier Robert  */
191c0b746e5SOllivier Robert #define	SPEED232	B300	/* uart speed (300 baud) */
192c0b746e5SOllivier Robert #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
193c0b746e5SOllivier Robert #define	REFID		"CHU"	/* reference ID */
194224ba2bdSOllivier Robert #define	DEVICE		"/dev/chu%d" /* device name and unit */
195224ba2bdSOllivier Robert #define	SPEED232	B300	/* UART speed (300 baud) */
196a151a66cSOllivier Robert #ifdef ICOM
1979c2daa00SOllivier Robert #define TUNE		.001	/* offset for narrow filter (kHz) */
1989c2daa00SOllivier Robert #define DWELL		5	/* minutes in a probe cycle */
199a151a66cSOllivier Robert #define NCHAN		3	/* number of channels */
2009c2daa00SOllivier Robert #define ISTAGE		3	/* number of integrator stages */
201a151a66cSOllivier Robert #endif /* ICOM */
202c0b746e5SOllivier Robert 
2039c2daa00SOllivier Robert #ifdef HAVE_AUDIO
204c0b746e5SOllivier Robert /*
205c0b746e5SOllivier Robert  * Audio demodulator definitions
206c0b746e5SOllivier Robert  */
207a151a66cSOllivier Robert #define SECOND		8000	/* nominal sample rate (Hz) */
208c0b746e5SOllivier Robert #define BAUD		300	/* modulation rate (bps) */
209c0b746e5SOllivier Robert #define OFFSET		128	/* companded sample offset */
210c0b746e5SOllivier Robert #define SIZE		256	/* decompanding table size */
211c0b746e5SOllivier Robert #define	MAXSIG		6000.	/* maximum signal level */
2129c2daa00SOllivier Robert #define	MAXCLP		100	/* max clips above reference per s */
213c0b746e5SOllivier Robert #define LIMIT		1000.	/* soft limiter threshold */
214c0b746e5SOllivier Robert #define AGAIN		6.	/* baseband gain */
215c0b746e5SOllivier Robert #define LAG		10	/* discriminator lag */
216224ba2bdSOllivier Robert #define	DEVICE_AUDIO	"/dev/chu_audio" /* device name */
217224ba2bdSOllivier Robert #define	DESCRIPTION	"CHU Audio/Modem Receiver" /* WRU */
2189c2daa00SOllivier Robert #define	AUDIO_BUFSIZ	240	/* audio buffer size (30 ms) */
219c0b746e5SOllivier Robert #else
220224ba2bdSOllivier Robert #define	DESCRIPTION	"CHU Modem Receiver" /* WRU */
221224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
222c0b746e5SOllivier Robert 
223c0b746e5SOllivier Robert /*
224c0b746e5SOllivier Robert  * Decoder definitions
225c0b746e5SOllivier Robert  */
226c0b746e5SOllivier Robert #define CHAR		(11. / 300.) /* character time (s) */
227c0b746e5SOllivier Robert #define	FUDGE		.185	/* offset to first stop bit (s) */
228c0b746e5SOllivier Robert #define BURST		11	/* max characters per burst */
229c0b746e5SOllivier Robert #define MINCHAR		9	/* min characters per burst */
230c0b746e5SOllivier Robert #define MINDIST		28	/* min burst distance (of 40)  */
2319c2daa00SOllivier Robert #define MINBURST	4	/* min bursts in minute */
232c0b746e5SOllivier Robert #define MINSYNC		8	/* min sync distance (of 16) */
233c0b746e5SOllivier Robert #define MINSTAMP	20	/* min timestamps (of 60) */
2349c2daa00SOllivier Robert #define METRIC		50.	/* min channel metric */
2359c2daa00SOllivier Robert #define PANIC		1440	/* panic timeout (m) */
2369c2daa00SOllivier Robert #define HOLD		30	/* reach hold (m) */
237c0b746e5SOllivier Robert 
238c0b746e5SOllivier Robert /*
239c0b746e5SOllivier Robert  * Hex extension codes (>= 16)
240c0b746e5SOllivier Robert  */
2419c2daa00SOllivier Robert #define HEX_MISS	16	/* miss _ */
2429c2daa00SOllivier Robert #define HEX_SOFT	17	/* soft error * */
2439c2daa00SOllivier Robert #define HEX_HARD	18	/* hard error = */
244c0b746e5SOllivier Robert 
245c0b746e5SOllivier Robert /*
246a151a66cSOllivier Robert  * Status bits (status)
247c0b746e5SOllivier Robert  */
248a151a66cSOllivier Robert #define RUNT		0x0001	/* runt burst */
249a151a66cSOllivier Robert #define NOISE		0x0002	/* noise burst */
250a151a66cSOllivier Robert #define BFRAME		0x0004	/* invalid format B frame sync */
251a151a66cSOllivier Robert #define BFORMAT		0x0008	/* invalid format B data */
252a151a66cSOllivier Robert #define AFRAME		0x0010	/* invalid format A frame sync */
253a151a66cSOllivier Robert #define AFORMAT		0x0020	/* invalid format A data */
254a151a66cSOllivier Robert #define DECODE		0x0040	/* invalid data decode */
255a151a66cSOllivier Robert #define STAMP		0x0080	/* too few timestamps */
2569c2daa00SOllivier Robert #define AVALID		0x0100	/* valid A frame */
2579c2daa00SOllivier Robert #define BVALID		0x0200	/* valid B frame */
2589c2daa00SOllivier Robert #define INSYNC		0x0400	/* clock synchronized */
259a151a66cSOllivier Robert 
260a151a66cSOllivier Robert /*
261a151a66cSOllivier Robert  * Alarm status bits (alarm)
262a151a66cSOllivier Robert  *
263a151a66cSOllivier Robert  * These alarms are set at the end of a minute in which at least one
264a151a66cSOllivier Robert  * burst was received. SYNERR is raised if the AFRAME or BFRAME status
265a151a66cSOllivier Robert  * bits are set during the minute, FMTERR is raised if the AFORMAT or
266a151a66cSOllivier Robert  * BFORMAT status bits are set, DECERR is raised if the DECODE status
267a151a66cSOllivier Robert  * bit is set and TSPERR is raised if the STAMP status bit is set.
268a151a66cSOllivier Robert  */
269a151a66cSOllivier Robert #define SYNERR		0x01	/* frame sync error */
270a151a66cSOllivier Robert #define FMTERR		0x02	/* data format error */
271a151a66cSOllivier Robert #define DECERR		0x04	/* data decoding error */
272a151a66cSOllivier Robert #define TSPERR		0x08	/* insufficient data */
273c0b746e5SOllivier Robert 
274224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
2759c2daa00SOllivier Robert /*
2769c2daa00SOllivier Robert  * Maximum likelihood UART structure. There are eight of these
2779c2daa00SOllivier Robert  * corresponding to the number of phases.
2789c2daa00SOllivier Robert  */
279c0b746e5SOllivier Robert struct surv {
280c0b746e5SOllivier Robert 	double	shift[12];	/* mark register */
281a151a66cSOllivier Robert 	double	es_max, es_min;	/* max/min envelope signals */
282c0b746e5SOllivier Robert 	double	dist;		/* sample distance */
283c0b746e5SOllivier Robert 	int	uart;		/* decoded character */
284c0b746e5SOllivier Robert };
285224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
286c0b746e5SOllivier Robert 
2879c2daa00SOllivier Robert #ifdef ICOM
2889c2daa00SOllivier Robert /*
2899c2daa00SOllivier Robert  * CHU station structure. There are three of these corresponding to the
2909c2daa00SOllivier Robert  * three frequencies.
2919c2daa00SOllivier Robert  */
2929c2daa00SOllivier Robert struct xmtr {
2939c2daa00SOllivier Robert 	double	integ[ISTAGE];	/* circular integrator */
2949c2daa00SOllivier Robert 	double	metric;		/* integrator sum */
2959c2daa00SOllivier Robert 	int	iptr;		/* integrator pointer */
2969c2daa00SOllivier Robert 	int	probe;		/* dwells since last probe */
2979c2daa00SOllivier Robert };
2989c2daa00SOllivier Robert #endif /* ICOM */
2999c2daa00SOllivier Robert 
300c0b746e5SOllivier Robert /*
301c0b746e5SOllivier Robert  * CHU unit control structure
302c0b746e5SOllivier Robert  */
303c0b746e5SOllivier Robert struct chuunit {
304c0b746e5SOllivier Robert 	u_char	decode[20][16];	/* maximum likelihood decoding matrix */
305c0b746e5SOllivier Robert 	l_fp	cstamp[BURST];	/* character timestamps */
306c0b746e5SOllivier Robert 	l_fp	tstamp[MAXSTAGE]; /* timestamp samples */
307c0b746e5SOllivier Robert 	l_fp	timestamp;	/* current buffer timestamp */
308c0b746e5SOllivier Robert 	l_fp	laststamp;	/* last buffer timestamp */
309c0b746e5SOllivier Robert 	l_fp	charstamp;	/* character time as a l_fp */
310c0b746e5SOllivier Robert 	int	errflg;		/* error flags */
311a151a66cSOllivier Robert 	int	status;		/* status bits */
3129c2daa00SOllivier Robert 	char	ident[5];	/* station ID and channel */
313a151a66cSOllivier Robert #ifdef ICOM
314224ba2bdSOllivier Robert 	int	fd_icom;	/* ICOM file descriptor */
3159c2daa00SOllivier Robert 	int	chan;		/* data channel */
3169c2daa00SOllivier Robert 	int	achan;		/* active channel */
3179c2daa00SOllivier Robert 	int	dwell;		/* dwell cycle */
3189c2daa00SOllivier Robert 	struct xmtr xmtr[NCHAN]; /* station metric */
319a151a66cSOllivier Robert #endif /* ICOM */
320c0b746e5SOllivier Robert 
321c0b746e5SOllivier Robert 	/*
322c0b746e5SOllivier Robert 	 * Character burst variables
323c0b746e5SOllivier Robert 	 */
324c0b746e5SOllivier Robert 	int	cbuf[BURST];	/* character buffer */
325c0b746e5SOllivier Robert 	int	ntstamp;	/* number of timestamp samples */
326c0b746e5SOllivier Robert 	int	ndx;		/* buffer start index */
327c0b746e5SOllivier Robert 	int	prevsec;	/* previous burst second */
328c0b746e5SOllivier Robert 	int	burdist;	/* burst distance */
329c0b746e5SOllivier Robert 	int	syndist;	/* sync distance */
330c0b746e5SOllivier Robert 	int	burstcnt;	/* format A bursts this minute */
331c0b746e5SOllivier Robert 
332a151a66cSOllivier Robert 	/*
333a151a66cSOllivier Robert 	 * Format particulars
334a151a66cSOllivier Robert 	 */
335a151a66cSOllivier Robert 	int	leap;		/* leap/dut code */
336a151a66cSOllivier Robert 	int	dut;		/* UTC1 correction */
337a151a66cSOllivier Robert 	int	tai;		/* TAI - UTC correction */
338a151a66cSOllivier Robert 	int	dst;		/* Canadian DST code */
339a151a66cSOllivier Robert 
340224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
341c0b746e5SOllivier Robert 	/*
342c0b746e5SOllivier Robert 	 * Audio codec variables
343c0b746e5SOllivier Robert 	 */
344224ba2bdSOllivier Robert 	int	fd_audio;	/* audio port file descriptor */
345c0b746e5SOllivier Robert 	double	comp[SIZE];	/* decompanding table */
346c0b746e5SOllivier Robert 	int	port;		/* codec port */
347c0b746e5SOllivier Robert 	int	gain;		/* codec gain */
3489c2daa00SOllivier Robert 	int	mongain;	/* codec monitor gain */
349c0b746e5SOllivier Robert 	int	clipcnt;	/* sample clip count */
350c0b746e5SOllivier Robert 	int	seccnt;		/* second interval counter */
351c0b746e5SOllivier Robert 
352c0b746e5SOllivier Robert 	/*
353c0b746e5SOllivier Robert 	 * Modem variables
354c0b746e5SOllivier Robert 	 */
355c0b746e5SOllivier Robert 	l_fp	tick;		/* audio sample increment */
356c0b746e5SOllivier Robert 	double	bpf[9];		/* IIR bandpass filter */
357c0b746e5SOllivier Robert 	double	disc[LAG];	/* discriminator shift register */
358c0b746e5SOllivier Robert 	double	lpf[27];	/* FIR lowpass filter */
359c0b746e5SOllivier Robert 	double	monitor;	/* audio monitor */
360c0b746e5SOllivier Robert 	double	maxsignal;	/* signal level */
361c0b746e5SOllivier Robert 	int	discptr;	/* discriminator pointer */
362c0b746e5SOllivier Robert 
363c0b746e5SOllivier Robert 	/*
364c0b746e5SOllivier Robert 	 * Maximum likelihood UART variables
365c0b746e5SOllivier Robert 	 */
366c0b746e5SOllivier Robert 	double	baud;		/* baud interval */
367c0b746e5SOllivier Robert 	struct surv surv[8];	/* UART survivor structures */
368c0b746e5SOllivier Robert 	int	decptr;		/* decode pointer */
369c0b746e5SOllivier Robert 	int	dbrk;		/* holdoff counter */
370224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
371c0b746e5SOllivier Robert };
372c0b746e5SOllivier Robert 
373c0b746e5SOllivier Robert /*
374c0b746e5SOllivier Robert  * Function prototypes
375c0b746e5SOllivier Robert  */
376c0b746e5SOllivier Robert static	int	chu_start	P((int, struct peer *));
377c0b746e5SOllivier Robert static	void	chu_shutdown	P((int, struct peer *));
378c0b746e5SOllivier Robert static	void	chu_receive	P((struct recvbuf *));
379c0b746e5SOllivier Robert static	void	chu_poll	P((int, struct peer *));
380c0b746e5SOllivier Robert 
381c0b746e5SOllivier Robert /*
382c0b746e5SOllivier Robert  * More function prototypes
383c0b746e5SOllivier Robert  */
384c0b746e5SOllivier Robert static	void	chu_decode	P((struct peer *, int));
385c0b746e5SOllivier Robert static	void	chu_burst	P((struct peer *));
386c0b746e5SOllivier Robert static	void	chu_clear	P((struct peer *));
387a151a66cSOllivier Robert static	void	chu_a		P((struct peer *, int));
388a151a66cSOllivier Robert static	void	chu_b		P((struct peer *, int));
389c0b746e5SOllivier Robert static	int	chu_dist	P((int, int));
3909c2daa00SOllivier Robert static	double	chu_major	P((struct peer *));
391224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
392c0b746e5SOllivier Robert static	void	chu_uart	P((struct surv *, double));
393c0b746e5SOllivier Robert static	void	chu_rf		P((struct peer *, double));
394c0b746e5SOllivier Robert static	void	chu_gain	P((struct peer *));
395224ba2bdSOllivier Robert static	void	chu_audio_receive P((struct recvbuf *rbufp));
396224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
3979c2daa00SOllivier Robert #ifdef ICOM
3989c2daa00SOllivier Robert static	int	chu_newchan	P((struct peer *, double));
3999c2daa00SOllivier Robert #endif /* ICOM */
400224ba2bdSOllivier Robert static	void	chu_serial_receive P((struct recvbuf *rbufp));
401c0b746e5SOllivier Robert 
402c0b746e5SOllivier Robert /*
403c0b746e5SOllivier Robert  * Global variables
404c0b746e5SOllivier Robert  */
4059c2daa00SOllivier Robert static char hexchar[] = "0123456789abcdef_*=";
4069c2daa00SOllivier Robert 
407a151a66cSOllivier Robert #ifdef ICOM
4089c2daa00SOllivier Robert /*
4099c2daa00SOllivier Robert  * Note the tuned frequencies are 1 kHz higher than the carrier. CHU
4109c2daa00SOllivier Robert  * transmits on USB with carrier so we can use AM and the narrow SSB
4119c2daa00SOllivier Robert  * filter.
4129c2daa00SOllivier Robert  */
4139c2daa00SOllivier Robert static double qsy[NCHAN] = {3.330, 7.335, 14.670}; /* freq (MHz) */
414a151a66cSOllivier Robert #endif /* ICOM */
415c0b746e5SOllivier Robert 
416c0b746e5SOllivier Robert /*
417c0b746e5SOllivier Robert  * Transfer vector
418c0b746e5SOllivier Robert  */
419c0b746e5SOllivier Robert struct	refclock refclock_chu = {
420c0b746e5SOllivier Robert 	chu_start,		/* start up driver */
421c0b746e5SOllivier Robert 	chu_shutdown,		/* shut down driver */
422c0b746e5SOllivier Robert 	chu_poll,		/* transmit poll message */
423c0b746e5SOllivier Robert 	noentry,		/* not used (old chu_control) */
424c0b746e5SOllivier Robert 	noentry,		/* initialize driver (not used) */
425c0b746e5SOllivier Robert 	noentry,		/* not used (old chu_buginfo) */
426c0b746e5SOllivier Robert 	NOFLAGS			/* not used */
427c0b746e5SOllivier Robert };
428c0b746e5SOllivier Robert 
429c0b746e5SOllivier Robert 
430c0b746e5SOllivier Robert /*
431c0b746e5SOllivier Robert  * chu_start - open the devices and initialize data for processing
432c0b746e5SOllivier Robert  */
433c0b746e5SOllivier Robert static int
434c0b746e5SOllivier Robert chu_start(
435c0b746e5SOllivier Robert 	int	unit,		/* instance number (not used) */
436c0b746e5SOllivier Robert 	struct peer *peer	/* peer structure pointer */
437c0b746e5SOllivier Robert 	)
438c0b746e5SOllivier Robert {
439c0b746e5SOllivier Robert 	struct chuunit *up;
440c0b746e5SOllivier Robert 	struct refclockproc *pp;
441224ba2bdSOllivier Robert 	char device[20];	/* device name */
442c0b746e5SOllivier Robert 	int	fd;		/* file descriptor */
443a151a66cSOllivier Robert #ifdef ICOM
444a151a66cSOllivier Robert 	int	temp;
445a151a66cSOllivier Robert #endif /* ICOM */
446224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
447224ba2bdSOllivier Robert 	int	fd_audio;	/* audio port file descriptor */
448c0b746e5SOllivier Robert 	int	i;		/* index */
449c0b746e5SOllivier Robert 	double	step;		/* codec adjustment */
450c0b746e5SOllivier Robert 
451c0b746e5SOllivier Robert 	/*
452224ba2bdSOllivier Robert 	 * Open audio device.
453c0b746e5SOllivier Robert 	 */
4549c2daa00SOllivier Robert 	fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
455a151a66cSOllivier Robert #ifdef DEBUG
456224ba2bdSOllivier Robert 	if (fd_audio > 0 && debug)
457a151a66cSOllivier Robert 		audio_show();
458a151a66cSOllivier Robert #endif
459c0b746e5SOllivier Robert 
460c0b746e5SOllivier Robert 	/*
461a151a66cSOllivier Robert 	 * Open serial port in raw mode.
462c0b746e5SOllivier Robert 	 */
463224ba2bdSOllivier Robert 	if (fd_audio > 0) {
464224ba2bdSOllivier Robert 		fd = fd_audio;
465224ba2bdSOllivier Robert 	} else {
466224ba2bdSOllivier Robert 		sprintf(device, DEVICE, unit);
467224ba2bdSOllivier Robert 		fd = refclock_open(device, SPEED232, LDISC_RAW);
468c0b746e5SOllivier Robert 	}
469224ba2bdSOllivier Robert #else /* HAVE_AUDIO */
470224ba2bdSOllivier Robert 
471224ba2bdSOllivier Robert 	/*
472224ba2bdSOllivier Robert 	 * Open serial port in raw mode.
473224ba2bdSOllivier Robert 	 */
474224ba2bdSOllivier Robert 	sprintf(device, DEVICE, unit);
475224ba2bdSOllivier Robert 	fd = refclock_open(device, SPEED232, LDISC_RAW);
476224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
477224ba2bdSOllivier Robert 	if (fd <= 0)
478224ba2bdSOllivier Robert 		return (0);
479c0b746e5SOllivier Robert 
480c0b746e5SOllivier Robert 	/*
481c0b746e5SOllivier Robert 	 * Allocate and initialize unit structure
482c0b746e5SOllivier Robert 	 */
483c0b746e5SOllivier Robert 	if (!(up = (struct chuunit *)
484c0b746e5SOllivier Robert 	      emalloc(sizeof(struct chuunit)))) {
485224ba2bdSOllivier Robert 		close(fd);
486c0b746e5SOllivier Robert 		return (0);
487c0b746e5SOllivier Robert 	}
488c0b746e5SOllivier Robert 	memset((char *)up, 0, sizeof(struct chuunit));
489c0b746e5SOllivier Robert 	pp = peer->procptr;
490c0b746e5SOllivier Robert 	pp->unitptr = (caddr_t)up;
491c0b746e5SOllivier Robert 	pp->io.clock_recv = chu_receive;
492c0b746e5SOllivier Robert 	pp->io.srcclock = (caddr_t)peer;
493c0b746e5SOllivier Robert 	pp->io.datalen = 0;
494c0b746e5SOllivier Robert 	pp->io.fd = fd;
495c0b746e5SOllivier Robert 	if (!io_addclock(&pp->io)) {
496224ba2bdSOllivier Robert 		close(fd);
497c0b746e5SOllivier Robert 		free(up);
498c0b746e5SOllivier Robert 		return (0);
499c0b746e5SOllivier Robert 	}
500c0b746e5SOllivier Robert 
501c0b746e5SOllivier Robert 	/*
502c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
503c0b746e5SOllivier Robert 	 */
504c0b746e5SOllivier Robert 	peer->precision = PRECISION;
505c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
5069c2daa00SOllivier Robert 	strcpy(up->ident, "CHU");
5079c2daa00SOllivier Robert 	memcpy(&peer->refid, up->ident, 4);
508c0b746e5SOllivier Robert 	DTOLFP(CHAR, &up->charstamp);
509224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
510c0b746e5SOllivier Robert 
511c0b746e5SOllivier Robert 	/*
512c0b746e5SOllivier Robert 	 * The companded samples are encoded sign-magnitude. The table
513224ba2bdSOllivier Robert 	 * contains all the 256 values in the interest of speed. We do
514224ba2bdSOllivier Robert 	 * this even if the audio codec is not available. C'est la lazy.
515c0b746e5SOllivier Robert 	 */
516224ba2bdSOllivier Robert 	up->fd_audio = fd_audio;
517224ba2bdSOllivier Robert 	up->gain = 127;
518c0b746e5SOllivier Robert 	up->comp[0] = up->comp[OFFSET] = 0.;
519c0b746e5SOllivier Robert 	up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
520c0b746e5SOllivier Robert 	up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
521c0b746e5SOllivier Robert 	step = 2.;
522c0b746e5SOllivier Robert 	for (i = 3; i < OFFSET; i++) {
523c0b746e5SOllivier Robert 		up->comp[i] = up->comp[i - 1] + step;
524c0b746e5SOllivier Robert 		up->comp[OFFSET + i] = -up->comp[i];
525c0b746e5SOllivier Robert                 if (i % 16 == 0)
526c0b746e5SOllivier Robert                 	step *= 2.;
527c0b746e5SOllivier Robert 	}
528a151a66cSOllivier Robert 	DTOLFP(1. / SECOND, &up->tick);
529224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
530a151a66cSOllivier Robert #ifdef ICOM
531a151a66cSOllivier Robert 	temp = 0;
532a151a66cSOllivier Robert #ifdef DEBUG
533a151a66cSOllivier Robert 	if (debug > 1)
534a151a66cSOllivier Robert 		temp = P_TRACE;
535a151a66cSOllivier Robert #endif
5369c2daa00SOllivier Robert 	if (peer->ttl > 0) {
5379c2daa00SOllivier Robert 		if (peer->ttl & 0x80)
538a151a66cSOllivier Robert 			up->fd_icom = icom_init("/dev/icom", B1200,
539a151a66cSOllivier Robert 			    temp);
540a151a66cSOllivier Robert 		else
541a151a66cSOllivier Robert 			up->fd_icom = icom_init("/dev/icom", B9600,
542a151a66cSOllivier Robert 			    temp);
543a151a66cSOllivier Robert 	}
544a151a66cSOllivier Robert 	if (up->fd_icom > 0) {
5459c2daa00SOllivier Robert 		if (chu_newchan(peer, 0) != 0) {
546a151a66cSOllivier Robert 			NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
5479c2daa00SOllivier Robert 			    msyslog(LOG_NOTICE,
5489c2daa00SOllivier Robert 			    "icom: radio not found");
549a151a66cSOllivier Robert 			up->errflg = CEVNT_FAULT;
550a151a66cSOllivier Robert 			close(up->fd_icom);
551a151a66cSOllivier Robert 			up->fd_icom = 0;
552a151a66cSOllivier Robert 		} else {
5539c2daa00SOllivier Robert 			NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
5549c2daa00SOllivier Robert 			    msyslog(LOG_NOTICE,
5559c2daa00SOllivier Robert 			    "icom: autotune enabled");
556a151a66cSOllivier Robert 		}
557a151a66cSOllivier Robert 	}
558a151a66cSOllivier Robert #endif /* ICOM */
559c0b746e5SOllivier Robert 	return (1);
560c0b746e5SOllivier Robert }
561c0b746e5SOllivier Robert 
562c0b746e5SOllivier Robert 
563c0b746e5SOllivier Robert /*
564c0b746e5SOllivier Robert  * chu_shutdown - shut down the clock
565c0b746e5SOllivier Robert  */
566c0b746e5SOllivier Robert static void
567c0b746e5SOllivier Robert chu_shutdown(
568c0b746e5SOllivier Robert 	int	unit,		/* instance number (not used) */
569c0b746e5SOllivier Robert 	struct peer *peer	/* peer structure pointer */
570c0b746e5SOllivier Robert 	)
571c0b746e5SOllivier Robert {
572c0b746e5SOllivier Robert 	struct chuunit *up;
573c0b746e5SOllivier Robert 	struct refclockproc *pp;
574c0b746e5SOllivier Robert 
575c0b746e5SOllivier Robert 	pp = peer->procptr;
576c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
577224ba2bdSOllivier Robert 	if (up == NULL)
578224ba2bdSOllivier Robert 		return;
5799c2daa00SOllivier Robert 
580c0b746e5SOllivier Robert 	io_closeclock(&pp->io);
5819c2daa00SOllivier Robert #ifdef ICOM
582a151a66cSOllivier Robert 	if (up->fd_icom > 0)
583a151a66cSOllivier Robert 		close(up->fd_icom);
5849c2daa00SOllivier Robert #endif /* ICOM */
585c0b746e5SOllivier Robert 	free(up);
586c0b746e5SOllivier Robert }
587c0b746e5SOllivier Robert 
5889c2daa00SOllivier Robert 
589c0b746e5SOllivier Robert /*
590224ba2bdSOllivier Robert  * chu_receive - receive data from the audio or serial device
591c0b746e5SOllivier Robert  */
592c0b746e5SOllivier Robert static void
593c0b746e5SOllivier Robert chu_receive(
594c0b746e5SOllivier Robert 	struct recvbuf *rbufp	/* receive buffer structure pointer */
595c0b746e5SOllivier Robert 	)
596c0b746e5SOllivier Robert {
597224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
598224ba2bdSOllivier Robert 	struct chuunit *up;
599224ba2bdSOllivier Robert 	struct refclockproc *pp;
600224ba2bdSOllivier Robert 	struct peer *peer;
601224ba2bdSOllivier Robert 
602224ba2bdSOllivier Robert 	peer = (struct peer *)rbufp->recv_srcclock;
603224ba2bdSOllivier Robert 	pp = peer->procptr;
604224ba2bdSOllivier Robert 	up = (struct chuunit *)pp->unitptr;
605224ba2bdSOllivier Robert 
606224ba2bdSOllivier Robert 	/*
607224ba2bdSOllivier Robert 	 * If the audio codec is warmed up, the buffer contains codec
608224ba2bdSOllivier Robert 	 * samples which need to be demodulated and decoded into CHU
609224ba2bdSOllivier Robert 	 * characters using the software UART. Otherwise, the buffer
610224ba2bdSOllivier Robert 	 * contains CHU characters from the serial port, so the software
611224ba2bdSOllivier Robert 	 * UART is bypassed. In this case the CPU will probably run a
612224ba2bdSOllivier Robert 	 * few degrees cooler.
613224ba2bdSOllivier Robert 	 */
614224ba2bdSOllivier Robert 	if (up->fd_audio > 0)
615224ba2bdSOllivier Robert 		chu_audio_receive(rbufp);
616224ba2bdSOllivier Robert 	else
617224ba2bdSOllivier Robert 		chu_serial_receive(rbufp);
618224ba2bdSOllivier Robert #else
619224ba2bdSOllivier Robert 	chu_serial_receive(rbufp);
620224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
621224ba2bdSOllivier Robert }
622224ba2bdSOllivier Robert 
623224ba2bdSOllivier Robert 
6249c2daa00SOllivier Robert #ifdef HAVE_AUDIO
625224ba2bdSOllivier Robert /*
626224ba2bdSOllivier Robert  * chu_audio_receive - receive data from the audio device
627224ba2bdSOllivier Robert  */
628224ba2bdSOllivier Robert static void
629224ba2bdSOllivier Robert chu_audio_receive(
630224ba2bdSOllivier Robert 	struct recvbuf *rbufp	/* receive buffer structure pointer */
631224ba2bdSOllivier Robert 	)
632224ba2bdSOllivier Robert {
633c0b746e5SOllivier Robert 	struct chuunit *up;
634c0b746e5SOllivier Robert 	struct refclockproc *pp;
635c0b746e5SOllivier Robert 	struct peer *peer;
636c0b746e5SOllivier Robert 
637c0b746e5SOllivier Robert 	double	sample;		/* codec sample */
638c0b746e5SOllivier Robert 	u_char	*dpt;		/* buffer pointer */
6399c2daa00SOllivier Robert 	int	bufcnt;		/* buffer counter */
640c0b746e5SOllivier Robert 	l_fp	ltemp;		/* l_fp temp */
641c0b746e5SOllivier Robert 
642c0b746e5SOllivier Robert 	peer = (struct peer *)rbufp->recv_srcclock;
643c0b746e5SOllivier Robert 	pp = peer->procptr;
644c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
645c0b746e5SOllivier Robert 
646c0b746e5SOllivier Robert 	/*
647c0b746e5SOllivier Robert 	 * Main loop - read until there ain't no more. Note codec
648c0b746e5SOllivier Robert 	 * samples are bit-inverted.
649c0b746e5SOllivier Robert 	 */
6509c2daa00SOllivier Robert 	DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
6519c2daa00SOllivier Robert 	L_SUB(&rbufp->recv_time, &ltemp);
652c0b746e5SOllivier Robert 	up->timestamp = rbufp->recv_time;
6539c2daa00SOllivier Robert 	dpt = rbufp->recv_buffer;
6549c2daa00SOllivier Robert 	for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
6559c2daa00SOllivier Robert 		sample = up->comp[~*dpt++ & 0xff];
656c0b746e5SOllivier Robert 
657c0b746e5SOllivier Robert 		/*
658c0b746e5SOllivier Robert 		 * Clip noise spikes greater than MAXSIG. If no clips,
659c0b746e5SOllivier Robert 		 * increase the gain a tad; if the clips are too high,
660c0b746e5SOllivier Robert 		 * decrease a tad.
661c0b746e5SOllivier Robert 		 */
662c0b746e5SOllivier Robert 		if (sample > MAXSIG) {
663c0b746e5SOllivier Robert 			sample = MAXSIG;
664c0b746e5SOllivier Robert 			up->clipcnt++;
665c0b746e5SOllivier Robert 		} else if (sample < -MAXSIG) {
666c0b746e5SOllivier Robert 			sample = -MAXSIG;
667c0b746e5SOllivier Robert 			up->clipcnt++;
668c0b746e5SOllivier Robert 		}
6699c2daa00SOllivier Robert 		chu_rf(peer, sample);
6709c2daa00SOllivier Robert 		L_ADD(&up->timestamp, &up->tick);
6719c2daa00SOllivier Robert 
6729c2daa00SOllivier Robert 		/*
6739c2daa00SOllivier Robert 		 * Once each second ride gain.
6749c2daa00SOllivier Robert 		 */
675a151a66cSOllivier Robert 		up->seccnt = (up->seccnt + 1) % SECOND;
676c0b746e5SOllivier Robert 		if (up->seccnt == 0) {
6779c2daa00SOllivier Robert 			pp->second = (pp->second + 1) % 60;
6789c2daa00SOllivier Robert 			chu_gain(peer);
6799c2daa00SOllivier Robert 		}
6809c2daa00SOllivier Robert 	}
6819c2daa00SOllivier Robert 
6829c2daa00SOllivier Robert 	/*
6839c2daa00SOllivier Robert 	 * Set the input port and monitor gain for the next buffer.
6849c2daa00SOllivier Robert 	 */
685c0b746e5SOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG2)
686a151a66cSOllivier Robert 		up->port = 2;
687c0b746e5SOllivier Robert 	else
688a151a66cSOllivier Robert 		up->port = 1;
689c0b746e5SOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG3)
6909c2daa00SOllivier Robert 		up->mongain = MONGAIN;
6919c2daa00SOllivier Robert 	else
6929c2daa00SOllivier Robert 		up->mongain = 0;
693c0b746e5SOllivier Robert }
694c0b746e5SOllivier Robert 
695c0b746e5SOllivier Robert 
696c0b746e5SOllivier Robert /*
697c0b746e5SOllivier Robert  * chu_rf - filter and demodulate the FSK signal
698c0b746e5SOllivier Robert  *
699c0b746e5SOllivier Robert  * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
700c0b746e5SOllivier Robert  * and space 2025 Hz. It uses a bandpass filter followed by a soft
701c0b746e5SOllivier Robert  * limiter, FM discriminator and lowpass filter. A maximum likelihood
702c0b746e5SOllivier Robert  * decoder samples the baseband signal at eight times the baud rate and
703c0b746e5SOllivier Robert  * detects the start bit of each character.
704c0b746e5SOllivier Robert  *
705c0b746e5SOllivier Robert  * The filters are built for speed, which explains the rather clumsy
706c0b746e5SOllivier Robert  * code. Hopefully, the compiler will efficiently implement the move-
707c0b746e5SOllivier Robert  * and-muiltiply-and-add operations.
708c0b746e5SOllivier Robert  */
709a151a66cSOllivier Robert static void
710c0b746e5SOllivier Robert chu_rf(
711c0b746e5SOllivier Robert 	struct peer *peer,	/* peer structure pointer */
712c0b746e5SOllivier Robert 	double	sample		/* analog sample */
713c0b746e5SOllivier Robert 	)
714c0b746e5SOllivier Robert {
715c0b746e5SOllivier Robert 	struct refclockproc *pp;
716c0b746e5SOllivier Robert 	struct chuunit *up;
717c0b746e5SOllivier Robert 	struct surv *sp;
718c0b746e5SOllivier Robert 
719c0b746e5SOllivier Robert 	/*
720c0b746e5SOllivier Robert 	 * Local variables
721c0b746e5SOllivier Robert 	 */
722c0b746e5SOllivier Robert 	double	signal;		/* bandpass signal */
723c0b746e5SOllivier Robert 	double	limit;		/* limiter signal */
724c0b746e5SOllivier Robert 	double	disc;		/* discriminator signal */
725c0b746e5SOllivier Robert 	double	lpf;		/* lowpass signal */
726c0b746e5SOllivier Robert 	double	span;		/* UART signal span */
727c0b746e5SOllivier Robert 	double	dist;		/* UART signal distance */
728a151a66cSOllivier Robert 	int	i, j;
729c0b746e5SOllivier Robert 
730c0b746e5SOllivier Robert 	pp = peer->procptr;
731c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
732a151a66cSOllivier Robert 
733c0b746e5SOllivier Robert 	/*
734c0b746e5SOllivier Robert 	 * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
735c0b746e5SOllivier Robert 	 * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB.
736c0b746e5SOllivier Robert 	 */
737c0b746e5SOllivier Robert 	signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
738c0b746e5SOllivier Robert 	signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
739c0b746e5SOllivier Robert 	signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
740c0b746e5SOllivier Robert 	signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
741c0b746e5SOllivier Robert 	signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
742c0b746e5SOllivier Robert 	signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
743c0b746e5SOllivier Robert 	signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
744c0b746e5SOllivier Robert 	signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
745c0b746e5SOllivier Robert 	up->bpf[0] = sample - signal;
746c0b746e5SOllivier Robert 	signal = up->bpf[0] * 6.176213e-03
747c0b746e5SOllivier Robert 	    + up->bpf[1] * 3.156599e-03
748c0b746e5SOllivier Robert 	    + up->bpf[2] * 7.567487e-03
749c0b746e5SOllivier Robert 	    + up->bpf[3] * 4.344580e-03
750c0b746e5SOllivier Robert 	    + up->bpf[4] * 1.190128e-02
751c0b746e5SOllivier Robert 	    + up->bpf[5] * 4.344580e-03
752c0b746e5SOllivier Robert 	    + up->bpf[6] * 7.567487e-03
753c0b746e5SOllivier Robert 	    + up->bpf[7] * 3.156599e-03
754c0b746e5SOllivier Robert 	    + up->bpf[8] * 6.176213e-03;
755c0b746e5SOllivier Robert 
756c0b746e5SOllivier Robert 	up->monitor = signal / 4.;	/* note monitor after filter */
757c0b746e5SOllivier Robert 
758c0b746e5SOllivier Robert 	/*
759c0b746e5SOllivier Robert 	 * Soft limiter/discriminator. The 11-sample discriminator lag
760c0b746e5SOllivier Robert 	 * interval corresponds to three cycles of 2125 Hz, which
761c0b746e5SOllivier Robert 	 * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
762c0b746e5SOllivier Robert 	 * Hz. The discriminator output varies +-0.5 interval for input
763c0b746e5SOllivier Robert 	 * frequency 2025-2225 Hz. However, we don't get to sample at
764c0b746e5SOllivier Robert 	 * this frequency, so the discriminator output is biased. Life
765c0b746e5SOllivier Robert 	 * at 8000 Hz sucks.
766c0b746e5SOllivier Robert 	 */
767c0b746e5SOllivier Robert 	limit = signal;
768c0b746e5SOllivier Robert 	if (limit > LIMIT)
769c0b746e5SOllivier Robert 		limit = LIMIT;
770c0b746e5SOllivier Robert 	else if (limit < -LIMIT)
771c0b746e5SOllivier Robert 		limit = -LIMIT;
772c0b746e5SOllivier Robert 	disc = up->disc[up->discptr] * -limit;
773c0b746e5SOllivier Robert 	up->disc[up->discptr] = limit;
774c0b746e5SOllivier Robert 	up->discptr = (up->discptr + 1 ) % LAG;
775c0b746e5SOllivier Robert 	if (disc >= 0)
776224ba2bdSOllivier Robert 		disc = SQRT(disc);
777c0b746e5SOllivier Robert 	else
778224ba2bdSOllivier Robert 		disc = -SQRT(-disc);
779c0b746e5SOllivier Robert 
780c0b746e5SOllivier Robert 	/*
781c0b746e5SOllivier Robert 	 * Lowpass filter. Raised cosine, Ts = 1 / 300, beta = 0.1.
782c0b746e5SOllivier Robert 	 */
783c0b746e5SOllivier Robert 	lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
784c0b746e5SOllivier Robert 	lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
785c0b746e5SOllivier Robert 	lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
786c0b746e5SOllivier Robert 	lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
787c0b746e5SOllivier Robert 	lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
788c0b746e5SOllivier Robert 	lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
789c0b746e5SOllivier Robert 	lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
790c0b746e5SOllivier Robert 	lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
791c0b746e5SOllivier Robert 	lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
792c0b746e5SOllivier Robert 	lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
793c0b746e5SOllivier Robert 	lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
794c0b746e5SOllivier Robert 	lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
795c0b746e5SOllivier Robert 	lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
796c0b746e5SOllivier Robert 	lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
797c0b746e5SOllivier Robert 	lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
798c0b746e5SOllivier Robert 	lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
799c0b746e5SOllivier Robert 	lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
800c0b746e5SOllivier Robert 	lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
801c0b746e5SOllivier Robert 	lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
802c0b746e5SOllivier Robert 	lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
803c0b746e5SOllivier Robert 	lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
804c0b746e5SOllivier Robert 	lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
805c0b746e5SOllivier Robert 	lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
806c0b746e5SOllivier Robert 	lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
807c0b746e5SOllivier Robert 	lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
808c0b746e5SOllivier Robert 	lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
809c0b746e5SOllivier Robert 	lpf += up->lpf[0] = disc * 2.538771e-02;
810a151a66cSOllivier Robert 
811c0b746e5SOllivier Robert 	/*
812c0b746e5SOllivier Robert 	 * Maximum likelihood decoder. The UART updates each of the
813c0b746e5SOllivier Robert 	 * eight survivors and determines the span, slice level and
814c0b746e5SOllivier Robert 	 * tentative decoded character. Valid 11-bit characters are
815c0b746e5SOllivier Robert 	 * framed so that bit 1 and bit 11 (stop bits) are mark and bit
816c0b746e5SOllivier Robert 	 * 2 (start bit) is space. When a valid character is found, the
817c0b746e5SOllivier Robert 	 * survivor with maximum distance determines the final decoded
818c0b746e5SOllivier Robert 	 * character.
819c0b746e5SOllivier Robert 	 */
820a151a66cSOllivier Robert 	up->baud += 1. / SECOND;
821c0b746e5SOllivier Robert 	if (up->baud > 1. / (BAUD * 8.)) {
822c0b746e5SOllivier Robert 		up->baud -= 1. / (BAUD * 8.);
823c0b746e5SOllivier Robert 		sp = &up->surv[up->decptr];
824a151a66cSOllivier Robert 		span = sp->es_max - sp->es_min;
825c0b746e5SOllivier Robert 		up->maxsignal += (span - up->maxsignal) / 80.;
826c0b746e5SOllivier Robert 		if (up->dbrk > 0) {
827c0b746e5SOllivier Robert 			up->dbrk--;
828c0b746e5SOllivier Robert 		} else if ((sp->uart & 0x403) == 0x401 && span > 1000.)
829c0b746e5SOllivier Robert 		    {
830c0b746e5SOllivier Robert 			dist = 0;
831c0b746e5SOllivier Robert 			j = 0;
832c0b746e5SOllivier Robert 			for (i = 0; i < 8; i++) {
833c0b746e5SOllivier Robert 				if (up->surv[i].dist > dist) {
834c0b746e5SOllivier Robert 					dist = up->surv[i].dist;
835c0b746e5SOllivier Robert 					j = i;
836c0b746e5SOllivier Robert 				}
837c0b746e5SOllivier Robert 			}
838c0b746e5SOllivier Robert 			chu_decode(peer, (up->surv[j].uart >> 2) &
839c0b746e5SOllivier Robert 			    0xff);
840c0b746e5SOllivier Robert 			up->dbrk = 80;
841c0b746e5SOllivier Robert 		}
842c0b746e5SOllivier Robert 		up->decptr = (up->decptr + 1) % 8;
843c0b746e5SOllivier Robert 		chu_uart(sp, -lpf * AGAIN);
844c0b746e5SOllivier Robert 	}
845c0b746e5SOllivier Robert }
846c0b746e5SOllivier Robert 
847c0b746e5SOllivier Robert 
848c0b746e5SOllivier Robert /*
849c0b746e5SOllivier Robert  * chu_uart - maximum likelihood UART
850c0b746e5SOllivier Robert  *
851c0b746e5SOllivier Robert  * This routine updates a shift register holding the last 11 envelope
852c0b746e5SOllivier Robert  * samples. It then computes the slice level and span over these samples
853c0b746e5SOllivier Robert  * and determines the tentative data bits and distance. The calling
854c0b746e5SOllivier Robert  * program selects over the last eight survivors the one with maximum
855c0b746e5SOllivier Robert  * distance to determine the decoded character.
856c0b746e5SOllivier Robert  */
857a151a66cSOllivier Robert static void
858c0b746e5SOllivier Robert chu_uart(
859c0b746e5SOllivier Robert 	struct surv *sp,	/* survivor structure pointer */
860c0b746e5SOllivier Robert 	double	sample		/* baseband signal */
861c0b746e5SOllivier Robert 	)
862c0b746e5SOllivier Robert {
863a151a66cSOllivier Robert 	double	es_max, es_min;	/* max/min envelope */
864c0b746e5SOllivier Robert 	double	slice;		/* slice level */
865c0b746e5SOllivier Robert 	double	dist;		/* distance */
866a151a66cSOllivier Robert 	double	dtemp;
867a151a66cSOllivier Robert 	int	i;
868c0b746e5SOllivier Robert 
869c0b746e5SOllivier Robert 	/*
870c0b746e5SOllivier Robert 	 * Save the sample and shift right. At the same time, measure
871c0b746e5SOllivier Robert 	 * the maximum and minimum over all eleven samples.
872c0b746e5SOllivier Robert 	 */
873a151a66cSOllivier Robert 	es_max = -1e6;
874a151a66cSOllivier Robert 	es_min = 1e6;
875c0b746e5SOllivier Robert 	sp->shift[0] = sample;
876c0b746e5SOllivier Robert 	for (i = 11; i > 0; i--) {
877c0b746e5SOllivier Robert 		sp->shift[i] = sp->shift[i - 1];
878a151a66cSOllivier Robert 		if (sp->shift[i] > es_max)
879a151a66cSOllivier Robert 			es_max = sp->shift[i];
880a151a66cSOllivier Robert 		if (sp->shift[i] < es_min)
881a151a66cSOllivier Robert 			es_min = sp->shift[i];
882c0b746e5SOllivier Robert 	}
883c0b746e5SOllivier Robert 
884c0b746e5SOllivier Robert 	/*
885c0b746e5SOllivier Robert 	 * Determine the slice level midway beteen the maximum and
886c0b746e5SOllivier Robert 	 * minimum and the span as the maximum less the minimum. Compute
887c0b746e5SOllivier Robert 	 * the distance on the assumption the first and last bits must
888c0b746e5SOllivier Robert 	 * be mark, the second space and the rest either mark or space.
889c0b746e5SOllivier Robert 	 */
890a151a66cSOllivier Robert 	slice = (es_max + es_min) / 2.;
891c0b746e5SOllivier Robert 	dist = 0;
892c0b746e5SOllivier Robert 	sp->uart = 0;
893c0b746e5SOllivier Robert 	for (i = 1; i < 12; i++) {
894c0b746e5SOllivier Robert 		sp->uart <<= 1;
895c0b746e5SOllivier Robert 		dtemp = sp->shift[i];
896c0b746e5SOllivier Robert 		if (dtemp > slice)
897c0b746e5SOllivier Robert 			sp->uart |= 0x1;
898c0b746e5SOllivier Robert 		if (i == 1 || i == 11) {
899a151a66cSOllivier Robert 			dist += dtemp - es_min;
900c0b746e5SOllivier Robert 		} else if (i == 10) {
901a151a66cSOllivier Robert 			dist += es_max - dtemp;
902c0b746e5SOllivier Robert 		} else {
903c0b746e5SOllivier Robert 			if (dtemp > slice)
904a151a66cSOllivier Robert 				dist += dtemp - es_min;
905c0b746e5SOllivier Robert 			else
906a151a66cSOllivier Robert 				dist += es_max - dtemp;
907c0b746e5SOllivier Robert 		}
908c0b746e5SOllivier Robert 	}
909a151a66cSOllivier Robert 	sp->es_max = es_max;
910a151a66cSOllivier Robert 	sp->es_min = es_min;
911a151a66cSOllivier Robert 	sp->dist = dist / (11 * (es_max - es_min));
912c0b746e5SOllivier Robert }
913224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
914c0b746e5SOllivier Robert 
915c0b746e5SOllivier Robert 
916c0b746e5SOllivier Robert /*
917224ba2bdSOllivier Robert  * chu_serial_receive - receive data from the serial device
918c0b746e5SOllivier Robert  */
919c0b746e5SOllivier Robert static void
920224ba2bdSOllivier Robert chu_serial_receive(
921c0b746e5SOllivier Robert 	struct recvbuf *rbufp	/* receive buffer structure pointer */
922c0b746e5SOllivier Robert 	)
923c0b746e5SOllivier Robert {
924c0b746e5SOllivier Robert 	struct chuunit *up;
925c0b746e5SOllivier Robert 	struct refclockproc *pp;
926c0b746e5SOllivier Robert 	struct peer *peer;
927c0b746e5SOllivier Robert 
928c0b746e5SOllivier Robert 	u_char	*dpt;		/* receive buffer pointer */
929c0b746e5SOllivier Robert 
930c0b746e5SOllivier Robert 	peer = (struct peer *)rbufp->recv_srcclock;
931c0b746e5SOllivier Robert 	pp = peer->procptr;
932c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
933c0b746e5SOllivier Robert 
934c0b746e5SOllivier Robert 	/*
935c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp.
936c0b746e5SOllivier Robert 	 */
937c0b746e5SOllivier Robert 	up->timestamp = rbufp->recv_time;
938c0b746e5SOllivier Robert 	dpt = (u_char *)&rbufp->recv_space;
939c0b746e5SOllivier Robert 	chu_decode(peer, *dpt);
940c0b746e5SOllivier Robert }
941c0b746e5SOllivier Robert 
942c0b746e5SOllivier Robert 
943c0b746e5SOllivier Robert /*
944224ba2bdSOllivier Robert  * chu_decode - decode the character data
945c0b746e5SOllivier Robert  */
946c0b746e5SOllivier Robert static void
947c0b746e5SOllivier Robert chu_decode(
948c0b746e5SOllivier Robert 	struct peer *peer,	/* peer structure pointer */
949c0b746e5SOllivier Robert 	int	hexhex		/* data character */
950c0b746e5SOllivier Robert 	)
951c0b746e5SOllivier Robert {
952c0b746e5SOllivier Robert 	struct refclockproc *pp;
953c0b746e5SOllivier Robert 	struct chuunit *up;
954c0b746e5SOllivier Robert 
955c0b746e5SOllivier Robert 	l_fp	tstmp;		/* timestamp temp */
956a151a66cSOllivier Robert 	double	dtemp;
957c0b746e5SOllivier Robert 
958c0b746e5SOllivier Robert 	pp = peer->procptr;
959c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
960c0b746e5SOllivier Robert 
961c0b746e5SOllivier Robert 	/*
962c0b746e5SOllivier Robert 	 * If the interval since the last character is greater than the
963c0b746e5SOllivier Robert 	 * longest burst, process the last burst and start a new one. If
964c0b746e5SOllivier Robert 	 * the interval is less than this but greater than two
965c0b746e5SOllivier Robert 	 * characters, consider this a noise burst and reject it.
966c0b746e5SOllivier Robert 	 */
967c0b746e5SOllivier Robert 	tstmp = up->timestamp;
968c0b746e5SOllivier Robert 	if (L_ISZERO(&up->laststamp))
969c0b746e5SOllivier Robert 		up->laststamp = up->timestamp;
970c0b746e5SOllivier Robert 	L_SUB(&tstmp, &up->laststamp);
971c0b746e5SOllivier Robert 	up->laststamp = up->timestamp;
972c0b746e5SOllivier Robert 	LFPTOD(&tstmp, dtemp);
973c0b746e5SOllivier Robert 	if (dtemp > BURST * CHAR) {
974c0b746e5SOllivier Robert 		chu_burst(peer);
975c0b746e5SOllivier Robert 		up->ndx = 0;
976c0b746e5SOllivier Robert 	} else if (dtemp > 2.5 * CHAR) {
977c0b746e5SOllivier Robert 		up->ndx = 0;
978c0b746e5SOllivier Robert 	}
979c0b746e5SOllivier Robert 
980c0b746e5SOllivier Robert 	/*
981c0b746e5SOllivier Robert 	 * Append the character to the current burst and append the
982c0b746e5SOllivier Robert 	 * timestamp to the timestamp list.
983c0b746e5SOllivier Robert 	 */
984c0b746e5SOllivier Robert 	if (up->ndx < BURST) {
985c0b746e5SOllivier Robert 		up->cbuf[up->ndx] = hexhex & 0xff;
986c0b746e5SOllivier Robert 		up->cstamp[up->ndx] = up->timestamp;
987c0b746e5SOllivier Robert 		up->ndx++;
988c0b746e5SOllivier Robert 
989c0b746e5SOllivier Robert 	}
990c0b746e5SOllivier Robert }
991c0b746e5SOllivier Robert 
992c0b746e5SOllivier Robert 
993c0b746e5SOllivier Robert /*
994c0b746e5SOllivier Robert  * chu_burst - search for valid burst format
995c0b746e5SOllivier Robert  */
996c0b746e5SOllivier Robert static void
997c0b746e5SOllivier Robert chu_burst(
998c0b746e5SOllivier Robert 	struct peer *peer
999c0b746e5SOllivier Robert 	)
1000c0b746e5SOllivier Robert {
1001c0b746e5SOllivier Robert 	struct chuunit *up;
1002c0b746e5SOllivier Robert 	struct refclockproc *pp;
1003c0b746e5SOllivier Robert 
1004a151a66cSOllivier Robert 	int	i;
1005c0b746e5SOllivier Robert 
1006c0b746e5SOllivier Robert 	pp = peer->procptr;
1007c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1008c0b746e5SOllivier Robert 
1009c0b746e5SOllivier Robert 	/*
1010c0b746e5SOllivier Robert 	 * Correlate a block of five characters with the next block of
1011c0b746e5SOllivier Robert 	 * five characters. The burst distance is defined as the number
1012c0b746e5SOllivier Robert 	 * of bits that match in the two blocks for format A and that
1013c0b746e5SOllivier Robert 	 * match the inverse for format B.
1014c0b746e5SOllivier Robert 	 */
1015c0b746e5SOllivier Robert 	if (up->ndx < MINCHAR) {
1016a151a66cSOllivier Robert 		up->status |= RUNT;
1017c0b746e5SOllivier Robert 		return;
1018c0b746e5SOllivier Robert 	}
1019c0b746e5SOllivier Robert 	up->burdist = 0;
1020c0b746e5SOllivier Robert 	for (i = 0; i < 5 && i < up->ndx - 5; i++)
1021c0b746e5SOllivier Robert 		up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
1022c0b746e5SOllivier Robert 
1023c0b746e5SOllivier Robert 	/*
1024c0b746e5SOllivier Robert 	 * If the burst distance is at least MINDIST, this must be a
1025c0b746e5SOllivier Robert 	 * format A burst; if the value is not greater than -MINDIST, it
1026a151a66cSOllivier Robert 	 * must be a format B burst. If the B burst is perfect, we
1027a151a66cSOllivier Robert 	 * believe it; otherwise, it is a noise burst and of no use to
1028a151a66cSOllivier Robert 	 * anybody.
1029c0b746e5SOllivier Robert 	 */
1030c0b746e5SOllivier Robert 	if (up->burdist >= MINDIST) {
1031a151a66cSOllivier Robert 		chu_a(peer, up->ndx);
1032c0b746e5SOllivier Robert 	} else if (up->burdist <= -MINDIST) {
1033a151a66cSOllivier Robert 		chu_b(peer, up->ndx);
1034c0b746e5SOllivier Robert 	} else {
1035a151a66cSOllivier Robert 		up->status |= NOISE;
1036c0b746e5SOllivier Robert 		return;
1037c0b746e5SOllivier Robert 	}
1038c0b746e5SOllivier Robert 
1039c0b746e5SOllivier Robert 	/*
1040c0b746e5SOllivier Robert 	 * If this is a valid burst, wait a guard time of ten seconds to
1041c0b746e5SOllivier Robert 	 * allow for more bursts, then arm the poll update routine to
1042c0b746e5SOllivier Robert 	 * process the minute. Don't do this if this is called from the
1043c0b746e5SOllivier Robert 	 * timer interrupt routine.
1044c0b746e5SOllivier Robert 	 */
1045a151a66cSOllivier Robert 	if (peer->outdate != current_time)
1046c0b746e5SOllivier Robert 		peer->nextdate = current_time + 10;
1047c0b746e5SOllivier Robert }
1048c0b746e5SOllivier Robert 
1049c0b746e5SOllivier Robert 
1050c0b746e5SOllivier Robert /*
1051a151a66cSOllivier Robert  * chu_b - decode format B burst
1052c0b746e5SOllivier Robert  */
1053c0b746e5SOllivier Robert static void
1054a151a66cSOllivier Robert chu_b(
1055c0b746e5SOllivier Robert 	struct peer *peer,
1056c0b746e5SOllivier Robert 	int	nchar
1057c0b746e5SOllivier Robert 	)
1058c0b746e5SOllivier Robert {
1059c0b746e5SOllivier Robert 	struct	refclockproc *pp;
1060c0b746e5SOllivier Robert 	struct	chuunit *up;
1061c0b746e5SOllivier Robert 
1062c0b746e5SOllivier Robert 	u_char	code[11];	/* decoded timecode */
1063a151a66cSOllivier Robert 	char	tbuf[80];	/* trace buffer */
1064c0b746e5SOllivier Robert 	l_fp	offset;		/* timestamp offset */
1065a151a66cSOllivier Robert 	int	i;
1066c0b746e5SOllivier Robert 
1067c0b746e5SOllivier Robert 	pp = peer->procptr;
1068c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1069c0b746e5SOllivier Robert 
1070c0b746e5SOllivier Robert 	/*
1071c0b746e5SOllivier Robert 	 * In a format B burst, a character is considered valid only if
1072c0b746e5SOllivier Robert 	 * the first occurrence matches the last occurrence. The burst
1073c0b746e5SOllivier Robert 	 * is considered valid only if all characters are valid; that
10749c2daa00SOllivier Robert 	 * is, only if the distance is 40. Note that once a valid frame
10759c2daa00SOllivier Robert 	 * has been found errors are ignored.
1076c0b746e5SOllivier Robert 	 */
1077a151a66cSOllivier Robert 	sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar,
1078a151a66cSOllivier Robert 	    -up->burdist);
1079c0b746e5SOllivier Robert 	for (i = 0; i < nchar; i++)
10809c2daa00SOllivier Robert 		sprintf(&tbuf[strlen(tbuf)], "%02x", up->cbuf[i]);
1081c0b746e5SOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG4)
1082a151a66cSOllivier Robert 		record_clock_stats(&peer->srcadr, tbuf);
1083c0b746e5SOllivier Robert #ifdef DEBUG
1084a151a66cSOllivier Robert 	if (debug)
1085a151a66cSOllivier Robert 		printf("%s\n", tbuf);
1086c0b746e5SOllivier Robert #endif
1087a151a66cSOllivier Robert 	if (up->burdist > -40) {
1088a151a66cSOllivier Robert 		up->status |= BFRAME;
1089c0b746e5SOllivier Robert 		return;
1090c0b746e5SOllivier Robert 	}
10919c2daa00SOllivier Robert 	up->status |= BVALID;
1092c0b746e5SOllivier Robert 
1093c0b746e5SOllivier Robert 	/*
1094c0b746e5SOllivier Robert 	 * Convert the burst data to internal format. If this succeeds,
1095a151a66cSOllivier Robert 	 * save the timestamps for later.
1096c0b746e5SOllivier Robert 	 */
1097c0b746e5SOllivier Robert 	for (i = 0; i < 5; i++) {
1098c0b746e5SOllivier Robert 		code[2 * i] = hexchar[up->cbuf[i] & 0xf];
1099c0b746e5SOllivier Robert 		code[2 * i + 1] = hexchar[(up->cbuf[i] >>
1100c0b746e5SOllivier Robert 		    4) & 0xf];
1101c0b746e5SOllivier Robert 	}
1102a151a66cSOllivier Robert 	if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
1103a151a66cSOllivier Robert 	    &pp->year, &up->tai, &up->dst) != 5) {
1104a151a66cSOllivier Robert 		up->status |= BFORMAT;
1105c0b746e5SOllivier Robert 		return;
1106c0b746e5SOllivier Robert 	}
1107a151a66cSOllivier Robert 	if (up->leap & 0x8)
1108a151a66cSOllivier Robert 		up->dut = -up->dut;
1109c0b746e5SOllivier Robert 	offset.l_ui = 31;
1110c0b746e5SOllivier Robert 	offset.l_f = 0;
1111c0b746e5SOllivier Robert 	for (i = 0; i < nchar && i < 10; i++) {
1112c0b746e5SOllivier Robert 		up->tstamp[up->ntstamp] = up->cstamp[i];
1113c0b746e5SOllivier Robert 		L_SUB(&up->tstamp[up->ntstamp], &offset);
1114c0b746e5SOllivier Robert 		L_ADD(&offset, &up->charstamp);
1115c0b746e5SOllivier Robert 		if (up->ntstamp < MAXSTAGE)
1116c0b746e5SOllivier Robert 			up->ntstamp++;
1117c0b746e5SOllivier Robert 	}
1118c0b746e5SOllivier Robert }
1119c0b746e5SOllivier Robert 
1120c0b746e5SOllivier Robert 
1121c0b746e5SOllivier Robert /*
1122a151a66cSOllivier Robert  * chu_a - decode format A burst
1123c0b746e5SOllivier Robert  */
1124c0b746e5SOllivier Robert static void
1125a151a66cSOllivier Robert chu_a(
1126c0b746e5SOllivier Robert 	struct peer *peer,
1127c0b746e5SOllivier Robert 	int nchar
1128c0b746e5SOllivier Robert 	)
1129c0b746e5SOllivier Robert {
1130c0b746e5SOllivier Robert 	struct refclockproc *pp;
1131c0b746e5SOllivier Robert 	struct chuunit *up;
1132c0b746e5SOllivier Robert 
1133a151a66cSOllivier Robert 	char	tbuf[80];	/* trace buffer */
1134c0b746e5SOllivier Robert 	l_fp	offset;		/* timestamp offset */
1135c0b746e5SOllivier Robert 	int	val;		/* distance */
1136a151a66cSOllivier Robert 	int	temp;
1137a151a66cSOllivier Robert 	int	i, j, k;
1138c0b746e5SOllivier Robert 
1139c0b746e5SOllivier Robert 	pp = peer->procptr;
1140c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1141c0b746e5SOllivier Robert 
1142c0b746e5SOllivier Robert 	/*
1143c0b746e5SOllivier Robert 	 * Determine correct burst phase. There are three cases
1144c0b746e5SOllivier Robert 	 * corresponding to in-phase, one character early or one
1145c0b746e5SOllivier Robert 	 * character late. These cases are distinguished by the position
1146c0b746e5SOllivier Robert 	 * of the framing digits x6 at positions 0 and 5 and x3 at
1147c0b746e5SOllivier Robert 	 * positions 4 and 9. The correct phase is when the distance
1148c0b746e5SOllivier Robert 	 * relative to the framing digits is maximum. The burst is valid
1149c0b746e5SOllivier Robert 	 * only if the maximum distance is at least MINSYNC.
1150c0b746e5SOllivier Robert 	 */
1151c0b746e5SOllivier Robert 	up->syndist = k = 0;
1152c0b746e5SOllivier Robert 	val = -16;
1153c0b746e5SOllivier Robert 	for (i = -1; i < 2; i++) {
1154c0b746e5SOllivier Robert 		temp = up->cbuf[i + 4] & 0xf;
1155c0b746e5SOllivier Robert 		if (i >= 0)
1156c0b746e5SOllivier Robert 			temp |= (up->cbuf[i] & 0xf) << 4;
1157c0b746e5SOllivier Robert 		val = chu_dist(temp, 0x63);
1158c0b746e5SOllivier Robert 		temp = (up->cbuf[i + 5] & 0xf) << 4;
1159c0b746e5SOllivier Robert 		if (i + 9 < nchar)
1160c0b746e5SOllivier Robert 			temp |= up->cbuf[i + 9] & 0xf;
1161c0b746e5SOllivier Robert 		val += chu_dist(temp, 0x63);
1162c0b746e5SOllivier Robert 		if (val > up->syndist) {
1163c0b746e5SOllivier Robert 			up->syndist = val;
1164c0b746e5SOllivier Robert 			k = i;
1165c0b746e5SOllivier Robert 		}
1166c0b746e5SOllivier Robert 	}
1167c0b746e5SOllivier Robert 	temp = (up->cbuf[k + 4] >> 4) & 0xf;
1168c0b746e5SOllivier Robert 	if (temp > 9 || k + 9 >= nchar || temp != ((up->cbuf[k + 9] >>
1169c0b746e5SOllivier Robert 	    4) & 0xf))
1170c0b746e5SOllivier Robert 		temp = 0;
1171224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
1172224ba2bdSOllivier Robert 	if (up->fd_audio)
1173a151a66cSOllivier Robert 		sprintf(tbuf, "chuA %04x %4.0f %2d %2d %2d %2d %1d ",
1174a151a66cSOllivier Robert 		    up->status, up->maxsignal, nchar, up->burdist, k,
1175a151a66cSOllivier Robert 		    up->syndist, temp);
1176224ba2bdSOllivier Robert 	else
1177224ba2bdSOllivier Robert 		sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ",
1178224ba2bdSOllivier Robert 		    up->status, nchar, up->burdist, k, up->syndist,
1179224ba2bdSOllivier Robert 		    temp);
1180224ba2bdSOllivier Robert 
1181c0b746e5SOllivier Robert #else
1182a151a66cSOllivier Robert 	sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ", up->status,
1183a151a66cSOllivier Robert 	    nchar, up->burdist, k, up->syndist, temp);
1184224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
1185c0b746e5SOllivier Robert 	for (i = 0; i < nchar; i++)
1186a151a66cSOllivier Robert 		sprintf(&tbuf[strlen(tbuf)], "%02x",
1187c0b746e5SOllivier Robert 		    up->cbuf[i]);
1188c0b746e5SOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG4)
1189a151a66cSOllivier Robert 		record_clock_stats(&peer->srcadr, tbuf);
1190c0b746e5SOllivier Robert #ifdef DEBUG
1191a151a66cSOllivier Robert 	if (debug)
1192a151a66cSOllivier Robert 		printf("%s\n", tbuf);
1193c0b746e5SOllivier Robert #endif
1194c0b746e5SOllivier Robert 	if (up->syndist < MINSYNC) {
1195a151a66cSOllivier Robert 		up->status |= AFRAME;
1196c0b746e5SOllivier Robert 		return;
1197c0b746e5SOllivier Robert 	}
1198c0b746e5SOllivier Robert 
1199c0b746e5SOllivier Robert 	/*
1200c0b746e5SOllivier Robert 	 * A valid burst requires the first seconds number to match the
1201c0b746e5SOllivier Robert 	 * last seconds number. If so, the burst timestamps are
1202c0b746e5SOllivier Robert 	 * corrected to the current minute and saved for later
1203c0b746e5SOllivier Robert 	 * processing. In addition, the seconds decode is advanced from
1204c0b746e5SOllivier Robert 	 * the previous burst to the current one.
1205c0b746e5SOllivier Robert 	 */
1206c0b746e5SOllivier Robert 	if (temp != 0) {
12079c2daa00SOllivier Robert 		pp->second = 30 + temp;
1208c0b746e5SOllivier Robert 		offset.l_ui = 30 + temp;
1209c0b746e5SOllivier Robert 		offset.l_f = 0;
1210c0b746e5SOllivier Robert 		i = 0;
1211c0b746e5SOllivier Robert 		if (k < 0)
1212c0b746e5SOllivier Robert 			offset = up->charstamp;
1213c0b746e5SOllivier Robert 		else if (k > 0)
1214c0b746e5SOllivier Robert 			i = 1;
1215c0b746e5SOllivier Robert 		for (; i < nchar && i < k + 10; i++) {
1216c0b746e5SOllivier Robert 			up->tstamp[up->ntstamp] = up->cstamp[i];
1217c0b746e5SOllivier Robert 			L_SUB(&up->tstamp[up->ntstamp], &offset);
1218c0b746e5SOllivier Robert 			L_ADD(&offset, &up->charstamp);
1219c0b746e5SOllivier Robert 			if (up->ntstamp < MAXSTAGE)
1220c0b746e5SOllivier Robert 				up->ntstamp++;
1221c0b746e5SOllivier Robert 		}
1222c0b746e5SOllivier Robert 		while (temp > up->prevsec) {
1223c0b746e5SOllivier Robert 			for (j = 15; j > 0; j--) {
1224c0b746e5SOllivier Robert 				up->decode[9][j] = up->decode[9][j - 1];
1225c0b746e5SOllivier Robert 				up->decode[19][j] =
1226c0b746e5SOllivier Robert 				    up->decode[19][j - 1];
1227c0b746e5SOllivier Robert 			}
1228c0b746e5SOllivier Robert 			up->decode[9][j] = up->decode[19][j] = 0;
1229c0b746e5SOllivier Robert 			up->prevsec++;
1230c0b746e5SOllivier Robert 		}
1231c0b746e5SOllivier Robert 	}
1232c0b746e5SOllivier Robert 	i = -(2 * k);
1233c0b746e5SOllivier Robert 	for (j = 0; j < nchar; j++) {
1234c0b746e5SOllivier Robert 		if (i < 0 || i > 19) {
1235c0b746e5SOllivier Robert 			i += 2;
1236c0b746e5SOllivier Robert 			continue;
1237c0b746e5SOllivier Robert 		}
1238a151a66cSOllivier Robert 		up->decode[i][up->cbuf[j] & 0xf]++;
1239a151a66cSOllivier Robert 		i++;
1240a151a66cSOllivier Robert 		up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
1241a151a66cSOllivier Robert 		i++;
1242c0b746e5SOllivier Robert 	}
12439c2daa00SOllivier Robert 	up->status |= AVALID;
1244c0b746e5SOllivier Robert 	up->burstcnt++;
1245c0b746e5SOllivier Robert }
1246c0b746e5SOllivier Robert 
1247c0b746e5SOllivier Robert 
1248c0b746e5SOllivier Robert /*
1249c0b746e5SOllivier Robert  * chu_poll - called by the transmit procedure
1250c0b746e5SOllivier Robert  */
1251c0b746e5SOllivier Robert static void
1252c0b746e5SOllivier Robert chu_poll(
1253c0b746e5SOllivier Robert 	int unit,
1254a151a66cSOllivier Robert 	struct peer *peer	/* peer structure pointer */
1255c0b746e5SOllivier Robert 	)
1256c0b746e5SOllivier Robert {
1257c0b746e5SOllivier Robert 	struct refclockproc *pp;
1258c0b746e5SOllivier Robert 	struct chuunit *up;
12599c2daa00SOllivier Robert 	l_fp	offset;
1260a151a66cSOllivier Robert 	char	synchar, qual, leapchar;
12619c2daa00SOllivier Robert 	int	minset, i;
12629c2daa00SOllivier Robert 	double	dtemp;
12639c2daa00SOllivier Robert 
1264c0b746e5SOllivier Robert 	pp = peer->procptr;
1265c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1266a151a66cSOllivier Robert 	if (pp->coderecv == pp->codeproc)
1267a151a66cSOllivier Robert 		up->errflg = CEVNT_TIMEOUT;
1268a151a66cSOllivier Robert 	else
1269a151a66cSOllivier Robert 		pp->polls++;
12709c2daa00SOllivier Robert 
12719c2daa00SOllivier Robert 	/*
12729c2daa00SOllivier Robert 	 * If once in sync and the radio has not been heard for awhile
12739c2daa00SOllivier Robert 	 * (30 m), it is no longer reachable. If not heard in a long
12749c2daa00SOllivier Robert 	 * while (one day), turn out the lights and start from scratch.
12759c2daa00SOllivier Robert 	 */
1276a151a66cSOllivier Robert 	minset = ((current_time - peer->update) + 30) / 60;
1277a151a66cSOllivier Robert 	if (up->status & INSYNC) {
1278a151a66cSOllivier Robert 		if (minset > PANIC)
1279a151a66cSOllivier Robert 			up->status = 0;
12809c2daa00SOllivier Robert 		else if (minset <= HOLD)
1281a151a66cSOllivier Robert 			peer->reach |= 1;
1282a151a66cSOllivier Robert 	}
1283c0b746e5SOllivier Robert 
1284c0b746e5SOllivier Robert 	/*
1285c0b746e5SOllivier Robert 	 * Process the last burst, if still in the burst buffer.
12869c2daa00SOllivier Robert 	 * Don't mess with anything if nothing has been heard. If the
12879c2daa00SOllivier Robert 	 * minute contains a valid A frame and valid B frame, assume
12889c2daa00SOllivier Robert 	 * synchronized; however, believe the time only if within metric
12899c2daa00SOllivier Robert 	 * threshold. Note the quality indicator is only for
12909c2daa00SOllivier Robert 	 * diagnostics; the data are used only if in sync and above
12919c2daa00SOllivier Robert 	 * metric threshold.
1292c0b746e5SOllivier Robert 	 */
1293c0b746e5SOllivier Robert 	chu_burst(peer);
12949c2daa00SOllivier Robert 	if (up->burstcnt == 0) {
1295a151a66cSOllivier Robert #ifdef ICOM
12969c2daa00SOllivier Robert 		chu_newchan(peer, 0);
1297a151a66cSOllivier Robert #endif /* ICOM */
1298a151a66cSOllivier Robert 		return;
12999c2daa00SOllivier Robert 	}
13009c2daa00SOllivier Robert 	dtemp = chu_major(peer);
1301a151a66cSOllivier Robert 	qual = 0;
1302a151a66cSOllivier Robert 	if (up->status & (BFRAME | AFRAME))
1303a151a66cSOllivier Robert 		qual |= SYNERR;
1304a151a66cSOllivier Robert 	if (up->status & (BFORMAT | AFORMAT))
1305a151a66cSOllivier Robert 		qual |= FMTERR;
1306a151a66cSOllivier Robert 	if (up->status & DECODE)
1307a151a66cSOllivier Robert 		qual |= DECERR;
1308a151a66cSOllivier Robert 	if (up->status & STAMP)
1309a151a66cSOllivier Robert 		qual |= TSPERR;
13109c2daa00SOllivier Robert 	if (up->status & AVALID && up->status & BVALID)
13119c2daa00SOllivier Robert 		up->status |= INSYNC;
1312a151a66cSOllivier Robert 	synchar = leapchar = ' ';
1313a151a66cSOllivier Robert 	if (!(up->status & INSYNC)) {
1314a151a66cSOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
1315a151a66cSOllivier Robert 		synchar = '?';
1316a151a66cSOllivier Robert 	} else if (up->leap & 0x2) {
1317a151a66cSOllivier Robert 		pp->leap = LEAP_ADDSECOND;
1318a151a66cSOllivier Robert 		leapchar = 'L';
1319ce265a54SOllivier Robert 	} else if (up->leap & 0x4) {
1320ce265a54SOllivier Robert 		pp->leap = LEAP_DELSECOND;
1321ce265a54SOllivier Robert 		leapchar = 'l';
1322a151a66cSOllivier Robert 	} else {
1323a151a66cSOllivier Robert 		pp->leap = LEAP_NOWARNING;
1324a151a66cSOllivier Robert 	}
1325224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
1326224ba2bdSOllivier Robert 	if (up->fd_audio)
1327a151a66cSOllivier Robert 		sprintf(pp->a_lastcode,
13289c2daa00SOllivier Robert 		    "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
1329224ba2bdSOllivier Robert 		    synchar, qual, pp->year, pp->day, pp->hour,
1330224ba2bdSOllivier Robert 		    pp->minute, pp->second, leapchar, up->dst, up->dut,
13319c2daa00SOllivier Robert 		    minset, up->gain, up->ident, dtemp, up->ntstamp);
1332224ba2bdSOllivier Robert 	else
1333224ba2bdSOllivier Robert 		sprintf(pp->a_lastcode,
13349c2daa00SOllivier Robert 		    "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
1335224ba2bdSOllivier Robert 		    synchar, qual, pp->year, pp->day, pp->hour,
1336224ba2bdSOllivier Robert 		    pp->minute, pp->second, leapchar, up->dst, up->dut,
13379c2daa00SOllivier Robert 		    minset, up->ident, dtemp, up->ntstamp);
1338a151a66cSOllivier Robert #else
1339a151a66cSOllivier Robert 	sprintf(pp->a_lastcode,
13409c2daa00SOllivier Robert 	    "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
1341a151a66cSOllivier Robert 	    synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
13429c2daa00SOllivier Robert 	    pp->second, leapchar, up->dst, up->dut, minset, up->ident,
13439c2daa00SOllivier Robert 	    dtemp, up->ntstamp);
1344224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
1345a151a66cSOllivier Robert 	pp->lencode = strlen(pp->a_lastcode);
1346c0b746e5SOllivier Robert 
1347c0b746e5SOllivier Robert 	/*
13489c2daa00SOllivier Robert 	 * If in sync and the signal metric is above threshold, the
13499c2daa00SOllivier Robert 	 * timecode is ipso fatso valid and can be selected to
13509c2daa00SOllivier Robert 	 * discipline the clock. Be sure not to leave stray timestamps
13519c2daa00SOllivier Robert 	 * around if signals are too weak or the clock time is invalid.
1352c0b746e5SOllivier Robert 	 */
13539c2daa00SOllivier Robert 	if (up->status & INSYNC && dtemp > METRIC) {
13549c2daa00SOllivier Robert 		if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
13559c2daa00SOllivier Robert 		    up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
13569c2daa00SOllivier Robert 			up->errflg = CEVNT_BADTIME;
13579c2daa00SOllivier Robert 		} else {
13589c2daa00SOllivier Robert 			offset.l_uf = 0;
13599c2daa00SOllivier Robert 			for (i = 0; i < up->ntstamp; i++)
13609c2daa00SOllivier Robert 				refclock_process_offset(pp, offset,
13619c2daa00SOllivier Robert 				    up->tstamp[i], FUDGE +
13629c2daa00SOllivier Robert 				    pp->fudgetime1);
13639c2daa00SOllivier Robert 			pp->lastref = up->timestamp;
1364a151a66cSOllivier Robert 			refclock_receive(peer);
13659c2daa00SOllivier Robert 		}
13669c2daa00SOllivier Robert 		record_clock_stats(&peer->srcadr, pp->a_lastcode);
1367a151a66cSOllivier Robert 	} else if (pp->sloppyclockflag & CLK_FLAG4) {
1368a151a66cSOllivier Robert 		record_clock_stats(&peer->srcadr, pp->a_lastcode);
1369a151a66cSOllivier Robert 	}
1370a151a66cSOllivier Robert #ifdef DEBUG
1371a151a66cSOllivier Robert 	if (debug)
1372a151a66cSOllivier Robert 		printf("chu: timecode %d %s\n", pp->lencode,
1373a151a66cSOllivier Robert 		    pp->a_lastcode);
1374a151a66cSOllivier Robert #endif
13759c2daa00SOllivier Robert #ifdef ICOM
13769c2daa00SOllivier Robert 	chu_newchan(peer, dtemp);
13779c2daa00SOllivier Robert #endif /* ICOM */
1378a151a66cSOllivier Robert 	chu_clear(peer);
1379a151a66cSOllivier Robert 	if (up->errflg)
1380a151a66cSOllivier Robert 		refclock_report(peer, up->errflg);
1381a151a66cSOllivier Robert 	up->errflg = 0;
1382a151a66cSOllivier Robert }
1383a151a66cSOllivier Robert 
1384a151a66cSOllivier Robert 
1385a151a66cSOllivier Robert /*
1386a151a66cSOllivier Robert  * chu_major - majority decoder
1387a151a66cSOllivier Robert  */
13889c2daa00SOllivier Robert static double
1389a151a66cSOllivier Robert chu_major(
1390a151a66cSOllivier Robert 	struct peer *peer	/* peer structure pointer */
1391a151a66cSOllivier Robert 	)
1392a151a66cSOllivier Robert {
1393a151a66cSOllivier Robert 	struct refclockproc *pp;
1394a151a66cSOllivier Robert 	struct chuunit *up;
1395a151a66cSOllivier Robert 
1396a151a66cSOllivier Robert 	u_char	code[11];	/* decoded timecode */
13979c2daa00SOllivier Robert 	int	mindist;	/* minimum distance */
1398a151a66cSOllivier Robert 	int	val1, val2;	/* maximum distance */
1399a151a66cSOllivier Robert 	int	synchar;	/* stray cat */
1400a151a66cSOllivier Robert 	int	temp;
1401a151a66cSOllivier Robert 	int	i, j, k;
1402a151a66cSOllivier Robert 
1403a151a66cSOllivier Robert 	pp = peer->procptr;
1404a151a66cSOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1405a151a66cSOllivier Robert 
1406a151a66cSOllivier Robert 	/*
1407a151a66cSOllivier Robert 	 * Majority decoder. Each burst encodes two replications at each
1408a151a66cSOllivier Robert 	 * digit position in the timecode. Each row of the decoding
14099c2daa00SOllivier Robert 	 * matrix encodes the number of occurrences of each digit found
1410a151a66cSOllivier Robert 	 * at the corresponding position. The maximum over all
14119c2daa00SOllivier Robert 	 * occurrences at each position is the distance for this
14129c2daa00SOllivier Robert 	 * position and the corresponding digit is the maximum
14139c2daa00SOllivier Robert 	 * likelihood candidate. If the distance is zero, assume a miss
14149c2daa00SOllivier Robert 	 * '_'; if the distance is not more than half the total number
14159c2daa00SOllivier Robert 	 * of occurrences, assume a soft error '*'; if two different
14169c2daa00SOllivier Robert 	 * digits with the same distance are found, assume a hard error
14179c2daa00SOllivier Robert 	 * '='. These will later cause a format error when the timecode
14189c2daa00SOllivier Robert 	 * is interpreted. The decoding distance is defined as the
14199c2daa00SOllivier Robert 	 * minimum distance over the first nine digits. The tenth digit
14209c2daa00SOllivier Robert 	 * varies over the seconds, so we don't count it.
1421a151a66cSOllivier Robert 	 */
14229c2daa00SOllivier Robert 	mindist = 16;
1423a151a66cSOllivier Robert 	for (i = 0; i < 9; i++) {
1424c0b746e5SOllivier Robert 		val1 = val2 = 0;
1425c0b746e5SOllivier Robert 		k = 0;
1426c0b746e5SOllivier Robert 		for (j = 0; j < 16; j++) {
1427c0b746e5SOllivier Robert 			temp = up->decode[i][j] + up->decode[i + 10][j];
1428c0b746e5SOllivier Robert 			if (temp > val1) {
1429c0b746e5SOllivier Robert 				val2 = val1;
1430c0b746e5SOllivier Robert 				val1 = temp;
1431c0b746e5SOllivier Robert 				k = j;
1432c0b746e5SOllivier Robert 			}
1433c0b746e5SOllivier Robert 		}
1434a151a66cSOllivier Robert 		if (val1 == 0)
1435a151a66cSOllivier Robert 			code[i] = HEX_MISS;
1436a151a66cSOllivier Robert 		else if (val1 == val2)
1437c0b746e5SOllivier Robert 			code[i] = HEX_HARD;
1438a151a66cSOllivier Robert 		else if (val1 <= up->burstcnt)
1439c0b746e5SOllivier Robert 			code[i] = HEX_SOFT;
1440c0b746e5SOllivier Robert 		else
1441c0b746e5SOllivier Robert 			code[i] = k;
14429c2daa00SOllivier Robert 		if (val1 < mindist)
14439c2daa00SOllivier Robert 			mindist = val1;
1444c0b746e5SOllivier Robert 		code[i] = hexchar[code[i]];
1445c0b746e5SOllivier Robert 	}
1446c0b746e5SOllivier Robert 	code[i] = 0;
1447a151a66cSOllivier Robert 
1448a151a66cSOllivier Robert 	/*
14499c2daa00SOllivier Robert 	 * A valid timecode requires a minimum distance at least half
14509c2daa00SOllivier Robert 	 * the total number of occurrences. A valid timecode also
14519c2daa00SOllivier Robert 	 * requires at least 20 valid timestamps.
1452a151a66cSOllivier Robert 	 */
14539c2daa00SOllivier Robert 	if (up->burstcnt < MINBURST || mindist < up->burstcnt)
1454a151a66cSOllivier Robert 		up->status |= DECODE;
1455c0b746e5SOllivier Robert 	if (up->ntstamp < MINSTAMP)
1456a151a66cSOllivier Robert 		up->status |= STAMP;
1457c0b746e5SOllivier Robert 
1458c0b746e5SOllivier Robert 	/*
1459c0b746e5SOllivier Robert 	 * Compute the timecode timestamp from the days, hours and
1460c0b746e5SOllivier Robert 	 * minutes of the timecode. Use clocktime() for the aggregate
1461c0b746e5SOllivier Robert 	 * minutes and the minute offset computed from the burst
1462c0b746e5SOllivier Robert 	 * seconds. Note that this code relies on the filesystem time
1463c0b746e5SOllivier Robert 	 * for the years and does not use the years of the timecode.
1464c0b746e5SOllivier Robert 	 */
1465a151a66cSOllivier Robert 	if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
1466a151a66cSOllivier Robert 	    &pp->hour, &pp->minute) != 4) {
1467a151a66cSOllivier Robert 		up->status |= AFORMAT;
1468a151a66cSOllivier Robert 		return (0);
1469a151a66cSOllivier Robert 	}
1470a151a66cSOllivier Robert 	if (up->status & (DECODE | STAMP)) {
1471a151a66cSOllivier Robert 		up->errflg = CEVNT_BADREPLY;
1472a151a66cSOllivier Robert 		return (0);
1473c0b746e5SOllivier Robert 	}
14749c2daa00SOllivier Robert 	return (mindist * 100. / (2. * up->burstcnt));
1475c0b746e5SOllivier Robert }
1476c0b746e5SOllivier Robert 
1477c0b746e5SOllivier Robert 
1478c0b746e5SOllivier Robert /*
1479c0b746e5SOllivier Robert  * chu_clear - clear decoding matrix
1480c0b746e5SOllivier Robert  */
1481c0b746e5SOllivier Robert static void
1482c0b746e5SOllivier Robert chu_clear(
1483a151a66cSOllivier Robert 	struct peer *peer	/* peer structure pointer */
1484c0b746e5SOllivier Robert 	)
1485c0b746e5SOllivier Robert {
1486c0b746e5SOllivier Robert 	struct refclockproc *pp;
1487c0b746e5SOllivier Robert 	struct chuunit *up;
1488a151a66cSOllivier Robert 	int	i, j;
1489c0b746e5SOllivier Robert 
1490c0b746e5SOllivier Robert 	pp = peer->procptr;
1491c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1492c0b746e5SOllivier Robert 
1493c0b746e5SOllivier Robert 	/*
1494a151a66cSOllivier Robert 	 * Clear stuff for the minute.
1495c0b746e5SOllivier Robert 	 */
1496a151a66cSOllivier Robert 	up->ndx = up->prevsec = 0;
14979c2daa00SOllivier Robert 	up->burstcnt = up->ntstamp = 0;
14989c2daa00SOllivier Robert 	up->status &= INSYNC;
1499c0b746e5SOllivier Robert 	for (i = 0; i < 20; i++) {
1500c0b746e5SOllivier Robert 		for (j = 0; j < 16; j++)
1501c0b746e5SOllivier Robert 			up->decode[i][j] = 0;
1502c0b746e5SOllivier Robert 	}
1503c0b746e5SOllivier Robert }
1504c0b746e5SOllivier Robert 
15059c2daa00SOllivier Robert #ifdef ICOM
15069c2daa00SOllivier Robert /*
15079c2daa00SOllivier Robert  * chu_newchan - called once per minute to find the best channel;
15089c2daa00SOllivier Robert  * returns zero on success, nonzero if ICOM error.
15099c2daa00SOllivier Robert  */
15109c2daa00SOllivier Robert static int
15119c2daa00SOllivier Robert chu_newchan(
15129c2daa00SOllivier Robert 	struct peer *peer,
15139c2daa00SOllivier Robert 	double	met
15149c2daa00SOllivier Robert 	)
15159c2daa00SOllivier Robert {
15169c2daa00SOllivier Robert 	struct chuunit *up;
15179c2daa00SOllivier Robert 	struct refclockproc *pp;
15189c2daa00SOllivier Robert 	struct xmtr *sp;
15199c2daa00SOllivier Robert 	char	tbuf[80];	/* trace buffer */
15209c2daa00SOllivier Robert 	int	rval;
15219c2daa00SOllivier Robert 	double	metric;
15229c2daa00SOllivier Robert 	int	i, j;
15239c2daa00SOllivier Robert 
15249c2daa00SOllivier Robert 	pp = peer->procptr;
15259c2daa00SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
15269c2daa00SOllivier Robert 
15279c2daa00SOllivier Robert 	/*
15289c2daa00SOllivier Robert 	 * The radio can be tuned to three channels: 0 (3330 kHz), 1
15299c2daa00SOllivier Robert 	 * (7335 kHz) and 2 (14670 kHz). There are five one-minute
15309c2daa00SOllivier Robert 	 * dwells in each cycle. During the first dwell the radio is
15319c2daa00SOllivier Robert 	 * tuned to one of three probe channels; during the remaining
15329c2daa00SOllivier Robert 	 * four dwells the radio is tuned to the data channel. The probe
15339c2daa00SOllivier Robert 	 * channel is selects as the least recently used. At the end of
15349c2daa00SOllivier Robert 	 * each dwell the channel metrics are measured and the highest
15359c2daa00SOllivier Robert 	 * one is selected as the data channel.
15369c2daa00SOllivier Robert 	 */
15379c2daa00SOllivier Robert 	if (up->fd_icom <= 0)
15389c2daa00SOllivier Robert 		return (0);
15399c2daa00SOllivier Robert 
15409c2daa00SOllivier Robert 	sp = &up->xmtr[up->achan];
15419c2daa00SOllivier Robert 	sp->metric -= sp->integ[sp->iptr];
15429c2daa00SOllivier Robert 	sp->integ[sp->iptr] = met;
15439c2daa00SOllivier Robert 	sp->metric += sp->integ[sp->iptr];
15449c2daa00SOllivier Robert 	sp->iptr = (sp->iptr + 1) % ISTAGE;
15459c2daa00SOllivier Robert 	metric = 0;
15469c2daa00SOllivier Robert 	j = 0;
15479c2daa00SOllivier Robert 	for (i = 0; i < NCHAN; i++) {
15489c2daa00SOllivier Robert 		up->xmtr[i].probe++;
15499c2daa00SOllivier Robert 		if (i == up->achan)
15509c2daa00SOllivier Robert 			up->xmtr[i].probe = 0;
15519c2daa00SOllivier Robert 		if (up->xmtr[i].metric < metric)
15529c2daa00SOllivier Robert 			continue;
15539c2daa00SOllivier Robert 		metric = up->xmtr[i].metric;
15549c2daa00SOllivier Robert 		j = i;
15559c2daa00SOllivier Robert 	}
15569c2daa00SOllivier Robert 	if (j != up->chan && metric > 0) {
15579c2daa00SOllivier Robert 		up->chan = j;
15589c2daa00SOllivier Robert 		sprintf(tbuf, "chu: QSY to %.3f MHz metric %.0f",
15599c2daa00SOllivier Robert 		    qsy[up->chan], metric);
15609c2daa00SOllivier Robert 		if (pp->sloppyclockflag & CLK_FLAG4)
15619c2daa00SOllivier Robert 			record_clock_stats(&peer->srcadr, tbuf);
15629c2daa00SOllivier Robert #ifdef DEBUG
15639c2daa00SOllivier Robert 		if (debug)
15649c2daa00SOllivier Robert 			printf("%s\n", tbuf);
15659c2daa00SOllivier Robert #endif
15669c2daa00SOllivier Robert 	}
15679c2daa00SOllivier Robert 
15689c2daa00SOllivier Robert 	/*
15699c2daa00SOllivier Robert 	 * Start the next dwell. We speed up the initial sync a little.
15709c2daa00SOllivier Robert 	 * If not in sync and no bursts were heard the previous dwell,
15719c2daa00SOllivier Robert 	 * restart the probe.
15729c2daa00SOllivier Robert 	 */
15739c2daa00SOllivier Robert 	rval = 0;
15749c2daa00SOllivier Robert 	if (up->burstcnt == 0 && !(up->status & INSYNC))
15759c2daa00SOllivier Robert 		up->dwell = 0;
15769c2daa00SOllivier Robert #ifdef DEBUG
15779c2daa00SOllivier Robert 	if (debug)
15789c2daa00SOllivier Robert 		printf(
15799c2daa00SOllivier Robert 		    "chu: at %ld dwell %d achan %d metric %.0f chan %d\n",
15809c2daa00SOllivier Robert 		    current_time, up->dwell, up->achan, sp->metric,
15819c2daa00SOllivier Robert 		    up->chan);
15829c2daa00SOllivier Robert #endif
15839c2daa00SOllivier Robert 	if (up->dwell == 0) {
15849c2daa00SOllivier Robert 		rval = 0;
15859c2daa00SOllivier Robert 		for (i = 0; i < NCHAN; i++) {
15869c2daa00SOllivier Robert 			if (up->xmtr[i].probe < rval)
15879c2daa00SOllivier Robert 				continue;
15889c2daa00SOllivier Robert 			rval = up->xmtr[i].probe;
15899c2daa00SOllivier Robert 			up->achan = i;
15909c2daa00SOllivier Robert 		}
15919c2daa00SOllivier Robert 		rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
15929c2daa00SOllivier Robert 		    qsy[up->achan] + TUNE);
15939c2daa00SOllivier Robert #ifdef DEBUG
15949c2daa00SOllivier Robert 		if (debug)
15959c2daa00SOllivier Robert 			printf("chu: at %ld probe channel %d\n",
15969c2daa00SOllivier Robert 		    current_time, up->achan);
15979c2daa00SOllivier Robert #endif
15989c2daa00SOllivier Robert 	} else {
15999c2daa00SOllivier Robert 		if (up->achan != up->chan) {
16009c2daa00SOllivier Robert 			rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
16019c2daa00SOllivier Robert 			    qsy[up->chan] + TUNE);
16029c2daa00SOllivier Robert 			up->achan = up->chan;
16039c2daa00SOllivier Robert 		}
16049c2daa00SOllivier Robert 	}
16059c2daa00SOllivier Robert 	sprintf(up->ident, "CHU%d", up->achan);
16069c2daa00SOllivier Robert 	memcpy(&peer->refid, up->ident, 4);
16079c2daa00SOllivier Robert 	up->dwell = (up->dwell + 1) % DWELL;
16089c2daa00SOllivier Robert 	return (rval);
16099c2daa00SOllivier Robert }
16109c2daa00SOllivier Robert #endif /* ICOM */
1611c0b746e5SOllivier Robert 
1612c0b746e5SOllivier Robert /*
1613c0b746e5SOllivier Robert  * chu_dist - determine the distance of two octet arguments
1614c0b746e5SOllivier Robert  */
1615c0b746e5SOllivier Robert static int
1616c0b746e5SOllivier Robert chu_dist(
1617c0b746e5SOllivier Robert 	int	x,		/* an octet of bits */
1618c0b746e5SOllivier Robert 	int	y		/* another octet of bits */
1619c0b746e5SOllivier Robert 	)
1620c0b746e5SOllivier Robert {
1621c0b746e5SOllivier Robert 	int	val;		/* bit count */
1622a151a66cSOllivier Robert 	int	temp;
1623a151a66cSOllivier Robert 	int	i;
1624c0b746e5SOllivier Robert 
1625c0b746e5SOllivier Robert 	/*
1626c0b746e5SOllivier Robert 	 * The distance is determined as the weight of the exclusive OR
1627c0b746e5SOllivier Robert 	 * of the two arguments. The weight is determined by the number
1628c0b746e5SOllivier Robert 	 * of one bits in the result. Each one bit increases the weight,
1629c0b746e5SOllivier Robert 	 * while each zero bit decreases it.
1630c0b746e5SOllivier Robert 	 */
1631c0b746e5SOllivier Robert 	temp = x ^ y;
1632c0b746e5SOllivier Robert 	val = 0;
1633c0b746e5SOllivier Robert 	for (i = 0; i < 8; i++) {
1634c0b746e5SOllivier Robert 		if ((temp & 0x1) == 0)
1635c0b746e5SOllivier Robert 			val++;
1636c0b746e5SOllivier Robert 		else
1637c0b746e5SOllivier Robert 			val--;
1638c0b746e5SOllivier Robert 		temp >>= 1;
1639c0b746e5SOllivier Robert 	}
1640c0b746e5SOllivier Robert 	return (val);
1641c0b746e5SOllivier Robert }
1642c0b746e5SOllivier Robert 
1643c0b746e5SOllivier Robert 
1644224ba2bdSOllivier Robert #ifdef HAVE_AUDIO
1645c0b746e5SOllivier Robert /*
1646c0b746e5SOllivier Robert  * chu_gain - adjust codec gain
1647c0b746e5SOllivier Robert  *
1648c0b746e5SOllivier Robert  * This routine is called once each second. If the signal envelope
1649c0b746e5SOllivier Robert  * amplitude is too low, the codec gain is bumped up by four units; if
1650c0b746e5SOllivier Robert  * too high, it is bumped down. The decoder is relatively insensitive to
1651c0b746e5SOllivier Robert  * amplitude, so this crudity works just fine. The input port is set and
1652c0b746e5SOllivier Robert  * the error flag is cleared, mostly to be ornery.
1653c0b746e5SOllivier Robert  */
1654c0b746e5SOllivier Robert static void
1655c0b746e5SOllivier Robert chu_gain(
1656c0b746e5SOllivier Robert 	struct peer *peer	/* peer structure pointer */
1657c0b746e5SOllivier Robert 	)
1658c0b746e5SOllivier Robert {
1659c0b746e5SOllivier Robert 	struct refclockproc *pp;
1660c0b746e5SOllivier Robert 	struct chuunit *up;
1661c0b746e5SOllivier Robert 
1662c0b746e5SOllivier Robert 	pp = peer->procptr;
1663c0b746e5SOllivier Robert 	up = (struct chuunit *)pp->unitptr;
1664c0b746e5SOllivier Robert 
1665c0b746e5SOllivier Robert 	/*
1666c0b746e5SOllivier Robert 	 * Apparently, the codec uses only the high order bits of the
1667c0b746e5SOllivier Robert 	 * gain control field. Thus, it may take awhile for changes to
1668a151a66cSOllivier Robert 	 * wiggle the hardware bits.
1669c0b746e5SOllivier Robert 	 */
1670c0b746e5SOllivier Robert 	if (up->clipcnt == 0) {
1671c0b746e5SOllivier Robert 		up->gain += 4;
16729c2daa00SOllivier Robert 		if (up->gain > MAXGAIN)
16739c2daa00SOllivier Robert 			up->gain = MAXGAIN;
16749c2daa00SOllivier Robert 	} else if (up->clipcnt > MAXCLP) {
1675c0b746e5SOllivier Robert 		up->gain -= 4;
1676a151a66cSOllivier Robert 		if (up->gain < 0)
1677a151a66cSOllivier Robert 			up->gain = 0;
1678c0b746e5SOllivier Robert 	}
16799c2daa00SOllivier Robert 	audio_gain(up->gain, up->mongain, up->port);
1680a151a66cSOllivier Robert 	up->clipcnt = 0;
1681c0b746e5SOllivier Robert }
1682224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */
1683c0b746e5SOllivier Robert 
1684a151a66cSOllivier Robert 
1685c0b746e5SOllivier Robert #else
1686c0b746e5SOllivier Robert int refclock_chu_bs;
1687c0b746e5SOllivier Robert #endif /* REFCLOCK */
1688