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, <emp); 6519c2daa00SOllivier Robert L_SUB(&rbufp->recv_time, <emp); 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