1*eabc0478Schristos /* $NetBSD: refclock_chu.c,v 1.11 2024/08/18 20:47:18 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * refclock_chu - clock driver for Canadian CHU time/frequency station 5abb0f93cSkardel */ 6abb0f93cSkardel #ifdef HAVE_CONFIG_H 7abb0f93cSkardel #include <config.h> 8abb0f93cSkardel #endif 9abb0f93cSkardel 102950cc38Schristos #include "ntp_types.h" 112950cc38Schristos 12abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_CHU) 13abb0f93cSkardel 14abb0f93cSkardel #include "ntpd.h" 15abb0f93cSkardel #include "ntp_io.h" 16abb0f93cSkardel #include "ntp_refclock.h" 17abb0f93cSkardel #include "ntp_calendar.h" 18abb0f93cSkardel #include "ntp_stdlib.h" 19abb0f93cSkardel 20abb0f93cSkardel #include <stdio.h> 21abb0f93cSkardel #include <ctype.h> 22abb0f93cSkardel #include <math.h> 23abb0f93cSkardel 24abb0f93cSkardel #ifdef HAVE_AUDIO 25abb0f93cSkardel #include "audio.h" 26abb0f93cSkardel #endif /* HAVE_AUDIO */ 27abb0f93cSkardel 28abb0f93cSkardel #define ICOM 1 /* undefine to suppress ICOM code */ 29abb0f93cSkardel 30abb0f93cSkardel #ifdef ICOM 31abb0f93cSkardel #include "icom.h" 32abb0f93cSkardel #endif /* ICOM */ 33abb0f93cSkardel /* 34abb0f93cSkardel * Audio CHU demodulator/decoder 35abb0f93cSkardel * 36abb0f93cSkardel * This driver synchronizes the computer time using data encoded in 37abb0f93cSkardel * radio transmissions from Canadian time/frequency station CHU in 38abb0f93cSkardel * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz, 39abb0f93cSkardel * 7850 kHz and 14670 kHz in upper sideband, compatible AM mode. An 40abb0f93cSkardel * ordinary shortwave receiver can be tuned manually to one of these 41abb0f93cSkardel * frequencies or, in the case of ICOM receivers, the receiver can be 42abb0f93cSkardel * tuned automatically as propagation conditions change throughout the 43abb0f93cSkardel * day and season. 44abb0f93cSkardel * 45abb0f93cSkardel * The driver requires an audio codec or sound card with sampling rate 8 46abb0f93cSkardel * kHz and mu-law companding. This is the same standard as used by the 47abb0f93cSkardel * telephone industry and is supported by most hardware and operating 48abb0f93cSkardel * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this 49abb0f93cSkardel * implementation, only one audio driver and codec can be supported on a 50abb0f93cSkardel * single machine. 51abb0f93cSkardel * 52abb0f93cSkardel * The driver can be compiled to use a Bell 103 compatible modem or 53abb0f93cSkardel * modem chip to receive the radio signal and demodulate the data. 54abb0f93cSkardel * Alternatively, the driver can be compiled to use the audio codec of 55abb0f93cSkardel * the workstation or another with compatible audio drivers. In the 56abb0f93cSkardel * latter case, the driver implements the modem using DSP routines, so 57abb0f93cSkardel * the radio can be connected directly to either the microphone on line 58abb0f93cSkardel * input port. In either case, the driver decodes the data using a 59abb0f93cSkardel * maximum-likelihood technique which exploits the considerable degree 60abb0f93cSkardel * of redundancy available to maximize accuracy and minimize errors. 61abb0f93cSkardel * 62abb0f93cSkardel * The CHU time broadcast includes an audio signal compatible with the 63abb0f93cSkardel * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). The signal 64abb0f93cSkardel * consists of nine, ten-character bursts transmitted at 300 bps between 65abb0f93cSkardel * seconds 31 and 39 of each minute. Each character consists of eight 66abb0f93cSkardel * data bits plus one start bit and two stop bits to encode two hex 67abb0f93cSkardel * digits. The burst data consist of five characters (ten hex digits) 68abb0f93cSkardel * followed by a repeat of these characters. In format A, the characters 69abb0f93cSkardel * are repeated in the same polarity; in format B, the characters are 70abb0f93cSkardel * repeated in the opposite polarity. 71abb0f93cSkardel * 72abb0f93cSkardel * Format A bursts are sent at seconds 32 through 39 of the minute in 73abb0f93cSkardel * hex digits (nibble swapped) 74abb0f93cSkardel * 75abb0f93cSkardel * 6dddhhmmss6dddhhmmss 76abb0f93cSkardel * 77abb0f93cSkardel * The first ten digits encode a frame marker (6) followed by the day 78abb0f93cSkardel * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since 79abb0f93cSkardel * format A bursts are sent during the third decade of seconds the tens 80abb0f93cSkardel * digit of ss is always 3. The driver uses this to determine correct 81abb0f93cSkardel * burst synchronization. These digits are then repeated with the same 82abb0f93cSkardel * polarity. 83abb0f93cSkardel * 84abb0f93cSkardel * Format B bursts are sent at second 31 of the minute in hex digits 85abb0f93cSkardel * 86abb0f93cSkardel * xdyyyyttaaxdyyyyttaa 87abb0f93cSkardel * 88abb0f93cSkardel * The first ten digits encode a code (x described below) followed by 89abb0f93cSkardel * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI - 90abb0f93cSkardel * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These 91abb0f93cSkardel * digits are then repeated with inverted polarity. 92abb0f93cSkardel * 93abb0f93cSkardel * The x is coded 94abb0f93cSkardel * 95abb0f93cSkardel * 1 Sign of DUT (0 = +) 96abb0f93cSkardel * 2 Leap second warning. One second will be added. 97abb0f93cSkardel * 4 Leap second warning. One second will be subtracted. 98abb0f93cSkardel * 8 Even parity bit for this nibble. 99abb0f93cSkardel * 100abb0f93cSkardel * By design, the last stop bit of the last character in the burst 101abb0f93cSkardel * coincides with 0.5 second. Since characters have 11 bits and are 102abb0f93cSkardel * transmitted at 300 bps, the last stop bit of the first character 103abb0f93cSkardel * coincides with 0.5 - 9 * 11/300 = 0.170 second. Depending on the 104abb0f93cSkardel * UART, character interrupts can vary somewhere between the end of bit 105abb0f93cSkardel * 9 and end of bit 11. These eccentricities can be corrected along with 106abb0f93cSkardel * the radio propagation delay using fudge time 1. 107abb0f93cSkardel * 108abb0f93cSkardel * Debugging aids 109abb0f93cSkardel * 110abb0f93cSkardel * The timecode format used for debugging and data recording includes 111abb0f93cSkardel * data helpful in diagnosing problems with the radio signal and serial 112abb0f93cSkardel * connections. With debugging enabled (-d on the ntpd command line), 113abb0f93cSkardel * the driver produces one line for each burst in two formats 114abb0f93cSkardel * corresponding to format A and B.Each line begins with the format code 115abb0f93cSkardel * chuA or chuB followed by the status code and signal level (0-9999). 116abb0f93cSkardel * The remainder of the line is as follows. 117abb0f93cSkardel * 118abb0f93cSkardel * Following is format A: 119abb0f93cSkardel * 120abb0f93cSkardel * n b f s m code 121abb0f93cSkardel * 122abb0f93cSkardel * where n is the number of characters in the burst (0-10), b the burst 123abb0f93cSkardel * distance (0-40), f the field alignment (-1, 0, 1), s the 124abb0f93cSkardel * synchronization distance (0-16), m the burst number (2-9) and code 125abb0f93cSkardel * the burst characters as received. Note that the hex digits in each 126abb0f93cSkardel * character are reversed, so the burst 127abb0f93cSkardel * 128abb0f93cSkardel * 10 38 0 16 9 06851292930685129293 129abb0f93cSkardel * 130abb0f93cSkardel * is interpreted as containing 10 characters with burst distance 38, 131abb0f93cSkardel * field alignment 0, synchronization distance 16 and burst number 9. 132abb0f93cSkardel * The nibble-swapped timecode shows day 58, hour 21, minute 29 and 133abb0f93cSkardel * second 39. 134abb0f93cSkardel * 135abb0f93cSkardel * Following is format B: 136abb0f93cSkardel * 137abb0f93cSkardel * n b s code 138abb0f93cSkardel * 139abb0f93cSkardel * where n is the number of characters in the burst (0-10), b the burst 140abb0f93cSkardel * distance (0-40), s the synchronization distance (0-40) and code the 141abb0f93cSkardel * burst characters as received. Note that the hex digits in each 142abb0f93cSkardel * character are reversed and the last ten digits inverted, so the burst 143abb0f93cSkardel * 144abb0f93cSkardel * 10 40 1091891300ef6e76ec 145abb0f93cSkardel * 146abb0f93cSkardel * is interpreted as containing 10 characters with burst distance 40. 147abb0f93cSkardel * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI 148abb0f93cSkardel * - UTC 31 seconds. 149abb0f93cSkardel * 150abb0f93cSkardel * Each line is preceeded by the code chuA or chuB, as appropriate. If 151abb0f93cSkardel * the audio driver is compiled, the current gain (0-255) and relative 152abb0f93cSkardel * signal level (0-9999) follow the code. The receiver volume control 153abb0f93cSkardel * should be set so that the gain is somewhere near the middle of the 154abb0f93cSkardel * range 0-255, which results in a signal level near 1000. 155abb0f93cSkardel * 156abb0f93cSkardel * In addition to the above, the reference timecode is updated and 157abb0f93cSkardel * written to the clockstats file and debug score after the last burst 158abb0f93cSkardel * received in the minute. The format is 159abb0f93cSkardel * 160abb0f93cSkardel * sq yyyy ddd hh:mm:ss l s dd t agc ident m b 161abb0f93cSkardel * 162abb0f93cSkardel * s '?' before first synchronized and ' ' after that 163abb0f93cSkardel * q status code (see below) 164abb0f93cSkardel * yyyy year 165abb0f93cSkardel * ddd day of year 166abb0f93cSkardel * hh:mm:ss time of day 167abb0f93cSkardel * l leap second indicator (space, L or D) 168abb0f93cSkardel * dst Canadian daylight code (opaque) 169abb0f93cSkardel * t number of minutes since last synchronized 170abb0f93cSkardel * agc audio gain (0 - 255) 171abb0f93cSkardel * ident identifier (CHU0 3330 kHz, CHU1 7850 kHz, CHU2 14670 kHz) 172abb0f93cSkardel * m signal metric (0 - 100) 173abb0f93cSkardel * b number of timecodes for the previous minute (0 - 59) 174abb0f93cSkardel * 175abb0f93cSkardel * Fudge factors 176abb0f93cSkardel * 177abb0f93cSkardel * For accuracies better than the low millisceconds, fudge time1 can be 178abb0f93cSkardel * set to the radio propagation delay from CHU to the receiver. This can 179abb0f93cSkardel * be done conviently using the minimuf program. 180abb0f93cSkardel * 181abb0f93cSkardel * Fudge flag4 causes the dubugging output described above to be 182abb0f93cSkardel * recorded in the clockstats file. When the audio driver is compiled, 183abb0f93cSkardel * fudge flag2 selects the audio input port, where 0 is the mike port 184abb0f93cSkardel * (default) and 1 is the line-in port. It does not seem useful to 185abb0f93cSkardel * select the compact disc player port. Fudge flag3 enables audio 186abb0f93cSkardel * monitoring of the input signal. For this purpose, the monitor gain is 187abb0f93cSkardel * set to a default value. 188abb0f93cSkardel * 189abb0f93cSkardel * The audio codec code is normally compiled in the driver if the 190abb0f93cSkardel * architecture supports it (HAVE_AUDIO defined), but is used only if 191abb0f93cSkardel * the link /dev/chu_audio is defined and valid. The serial port code is 192abb0f93cSkardel * always compiled in the driver, but is used only if the autdio codec 193abb0f93cSkardel * is not available and the link /dev/chu%d is defined and valid. 194abb0f93cSkardel * 195abb0f93cSkardel * The ICOM code is normally compiled in the driver if selected (ICOM 196abb0f93cSkardel * defined), but is used only if the link /dev/icom%d is defined and 197abb0f93cSkardel * valid and the mode keyword on the server configuration command 198abb0f93cSkardel * specifies a nonzero mode (ICOM ID select code). The C-IV speed is 199abb0f93cSkardel * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps 200abb0f93cSkardel * if one. The C-IV trace is turned on if the debug level is greater 201abb0f93cSkardel * than one. 202abb0f93cSkardel * 203abb0f93cSkardel * Alarm codes 204abb0f93cSkardel * 205abb0f93cSkardel * CEVNT_BADTIME invalid date or time 206abb0f93cSkardel * CEVNT_PROP propagation failure - no stations heard 207abb0f93cSkardel */ 208abb0f93cSkardel /* 209abb0f93cSkardel * Interface definitions 210abb0f93cSkardel */ 211abb0f93cSkardel #define SPEED232 B300 /* uart speed (300 baud) */ 212abb0f93cSkardel #define PRECISION (-10) /* precision assumed (about 1 ms) */ 213abb0f93cSkardel #define REFID "CHU" /* reference ID */ 214abb0f93cSkardel #define DEVICE "/dev/chu%d" /* device name and unit */ 215abb0f93cSkardel #define SPEED232 B300 /* UART speed (300 baud) */ 216abb0f93cSkardel #ifdef ICOM 217abb0f93cSkardel #define TUNE .001 /* offset for narrow filter (MHz) */ 218abb0f93cSkardel #define DWELL 5 /* minutes in a dwell */ 219abb0f93cSkardel #define NCHAN 3 /* number of channels */ 220abb0f93cSkardel #define ISTAGE 3 /* number of integrator stages */ 221abb0f93cSkardel #endif /* ICOM */ 222abb0f93cSkardel 223abb0f93cSkardel #ifdef HAVE_AUDIO 224abb0f93cSkardel /* 225abb0f93cSkardel * Audio demodulator definitions 226abb0f93cSkardel */ 227abb0f93cSkardel #define SECOND 8000 /* nominal sample rate (Hz) */ 228abb0f93cSkardel #define BAUD 300 /* modulation rate (bps) */ 229abb0f93cSkardel #define OFFSET 128 /* companded sample offset */ 230abb0f93cSkardel #define SIZE 256 /* decompanding table size */ 231abb0f93cSkardel #define MAXAMP 6000. /* maximum signal level */ 232abb0f93cSkardel #define MAXCLP 100 /* max clips above reference per s */ 233abb0f93cSkardel #define SPAN 800. /* min envelope span */ 234abb0f93cSkardel #define LIMIT 1000. /* soft limiter threshold */ 235abb0f93cSkardel #define AGAIN 6. /* baseband gain */ 236abb0f93cSkardel #define LAG 10 /* discriminator lag */ 237abb0f93cSkardel #define DEVICE_AUDIO "/dev/audio" /* device name */ 238abb0f93cSkardel #define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */ 239abb0f93cSkardel #define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */ 240abb0f93cSkardel #else 241abb0f93cSkardel #define DESCRIPTION "CHU Modem Receiver" /* WRU */ 242abb0f93cSkardel #endif /* HAVE_AUDIO */ 243abb0f93cSkardel 244abb0f93cSkardel /* 245abb0f93cSkardel * Decoder definitions 246abb0f93cSkardel */ 247abb0f93cSkardel #define CHAR (11. / 300.) /* character time (s) */ 248abb0f93cSkardel #define BURST 11 /* max characters per burst */ 2492950cc38Schristos #define MINCHARS 9 /* min characters per burst */ 250abb0f93cSkardel #define MINDIST 28 /* min burst distance (of 40) */ 251abb0f93cSkardel #define MINSYNC 8 /* min sync distance (of 16) */ 252abb0f93cSkardel #define MINSTAMP 20 /* min timestamps (of 60) */ 253abb0f93cSkardel #define MINMETRIC 50 /* min channel metric (of 160) */ 254abb0f93cSkardel 255abb0f93cSkardel /* 256abb0f93cSkardel * The on-time synchronization point for the driver is the last stop bit 257abb0f93cSkardel * of the first character 170 ms. The modem delay is 0.8 ms, while the 258abb0f93cSkardel * receiver delay is approxmately 4.7 ms at 2125 Hz. The fudge value 1.3 259abb0f93cSkardel * ms due to the codec and other causes was determined by calibrating to 260abb0f93cSkardel * a PPS signal from a GPS receiver. The additional propagation delay 261abb0f93cSkardel * specific to each receiver location can be programmed in the fudge 262abb0f93cSkardel * time1. 263abb0f93cSkardel * 264abb0f93cSkardel * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are 265abb0f93cSkardel * generally within 0.5 ms short term with 0.3 ms jitter. The long-term 266abb0f93cSkardel * offsets vary up to 0.3 ms due to ionospheric layer height variations. 267abb0f93cSkardel * The processor load due to the driver is 0.4 percent. 268abb0f93cSkardel */ 269abb0f93cSkardel #define PDELAY ((170 + .8 + 4.7 + 1.3) / 1000) /* system delay (s) */ 270abb0f93cSkardel 271abb0f93cSkardel /* 272abb0f93cSkardel * Status bits (status) 273abb0f93cSkardel */ 274abb0f93cSkardel #define RUNT 0x0001 /* runt burst */ 275abb0f93cSkardel #define NOISE 0x0002 /* noise burst */ 276abb0f93cSkardel #define BFRAME 0x0004 /* invalid format B frame sync */ 277abb0f93cSkardel #define BFORMAT 0x0008 /* invalid format B data */ 278abb0f93cSkardel #define AFRAME 0x0010 /* invalid format A frame sync */ 279abb0f93cSkardel #define AFORMAT 0x0020 /* invalid format A data */ 280abb0f93cSkardel #define DECODE 0x0040 /* invalid data decode */ 281abb0f93cSkardel #define STAMP 0x0080 /* too few timestamps */ 282abb0f93cSkardel #define AVALID 0x0100 /* valid A frame */ 283abb0f93cSkardel #define BVALID 0x0200 /* valid B frame */ 284abb0f93cSkardel #define INSYNC 0x0400 /* clock synchronized */ 285abb0f93cSkardel #define METRIC 0x0800 /* one or more stations heard */ 286abb0f93cSkardel 287abb0f93cSkardel /* 288abb0f93cSkardel * Alarm status bits (alarm) 289abb0f93cSkardel * 290abb0f93cSkardel * These alarms are set at the end of a minute in which at least one 291abb0f93cSkardel * burst was received. SYNERR is raised if the AFRAME or BFRAME status 292abb0f93cSkardel * bits are set during the minute, FMTERR is raised if the AFORMAT or 293abb0f93cSkardel * BFORMAT status bits are set, DECERR is raised if the DECODE status 294abb0f93cSkardel * bit is set and TSPERR is raised if the STAMP status bit is set. 295abb0f93cSkardel */ 296abb0f93cSkardel #define SYNERR 0x01 /* frame sync error */ 297abb0f93cSkardel #define FMTERR 0x02 /* data format error */ 298abb0f93cSkardel #define DECERR 0x04 /* data decoding error */ 299abb0f93cSkardel #define TSPERR 0x08 /* insufficient data */ 300abb0f93cSkardel 301abb0f93cSkardel #ifdef HAVE_AUDIO 302abb0f93cSkardel /* 303abb0f93cSkardel * Maximum-likelihood UART structure. There are eight of these 304abb0f93cSkardel * corresponding to the number of phases. 305abb0f93cSkardel */ 306abb0f93cSkardel struct surv { 307abb0f93cSkardel l_fp cstamp; /* last bit timestamp */ 308abb0f93cSkardel double shift[12]; /* sample shift register */ 309abb0f93cSkardel double span; /* shift register envelope span */ 310abb0f93cSkardel double dist; /* sample distance */ 311abb0f93cSkardel int uart; /* decoded character */ 312abb0f93cSkardel }; 313abb0f93cSkardel #endif /* HAVE_AUDIO */ 314abb0f93cSkardel 315abb0f93cSkardel #ifdef ICOM 316abb0f93cSkardel /* 317abb0f93cSkardel * CHU station structure. There are three of these corresponding to the 318abb0f93cSkardel * three frequencies. 319abb0f93cSkardel */ 320abb0f93cSkardel struct xmtr { 321abb0f93cSkardel double integ[ISTAGE]; /* circular integrator */ 322abb0f93cSkardel double metric; /* integrator sum */ 323abb0f93cSkardel int iptr; /* integrator pointer */ 324abb0f93cSkardel int probe; /* dwells since last probe */ 325abb0f93cSkardel }; 326abb0f93cSkardel #endif /* ICOM */ 327abb0f93cSkardel 328abb0f93cSkardel /* 329abb0f93cSkardel * CHU unit control structure 330abb0f93cSkardel */ 331abb0f93cSkardel struct chuunit { 332abb0f93cSkardel u_char decode[20][16]; /* maximum-likelihood decoding matrix */ 333abb0f93cSkardel l_fp cstamp[BURST]; /* character timestamps */ 334abb0f93cSkardel l_fp tstamp[MAXSTAGE]; /* timestamp samples */ 335abb0f93cSkardel l_fp timestamp; /* current buffer timestamp */ 336abb0f93cSkardel l_fp laststamp; /* last buffer timestamp */ 337abb0f93cSkardel l_fp charstamp; /* character time as a l_fp */ 338abb0f93cSkardel int second; /* counts the seconds of the minute */ 339abb0f93cSkardel int errflg; /* error flags */ 340abb0f93cSkardel int status; /* status bits */ 341abb0f93cSkardel char ident[5]; /* station ID and channel */ 342abb0f93cSkardel #ifdef ICOM 343abb0f93cSkardel int fd_icom; /* ICOM file descriptor */ 344abb0f93cSkardel int chan; /* radio channel */ 345abb0f93cSkardel int dwell; /* dwell cycle */ 346abb0f93cSkardel struct xmtr xmtr[NCHAN]; /* station metric */ 347abb0f93cSkardel #endif /* ICOM */ 348abb0f93cSkardel 349abb0f93cSkardel /* 350abb0f93cSkardel * Character burst variables 351abb0f93cSkardel */ 352abb0f93cSkardel int cbuf[BURST]; /* character buffer */ 353abb0f93cSkardel int ntstamp; /* number of timestamp samples */ 354abb0f93cSkardel int ndx; /* buffer start index */ 355abb0f93cSkardel int prevsec; /* previous burst second */ 356abb0f93cSkardel int burdist; /* burst distance */ 357abb0f93cSkardel int syndist; /* sync distance */ 358abb0f93cSkardel int burstcnt; /* format A bursts this minute */ 359abb0f93cSkardel double maxsignal; /* signal level (modem only) */ 360abb0f93cSkardel int gain; /* codec gain (modem only) */ 361abb0f93cSkardel 362abb0f93cSkardel /* 363abb0f93cSkardel * Format particulars 364abb0f93cSkardel */ 365abb0f93cSkardel int leap; /* leap/dut code */ 366abb0f93cSkardel int dut; /* UTC1 correction */ 367abb0f93cSkardel int tai; /* TAI - UTC correction */ 368abb0f93cSkardel int dst; /* Canadian DST code */ 369abb0f93cSkardel 370abb0f93cSkardel #ifdef HAVE_AUDIO 371abb0f93cSkardel /* 372abb0f93cSkardel * Audio codec variables 373abb0f93cSkardel */ 374abb0f93cSkardel int fd_audio; /* audio port file descriptor */ 375abb0f93cSkardel double comp[SIZE]; /* decompanding table */ 376abb0f93cSkardel int port; /* codec port */ 377abb0f93cSkardel int mongain; /* codec monitor gain */ 378abb0f93cSkardel int clipcnt; /* sample clip count */ 379abb0f93cSkardel int seccnt; /* second interval counter */ 380abb0f93cSkardel 381abb0f93cSkardel /* 382abb0f93cSkardel * Modem variables 383abb0f93cSkardel */ 384abb0f93cSkardel l_fp tick; /* audio sample increment */ 385abb0f93cSkardel double bpf[9]; /* IIR bandpass filter */ 386abb0f93cSkardel double disc[LAG]; /* discriminator shift register */ 387abb0f93cSkardel double lpf[27]; /* FIR lowpass filter */ 388abb0f93cSkardel double monitor; /* audio monitor */ 389abb0f93cSkardel int discptr; /* discriminator pointer */ 390abb0f93cSkardel 391abb0f93cSkardel /* 392abb0f93cSkardel * Maximum-likelihood UART variables 393abb0f93cSkardel */ 394abb0f93cSkardel double baud; /* baud interval */ 395abb0f93cSkardel struct surv surv[8]; /* UART survivor structures */ 396abb0f93cSkardel int decptr; /* decode pointer */ 397abb0f93cSkardel int decpha; /* decode phase */ 398abb0f93cSkardel int dbrk; /* holdoff counter */ 399abb0f93cSkardel #endif /* HAVE_AUDIO */ 400abb0f93cSkardel }; 401abb0f93cSkardel 402abb0f93cSkardel /* 403abb0f93cSkardel * Function prototypes 404abb0f93cSkardel */ 405abb0f93cSkardel static int chu_start (int, struct peer *); 406abb0f93cSkardel static void chu_shutdown (int, struct peer *); 407abb0f93cSkardel static void chu_receive (struct recvbuf *); 408abb0f93cSkardel static void chu_second (int, struct peer *); 409abb0f93cSkardel static void chu_poll (int, struct peer *); 410abb0f93cSkardel 411abb0f93cSkardel /* 412abb0f93cSkardel * More function prototypes 413abb0f93cSkardel */ 414abb0f93cSkardel static void chu_decode (struct peer *, int, l_fp); 415abb0f93cSkardel static void chu_burst (struct peer *); 416abb0f93cSkardel static void chu_clear (struct peer *); 417abb0f93cSkardel static void chu_a (struct peer *, int); 418abb0f93cSkardel static void chu_b (struct peer *, int); 419abb0f93cSkardel static int chu_dist (int, int); 420abb0f93cSkardel static double chu_major (struct peer *); 421abb0f93cSkardel #ifdef HAVE_AUDIO 422abb0f93cSkardel static void chu_uart (struct surv *, double); 423abb0f93cSkardel static void chu_rf (struct peer *, double); 424abb0f93cSkardel static void chu_gain (struct peer *); 425abb0f93cSkardel static void chu_audio_receive (struct recvbuf *rbufp); 426abb0f93cSkardel #endif /* HAVE_AUDIO */ 427abb0f93cSkardel #ifdef ICOM 428abb0f93cSkardel static int chu_newchan (struct peer *, double); 429abb0f93cSkardel #endif /* ICOM */ 430abb0f93cSkardel static void chu_serial_receive (struct recvbuf *rbufp); 431abb0f93cSkardel 432abb0f93cSkardel /* 433abb0f93cSkardel * Global variables 434abb0f93cSkardel */ 435abb0f93cSkardel static char hexchar[] = "0123456789abcdef_*="; 436abb0f93cSkardel 437abb0f93cSkardel #ifdef ICOM 438abb0f93cSkardel /* 439abb0f93cSkardel * Note the tuned frequencies are 1 kHz higher than the carrier. CHU 440abb0f93cSkardel * transmits on USB with carrier so we can use AM and the narrow SSB 441abb0f93cSkardel * filter. 442abb0f93cSkardel */ 443abb0f93cSkardel static double qsy[NCHAN] = {3.330, 7.850, 14.670}; /* freq (MHz) */ 444abb0f93cSkardel #endif /* ICOM */ 445abb0f93cSkardel 446abb0f93cSkardel /* 447abb0f93cSkardel * Transfer vector 448abb0f93cSkardel */ 449abb0f93cSkardel struct refclock refclock_chu = { 450abb0f93cSkardel chu_start, /* start up driver */ 451abb0f93cSkardel chu_shutdown, /* shut down driver */ 452abb0f93cSkardel chu_poll, /* transmit poll message */ 453abb0f93cSkardel noentry, /* not used (old chu_control) */ 454abb0f93cSkardel noentry, /* initialize driver (not used) */ 455abb0f93cSkardel noentry, /* not used (old chu_buginfo) */ 456abb0f93cSkardel chu_second /* housekeeping timer */ 457abb0f93cSkardel }; 458abb0f93cSkardel 459abb0f93cSkardel 460abb0f93cSkardel /* 461abb0f93cSkardel * chu_start - open the devices and initialize data for processing 462abb0f93cSkardel */ 463abb0f93cSkardel static int 464abb0f93cSkardel chu_start( 465abb0f93cSkardel int unit, /* instance number (not used) */ 466abb0f93cSkardel struct peer *peer /* peer structure pointer */ 467abb0f93cSkardel ) 468abb0f93cSkardel { 469abb0f93cSkardel struct chuunit *up; 470abb0f93cSkardel struct refclockproc *pp; 471abb0f93cSkardel char device[20]; /* device name */ 472abb0f93cSkardel int fd; /* file descriptor */ 473abb0f93cSkardel #ifdef ICOM 474abb0f93cSkardel int temp; 475abb0f93cSkardel #endif /* ICOM */ 476abb0f93cSkardel #ifdef HAVE_AUDIO 477abb0f93cSkardel int fd_audio; /* audio port file descriptor */ 478abb0f93cSkardel int i; /* index */ 479abb0f93cSkardel double step; /* codec adjustment */ 480abb0f93cSkardel 481abb0f93cSkardel /* 482abb0f93cSkardel * Open audio device. Don't complain if not there. 483abb0f93cSkardel */ 484abb0f93cSkardel fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit); 4852950cc38Schristos 486abb0f93cSkardel #ifdef DEBUG 4872950cc38Schristos if (fd_audio >= 0 && debug) 488abb0f93cSkardel audio_show(); 489abb0f93cSkardel #endif 490abb0f93cSkardel 491abb0f93cSkardel /* 492abb0f93cSkardel * If audio is unavailable, Open serial port in raw mode. 493abb0f93cSkardel */ 4942950cc38Schristos if (fd_audio >= 0) { 495abb0f93cSkardel fd = fd_audio; 496abb0f93cSkardel } else { 497f003fb54Skardel snprintf(device, sizeof(device), DEVICE, unit); 498*eabc0478Schristos fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_RAW); 499abb0f93cSkardel } 500abb0f93cSkardel #else /* HAVE_AUDIO */ 501abb0f93cSkardel 502abb0f93cSkardel /* 503abb0f93cSkardel * Open serial port in raw mode. 504abb0f93cSkardel */ 505f003fb54Skardel snprintf(device, sizeof(device), DEVICE, unit); 506*eabc0478Schristos fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_RAW); 507abb0f93cSkardel #endif /* HAVE_AUDIO */ 5082950cc38Schristos 5092950cc38Schristos if (fd < 0) 510abb0f93cSkardel return (0); 511abb0f93cSkardel 512abb0f93cSkardel /* 513abb0f93cSkardel * Allocate and initialize unit structure 514abb0f93cSkardel */ 5152950cc38Schristos up = emalloc_zero(sizeof(*up)); 516abb0f93cSkardel pp = peer->procptr; 5172950cc38Schristos pp->unitptr = up; 518abb0f93cSkardel pp->io.clock_recv = chu_receive; 5192950cc38Schristos pp->io.srcclock = peer; 520abb0f93cSkardel pp->io.datalen = 0; 521abb0f93cSkardel pp->io.fd = fd; 522abb0f93cSkardel if (!io_addclock(&pp->io)) { 523abb0f93cSkardel close(fd); 524f003fb54Skardel pp->io.fd = -1; 525abb0f93cSkardel free(up); 526f003fb54Skardel pp->unitptr = NULL; 527abb0f93cSkardel return (0); 528abb0f93cSkardel } 529abb0f93cSkardel 530abb0f93cSkardel /* 531abb0f93cSkardel * Initialize miscellaneous variables 532abb0f93cSkardel */ 533abb0f93cSkardel peer->precision = PRECISION; 534abb0f93cSkardel pp->clockdesc = DESCRIPTION; 5352950cc38Schristos strlcpy(up->ident, "CHU", sizeof(up->ident)); 536abb0f93cSkardel memcpy(&pp->refid, up->ident, 4); 537abb0f93cSkardel DTOLFP(CHAR, &up->charstamp); 538abb0f93cSkardel #ifdef HAVE_AUDIO 539abb0f93cSkardel 540abb0f93cSkardel /* 541abb0f93cSkardel * The companded samples are encoded sign-magnitude. The table 542abb0f93cSkardel * contains all the 256 values in the interest of speed. We do 543abb0f93cSkardel * this even if the audio codec is not available. C'est la lazy. 544abb0f93cSkardel */ 545abb0f93cSkardel up->fd_audio = fd_audio; 546abb0f93cSkardel up->gain = 127; 547abb0f93cSkardel up->comp[0] = up->comp[OFFSET] = 0.; 548abb0f93cSkardel up->comp[1] = 1; up->comp[OFFSET + 1] = -1.; 549abb0f93cSkardel up->comp[2] = 3; up->comp[OFFSET + 2] = -3.; 550abb0f93cSkardel step = 2.; 551abb0f93cSkardel for (i = 3; i < OFFSET; i++) { 552abb0f93cSkardel up->comp[i] = up->comp[i - 1] + step; 553abb0f93cSkardel up->comp[OFFSET + i] = -up->comp[i]; 554abb0f93cSkardel if (i % 16 == 0) 555abb0f93cSkardel step *= 2.; 556abb0f93cSkardel } 557abb0f93cSkardel DTOLFP(1. / SECOND, &up->tick); 558abb0f93cSkardel #endif /* HAVE_AUDIO */ 559abb0f93cSkardel #ifdef ICOM 560abb0f93cSkardel temp = 0; 561abb0f93cSkardel #ifdef DEBUG 562abb0f93cSkardel if (debug > 1) 563abb0f93cSkardel temp = P_TRACE; 564abb0f93cSkardel #endif 565abb0f93cSkardel if (peer->ttl > 0) { 566abb0f93cSkardel if (peer->ttl & 0x80) 567abb0f93cSkardel up->fd_icom = icom_init("/dev/icom", B1200, 568abb0f93cSkardel temp); 569abb0f93cSkardel else 570abb0f93cSkardel up->fd_icom = icom_init("/dev/icom", B9600, 571abb0f93cSkardel temp); 572abb0f93cSkardel } 573abb0f93cSkardel if (up->fd_icom > 0) { 574abb0f93cSkardel if (chu_newchan(peer, 0) != 0) { 575abb0f93cSkardel msyslog(LOG_NOTICE, "icom: radio not found"); 576abb0f93cSkardel close(up->fd_icom); 577abb0f93cSkardel up->fd_icom = 0; 578abb0f93cSkardel } else { 579abb0f93cSkardel msyslog(LOG_NOTICE, "icom: autotune enabled"); 580abb0f93cSkardel } 581abb0f93cSkardel } 582abb0f93cSkardel #endif /* ICOM */ 583abb0f93cSkardel return (1); 584abb0f93cSkardel } 585abb0f93cSkardel 586abb0f93cSkardel 587abb0f93cSkardel /* 588abb0f93cSkardel * chu_shutdown - shut down the clock 589abb0f93cSkardel */ 590abb0f93cSkardel static void 591abb0f93cSkardel chu_shutdown( 592abb0f93cSkardel int unit, /* instance number (not used) */ 593abb0f93cSkardel struct peer *peer /* peer structure pointer */ 594abb0f93cSkardel ) 595abb0f93cSkardel { 596abb0f93cSkardel struct chuunit *up; 597abb0f93cSkardel struct refclockproc *pp; 598abb0f93cSkardel 599abb0f93cSkardel pp = peer->procptr; 6002950cc38Schristos up = pp->unitptr; 601abb0f93cSkardel if (up == NULL) 602abb0f93cSkardel return; 603abb0f93cSkardel 604abb0f93cSkardel io_closeclock(&pp->io); 605abb0f93cSkardel #ifdef ICOM 606abb0f93cSkardel if (up->fd_icom > 0) 607abb0f93cSkardel close(up->fd_icom); 608abb0f93cSkardel #endif /* ICOM */ 609abb0f93cSkardel free(up); 610abb0f93cSkardel } 611abb0f93cSkardel 612abb0f93cSkardel 613abb0f93cSkardel /* 614abb0f93cSkardel * chu_receive - receive data from the audio or serial device 615abb0f93cSkardel */ 616abb0f93cSkardel static void 617abb0f93cSkardel chu_receive( 618abb0f93cSkardel struct recvbuf *rbufp /* receive buffer structure pointer */ 619abb0f93cSkardel ) 620abb0f93cSkardel { 621abb0f93cSkardel #ifdef HAVE_AUDIO 622abb0f93cSkardel struct chuunit *up; 623abb0f93cSkardel struct refclockproc *pp; 624abb0f93cSkardel struct peer *peer; 625abb0f93cSkardel 6262950cc38Schristos peer = rbufp->recv_peer; 627abb0f93cSkardel pp = peer->procptr; 6282950cc38Schristos up = pp->unitptr; 629abb0f93cSkardel 630abb0f93cSkardel /* 631abb0f93cSkardel * If the audio codec is warmed up, the buffer contains codec 632abb0f93cSkardel * samples which need to be demodulated and decoded into CHU 633abb0f93cSkardel * characters using the software UART. Otherwise, the buffer 634abb0f93cSkardel * contains CHU characters from the serial port, so the software 635abb0f93cSkardel * UART is bypassed. In this case the CPU will probably run a 636abb0f93cSkardel * few degrees cooler. 637abb0f93cSkardel */ 638abb0f93cSkardel if (up->fd_audio > 0) 639abb0f93cSkardel chu_audio_receive(rbufp); 640abb0f93cSkardel else 641abb0f93cSkardel chu_serial_receive(rbufp); 642abb0f93cSkardel #else 643abb0f93cSkardel chu_serial_receive(rbufp); 644abb0f93cSkardel #endif /* HAVE_AUDIO */ 645abb0f93cSkardel } 646abb0f93cSkardel 647abb0f93cSkardel 648abb0f93cSkardel #ifdef HAVE_AUDIO 649abb0f93cSkardel /* 650abb0f93cSkardel * chu_audio_receive - receive data from the audio device 651abb0f93cSkardel */ 652abb0f93cSkardel static void 653abb0f93cSkardel chu_audio_receive( 654abb0f93cSkardel struct recvbuf *rbufp /* receive buffer structure pointer */ 655abb0f93cSkardel ) 656abb0f93cSkardel { 657abb0f93cSkardel struct chuunit *up; 658abb0f93cSkardel struct refclockproc *pp; 659abb0f93cSkardel struct peer *peer; 660abb0f93cSkardel 661abb0f93cSkardel double sample; /* codec sample */ 662abb0f93cSkardel u_char *dpt; /* buffer pointer */ 663abb0f93cSkardel int bufcnt; /* buffer counter */ 664abb0f93cSkardel l_fp ltemp; /* l_fp temp */ 665abb0f93cSkardel 6662950cc38Schristos peer = rbufp->recv_peer; 667abb0f93cSkardel pp = peer->procptr; 6682950cc38Schristos up = pp->unitptr; 669abb0f93cSkardel 670abb0f93cSkardel /* 671abb0f93cSkardel * Main loop - read until there ain't no more. Note codec 672abb0f93cSkardel * samples are bit-inverted. 673abb0f93cSkardel */ 674abb0f93cSkardel DTOLFP((double)rbufp->recv_length / SECOND, <emp); 675abb0f93cSkardel L_SUB(&rbufp->recv_time, <emp); 676abb0f93cSkardel up->timestamp = rbufp->recv_time; 677abb0f93cSkardel dpt = rbufp->recv_buffer; 678abb0f93cSkardel for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) { 679abb0f93cSkardel sample = up->comp[~*dpt++ & 0xff]; 680abb0f93cSkardel 681abb0f93cSkardel /* 682abb0f93cSkardel * Clip noise spikes greater than MAXAMP. If no clips, 683abb0f93cSkardel * increase the gain a tad; if the clips are too high, 684abb0f93cSkardel * decrease a tad. 685abb0f93cSkardel */ 686abb0f93cSkardel if (sample > MAXAMP) { 687abb0f93cSkardel sample = MAXAMP; 688abb0f93cSkardel up->clipcnt++; 689abb0f93cSkardel } else if (sample < -MAXAMP) { 690abb0f93cSkardel sample = -MAXAMP; 691abb0f93cSkardel up->clipcnt++; 692abb0f93cSkardel } 693abb0f93cSkardel chu_rf(peer, sample); 694abb0f93cSkardel L_ADD(&up->timestamp, &up->tick); 695abb0f93cSkardel 696abb0f93cSkardel /* 697abb0f93cSkardel * Once each second ride gain. 698abb0f93cSkardel */ 699abb0f93cSkardel up->seccnt = (up->seccnt + 1) % SECOND; 700abb0f93cSkardel if (up->seccnt == 0) { 701abb0f93cSkardel chu_gain(peer); 702abb0f93cSkardel } 703abb0f93cSkardel } 704abb0f93cSkardel 705abb0f93cSkardel /* 706abb0f93cSkardel * Set the input port and monitor gain for the next buffer. 707abb0f93cSkardel */ 708abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG2) 709abb0f93cSkardel up->port = 2; 710abb0f93cSkardel else 711abb0f93cSkardel up->port = 1; 712abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG3) 713abb0f93cSkardel up->mongain = MONGAIN; 714abb0f93cSkardel else 715abb0f93cSkardel up->mongain = 0; 716abb0f93cSkardel } 717abb0f93cSkardel 718abb0f93cSkardel 719abb0f93cSkardel /* 720abb0f93cSkardel * chu_rf - filter and demodulate the FSK signal 721abb0f93cSkardel * 722abb0f93cSkardel * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz 723abb0f93cSkardel * and space 2025 Hz. It uses a bandpass filter followed by a soft 724abb0f93cSkardel * limiter, FM discriminator and lowpass filter. A maximum-likelihood 725abb0f93cSkardel * decoder samples the baseband signal at eight times the baud rate and 726abb0f93cSkardel * detects the start bit of each character. 727abb0f93cSkardel * 728abb0f93cSkardel * The filters are built for speed, which explains the rather clumsy 729abb0f93cSkardel * code. Hopefully, the compiler will efficiently implement the move- 730abb0f93cSkardel * and-muiltiply-and-add operations. 731abb0f93cSkardel */ 732abb0f93cSkardel static void 733abb0f93cSkardel chu_rf( 734abb0f93cSkardel struct peer *peer, /* peer structure pointer */ 735abb0f93cSkardel double sample /* analog sample */ 736abb0f93cSkardel ) 737abb0f93cSkardel { 738abb0f93cSkardel struct refclockproc *pp; 739abb0f93cSkardel struct chuunit *up; 740abb0f93cSkardel struct surv *sp; 741abb0f93cSkardel 742abb0f93cSkardel /* 743abb0f93cSkardel * Local variables 744abb0f93cSkardel */ 745abb0f93cSkardel double signal; /* bandpass signal */ 746abb0f93cSkardel double limit; /* limiter signal */ 747abb0f93cSkardel double disc; /* discriminator signal */ 748abb0f93cSkardel double lpf; /* lowpass signal */ 749abb0f93cSkardel double dist; /* UART signal distance */ 750abb0f93cSkardel int i, j; 751abb0f93cSkardel 752abb0f93cSkardel pp = peer->procptr; 7532950cc38Schristos up = pp->unitptr; 754abb0f93cSkardel 755abb0f93cSkardel /* 756abb0f93cSkardel * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered 757abb0f93cSkardel * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB, 758abb0f93cSkardel * phase delay 0.24 ms. 759abb0f93cSkardel */ 760abb0f93cSkardel signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01; 761abb0f93cSkardel signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01; 762abb0f93cSkardel signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00; 763abb0f93cSkardel signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00; 764abb0f93cSkardel signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00; 765abb0f93cSkardel signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00; 766abb0f93cSkardel signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00; 767abb0f93cSkardel signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01; 768abb0f93cSkardel up->bpf[0] = sample - signal; 769abb0f93cSkardel signal = up->bpf[0] * 6.176213e-03 770abb0f93cSkardel + up->bpf[1] * 3.156599e-03 771abb0f93cSkardel + up->bpf[2] * 7.567487e-03 772abb0f93cSkardel + up->bpf[3] * 4.344580e-03 773abb0f93cSkardel + up->bpf[4] * 1.190128e-02 774abb0f93cSkardel + up->bpf[5] * 4.344580e-03 775abb0f93cSkardel + up->bpf[6] * 7.567487e-03 776abb0f93cSkardel + up->bpf[7] * 3.156599e-03 777abb0f93cSkardel + up->bpf[8] * 6.176213e-03; 778abb0f93cSkardel 779abb0f93cSkardel up->monitor = signal / 4.; /* note monitor after filter */ 780abb0f93cSkardel 781abb0f93cSkardel /* 782abb0f93cSkardel * Soft limiter/discriminator. The 11-sample discriminator lag 783abb0f93cSkardel * interval corresponds to three cycles of 2125 Hz, which 784abb0f93cSkardel * requires the sample frequency to be 2125 * 11 / 3 = 7791.7 785abb0f93cSkardel * Hz. The discriminator output varies +-0.5 interval for input 786abb0f93cSkardel * frequency 2025-2225 Hz. However, we don't get to sample at 787abb0f93cSkardel * this frequency, so the discriminator output is biased. Life 788abb0f93cSkardel * at 8000 Hz sucks. 789abb0f93cSkardel */ 790abb0f93cSkardel limit = signal; 791abb0f93cSkardel if (limit > LIMIT) 792abb0f93cSkardel limit = LIMIT; 793abb0f93cSkardel else if (limit < -LIMIT) 794abb0f93cSkardel limit = -LIMIT; 795abb0f93cSkardel disc = up->disc[up->discptr] * -limit; 796abb0f93cSkardel up->disc[up->discptr] = limit; 797abb0f93cSkardel up->discptr = (up->discptr + 1 ) % LAG; 798abb0f93cSkardel if (disc >= 0) 799abb0f93cSkardel disc = SQRT(disc); 800abb0f93cSkardel else 801abb0f93cSkardel disc = -SQRT(-disc); 802abb0f93cSkardel 803abb0f93cSkardel /* 804abb0f93cSkardel * Lowpass filter. Raised cosine FIR, Ts = 1 / 300, beta = 0.1. 805abb0f93cSkardel */ 806abb0f93cSkardel lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02; 807abb0f93cSkardel lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01; 808abb0f93cSkardel lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01; 809abb0f93cSkardel lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01; 810abb0f93cSkardel lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01; 811abb0f93cSkardel lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01; 812abb0f93cSkardel lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01; 813abb0f93cSkardel lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01; 814abb0f93cSkardel lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01; 815abb0f93cSkardel lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01; 816abb0f93cSkardel lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01; 817abb0f93cSkardel lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01; 818abb0f93cSkardel lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01; 819abb0f93cSkardel lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00; 820abb0f93cSkardel lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01; 821abb0f93cSkardel lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01; 822abb0f93cSkardel lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01; 823abb0f93cSkardel lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01; 824abb0f93cSkardel lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01; 825abb0f93cSkardel lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01; 826abb0f93cSkardel lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01; 827abb0f93cSkardel lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01; 828abb0f93cSkardel lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01; 829abb0f93cSkardel lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01; 830abb0f93cSkardel lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01; 831abb0f93cSkardel lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01; 832abb0f93cSkardel lpf += up->lpf[0] = disc * 2.538771e-02; 833abb0f93cSkardel 834abb0f93cSkardel /* 835abb0f93cSkardel * Maximum-likelihood decoder. The UART updates each of the 836abb0f93cSkardel * eight survivors and determines the span, slice level and 837abb0f93cSkardel * tentative decoded character. Valid 11-bit characters are 838abb0f93cSkardel * framed so that bit 10 and bit 11 (stop bits) are mark and bit 839abb0f93cSkardel * 1 (start bit) is space. When a valid character is found, the 840abb0f93cSkardel * survivor with maximum distance determines the final decoded 841abb0f93cSkardel * character. 842abb0f93cSkardel */ 843abb0f93cSkardel up->baud += 1. / SECOND; 844abb0f93cSkardel if (up->baud > 1. / (BAUD * 8.)) { 845abb0f93cSkardel up->baud -= 1. / (BAUD * 8.); 846abb0f93cSkardel up->decptr = (up->decptr + 1) % 8; 847abb0f93cSkardel sp = &up->surv[up->decptr]; 848abb0f93cSkardel sp->cstamp = up->timestamp; 849abb0f93cSkardel chu_uart(sp, -lpf * AGAIN); 850abb0f93cSkardel if (up->dbrk > 0) { 851abb0f93cSkardel up->dbrk--; 852abb0f93cSkardel if (up->dbrk > 0) 853abb0f93cSkardel return; 854abb0f93cSkardel 855abb0f93cSkardel up->decpha = up->decptr; 856abb0f93cSkardel } 857abb0f93cSkardel if (up->decptr != up->decpha) 858abb0f93cSkardel return; 859abb0f93cSkardel 860abb0f93cSkardel dist = 0; 861abb0f93cSkardel j = -1; 862abb0f93cSkardel for (i = 0; i < 8; i++) { 863abb0f93cSkardel 864abb0f93cSkardel /* 865abb0f93cSkardel * The timestamp is taken at the last bit, so 866abb0f93cSkardel * for correct decoding we reqire sufficient 867abb0f93cSkardel * span and correct start bit and two stop bits. 868abb0f93cSkardel */ 869abb0f93cSkardel if ((up->surv[i].uart & 0x601) != 0x600 || 870abb0f93cSkardel up->surv[i].span < SPAN) 871abb0f93cSkardel continue; 872abb0f93cSkardel 873abb0f93cSkardel if (up->surv[i].dist > dist) { 874abb0f93cSkardel dist = up->surv[i].dist; 875abb0f93cSkardel j = i; 876abb0f93cSkardel } 877abb0f93cSkardel } 878abb0f93cSkardel if (j < 0) 879abb0f93cSkardel return; 880abb0f93cSkardel 881abb0f93cSkardel /* 882abb0f93cSkardel * Process the character, then blank the decoder until 883abb0f93cSkardel * the end of the next character.This sets the decoding 884abb0f93cSkardel * phase of the entire burst from the phase of the first 885abb0f93cSkardel * character. 886abb0f93cSkardel */ 887abb0f93cSkardel up->maxsignal = up->surv[j].span; 888abb0f93cSkardel chu_decode(peer, (up->surv[j].uart >> 1) & 0xff, 889abb0f93cSkardel up->surv[j].cstamp); 890abb0f93cSkardel up->dbrk = 88; 891abb0f93cSkardel } 892abb0f93cSkardel } 893abb0f93cSkardel 894abb0f93cSkardel 895abb0f93cSkardel /* 896abb0f93cSkardel * chu_uart - maximum-likelihood UART 897abb0f93cSkardel * 898abb0f93cSkardel * This routine updates a shift register holding the last 11 envelope 899abb0f93cSkardel * samples. It then computes the slice level and span over these samples 900abb0f93cSkardel * and determines the tentative data bits and distance. The calling 901abb0f93cSkardel * program selects over the last eight survivors the one with maximum 902abb0f93cSkardel * distance to determine the decoded character. 903abb0f93cSkardel */ 904abb0f93cSkardel static void 905abb0f93cSkardel chu_uart( 906abb0f93cSkardel struct surv *sp, /* survivor structure pointer */ 907abb0f93cSkardel double sample /* baseband signal */ 908abb0f93cSkardel ) 909abb0f93cSkardel { 910abb0f93cSkardel double es_max, es_min; /* max/min envelope */ 911abb0f93cSkardel double slice; /* slice level */ 912abb0f93cSkardel double dist; /* distance */ 913abb0f93cSkardel double dtemp; 914abb0f93cSkardel int i; 915abb0f93cSkardel 916abb0f93cSkardel /* 917abb0f93cSkardel * Save the sample and shift right. At the same time, measure 918abb0f93cSkardel * the maximum and minimum over all eleven samples. 919abb0f93cSkardel */ 920abb0f93cSkardel es_max = -1e6; 921abb0f93cSkardel es_min = 1e6; 922abb0f93cSkardel sp->shift[0] = sample; 923abb0f93cSkardel for (i = 11; i > 0; i--) { 924abb0f93cSkardel sp->shift[i] = sp->shift[i - 1]; 925abb0f93cSkardel if (sp->shift[i] > es_max) 926abb0f93cSkardel es_max = sp->shift[i]; 927abb0f93cSkardel if (sp->shift[i] < es_min) 928abb0f93cSkardel es_min = sp->shift[i]; 929abb0f93cSkardel } 930abb0f93cSkardel 931abb0f93cSkardel /* 932abb0f93cSkardel * Determine the span as the maximum less the minimum and the 933abb0f93cSkardel * slice level as the minimum plus a fraction of the span. Note 934abb0f93cSkardel * the slight bias toward mark to correct for the modem tendency 935abb0f93cSkardel * to make more mark than space errors. Compute the distance on 936abb0f93cSkardel * the assumption the last two bits must be mark, the first 937abb0f93cSkardel * space and the rest either mark or space. 938abb0f93cSkardel */ 939abb0f93cSkardel sp->span = es_max - es_min; 940abb0f93cSkardel slice = es_min + .45 * sp->span; 941abb0f93cSkardel dist = 0; 942abb0f93cSkardel sp->uart = 0; 943abb0f93cSkardel for (i = 1; i < 12; i++) { 944abb0f93cSkardel sp->uart <<= 1; 945abb0f93cSkardel dtemp = sp->shift[i]; 946abb0f93cSkardel if (dtemp > slice) 947abb0f93cSkardel sp->uart |= 0x1; 948abb0f93cSkardel if (i == 1 || i == 2) { 949abb0f93cSkardel dist += dtemp - es_min; 950abb0f93cSkardel } else if (i == 11) { 951abb0f93cSkardel dist += es_max - dtemp; 952abb0f93cSkardel } else { 953abb0f93cSkardel if (dtemp > slice) 954abb0f93cSkardel dist += dtemp - es_min; 955abb0f93cSkardel else 956abb0f93cSkardel dist += es_max - dtemp; 957abb0f93cSkardel } 958abb0f93cSkardel } 959abb0f93cSkardel sp->dist = dist / (11 * sp->span); 960abb0f93cSkardel } 961abb0f93cSkardel #endif /* HAVE_AUDIO */ 962abb0f93cSkardel 963abb0f93cSkardel 964abb0f93cSkardel /* 965abb0f93cSkardel * chu_serial_receive - receive data from the serial device 966abb0f93cSkardel */ 967abb0f93cSkardel static void 968abb0f93cSkardel chu_serial_receive( 969abb0f93cSkardel struct recvbuf *rbufp /* receive buffer structure pointer */ 970abb0f93cSkardel ) 971abb0f93cSkardel { 972abb0f93cSkardel struct peer *peer; 973abb0f93cSkardel 974abb0f93cSkardel u_char *dpt; /* receive buffer pointer */ 975abb0f93cSkardel 9762950cc38Schristos peer = rbufp->recv_peer; 977abb0f93cSkardel 978abb0f93cSkardel dpt = (u_char *)&rbufp->recv_space; 979abb0f93cSkardel chu_decode(peer, *dpt, rbufp->recv_time); 980abb0f93cSkardel } 981abb0f93cSkardel 982abb0f93cSkardel 983abb0f93cSkardel /* 984abb0f93cSkardel * chu_decode - decode the character data 985abb0f93cSkardel */ 986abb0f93cSkardel static void 987abb0f93cSkardel chu_decode( 988abb0f93cSkardel struct peer *peer, /* peer structure pointer */ 989abb0f93cSkardel int hexhex, /* data character */ 990abb0f93cSkardel l_fp cstamp /* data character timestamp */ 991abb0f93cSkardel ) 992abb0f93cSkardel { 993abb0f93cSkardel struct refclockproc *pp; 994abb0f93cSkardel struct chuunit *up; 995abb0f93cSkardel 996abb0f93cSkardel l_fp tstmp; /* timestamp temp */ 997abb0f93cSkardel double dtemp; 998abb0f93cSkardel 999abb0f93cSkardel pp = peer->procptr; 10002950cc38Schristos up = pp->unitptr; 1001abb0f93cSkardel 1002abb0f93cSkardel /* 1003abb0f93cSkardel * If the interval since the last character is greater than the 1004abb0f93cSkardel * longest burst, process the last burst and start a new one. If 1005abb0f93cSkardel * the interval is less than this but greater than two 1006abb0f93cSkardel * characters, consider this a noise burst and reject it. 1007abb0f93cSkardel */ 1008abb0f93cSkardel tstmp = up->timestamp; 1009abb0f93cSkardel if (L_ISZERO(&up->laststamp)) 1010abb0f93cSkardel up->laststamp = up->timestamp; 1011abb0f93cSkardel L_SUB(&tstmp, &up->laststamp); 1012abb0f93cSkardel up->laststamp = up->timestamp; 1013abb0f93cSkardel LFPTOD(&tstmp, dtemp); 1014abb0f93cSkardel if (dtemp > BURST * CHAR) { 1015abb0f93cSkardel chu_burst(peer); 1016abb0f93cSkardel up->ndx = 0; 1017abb0f93cSkardel } else if (dtemp > 2.5 * CHAR) { 1018abb0f93cSkardel up->ndx = 0; 1019abb0f93cSkardel } 1020abb0f93cSkardel 1021abb0f93cSkardel /* 1022abb0f93cSkardel * Append the character to the current burst and append the 1023abb0f93cSkardel * character timestamp to the timestamp list. 1024abb0f93cSkardel */ 1025abb0f93cSkardel if (up->ndx < BURST) { 1026abb0f93cSkardel up->cbuf[up->ndx] = hexhex & 0xff; 1027abb0f93cSkardel up->cstamp[up->ndx] = cstamp; 1028abb0f93cSkardel up->ndx++; 1029abb0f93cSkardel 1030abb0f93cSkardel } 1031abb0f93cSkardel } 1032abb0f93cSkardel 1033abb0f93cSkardel 1034abb0f93cSkardel /* 1035abb0f93cSkardel * chu_burst - search for valid burst format 1036abb0f93cSkardel */ 1037abb0f93cSkardel static void 1038abb0f93cSkardel chu_burst( 1039abb0f93cSkardel struct peer *peer 1040abb0f93cSkardel ) 1041abb0f93cSkardel { 1042abb0f93cSkardel struct chuunit *up; 1043abb0f93cSkardel struct refclockproc *pp; 1044abb0f93cSkardel 1045abb0f93cSkardel int i; 1046abb0f93cSkardel 1047abb0f93cSkardel pp = peer->procptr; 10482950cc38Schristos up = pp->unitptr; 1049abb0f93cSkardel 1050abb0f93cSkardel /* 1051abb0f93cSkardel * Correlate a block of five characters with the next block of 1052abb0f93cSkardel * five characters. The burst distance is defined as the number 1053abb0f93cSkardel * of bits that match in the two blocks for format A and that 1054abb0f93cSkardel * match the inverse for format B. 1055abb0f93cSkardel */ 10562950cc38Schristos if (up->ndx < MINCHARS) { 1057abb0f93cSkardel up->status |= RUNT; 1058abb0f93cSkardel return; 1059abb0f93cSkardel } 1060abb0f93cSkardel up->burdist = 0; 1061abb0f93cSkardel for (i = 0; i < 5 && i < up->ndx - 5; i++) 1062abb0f93cSkardel up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]); 1063abb0f93cSkardel 1064abb0f93cSkardel /* 1065abb0f93cSkardel * If the burst distance is at least MINDIST, this must be a 1066abb0f93cSkardel * format A burst; if the value is not greater than -MINDIST, it 1067abb0f93cSkardel * must be a format B burst. If the B burst is perfect, we 1068abb0f93cSkardel * believe it; otherwise, it is a noise burst and of no use to 1069abb0f93cSkardel * anybody. 1070abb0f93cSkardel */ 1071abb0f93cSkardel if (up->burdist >= MINDIST) { 1072abb0f93cSkardel chu_a(peer, up->ndx); 1073abb0f93cSkardel } else if (up->burdist <= -MINDIST) { 1074abb0f93cSkardel chu_b(peer, up->ndx); 1075abb0f93cSkardel } else { 1076abb0f93cSkardel up->status |= NOISE; 1077abb0f93cSkardel return; 1078abb0f93cSkardel } 1079abb0f93cSkardel 1080abb0f93cSkardel /* 1081abb0f93cSkardel * If this is a valid burst, wait a guard time of ten seconds to 1082abb0f93cSkardel * allow for more bursts, then arm the poll update routine to 1083abb0f93cSkardel * process the minute. Don't do this if this is called from the 1084abb0f93cSkardel * timer interrupt routine. 1085abb0f93cSkardel */ 1086abb0f93cSkardel if (peer->outdate != current_time) 1087abb0f93cSkardel peer->nextdate = current_time + 10; 1088abb0f93cSkardel } 1089abb0f93cSkardel 1090abb0f93cSkardel 1091abb0f93cSkardel /* 1092abb0f93cSkardel * chu_b - decode format B burst 1093abb0f93cSkardel */ 1094abb0f93cSkardel static void 1095abb0f93cSkardel chu_b( 1096abb0f93cSkardel struct peer *peer, 1097abb0f93cSkardel int nchar 1098abb0f93cSkardel ) 1099abb0f93cSkardel { 1100abb0f93cSkardel struct refclockproc *pp; 1101abb0f93cSkardel struct chuunit *up; 1102abb0f93cSkardel 1103abb0f93cSkardel u_char code[11]; /* decoded timecode */ 1104abb0f93cSkardel char tbuf[80]; /* trace buffer */ 1105f003fb54Skardel char * p; 1106f003fb54Skardel size_t chars; 1107f003fb54Skardel size_t cb; 1108abb0f93cSkardel int i; 1109abb0f93cSkardel 1110abb0f93cSkardel pp = peer->procptr; 11112950cc38Schristos up = pp->unitptr; 1112abb0f93cSkardel 1113abb0f93cSkardel /* 1114abb0f93cSkardel * In a format B burst, a character is considered valid only if 1115abb0f93cSkardel * the first occurence matches the last occurence. The burst is 1116abb0f93cSkardel * considered valid only if all characters are valid; that is, 1117abb0f93cSkardel * only if the distance is 40. Note that once a valid frame has 1118abb0f93cSkardel * been found errors are ignored. 1119abb0f93cSkardel */ 1120f003fb54Skardel snprintf(tbuf, sizeof(tbuf), "chuB %04x %4.0f %2d %2d ", 1121f003fb54Skardel up->status, up->maxsignal, nchar, -up->burdist); 1122f003fb54Skardel cb = sizeof(tbuf); 1123f003fb54Skardel p = tbuf; 1124f003fb54Skardel for (i = 0; i < nchar; i++) { 1125f003fb54Skardel chars = strlen(p); 1126f003fb54Skardel if (cb < chars + 1) { 1127f003fb54Skardel msyslog(LOG_ERR, "chu_b() fatal out buffer"); 1128f003fb54Skardel exit(1); 1129f003fb54Skardel } 1130f003fb54Skardel cb -= chars; 1131f003fb54Skardel p += chars; 1132f003fb54Skardel snprintf(p, cb, "%02x", up->cbuf[i]); 1133f003fb54Skardel } 1134abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG4) 1135abb0f93cSkardel record_clock_stats(&peer->srcadr, tbuf); 1136abb0f93cSkardel #ifdef DEBUG 1137abb0f93cSkardel if (debug) 1138abb0f93cSkardel printf("%s\n", tbuf); 1139abb0f93cSkardel #endif 1140abb0f93cSkardel if (up->burdist > -40) { 1141abb0f93cSkardel up->status |= BFRAME; 1142abb0f93cSkardel return; 1143abb0f93cSkardel } 1144abb0f93cSkardel 1145abb0f93cSkardel /* 1146abb0f93cSkardel * Convert the burst data to internal format. Don't bother with 1147abb0f93cSkardel * the timestamps. 1148abb0f93cSkardel */ 1149abb0f93cSkardel for (i = 0; i < 5; i++) { 1150abb0f93cSkardel code[2 * i] = hexchar[up->cbuf[i] & 0xf]; 1151abb0f93cSkardel code[2 * i + 1] = hexchar[(up->cbuf[i] >> 1152abb0f93cSkardel 4) & 0xf]; 1153abb0f93cSkardel } 1154abb0f93cSkardel if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut, 1155abb0f93cSkardel &pp->year, &up->tai, &up->dst) != 5) { 1156abb0f93cSkardel up->status |= BFORMAT; 1157abb0f93cSkardel return; 1158abb0f93cSkardel } 1159abb0f93cSkardel up->status |= BVALID; 1160abb0f93cSkardel if (up->leap & 0x8) 1161abb0f93cSkardel up->dut = -up->dut; 1162abb0f93cSkardel } 1163abb0f93cSkardel 1164abb0f93cSkardel 1165abb0f93cSkardel /* 1166abb0f93cSkardel * chu_a - decode format A burst 1167abb0f93cSkardel */ 1168abb0f93cSkardel static void 1169abb0f93cSkardel chu_a( 1170abb0f93cSkardel struct peer *peer, 1171abb0f93cSkardel int nchar 1172abb0f93cSkardel ) 1173abb0f93cSkardel { 1174abb0f93cSkardel struct refclockproc *pp; 1175abb0f93cSkardel struct chuunit *up; 1176abb0f93cSkardel 1177abb0f93cSkardel char tbuf[80]; /* trace buffer */ 1178f003fb54Skardel char * p; 1179f003fb54Skardel size_t chars; 1180f003fb54Skardel size_t cb; 1181abb0f93cSkardel l_fp offset; /* timestamp offset */ 1182abb0f93cSkardel int val; /* distance */ 1183abb0f93cSkardel int temp; 1184abb0f93cSkardel int i, j, k; 1185abb0f93cSkardel 1186abb0f93cSkardel pp = peer->procptr; 11872950cc38Schristos up = pp->unitptr; 1188abb0f93cSkardel 1189abb0f93cSkardel /* 1190abb0f93cSkardel * Determine correct burst phase. There are three cases 1191abb0f93cSkardel * corresponding to in-phase, one character early or one 1192abb0f93cSkardel * character late. These cases are distinguished by the position 1193abb0f93cSkardel * of the framing digits 0x6 at positions 0 and 5 and 0x3 at 1194abb0f93cSkardel * positions 4 and 9. The correct phase is when the distance 1195abb0f93cSkardel * relative to the framing digits is maximum. The burst is valid 1196abb0f93cSkardel * only if the maximum distance is at least MINSYNC. 1197abb0f93cSkardel */ 1198abb0f93cSkardel up->syndist = k = 0; 1199af12ab5eSchristos // val = -16; 1200abb0f93cSkardel for (i = -1; i < 2; i++) { 1201abb0f93cSkardel temp = up->cbuf[i + 4] & 0xf; 1202abb0f93cSkardel if (i >= 0) 1203abb0f93cSkardel temp |= (up->cbuf[i] & 0xf) << 4; 1204abb0f93cSkardel val = chu_dist(temp, 0x63); 1205abb0f93cSkardel temp = (up->cbuf[i + 5] & 0xf) << 4; 1206abb0f93cSkardel if (i + 9 < nchar) 1207abb0f93cSkardel temp |= up->cbuf[i + 9] & 0xf; 1208abb0f93cSkardel val += chu_dist(temp, 0x63); 1209abb0f93cSkardel if (val > up->syndist) { 1210abb0f93cSkardel up->syndist = val; 1211abb0f93cSkardel k = i; 1212abb0f93cSkardel } 1213abb0f93cSkardel } 1214abb0f93cSkardel 1215abb0f93cSkardel /* 1216abb0f93cSkardel * Extract the second number; it must be in the range 2 through 1217abb0f93cSkardel * 9 and the two repititions must be the same. 1218abb0f93cSkardel */ 1219abb0f93cSkardel temp = (up->cbuf[k + 4] >> 4) & 0xf; 1220abb0f93cSkardel if (temp < 2 || temp > 9 || k + 9 >= nchar || temp != 1221abb0f93cSkardel ((up->cbuf[k + 9] >> 4) & 0xf)) 1222abb0f93cSkardel temp = 0; 1223f003fb54Skardel snprintf(tbuf, sizeof(tbuf), 1224f003fb54Skardel "chuA %04x %4.0f %2d %2d %2d %2d %1d ", up->status, 1225f003fb54Skardel up->maxsignal, nchar, up->burdist, k, up->syndist, 1226f003fb54Skardel temp); 1227f003fb54Skardel cb = sizeof(tbuf); 1228f003fb54Skardel p = tbuf; 1229f003fb54Skardel for (i = 0; i < nchar; i++) { 1230f003fb54Skardel chars = strlen(p); 1231f003fb54Skardel if (cb < chars + 1) { 1232f003fb54Skardel msyslog(LOG_ERR, "chu_a() fatal out buffer"); 1233f003fb54Skardel exit(1); 1234f003fb54Skardel } 1235f003fb54Skardel cb -= chars; 1236f003fb54Skardel p += chars; 1237f003fb54Skardel snprintf(p, cb, "%02x", up->cbuf[i]); 1238f003fb54Skardel } 1239abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG4) 1240abb0f93cSkardel record_clock_stats(&peer->srcadr, tbuf); 1241abb0f93cSkardel #ifdef DEBUG 1242abb0f93cSkardel if (debug) 1243abb0f93cSkardel printf("%s\n", tbuf); 1244abb0f93cSkardel #endif 1245abb0f93cSkardel if (up->syndist < MINSYNC) { 1246abb0f93cSkardel up->status |= AFRAME; 1247abb0f93cSkardel return; 1248abb0f93cSkardel } 1249abb0f93cSkardel 1250abb0f93cSkardel /* 1251abb0f93cSkardel * A valid burst requires the first seconds number to match the 1252abb0f93cSkardel * last seconds number. If so, the burst timestamps are 1253abb0f93cSkardel * corrected to the current minute and saved for later 1254abb0f93cSkardel * processing. In addition, the seconds decode is advanced from 1255abb0f93cSkardel * the previous burst to the current one. 1256abb0f93cSkardel */ 1257abb0f93cSkardel if (temp == 0) { 1258abb0f93cSkardel up->status |= AFORMAT; 1259abb0f93cSkardel } else { 1260abb0f93cSkardel up->status |= AVALID; 1261abb0f93cSkardel up->second = pp->second = 30 + temp; 1262abb0f93cSkardel offset.l_ui = 30 + temp; 12632950cc38Schristos offset.l_uf = 0; 1264abb0f93cSkardel i = 0; 1265abb0f93cSkardel if (k < 0) 1266abb0f93cSkardel offset = up->charstamp; 1267abb0f93cSkardel else if (k > 0) 1268abb0f93cSkardel i = 1; 126968dbbb44Schristos for (; i < nchar && (i - 10) < k; i++) { 1270abb0f93cSkardel up->tstamp[up->ntstamp] = up->cstamp[i]; 1271abb0f93cSkardel L_SUB(&up->tstamp[up->ntstamp], &offset); 1272abb0f93cSkardel L_ADD(&offset, &up->charstamp); 1273abb0f93cSkardel if (up->ntstamp < MAXSTAGE - 1) 1274abb0f93cSkardel up->ntstamp++; 1275abb0f93cSkardel } 1276abb0f93cSkardel while (temp > up->prevsec) { 1277abb0f93cSkardel for (j = 15; j > 0; j--) { 1278abb0f93cSkardel up->decode[9][j] = up->decode[9][j - 1]; 1279abb0f93cSkardel up->decode[19][j] = 1280abb0f93cSkardel up->decode[19][j - 1]; 1281abb0f93cSkardel } 1282abb0f93cSkardel up->decode[9][j] = up->decode[19][j] = 0; 1283abb0f93cSkardel up->prevsec++; 1284abb0f93cSkardel } 1285abb0f93cSkardel } 1286abb0f93cSkardel 1287abb0f93cSkardel /* 1288abb0f93cSkardel * Stash the data in the decoding matrix. 1289abb0f93cSkardel */ 1290abb0f93cSkardel i = -(2 * k); 1291abb0f93cSkardel for (j = 0; j < nchar; j++) { 1292abb0f93cSkardel if (i < 0 || i > 18) { 1293abb0f93cSkardel i += 2; 1294abb0f93cSkardel continue; 1295abb0f93cSkardel } 1296abb0f93cSkardel up->decode[i][up->cbuf[j] & 0xf]++; 1297abb0f93cSkardel i++; 1298abb0f93cSkardel up->decode[i][(up->cbuf[j] >> 4) & 0xf]++; 1299abb0f93cSkardel i++; 1300abb0f93cSkardel } 1301abb0f93cSkardel up->burstcnt++; 1302abb0f93cSkardel } 1303abb0f93cSkardel 1304abb0f93cSkardel 1305abb0f93cSkardel /* 1306abb0f93cSkardel * chu_poll - called by the transmit procedure 1307abb0f93cSkardel */ 1308abb0f93cSkardel static void 1309abb0f93cSkardel chu_poll( 1310abb0f93cSkardel int unit, 1311abb0f93cSkardel struct peer *peer /* peer structure pointer */ 1312abb0f93cSkardel ) 1313abb0f93cSkardel { 1314abb0f93cSkardel struct refclockproc *pp; 1315abb0f93cSkardel 1316abb0f93cSkardel pp = peer->procptr; 1317abb0f93cSkardel pp->polls++; 1318abb0f93cSkardel } 1319abb0f93cSkardel 1320abb0f93cSkardel 1321abb0f93cSkardel /* 1322abb0f93cSkardel * chu_second - process minute data 1323abb0f93cSkardel */ 1324abb0f93cSkardel static void 1325abb0f93cSkardel chu_second( 1326abb0f93cSkardel int unit, 1327abb0f93cSkardel struct peer *peer /* peer structure pointer */ 1328abb0f93cSkardel ) 1329abb0f93cSkardel { 1330abb0f93cSkardel struct refclockproc *pp; 1331abb0f93cSkardel struct chuunit *up; 1332abb0f93cSkardel l_fp offset; 1333abb0f93cSkardel char synchar, qual, leapchar; 1334abb0f93cSkardel int minset, i; 1335abb0f93cSkardel double dtemp; 1336abb0f93cSkardel 1337abb0f93cSkardel pp = peer->procptr; 13382950cc38Schristos up = pp->unitptr; 1339abb0f93cSkardel 1340abb0f93cSkardel /* 1341abb0f93cSkardel * This routine is called once per minute to process the 1342abb0f93cSkardel * accumulated burst data. We do a bit of fancy footwork so that 1343abb0f93cSkardel * this doesn't run while burst data are being accumulated. 1344abb0f93cSkardel */ 1345abb0f93cSkardel up->second = (up->second + 1) % 60; 1346abb0f93cSkardel if (up->second != 0) 1347abb0f93cSkardel return; 1348abb0f93cSkardel 1349abb0f93cSkardel /* 1350abb0f93cSkardel * Process the last burst, if still in the burst buffer. 1351abb0f93cSkardel * If the minute contains a valid B frame with sufficient A 1352abb0f93cSkardel * frame metric, it is considered valid. However, the timecode 1353abb0f93cSkardel * is sent to clockstats even if invalid. 1354abb0f93cSkardel */ 1355abb0f93cSkardel chu_burst(peer); 1356abb0f93cSkardel minset = ((current_time - peer->update) + 30) / 60; 1357abb0f93cSkardel dtemp = chu_major(peer); 1358abb0f93cSkardel qual = 0; 1359abb0f93cSkardel if (up->status & (BFRAME | AFRAME)) 1360abb0f93cSkardel qual |= SYNERR; 1361abb0f93cSkardel if (up->status & (BFORMAT | AFORMAT)) 1362abb0f93cSkardel qual |= FMTERR; 1363abb0f93cSkardel if (up->status & DECODE) 1364abb0f93cSkardel qual |= DECERR; 1365abb0f93cSkardel if (up->status & STAMP) 1366abb0f93cSkardel qual |= TSPERR; 1367abb0f93cSkardel if (up->status & BVALID && dtemp >= MINMETRIC) 1368abb0f93cSkardel up->status |= INSYNC; 1369abb0f93cSkardel synchar = leapchar = ' '; 1370abb0f93cSkardel if (!(up->status & INSYNC)) { 1371abb0f93cSkardel pp->leap = LEAP_NOTINSYNC; 1372abb0f93cSkardel synchar = '?'; 1373abb0f93cSkardel } else if (up->leap & 0x2) { 1374abb0f93cSkardel pp->leap = LEAP_ADDSECOND; 1375abb0f93cSkardel leapchar = 'L'; 1376abb0f93cSkardel } else if (up->leap & 0x4) { 1377abb0f93cSkardel pp->leap = LEAP_DELSECOND; 1378abb0f93cSkardel leapchar = 'l'; 1379abb0f93cSkardel } else { 1380abb0f93cSkardel pp->leap = LEAP_NOWARNING; 1381abb0f93cSkardel } 1382f003fb54Skardel snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 1383abb0f93cSkardel "%c%1X %04d %03d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d", 1384abb0f93cSkardel synchar, qual, pp->year, pp->day, pp->hour, pp->minute, 1385abb0f93cSkardel pp->second, leapchar, up->dst, up->dut, minset, up->gain, 1386abb0f93cSkardel up->ident, dtemp, up->ntstamp); 1387abb0f93cSkardel pp->lencode = strlen(pp->a_lastcode); 1388abb0f93cSkardel 1389abb0f93cSkardel /* 1390abb0f93cSkardel * If in sync and the signal metric is above threshold, the 1391abb0f93cSkardel * timecode is ipso fatso valid and can be selected to 1392abb0f93cSkardel * discipline the clock. 1393abb0f93cSkardel */ 1394abb0f93cSkardel if (up->status & INSYNC && !(up->status & (DECODE | STAMP)) && 1395abb0f93cSkardel dtemp > MINMETRIC) { 1396abb0f93cSkardel if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, 1397abb0f93cSkardel up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { 1398abb0f93cSkardel up->errflg = CEVNT_BADTIME; 1399abb0f93cSkardel } else { 1400abb0f93cSkardel offset.l_uf = 0; 1401abb0f93cSkardel for (i = 0; i < up->ntstamp; i++) 1402abb0f93cSkardel refclock_process_offset(pp, offset, 1403abb0f93cSkardel up->tstamp[i], PDELAY + 1404abb0f93cSkardel pp->fudgetime1); 1405abb0f93cSkardel pp->lastref = up->timestamp; 1406abb0f93cSkardel refclock_receive(peer); 1407abb0f93cSkardel } 1408abb0f93cSkardel } 1409abb0f93cSkardel if (dtemp > 0) 1410abb0f93cSkardel record_clock_stats(&peer->srcadr, pp->a_lastcode); 1411abb0f93cSkardel #ifdef DEBUG 1412abb0f93cSkardel if (debug) 1413abb0f93cSkardel printf("chu: timecode %d %s\n", pp->lencode, 1414abb0f93cSkardel pp->a_lastcode); 1415abb0f93cSkardel #endif 1416abb0f93cSkardel #ifdef ICOM 1417abb0f93cSkardel chu_newchan(peer, dtemp); 1418abb0f93cSkardel #endif /* ICOM */ 1419abb0f93cSkardel chu_clear(peer); 1420abb0f93cSkardel if (up->errflg) 1421abb0f93cSkardel refclock_report(peer, up->errflg); 1422abb0f93cSkardel up->errflg = 0; 1423abb0f93cSkardel } 1424abb0f93cSkardel 1425abb0f93cSkardel 1426abb0f93cSkardel /* 1427abb0f93cSkardel * chu_major - majority decoder 1428abb0f93cSkardel */ 1429abb0f93cSkardel static double 1430abb0f93cSkardel chu_major( 1431abb0f93cSkardel struct peer *peer /* peer structure pointer */ 1432abb0f93cSkardel ) 1433abb0f93cSkardel { 1434abb0f93cSkardel struct refclockproc *pp; 1435abb0f93cSkardel struct chuunit *up; 1436abb0f93cSkardel 1437abb0f93cSkardel u_char code[11]; /* decoded timecode */ 1438abb0f93cSkardel int metric; /* distance metric */ 1439abb0f93cSkardel int val1; /* maximum distance */ 1440abb0f93cSkardel int synchar; /* stray cat */ 1441abb0f93cSkardel int temp; 1442abb0f93cSkardel int i, j, k; 1443abb0f93cSkardel 1444abb0f93cSkardel pp = peer->procptr; 14452950cc38Schristos up = pp->unitptr; 1446abb0f93cSkardel 1447abb0f93cSkardel /* 1448abb0f93cSkardel * Majority decoder. Each burst encodes two replications at each 1449abb0f93cSkardel * digit position in the timecode. Each row of the decoding 1450abb0f93cSkardel * matrix encodes the number of occurences of each digit found 1451abb0f93cSkardel * at the corresponding position. The maximum over all 1452abb0f93cSkardel * occurrences at each position is the distance for this 1453abb0f93cSkardel * position and the corresponding digit is the maximum- 1454abb0f93cSkardel * likelihood candidate. If the distance is not more than half 1455abb0f93cSkardel * the total number of occurences, a majority has not been found 1456abb0f93cSkardel * and the data are discarded. The decoding distance is defined 1457abb0f93cSkardel * as the sum of the distances over the first nine digits. The 1458abb0f93cSkardel * tenth digit varies over the seconds, so we don't count it. 1459abb0f93cSkardel */ 1460abb0f93cSkardel metric = 0; 1461abb0f93cSkardel for (i = 0; i < 9; i++) { 1462abb0f93cSkardel val1 = 0; 1463abb0f93cSkardel k = 0; 1464abb0f93cSkardel for (j = 0; j < 16; j++) { 1465abb0f93cSkardel temp = up->decode[i][j] + up->decode[i + 10][j]; 1466abb0f93cSkardel if (temp > val1) { 1467abb0f93cSkardel val1 = temp; 1468abb0f93cSkardel k = j; 1469abb0f93cSkardel } 1470abb0f93cSkardel } 1471abb0f93cSkardel if (val1 <= up->burstcnt) 1472abb0f93cSkardel up->status |= DECODE; 1473abb0f93cSkardel metric += val1; 1474abb0f93cSkardel code[i] = hexchar[k]; 1475abb0f93cSkardel } 1476abb0f93cSkardel 1477abb0f93cSkardel /* 1478abb0f93cSkardel * Compute the timecode timestamp from the days, hours and 1479abb0f93cSkardel * minutes of the timecode. Use clocktime() for the aggregate 1480abb0f93cSkardel * minutes and the minute offset computed from the burst 1481abb0f93cSkardel * seconds. Note that this code relies on the filesystem time 1482abb0f93cSkardel * for the years and does not use the years of the timecode. 1483abb0f93cSkardel */ 1484abb0f93cSkardel if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day, 1485abb0f93cSkardel &pp->hour, &pp->minute) != 4) 1486abb0f93cSkardel up->status |= DECODE; 1487abb0f93cSkardel if (up->ntstamp < MINSTAMP) 1488abb0f93cSkardel up->status |= STAMP; 1489abb0f93cSkardel return (metric); 1490abb0f93cSkardel } 1491abb0f93cSkardel 1492abb0f93cSkardel 1493abb0f93cSkardel /* 1494abb0f93cSkardel * chu_clear - clear decoding matrix 1495abb0f93cSkardel */ 1496abb0f93cSkardel static void 1497abb0f93cSkardel chu_clear( 1498abb0f93cSkardel struct peer *peer /* peer structure pointer */ 1499abb0f93cSkardel ) 1500abb0f93cSkardel { 1501abb0f93cSkardel struct refclockproc *pp; 1502abb0f93cSkardel struct chuunit *up; 1503abb0f93cSkardel int i, j; 1504abb0f93cSkardel 1505abb0f93cSkardel pp = peer->procptr; 15062950cc38Schristos up = pp->unitptr; 1507abb0f93cSkardel 1508abb0f93cSkardel /* 1509abb0f93cSkardel * Clear stuff for the minute. 1510abb0f93cSkardel */ 1511abb0f93cSkardel up->ndx = up->prevsec = 0; 1512abb0f93cSkardel up->burstcnt = up->ntstamp = 0; 1513abb0f93cSkardel up->status &= INSYNC | METRIC; 1514abb0f93cSkardel for (i = 0; i < 20; i++) { 1515abb0f93cSkardel for (j = 0; j < 16; j++) 1516abb0f93cSkardel up->decode[i][j] = 0; 1517abb0f93cSkardel } 1518abb0f93cSkardel } 1519abb0f93cSkardel 1520abb0f93cSkardel #ifdef ICOM 1521abb0f93cSkardel /* 1522abb0f93cSkardel * chu_newchan - called once per minute to find the best channel; 1523abb0f93cSkardel * returns zero on success, nonzero if ICOM error. 1524abb0f93cSkardel */ 1525abb0f93cSkardel static int 1526abb0f93cSkardel chu_newchan( 1527abb0f93cSkardel struct peer *peer, 1528abb0f93cSkardel double met 1529abb0f93cSkardel ) 1530abb0f93cSkardel { 1531abb0f93cSkardel struct chuunit *up; 1532abb0f93cSkardel struct refclockproc *pp; 1533abb0f93cSkardel struct xmtr *sp; 1534abb0f93cSkardel int rval; 1535abb0f93cSkardel double metric; 1536abb0f93cSkardel int i; 1537abb0f93cSkardel 1538abb0f93cSkardel pp = peer->procptr; 15392950cc38Schristos up = pp->unitptr; 1540abb0f93cSkardel 1541abb0f93cSkardel /* 1542abb0f93cSkardel * The radio can be tuned to three channels: 0 (3330 kHz), 1 1543abb0f93cSkardel * (7850 kHz) and 2 (14670 kHz). There are five one-minute 1544abb0f93cSkardel * dwells in each cycle. During the first dwell the radio is 1545abb0f93cSkardel * tuned to one of the three channels to measure the channel 1546abb0f93cSkardel * metric. The channel is selected as the one least recently 1547abb0f93cSkardel * measured. During the remaining four dwells the radio is tuned 1548abb0f93cSkardel * to the channel with the highest channel metric. 1549abb0f93cSkardel */ 1550abb0f93cSkardel if (up->fd_icom <= 0) 1551abb0f93cSkardel return (0); 1552abb0f93cSkardel 1553abb0f93cSkardel /* 1554abb0f93cSkardel * Update the current channel metric and age of all channels. 1555abb0f93cSkardel * Scan all channels for the highest metric. 1556abb0f93cSkardel */ 1557abb0f93cSkardel sp = &up->xmtr[up->chan]; 1558abb0f93cSkardel sp->metric -= sp->integ[sp->iptr]; 1559abb0f93cSkardel sp->integ[sp->iptr] = met; 1560abb0f93cSkardel sp->metric += sp->integ[sp->iptr]; 1561abb0f93cSkardel sp->probe = 0; 1562abb0f93cSkardel sp->iptr = (sp->iptr + 1) % ISTAGE; 1563abb0f93cSkardel metric = 0; 1564abb0f93cSkardel for (i = 0; i < NCHAN; i++) { 1565abb0f93cSkardel up->xmtr[i].probe++; 1566abb0f93cSkardel if (up->xmtr[i].metric > metric) { 1567abb0f93cSkardel up->status |= METRIC; 1568abb0f93cSkardel metric = up->xmtr[i].metric; 1569abb0f93cSkardel up->chan = i; 1570abb0f93cSkardel } 1571abb0f93cSkardel } 1572abb0f93cSkardel 1573abb0f93cSkardel /* 1574abb0f93cSkardel * Start the next dwell. If the first dwell or no stations have 1575abb0f93cSkardel * been heard, continue round-robin scan. 1576abb0f93cSkardel */ 1577abb0f93cSkardel up->dwell = (up->dwell + 1) % DWELL; 1578abb0f93cSkardel if (up->dwell == 0 || metric == 0) { 1579abb0f93cSkardel rval = 0; 1580abb0f93cSkardel for (i = 0; i < NCHAN; i++) { 1581abb0f93cSkardel if (up->xmtr[i].probe > rval) { 1582abb0f93cSkardel rval = up->xmtr[i].probe; 1583abb0f93cSkardel up->chan = i; 1584abb0f93cSkardel } 1585abb0f93cSkardel } 1586abb0f93cSkardel } 1587abb0f93cSkardel 1588abb0f93cSkardel /* Retune the radio at each dwell in case somebody nudges the 1589abb0f93cSkardel * tuning knob. 1590abb0f93cSkardel */ 1591abb0f93cSkardel rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan] + 1592abb0f93cSkardel TUNE); 1593f003fb54Skardel snprintf(up->ident, sizeof(up->ident), "CHU%d", up->chan); 1594abb0f93cSkardel memcpy(&pp->refid, up->ident, 4); 1595abb0f93cSkardel memcpy(&peer->refid, up->ident, 4); 1596abb0f93cSkardel if (metric == 0 && up->status & METRIC) { 1597abb0f93cSkardel up->status &= ~METRIC; 1598abb0f93cSkardel refclock_report(peer, CEVNT_PROP); 1599abb0f93cSkardel } 1600abb0f93cSkardel return (rval); 1601abb0f93cSkardel } 1602abb0f93cSkardel #endif /* ICOM */ 1603abb0f93cSkardel 1604abb0f93cSkardel 1605abb0f93cSkardel /* 1606abb0f93cSkardel * chu_dist - determine the distance of two octet arguments 1607abb0f93cSkardel */ 1608abb0f93cSkardel static int 1609abb0f93cSkardel chu_dist( 1610abb0f93cSkardel int x, /* an octet of bits */ 1611abb0f93cSkardel int y /* another octet of bits */ 1612abb0f93cSkardel ) 1613abb0f93cSkardel { 1614abb0f93cSkardel int val; /* bit count */ 1615abb0f93cSkardel int temp; 1616abb0f93cSkardel int i; 1617abb0f93cSkardel 1618abb0f93cSkardel /* 1619abb0f93cSkardel * The distance is determined as the weight of the exclusive OR 1620abb0f93cSkardel * of the two arguments. The weight is determined by the number 1621abb0f93cSkardel * of one bits in the result. Each one bit increases the weight, 1622abb0f93cSkardel * while each zero bit decreases it. 1623abb0f93cSkardel */ 1624abb0f93cSkardel temp = x ^ y; 1625abb0f93cSkardel val = 0; 1626abb0f93cSkardel for (i = 0; i < 8; i++) { 1627abb0f93cSkardel if ((temp & 0x1) == 0) 1628abb0f93cSkardel val++; 1629abb0f93cSkardel else 1630abb0f93cSkardel val--; 1631abb0f93cSkardel temp >>= 1; 1632abb0f93cSkardel } 1633abb0f93cSkardel return (val); 1634abb0f93cSkardel } 1635abb0f93cSkardel 1636abb0f93cSkardel 1637abb0f93cSkardel #ifdef HAVE_AUDIO 1638abb0f93cSkardel /* 1639abb0f93cSkardel * chu_gain - adjust codec gain 1640abb0f93cSkardel * 1641abb0f93cSkardel * This routine is called at the end of each second. During the second 1642abb0f93cSkardel * the number of signal clips above the MAXAMP threshold (6000). If 1643abb0f93cSkardel * there are no clips, the gain is bumped up; if there are more than 1644abb0f93cSkardel * MAXCLP clips (100), it is bumped down. The decoder is relatively 1645abb0f93cSkardel * insensitive to amplitude, so this crudity works just peachy. The 1646abb0f93cSkardel * routine also jiggles the input port and selectively mutes the 1647abb0f93cSkardel */ 1648abb0f93cSkardel static void 1649abb0f93cSkardel chu_gain( 1650abb0f93cSkardel struct peer *peer /* peer structure pointer */ 1651abb0f93cSkardel ) 1652abb0f93cSkardel { 1653abb0f93cSkardel struct refclockproc *pp; 1654abb0f93cSkardel struct chuunit *up; 1655abb0f93cSkardel 1656abb0f93cSkardel pp = peer->procptr; 16572950cc38Schristos up = pp->unitptr; 1658abb0f93cSkardel 1659abb0f93cSkardel /* 1660abb0f93cSkardel * Apparently, the codec uses only the high order bits of the 1661abb0f93cSkardel * gain control field. Thus, it may take awhile for changes to 1662abb0f93cSkardel * wiggle the hardware bits. 1663abb0f93cSkardel */ 1664abb0f93cSkardel if (up->clipcnt == 0) { 1665abb0f93cSkardel up->gain += 4; 1666abb0f93cSkardel if (up->gain > MAXGAIN) 1667abb0f93cSkardel up->gain = MAXGAIN; 1668abb0f93cSkardel } else if (up->clipcnt > MAXCLP) { 1669abb0f93cSkardel up->gain -= 4; 1670abb0f93cSkardel if (up->gain < 0) 1671abb0f93cSkardel up->gain = 0; 1672abb0f93cSkardel } 1673abb0f93cSkardel audio_gain(up->gain, up->mongain, up->port); 1674abb0f93cSkardel up->clipcnt = 0; 1675abb0f93cSkardel } 1676abb0f93cSkardel #endif /* HAVE_AUDIO */ 1677abb0f93cSkardel 1678abb0f93cSkardel 1679abb0f93cSkardel #else 16802950cc38Schristos NONEMPTY_TRANSLATION_UNIT 1681abb0f93cSkardel #endif /* REFCLOCK */ 1682