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 82b15cb3dSCy Schubert #include "ntp_types.h" 92b15cb3dSCy Schubert 10c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_CHU) 11c0b746e5SOllivier Robert 12c0b746e5SOllivier Robert #include "ntpd.h" 13c0b746e5SOllivier Robert #include "ntp_io.h" 14c0b746e5SOllivier Robert #include "ntp_refclock.h" 15c0b746e5SOllivier Robert #include "ntp_calendar.h" 16c0b746e5SOllivier Robert #include "ntp_stdlib.h" 17224ba2bdSOllivier Robert 18224ba2bdSOllivier Robert #include <stdio.h> 19224ba2bdSOllivier Robert #include <ctype.h> 20224ba2bdSOllivier Robert #include <math.h> 21224ba2bdSOllivier Robert 22224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 23a151a66cSOllivier Robert #include "audio.h" 24224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 25a151a66cSOllivier Robert 26a151a66cSOllivier Robert #define ICOM 1 /* undefine to suppress ICOM code */ 27a151a66cSOllivier Robert 28a151a66cSOllivier Robert #ifdef ICOM 29a151a66cSOllivier Robert #include "icom.h" 30a151a66cSOllivier Robert #endif /* ICOM */ 31c0b746e5SOllivier Robert /* 32a151a66cSOllivier Robert * Audio CHU demodulator/decoder 33c0b746e5SOllivier Robert * 34c0b746e5SOllivier Robert * This driver synchronizes the computer time using data encoded in 35c0b746e5SOllivier Robert * radio transmissions from Canadian time/frequency station CHU in 36c0b746e5SOllivier Robert * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz, 372b15cb3dSCy Schubert * 7850 kHz and 14670 kHz in upper sideband, compatible AM mode. An 38c0b746e5SOllivier Robert * ordinary shortwave receiver can be tuned manually to one of these 39c0b746e5SOllivier Robert * frequencies or, in the case of ICOM receivers, the receiver can be 402b15cb3dSCy Schubert * tuned automatically as propagation conditions change throughout the 412b15cb3dSCy Schubert * day and season. 42a151a66cSOllivier Robert * 432b15cb3dSCy Schubert * The driver requires an audio codec or sound card with sampling rate 8 442b15cb3dSCy Schubert * kHz and mu-law companding. This is the same standard as used by the 452b15cb3dSCy Schubert * telephone industry and is supported by most hardware and operating 462b15cb3dSCy Schubert * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this 472b15cb3dSCy Schubert * implementation, only one audio driver and codec can be supported on a 482b15cb3dSCy Schubert * single machine. 49c0b746e5SOllivier Robert * 50c0b746e5SOllivier Robert * The driver can be compiled to use a Bell 103 compatible modem or 51c0b746e5SOllivier Robert * modem chip to receive the radio signal and demodulate the data. 52c0b746e5SOllivier Robert * Alternatively, the driver can be compiled to use the audio codec of 532b15cb3dSCy Schubert * the workstation or another with compatible audio drivers. In the 54c0b746e5SOllivier Robert * latter case, the driver implements the modem using DSP routines, so 55c0b746e5SOllivier Robert * the radio can be connected directly to either the microphone on line 56c0b746e5SOllivier Robert * input port. In either case, the driver decodes the data using a 572b15cb3dSCy Schubert * maximum-likelihood technique which exploits the considerable degree 58c0b746e5SOllivier Robert * of redundancy available to maximize accuracy and minimize errors. 59c0b746e5SOllivier Robert * 60c0b746e5SOllivier Robert * The CHU time broadcast includes an audio signal compatible with the 612b15cb3dSCy Schubert * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). The signal 622b15cb3dSCy Schubert * consists of nine, ten-character bursts transmitted at 300 bps between 632b15cb3dSCy Schubert * seconds 31 and 39 of each minute. Each character consists of eight 642b15cb3dSCy Schubert * data bits plus one start bit and two stop bits to encode two hex 652b15cb3dSCy Schubert * digits. The burst data consist of five characters (ten hex digits) 662b15cb3dSCy Schubert * followed by a repeat of these characters. In format A, the characters 672b15cb3dSCy Schubert * are repeated in the same polarity; in format B, the characters are 682b15cb3dSCy Schubert * repeated in the opposite polarity. 69c0b746e5SOllivier Robert * 70c0b746e5SOllivier Robert * Format A bursts are sent at seconds 32 through 39 of the minute in 712b15cb3dSCy Schubert * hex digits (nibble swapped) 72c0b746e5SOllivier Robert * 73c0b746e5SOllivier Robert * 6dddhhmmss6dddhhmmss 74c0b746e5SOllivier Robert * 75c0b746e5SOllivier Robert * The first ten digits encode a frame marker (6) followed by the day 76c0b746e5SOllivier Robert * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since 77c0b746e5SOllivier Robert * format A bursts are sent during the third decade of seconds the tens 78c0b746e5SOllivier Robert * digit of ss is always 3. The driver uses this to determine correct 79c0b746e5SOllivier Robert * burst synchronization. These digits are then repeated with the same 80c0b746e5SOllivier Robert * polarity. 81c0b746e5SOllivier Robert * 82c0b746e5SOllivier Robert * Format B bursts are sent at second 31 of the minute in hex digits 83c0b746e5SOllivier Robert * 84c0b746e5SOllivier Robert * xdyyyyttaaxdyyyyttaa 85c0b746e5SOllivier Robert * 86c0b746e5SOllivier Robert * The first ten digits encode a code (x described below) followed by 87c0b746e5SOllivier Robert * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI - 88c0b746e5SOllivier Robert * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These 89c0b746e5SOllivier Robert * digits are then repeated with inverted polarity. 90c0b746e5SOllivier Robert * 91c0b746e5SOllivier Robert * The x is coded 92c0b746e5SOllivier Robert * 93c0b746e5SOllivier Robert * 1 Sign of DUT (0 = +) 94c0b746e5SOllivier Robert * 2 Leap second warning. One second will be added. 95c0b746e5SOllivier Robert * 4 Leap second warning. One second will be subtracted. 96c0b746e5SOllivier Robert * 8 Even parity bit for this nibble. 97c0b746e5SOllivier Robert * 98c0b746e5SOllivier Robert * By design, the last stop bit of the last character in the burst 99c0b746e5SOllivier Robert * coincides with 0.5 second. Since characters have 11 bits and are 100c0b746e5SOllivier Robert * transmitted at 300 bps, the last stop bit of the first character 1012b15cb3dSCy Schubert * coincides with 0.5 - 9 * 11/300 = 0.170 second. Depending on the 1022b15cb3dSCy Schubert * UART, character interrupts can vary somewhere between the end of bit 1032b15cb3dSCy Schubert * 9 and end of bit 11. These eccentricities can be corrected along with 1042b15cb3dSCy Schubert * the radio propagation delay using fudge time 1. 105c0b746e5SOllivier Robert * 106c0b746e5SOllivier Robert * Debugging aids 107c0b746e5SOllivier Robert * 108c0b746e5SOllivier Robert * The timecode format used for debugging and data recording includes 109c0b746e5SOllivier Robert * data helpful in diagnosing problems with the radio signal and serial 1109c2daa00SOllivier Robert * connections. With debugging enabled (-d on the ntpd command line), 1119c2daa00SOllivier Robert * the driver produces one line for each burst in two formats 1122b15cb3dSCy Schubert * corresponding to format A and B.Each line begins with the format code 1132b15cb3dSCy Schubert * chuA or chuB followed by the status code and signal level (0-9999). 1142b15cb3dSCy Schubert * The remainder of the line is as follows. 1152b15cb3dSCy Schubert * 1162b15cb3dSCy Schubert * Following is format A: 117c0b746e5SOllivier Robert * 118c0b746e5SOllivier Robert * n b f s m code 119c0b746e5SOllivier Robert * 1202b15cb3dSCy Schubert * where n is the number of characters in the burst (0-10), b the burst 121c0b746e5SOllivier Robert * distance (0-40), f the field alignment (-1, 0, 1), s the 122c0b746e5SOllivier Robert * synchronization distance (0-16), m the burst number (2-9) and code 123c0b746e5SOllivier Robert * the burst characters as received. Note that the hex digits in each 124c0b746e5SOllivier Robert * character are reversed, so the burst 125c0b746e5SOllivier Robert * 126c0b746e5SOllivier Robert * 10 38 0 16 9 06851292930685129293 127c0b746e5SOllivier Robert * 1282b15cb3dSCy Schubert * is interpreted as containing 10 characters with burst distance 38, 129c0b746e5SOllivier Robert * field alignment 0, synchronization distance 16 and burst number 9. 130c0b746e5SOllivier Robert * The nibble-swapped timecode shows day 58, hour 21, minute 29 and 131c0b746e5SOllivier Robert * second 39. 132c0b746e5SOllivier Robert * 133c0b746e5SOllivier Robert * Following is format B: 134c0b746e5SOllivier Robert * 135c0b746e5SOllivier Robert * n b s code 136c0b746e5SOllivier Robert * 1372b15cb3dSCy Schubert * where n is the number of characters in the burst (0-10), 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 * 1422b15cb3dSCy Schubert * 10 40 1091891300ef6e76ec 143c0b746e5SOllivier Robert * 1442b15cb3dSCy Schubert * is interpreted as containing 10 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 * 1482b15cb3dSCy Schubert * Each line is preceeded by the code chuA or chuB, as appropriate. If 1492b15cb3dSCy Schubert * the audio driver is compiled, the current gain (0-255) and relative 1502b15cb3dSCy Schubert * signal level (0-9999) follow the code. The receiver volume control 1512b15cb3dSCy Schubert * should be set so that the gain is somewhere near the middle of the 1522b15cb3dSCy Schubert * range 0-255, which results in a signal level near 1000. 1532b15cb3dSCy Schubert * 154c0b746e5SOllivier Robert * In addition to the above, the reference timecode is updated and 155c0b746e5SOllivier Robert * written to the clockstats file and debug score after the last burst 156c0b746e5SOllivier Robert * received in the minute. The format is 157c0b746e5SOllivier Robert * 1582b15cb3dSCy Schubert * sq yyyy ddd hh:mm:ss l s dd t agc ident m b 159c0b746e5SOllivier Robert * 1602b15cb3dSCy Schubert * s '?' before first synchronized and ' ' after that 1612b15cb3dSCy Schubert * q status code (see below) 1622b15cb3dSCy Schubert * yyyy year 1632b15cb3dSCy Schubert * ddd day of year 1642b15cb3dSCy Schubert * hh:mm:ss time of day 1652b15cb3dSCy Schubert * l leap second indicator (space, L or D) 1662b15cb3dSCy Schubert * dst Canadian daylight code (opaque) 1672b15cb3dSCy Schubert * t number of minutes since last synchronized 1682b15cb3dSCy Schubert * agc audio gain (0 - 255) 1692b15cb3dSCy Schubert * ident identifier (CHU0 3330 kHz, CHU1 7850 kHz, CHU2 14670 kHz) 1702b15cb3dSCy Schubert * m signal metric (0 - 100) 1712b15cb3dSCy Schubert * b number of timecodes for the previous minute (0 - 59) 172c0b746e5SOllivier Robert * 173c0b746e5SOllivier Robert * Fudge factors 174c0b746e5SOllivier Robert * 175c0b746e5SOllivier Robert * For accuracies better than the low millisceconds, fudge time1 can be 176c0b746e5SOllivier Robert * set to the radio propagation delay from CHU to the receiver. This can 1779c2daa00SOllivier Robert * be done conviently using the minimuf program. 178c0b746e5SOllivier Robert * 1799c2daa00SOllivier Robert * Fudge flag4 causes the dubugging output described above to be 1809c2daa00SOllivier Robert * recorded in the clockstats file. When the audio driver is compiled, 1819c2daa00SOllivier Robert * fudge flag2 selects the audio input port, where 0 is the mike port 1829c2daa00SOllivier Robert * (default) and 1 is the line-in port. It does not seem useful to 1839c2daa00SOllivier Robert * select the compact disc player port. Fudge flag3 enables audio 1849c2daa00SOllivier Robert * monitoring of the input signal. For this purpose, the monitor gain is 1859c2daa00SOllivier Robert * set to a default value. 186a151a66cSOllivier Robert * 187224ba2bdSOllivier Robert * The audio codec code is normally compiled in the driver if the 1889c2daa00SOllivier Robert * architecture supports it (HAVE_AUDIO defined), but is used only if 1899c2daa00SOllivier Robert * the link /dev/chu_audio is defined and valid. The serial port code is 1909c2daa00SOllivier Robert * always compiled in the driver, but is used only if the autdio codec 1919c2daa00SOllivier Robert * is not available and the link /dev/chu%d is defined and valid. 1929c2daa00SOllivier Robert * 193224ba2bdSOllivier Robert * The ICOM code is normally compiled in the driver if selected (ICOM 194224ba2bdSOllivier Robert * defined), but is used only if the link /dev/icom%d is defined and 195224ba2bdSOllivier Robert * valid and the mode keyword on the server configuration command 196224ba2bdSOllivier Robert * specifies a nonzero mode (ICOM ID select code). The C-IV speed is 197224ba2bdSOllivier Robert * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps 198224ba2bdSOllivier Robert * if one. The C-IV trace is turned on if the debug level is greater 199224ba2bdSOllivier Robert * than one. 2002b15cb3dSCy Schubert * 2012b15cb3dSCy Schubert * Alarm codes 2022b15cb3dSCy Schubert * 2032b15cb3dSCy Schubert * CEVNT_BADTIME invalid date or time 2042b15cb3dSCy Schubert * CEVNT_PROP propagation failure - no stations heard 205c0b746e5SOllivier Robert */ 206c0b746e5SOllivier Robert /* 207c0b746e5SOllivier Robert * Interface definitions 208c0b746e5SOllivier Robert */ 209c0b746e5SOllivier Robert #define SPEED232 B300 /* uart speed (300 baud) */ 210c0b746e5SOllivier Robert #define PRECISION (-10) /* precision assumed (about 1 ms) */ 211c0b746e5SOllivier Robert #define REFID "CHU" /* reference ID */ 212224ba2bdSOllivier Robert #define DEVICE "/dev/chu%d" /* device name and unit */ 213224ba2bdSOllivier Robert #define SPEED232 B300 /* UART speed (300 baud) */ 214a151a66cSOllivier Robert #ifdef ICOM 2152b15cb3dSCy Schubert #define TUNE .001 /* offset for narrow filter (MHz) */ 2162b15cb3dSCy Schubert #define DWELL 5 /* minutes in a dwell */ 217a151a66cSOllivier Robert #define NCHAN 3 /* number of channels */ 2189c2daa00SOllivier Robert #define ISTAGE 3 /* number of integrator stages */ 219a151a66cSOllivier Robert #endif /* ICOM */ 220c0b746e5SOllivier Robert 2219c2daa00SOllivier Robert #ifdef HAVE_AUDIO 222c0b746e5SOllivier Robert /* 223c0b746e5SOllivier Robert * Audio demodulator definitions 224c0b746e5SOllivier Robert */ 225a151a66cSOllivier Robert #define SECOND 8000 /* nominal sample rate (Hz) */ 226c0b746e5SOllivier Robert #define BAUD 300 /* modulation rate (bps) */ 227c0b746e5SOllivier Robert #define OFFSET 128 /* companded sample offset */ 228c0b746e5SOllivier Robert #define SIZE 256 /* decompanding table size */ 229ea906c41SOllivier Robert #define MAXAMP 6000. /* maximum signal level */ 2309c2daa00SOllivier Robert #define MAXCLP 100 /* max clips above reference per s */ 2312b15cb3dSCy Schubert #define SPAN 800. /* min envelope span */ 232c0b746e5SOllivier Robert #define LIMIT 1000. /* soft limiter threshold */ 233c0b746e5SOllivier Robert #define AGAIN 6. /* baseband gain */ 234c0b746e5SOllivier Robert #define LAG 10 /* discriminator lag */ 235ea906c41SOllivier Robert #define DEVICE_AUDIO "/dev/audio" /* device name */ 236224ba2bdSOllivier Robert #define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */ 2379c2daa00SOllivier Robert #define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */ 238c0b746e5SOllivier Robert #else 239224ba2bdSOllivier Robert #define DESCRIPTION "CHU Modem Receiver" /* WRU */ 240224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 241c0b746e5SOllivier Robert 242c0b746e5SOllivier Robert /* 243c0b746e5SOllivier Robert * Decoder definitions 244c0b746e5SOllivier Robert */ 245c0b746e5SOllivier Robert #define CHAR (11. / 300.) /* character time (s) */ 246c0b746e5SOllivier Robert #define BURST 11 /* max characters per burst */ 2472b15cb3dSCy Schubert #define MINCHARS 9 /* min characters per burst */ 248c0b746e5SOllivier Robert #define MINDIST 28 /* min burst distance (of 40) */ 249c0b746e5SOllivier Robert #define MINSYNC 8 /* min sync distance (of 16) */ 250c0b746e5SOllivier Robert #define MINSTAMP 20 /* min timestamps (of 60) */ 2512b15cb3dSCy Schubert #define MINMETRIC 50 /* min channel metric (of 160) */ 252c0b746e5SOllivier Robert 253c0b746e5SOllivier Robert /* 2542b15cb3dSCy Schubert * The on-time synchronization point for the driver is the last stop bit 2552b15cb3dSCy Schubert * of the first character 170 ms. The modem delay is 0.8 ms, while the 2562b15cb3dSCy Schubert * receiver delay is approxmately 4.7 ms at 2125 Hz. The fudge value 1.3 2572b15cb3dSCy Schubert * ms due to the codec and other causes was determined by calibrating to 2582b15cb3dSCy Schubert * a PPS signal from a GPS receiver. The additional propagation delay 2592b15cb3dSCy Schubert * specific to each receiver location can be programmed in the fudge 2602b15cb3dSCy Schubert * time1. 2612b15cb3dSCy Schubert * 2622b15cb3dSCy Schubert * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are 2632b15cb3dSCy Schubert * generally within 0.5 ms short term with 0.3 ms jitter. The long-term 2642b15cb3dSCy Schubert * offsets vary up to 0.3 ms due to ionospheric layer height variations. 2652b15cb3dSCy Schubert * The processor load due to the driver is 0.4 percent. 266c0b746e5SOllivier Robert */ 2672b15cb3dSCy Schubert #define PDELAY ((170 + .8 + 4.7 + 1.3) / 1000) /* system delay (s) */ 268c0b746e5SOllivier Robert 269c0b746e5SOllivier Robert /* 270a151a66cSOllivier Robert * Status bits (status) 271c0b746e5SOllivier Robert */ 272a151a66cSOllivier Robert #define RUNT 0x0001 /* runt burst */ 273a151a66cSOllivier Robert #define NOISE 0x0002 /* noise burst */ 274a151a66cSOllivier Robert #define BFRAME 0x0004 /* invalid format B frame sync */ 275a151a66cSOllivier Robert #define BFORMAT 0x0008 /* invalid format B data */ 276a151a66cSOllivier Robert #define AFRAME 0x0010 /* invalid format A frame sync */ 277a151a66cSOllivier Robert #define AFORMAT 0x0020 /* invalid format A data */ 278a151a66cSOllivier Robert #define DECODE 0x0040 /* invalid data decode */ 279a151a66cSOllivier Robert #define STAMP 0x0080 /* too few timestamps */ 2809c2daa00SOllivier Robert #define AVALID 0x0100 /* valid A frame */ 2819c2daa00SOllivier Robert #define BVALID 0x0200 /* valid B frame */ 2829c2daa00SOllivier Robert #define INSYNC 0x0400 /* clock synchronized */ 2832b15cb3dSCy Schubert #define METRIC 0x0800 /* one or more stations heard */ 284a151a66cSOllivier Robert 285a151a66cSOllivier Robert /* 286a151a66cSOllivier Robert * Alarm status bits (alarm) 287a151a66cSOllivier Robert * 288a151a66cSOllivier Robert * These alarms are set at the end of a minute in which at least one 289a151a66cSOllivier Robert * burst was received. SYNERR is raised if the AFRAME or BFRAME status 290a151a66cSOllivier Robert * bits are set during the minute, FMTERR is raised if the AFORMAT or 291a151a66cSOllivier Robert * BFORMAT status bits are set, DECERR is raised if the DECODE status 292a151a66cSOllivier Robert * bit is set and TSPERR is raised if the STAMP status bit is set. 293a151a66cSOllivier Robert */ 294a151a66cSOllivier Robert #define SYNERR 0x01 /* frame sync error */ 295a151a66cSOllivier Robert #define FMTERR 0x02 /* data format error */ 296a151a66cSOllivier Robert #define DECERR 0x04 /* data decoding error */ 297a151a66cSOllivier Robert #define TSPERR 0x08 /* insufficient data */ 298c0b746e5SOllivier Robert 299224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 3009c2daa00SOllivier Robert /* 3012b15cb3dSCy Schubert * Maximum-likelihood UART structure. There are eight of these 3029c2daa00SOllivier Robert * corresponding to the number of phases. 3039c2daa00SOllivier Robert */ 304c0b746e5SOllivier Robert struct surv { 3052b15cb3dSCy Schubert l_fp cstamp; /* last bit timestamp */ 3062b15cb3dSCy Schubert double shift[12]; /* sample shift register */ 3072b15cb3dSCy Schubert double span; /* shift register envelope span */ 308c0b746e5SOllivier Robert double dist; /* sample distance */ 309c0b746e5SOllivier Robert int uart; /* decoded character */ 310c0b746e5SOllivier Robert }; 311224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 312c0b746e5SOllivier Robert 3139c2daa00SOllivier Robert #ifdef ICOM 3149c2daa00SOllivier Robert /* 3159c2daa00SOllivier Robert * CHU station structure. There are three of these corresponding to the 3169c2daa00SOllivier Robert * three frequencies. 3179c2daa00SOllivier Robert */ 3189c2daa00SOllivier Robert struct xmtr { 3199c2daa00SOllivier Robert double integ[ISTAGE]; /* circular integrator */ 3209c2daa00SOllivier Robert double metric; /* integrator sum */ 3219c2daa00SOllivier Robert int iptr; /* integrator pointer */ 3229c2daa00SOllivier Robert int probe; /* dwells since last probe */ 3239c2daa00SOllivier Robert }; 3249c2daa00SOllivier Robert #endif /* ICOM */ 3259c2daa00SOllivier Robert 326c0b746e5SOllivier Robert /* 327c0b746e5SOllivier Robert * CHU unit control structure 328c0b746e5SOllivier Robert */ 329c0b746e5SOllivier Robert struct chuunit { 3302b15cb3dSCy Schubert u_char decode[20][16]; /* maximum-likelihood decoding matrix */ 331c0b746e5SOllivier Robert l_fp cstamp[BURST]; /* character timestamps */ 332c0b746e5SOllivier Robert l_fp tstamp[MAXSTAGE]; /* timestamp samples */ 333c0b746e5SOllivier Robert l_fp timestamp; /* current buffer timestamp */ 334c0b746e5SOllivier Robert l_fp laststamp; /* last buffer timestamp */ 335c0b746e5SOllivier Robert l_fp charstamp; /* character time as a l_fp */ 3362b15cb3dSCy Schubert int second; /* counts the seconds of the minute */ 337c0b746e5SOllivier Robert int errflg; /* error flags */ 338a151a66cSOllivier Robert int status; /* status bits */ 3399c2daa00SOllivier Robert char ident[5]; /* station ID and channel */ 340a151a66cSOllivier Robert #ifdef ICOM 341224ba2bdSOllivier Robert int fd_icom; /* ICOM file descriptor */ 3422b15cb3dSCy Schubert int chan; /* radio channel */ 3439c2daa00SOllivier Robert int dwell; /* dwell cycle */ 3449c2daa00SOllivier Robert struct xmtr xmtr[NCHAN]; /* station metric */ 345a151a66cSOllivier Robert #endif /* ICOM */ 346c0b746e5SOllivier Robert 347c0b746e5SOllivier Robert /* 348c0b746e5SOllivier Robert * Character burst variables 349c0b746e5SOllivier Robert */ 350c0b746e5SOllivier Robert int cbuf[BURST]; /* character buffer */ 351c0b746e5SOllivier Robert int ntstamp; /* number of timestamp samples */ 352c0b746e5SOllivier Robert int ndx; /* buffer start index */ 353c0b746e5SOllivier Robert int prevsec; /* previous burst second */ 354c0b746e5SOllivier Robert int burdist; /* burst distance */ 355c0b746e5SOllivier Robert int syndist; /* sync distance */ 356c0b746e5SOllivier Robert int burstcnt; /* format A bursts this minute */ 3572b15cb3dSCy Schubert double maxsignal; /* signal level (modem only) */ 3582b15cb3dSCy Schubert int gain; /* codec gain (modem only) */ 359c0b746e5SOllivier Robert 360a151a66cSOllivier Robert /* 361a151a66cSOllivier Robert * Format particulars 362a151a66cSOllivier Robert */ 363a151a66cSOllivier Robert int leap; /* leap/dut code */ 364a151a66cSOllivier Robert int dut; /* UTC1 correction */ 365a151a66cSOllivier Robert int tai; /* TAI - UTC correction */ 366a151a66cSOllivier Robert int dst; /* Canadian DST code */ 367a151a66cSOllivier Robert 368224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 369c0b746e5SOllivier Robert /* 370c0b746e5SOllivier Robert * Audio codec variables 371c0b746e5SOllivier Robert */ 372224ba2bdSOllivier Robert int fd_audio; /* audio port file descriptor */ 373c0b746e5SOllivier Robert double comp[SIZE]; /* decompanding table */ 374c0b746e5SOllivier Robert int port; /* codec port */ 3759c2daa00SOllivier Robert int mongain; /* codec monitor gain */ 376c0b746e5SOllivier Robert int clipcnt; /* sample clip count */ 377c0b746e5SOllivier Robert int seccnt; /* second interval counter */ 378c0b746e5SOllivier Robert 379c0b746e5SOllivier Robert /* 380c0b746e5SOllivier Robert * Modem variables 381c0b746e5SOllivier Robert */ 382c0b746e5SOllivier Robert l_fp tick; /* audio sample increment */ 383c0b746e5SOllivier Robert double bpf[9]; /* IIR bandpass filter */ 384c0b746e5SOllivier Robert double disc[LAG]; /* discriminator shift register */ 385c0b746e5SOllivier Robert double lpf[27]; /* FIR lowpass filter */ 386c0b746e5SOllivier Robert double monitor; /* audio monitor */ 387c0b746e5SOllivier Robert int discptr; /* discriminator pointer */ 388c0b746e5SOllivier Robert 389c0b746e5SOllivier Robert /* 3902b15cb3dSCy Schubert * Maximum-likelihood UART variables 391c0b746e5SOllivier Robert */ 392c0b746e5SOllivier Robert double baud; /* baud interval */ 393c0b746e5SOllivier Robert struct surv surv[8]; /* UART survivor structures */ 394c0b746e5SOllivier Robert int decptr; /* decode pointer */ 3952b15cb3dSCy Schubert int decpha; /* decode phase */ 396c0b746e5SOllivier Robert int dbrk; /* holdoff counter */ 397224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 398c0b746e5SOllivier Robert }; 399c0b746e5SOllivier Robert 400c0b746e5SOllivier Robert /* 401c0b746e5SOllivier Robert * Function prototypes 402c0b746e5SOllivier Robert */ 4032b15cb3dSCy Schubert static int chu_start (int, struct peer *); 4042b15cb3dSCy Schubert static void chu_shutdown (int, struct peer *); 4052b15cb3dSCy Schubert static void chu_receive (struct recvbuf *); 4062b15cb3dSCy Schubert static void chu_second (int, struct peer *); 4072b15cb3dSCy Schubert static void chu_poll (int, struct peer *); 408c0b746e5SOllivier Robert 409c0b746e5SOllivier Robert /* 410c0b746e5SOllivier Robert * More function prototypes 411c0b746e5SOllivier Robert */ 4122b15cb3dSCy Schubert static void chu_decode (struct peer *, int, l_fp); 4132b15cb3dSCy Schubert static void chu_burst (struct peer *); 4142b15cb3dSCy Schubert static void chu_clear (struct peer *); 4152b15cb3dSCy Schubert static void chu_a (struct peer *, int); 4162b15cb3dSCy Schubert static void chu_b (struct peer *, int); 4172b15cb3dSCy Schubert static int chu_dist (int, int); 4182b15cb3dSCy Schubert static double chu_major (struct peer *); 419224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 4202b15cb3dSCy Schubert static void chu_uart (struct surv *, double); 4212b15cb3dSCy Schubert static void chu_rf (struct peer *, double); 4222b15cb3dSCy Schubert static void chu_gain (struct peer *); 4232b15cb3dSCy Schubert static void chu_audio_receive (struct recvbuf *rbufp); 424224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 4259c2daa00SOllivier Robert #ifdef ICOM 4262b15cb3dSCy Schubert static int chu_newchan (struct peer *, double); 4279c2daa00SOllivier Robert #endif /* ICOM */ 4282b15cb3dSCy Schubert static void chu_serial_receive (struct recvbuf *rbufp); 429c0b746e5SOllivier Robert 430c0b746e5SOllivier Robert /* 431c0b746e5SOllivier Robert * Global variables 432c0b746e5SOllivier Robert */ 4339c2daa00SOllivier Robert static char hexchar[] = "0123456789abcdef_*="; 4349c2daa00SOllivier Robert 435a151a66cSOllivier Robert #ifdef ICOM 4369c2daa00SOllivier Robert /* 4379c2daa00SOllivier Robert * Note the tuned frequencies are 1 kHz higher than the carrier. CHU 4389c2daa00SOllivier Robert * transmits on USB with carrier so we can use AM and the narrow SSB 4399c2daa00SOllivier Robert * filter. 4409c2daa00SOllivier Robert */ 4412b15cb3dSCy Schubert static double qsy[NCHAN] = {3.330, 7.850, 14.670}; /* freq (MHz) */ 442a151a66cSOllivier Robert #endif /* ICOM */ 443c0b746e5SOllivier Robert 444c0b746e5SOllivier Robert /* 445c0b746e5SOllivier Robert * Transfer vector 446c0b746e5SOllivier Robert */ 447c0b746e5SOllivier Robert struct refclock refclock_chu = { 448c0b746e5SOllivier Robert chu_start, /* start up driver */ 449c0b746e5SOllivier Robert chu_shutdown, /* shut down driver */ 450c0b746e5SOllivier Robert chu_poll, /* transmit poll message */ 451c0b746e5SOllivier Robert noentry, /* not used (old chu_control) */ 452c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 453c0b746e5SOllivier Robert noentry, /* not used (old chu_buginfo) */ 4542b15cb3dSCy Schubert chu_second /* housekeeping timer */ 455c0b746e5SOllivier Robert }; 456c0b746e5SOllivier Robert 457c0b746e5SOllivier Robert 458c0b746e5SOllivier Robert /* 459c0b746e5SOllivier Robert * chu_start - open the devices and initialize data for processing 460c0b746e5SOllivier Robert */ 461c0b746e5SOllivier Robert static int 462c0b746e5SOllivier Robert chu_start( 463c0b746e5SOllivier Robert int unit, /* instance number (not used) */ 464c0b746e5SOllivier Robert struct peer *peer /* peer structure pointer */ 465c0b746e5SOllivier Robert ) 466c0b746e5SOllivier Robert { 467c0b746e5SOllivier Robert struct chuunit *up; 468c0b746e5SOllivier Robert struct refclockproc *pp; 469224ba2bdSOllivier Robert char device[20]; /* device name */ 470c0b746e5SOllivier Robert int fd; /* file descriptor */ 471a151a66cSOllivier Robert #ifdef ICOM 472a151a66cSOllivier Robert int temp; 473a151a66cSOllivier Robert #endif /* ICOM */ 474224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 475224ba2bdSOllivier Robert int fd_audio; /* audio port file descriptor */ 476c0b746e5SOllivier Robert int i; /* index */ 477c0b746e5SOllivier Robert double step; /* codec adjustment */ 478c0b746e5SOllivier Robert 479c0b746e5SOllivier Robert /* 4802b15cb3dSCy Schubert * Open audio device. Don't complain if not there. 481c0b746e5SOllivier Robert */ 4829c2daa00SOllivier Robert fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit); 4832b15cb3dSCy Schubert 484a151a66cSOllivier Robert #ifdef DEBUG 4852b15cb3dSCy Schubert if (fd_audio >= 0 && debug) 486a151a66cSOllivier Robert audio_show(); 487a151a66cSOllivier Robert #endif 488c0b746e5SOllivier Robert 489c0b746e5SOllivier Robert /* 4902b15cb3dSCy Schubert * If audio is unavailable, Open serial port in raw mode. 491c0b746e5SOllivier Robert */ 4922b15cb3dSCy Schubert if (fd_audio >= 0) { 493224ba2bdSOllivier Robert fd = fd_audio; 494224ba2bdSOllivier Robert } else { 4952b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 496224ba2bdSOllivier Robert fd = refclock_open(device, SPEED232, LDISC_RAW); 497c0b746e5SOllivier Robert } 498224ba2bdSOllivier Robert #else /* HAVE_AUDIO */ 499224ba2bdSOllivier Robert 500224ba2bdSOllivier Robert /* 501224ba2bdSOllivier Robert * Open serial port in raw mode. 502224ba2bdSOllivier Robert */ 5032b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 504224ba2bdSOllivier Robert fd = refclock_open(device, SPEED232, LDISC_RAW); 505224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 5062b15cb3dSCy Schubert 5072b15cb3dSCy Schubert if (fd < 0) 508224ba2bdSOllivier Robert return (0); 509c0b746e5SOllivier Robert 510c0b746e5SOllivier Robert /* 511c0b746e5SOllivier Robert * Allocate and initialize unit structure 512c0b746e5SOllivier Robert */ 5132b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 514c0b746e5SOllivier Robert pp = peer->procptr; 5152b15cb3dSCy Schubert pp->unitptr = up; 516c0b746e5SOllivier Robert pp->io.clock_recv = chu_receive; 5172b15cb3dSCy Schubert pp->io.srcclock = peer; 518c0b746e5SOllivier Robert pp->io.datalen = 0; 519c0b746e5SOllivier Robert pp->io.fd = fd; 520c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 521224ba2bdSOllivier Robert close(fd); 5222b15cb3dSCy Schubert pp->io.fd = -1; 523c0b746e5SOllivier Robert free(up); 5242b15cb3dSCy Schubert pp->unitptr = NULL; 525c0b746e5SOllivier Robert return (0); 526c0b746e5SOllivier Robert } 527c0b746e5SOllivier Robert 528c0b746e5SOllivier Robert /* 529c0b746e5SOllivier Robert * Initialize miscellaneous variables 530c0b746e5SOllivier Robert */ 531c0b746e5SOllivier Robert peer->precision = PRECISION; 532c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 5332b15cb3dSCy Schubert strlcpy(up->ident, "CHU", sizeof(up->ident)); 5342b15cb3dSCy Schubert memcpy(&pp->refid, up->ident, 4); 535c0b746e5SOllivier Robert DTOLFP(CHAR, &up->charstamp); 536224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 537c0b746e5SOllivier Robert 538c0b746e5SOllivier Robert /* 539c0b746e5SOllivier Robert * The companded samples are encoded sign-magnitude. The table 540224ba2bdSOllivier Robert * contains all the 256 values in the interest of speed. We do 541224ba2bdSOllivier Robert * this even if the audio codec is not available. C'est la lazy. 542c0b746e5SOllivier Robert */ 543224ba2bdSOllivier Robert up->fd_audio = fd_audio; 544224ba2bdSOllivier Robert up->gain = 127; 545c0b746e5SOllivier Robert up->comp[0] = up->comp[OFFSET] = 0.; 546c0b746e5SOllivier Robert up->comp[1] = 1; up->comp[OFFSET + 1] = -1.; 547c0b746e5SOllivier Robert up->comp[2] = 3; up->comp[OFFSET + 2] = -3.; 548c0b746e5SOllivier Robert step = 2.; 549c0b746e5SOllivier Robert for (i = 3; i < OFFSET; i++) { 550c0b746e5SOllivier Robert up->comp[i] = up->comp[i - 1] + step; 551c0b746e5SOllivier Robert up->comp[OFFSET + i] = -up->comp[i]; 552c0b746e5SOllivier Robert if (i % 16 == 0) 553c0b746e5SOllivier Robert step *= 2.; 554c0b746e5SOllivier Robert } 555a151a66cSOllivier Robert DTOLFP(1. / SECOND, &up->tick); 556224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 557a151a66cSOllivier Robert #ifdef ICOM 558a151a66cSOllivier Robert temp = 0; 559a151a66cSOllivier Robert #ifdef DEBUG 560a151a66cSOllivier Robert if (debug > 1) 561a151a66cSOllivier Robert temp = P_TRACE; 562a151a66cSOllivier Robert #endif 5639c2daa00SOllivier Robert if (peer->ttl > 0) { 5649c2daa00SOllivier Robert if (peer->ttl & 0x80) 565a151a66cSOllivier Robert up->fd_icom = icom_init("/dev/icom", B1200, 566a151a66cSOllivier Robert temp); 567a151a66cSOllivier Robert else 568a151a66cSOllivier Robert up->fd_icom = icom_init("/dev/icom", B9600, 569a151a66cSOllivier Robert temp); 570a151a66cSOllivier Robert } 571a151a66cSOllivier Robert if (up->fd_icom > 0) { 5729c2daa00SOllivier Robert if (chu_newchan(peer, 0) != 0) { 5732b15cb3dSCy Schubert msyslog(LOG_NOTICE, "icom: radio not found"); 574a151a66cSOllivier Robert close(up->fd_icom); 575a151a66cSOllivier Robert up->fd_icom = 0; 576a151a66cSOllivier Robert } else { 5772b15cb3dSCy Schubert msyslog(LOG_NOTICE, "icom: autotune enabled"); 578a151a66cSOllivier Robert } 579a151a66cSOllivier Robert } 580a151a66cSOllivier Robert #endif /* ICOM */ 581c0b746e5SOllivier Robert return (1); 582c0b746e5SOllivier Robert } 583c0b746e5SOllivier Robert 584c0b746e5SOllivier Robert 585c0b746e5SOllivier Robert /* 586c0b746e5SOllivier Robert * chu_shutdown - shut down the clock 587c0b746e5SOllivier Robert */ 588c0b746e5SOllivier Robert static void 589c0b746e5SOllivier Robert chu_shutdown( 590c0b746e5SOllivier Robert int unit, /* instance number (not used) */ 591c0b746e5SOllivier Robert struct peer *peer /* peer structure pointer */ 592c0b746e5SOllivier Robert ) 593c0b746e5SOllivier Robert { 594c0b746e5SOllivier Robert struct chuunit *up; 595c0b746e5SOllivier Robert struct refclockproc *pp; 596c0b746e5SOllivier Robert 597c0b746e5SOllivier Robert pp = peer->procptr; 5982b15cb3dSCy Schubert up = pp->unitptr; 599224ba2bdSOllivier Robert if (up == NULL) 600224ba2bdSOllivier Robert return; 6019c2daa00SOllivier Robert 602c0b746e5SOllivier Robert io_closeclock(&pp->io); 6039c2daa00SOllivier Robert #ifdef ICOM 604a151a66cSOllivier Robert if (up->fd_icom > 0) 605a151a66cSOllivier Robert close(up->fd_icom); 6069c2daa00SOllivier Robert #endif /* ICOM */ 607c0b746e5SOllivier Robert free(up); 608c0b746e5SOllivier Robert } 609c0b746e5SOllivier Robert 6109c2daa00SOllivier Robert 611c0b746e5SOllivier Robert /* 612224ba2bdSOllivier Robert * chu_receive - receive data from the audio or serial device 613c0b746e5SOllivier Robert */ 614c0b746e5SOllivier Robert static void 615c0b746e5SOllivier Robert chu_receive( 616c0b746e5SOllivier Robert struct recvbuf *rbufp /* receive buffer structure pointer */ 617c0b746e5SOllivier Robert ) 618c0b746e5SOllivier Robert { 619224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 620224ba2bdSOllivier Robert struct chuunit *up; 621224ba2bdSOllivier Robert struct refclockproc *pp; 622224ba2bdSOllivier Robert struct peer *peer; 623224ba2bdSOllivier Robert 6242b15cb3dSCy Schubert peer = rbufp->recv_peer; 625224ba2bdSOllivier Robert pp = peer->procptr; 6262b15cb3dSCy Schubert up = pp->unitptr; 627224ba2bdSOllivier Robert 628224ba2bdSOllivier Robert /* 629224ba2bdSOllivier Robert * If the audio codec is warmed up, the buffer contains codec 630224ba2bdSOllivier Robert * samples which need to be demodulated and decoded into CHU 631224ba2bdSOllivier Robert * characters using the software UART. Otherwise, the buffer 632224ba2bdSOllivier Robert * contains CHU characters from the serial port, so the software 633224ba2bdSOllivier Robert * UART is bypassed. In this case the CPU will probably run a 634224ba2bdSOllivier Robert * few degrees cooler. 635224ba2bdSOllivier Robert */ 636224ba2bdSOllivier Robert if (up->fd_audio > 0) 637224ba2bdSOllivier Robert chu_audio_receive(rbufp); 638224ba2bdSOllivier Robert else 639224ba2bdSOllivier Robert chu_serial_receive(rbufp); 640224ba2bdSOllivier Robert #else 641224ba2bdSOllivier Robert chu_serial_receive(rbufp); 642224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 643224ba2bdSOllivier Robert } 644224ba2bdSOllivier Robert 645224ba2bdSOllivier Robert 6469c2daa00SOllivier Robert #ifdef HAVE_AUDIO 647224ba2bdSOllivier Robert /* 648224ba2bdSOllivier Robert * chu_audio_receive - receive data from the audio device 649224ba2bdSOllivier Robert */ 650224ba2bdSOllivier Robert static void 651224ba2bdSOllivier Robert chu_audio_receive( 652224ba2bdSOllivier Robert struct recvbuf *rbufp /* receive buffer structure pointer */ 653224ba2bdSOllivier Robert ) 654224ba2bdSOllivier Robert { 655c0b746e5SOllivier Robert struct chuunit *up; 656c0b746e5SOllivier Robert struct refclockproc *pp; 657c0b746e5SOllivier Robert struct peer *peer; 658c0b746e5SOllivier Robert 659c0b746e5SOllivier Robert double sample; /* codec sample */ 660c0b746e5SOllivier Robert u_char *dpt; /* buffer pointer */ 6619c2daa00SOllivier Robert int bufcnt; /* buffer counter */ 662c0b746e5SOllivier Robert l_fp ltemp; /* l_fp temp */ 663c0b746e5SOllivier Robert 6642b15cb3dSCy Schubert peer = rbufp->recv_peer; 665c0b746e5SOllivier Robert pp = peer->procptr; 6662b15cb3dSCy Schubert up = pp->unitptr; 667c0b746e5SOllivier Robert 668c0b746e5SOllivier Robert /* 669c0b746e5SOllivier Robert * Main loop - read until there ain't no more. Note codec 670c0b746e5SOllivier Robert * samples are bit-inverted. 671c0b746e5SOllivier Robert */ 6729c2daa00SOllivier Robert DTOLFP((double)rbufp->recv_length / SECOND, <emp); 6739c2daa00SOllivier Robert L_SUB(&rbufp->recv_time, <emp); 674c0b746e5SOllivier Robert up->timestamp = rbufp->recv_time; 6759c2daa00SOllivier Robert dpt = rbufp->recv_buffer; 6769c2daa00SOllivier Robert for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) { 6779c2daa00SOllivier Robert sample = up->comp[~*dpt++ & 0xff]; 678c0b746e5SOllivier Robert 679c0b746e5SOllivier Robert /* 680ea906c41SOllivier Robert * Clip noise spikes greater than MAXAMP. If no clips, 681c0b746e5SOllivier Robert * increase the gain a tad; if the clips are too high, 682c0b746e5SOllivier Robert * decrease a tad. 683c0b746e5SOllivier Robert */ 684ea906c41SOllivier Robert if (sample > MAXAMP) { 685ea906c41SOllivier Robert sample = MAXAMP; 686c0b746e5SOllivier Robert up->clipcnt++; 687ea906c41SOllivier Robert } else if (sample < -MAXAMP) { 688ea906c41SOllivier Robert sample = -MAXAMP; 689c0b746e5SOllivier Robert up->clipcnt++; 690c0b746e5SOllivier Robert } 6919c2daa00SOllivier Robert chu_rf(peer, sample); 6929c2daa00SOllivier Robert L_ADD(&up->timestamp, &up->tick); 6939c2daa00SOllivier Robert 6949c2daa00SOllivier Robert /* 6959c2daa00SOllivier Robert * Once each second ride gain. 6969c2daa00SOllivier Robert */ 697a151a66cSOllivier Robert up->seccnt = (up->seccnt + 1) % SECOND; 698c0b746e5SOllivier Robert if (up->seccnt == 0) { 6999c2daa00SOllivier Robert chu_gain(peer); 7009c2daa00SOllivier Robert } 7019c2daa00SOllivier Robert } 7029c2daa00SOllivier Robert 7039c2daa00SOllivier Robert /* 7049c2daa00SOllivier Robert * Set the input port and monitor gain for the next buffer. 7059c2daa00SOllivier Robert */ 706c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG2) 707a151a66cSOllivier Robert up->port = 2; 708c0b746e5SOllivier Robert else 709a151a66cSOllivier Robert up->port = 1; 710c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG3) 7119c2daa00SOllivier Robert up->mongain = MONGAIN; 7129c2daa00SOllivier Robert else 7139c2daa00SOllivier Robert up->mongain = 0; 714c0b746e5SOllivier Robert } 715c0b746e5SOllivier Robert 716c0b746e5SOllivier Robert 717c0b746e5SOllivier Robert /* 718c0b746e5SOllivier Robert * chu_rf - filter and demodulate the FSK signal 719c0b746e5SOllivier Robert * 720c0b746e5SOllivier Robert * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz 721c0b746e5SOllivier Robert * and space 2025 Hz. It uses a bandpass filter followed by a soft 7222b15cb3dSCy Schubert * limiter, FM discriminator and lowpass filter. A maximum-likelihood 723c0b746e5SOllivier Robert * decoder samples the baseband signal at eight times the baud rate and 724c0b746e5SOllivier Robert * detects the start bit of each character. 725c0b746e5SOllivier Robert * 726c0b746e5SOllivier Robert * The filters are built for speed, which explains the rather clumsy 727c0b746e5SOllivier Robert * code. Hopefully, the compiler will efficiently implement the move- 728c0b746e5SOllivier Robert * and-muiltiply-and-add operations. 729c0b746e5SOllivier Robert */ 730a151a66cSOllivier Robert static void 731c0b746e5SOllivier Robert chu_rf( 732c0b746e5SOllivier Robert struct peer *peer, /* peer structure pointer */ 733c0b746e5SOllivier Robert double sample /* analog sample */ 734c0b746e5SOllivier Robert ) 735c0b746e5SOllivier Robert { 736c0b746e5SOllivier Robert struct refclockproc *pp; 737c0b746e5SOllivier Robert struct chuunit *up; 738c0b746e5SOllivier Robert struct surv *sp; 739c0b746e5SOllivier Robert 740c0b746e5SOllivier Robert /* 741c0b746e5SOllivier Robert * Local variables 742c0b746e5SOllivier Robert */ 743c0b746e5SOllivier Robert double signal; /* bandpass signal */ 744c0b746e5SOllivier Robert double limit; /* limiter signal */ 745c0b746e5SOllivier Robert double disc; /* discriminator signal */ 746c0b746e5SOllivier Robert double lpf; /* lowpass signal */ 747c0b746e5SOllivier Robert double dist; /* UART signal distance */ 748a151a66cSOllivier Robert int i, j; 749c0b746e5SOllivier Robert 750c0b746e5SOllivier Robert pp = peer->procptr; 7512b15cb3dSCy Schubert up = pp->unitptr; 752a151a66cSOllivier Robert 753c0b746e5SOllivier Robert /* 754c0b746e5SOllivier Robert * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered 7552b15cb3dSCy Schubert * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB, 7562b15cb3dSCy Schubert * phase delay 0.24 ms. 757c0b746e5SOllivier Robert */ 758c0b746e5SOllivier Robert signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01; 759c0b746e5SOllivier Robert signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01; 760c0b746e5SOllivier Robert signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00; 761c0b746e5SOllivier Robert signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00; 762c0b746e5SOllivier Robert signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00; 763c0b746e5SOllivier Robert signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00; 764c0b746e5SOllivier Robert signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00; 765c0b746e5SOllivier Robert signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01; 766c0b746e5SOllivier Robert up->bpf[0] = sample - signal; 767c0b746e5SOllivier Robert signal = up->bpf[0] * 6.176213e-03 768c0b746e5SOllivier Robert + up->bpf[1] * 3.156599e-03 769c0b746e5SOllivier Robert + up->bpf[2] * 7.567487e-03 770c0b746e5SOllivier Robert + up->bpf[3] * 4.344580e-03 771c0b746e5SOllivier Robert + up->bpf[4] * 1.190128e-02 772c0b746e5SOllivier Robert + up->bpf[5] * 4.344580e-03 773c0b746e5SOllivier Robert + up->bpf[6] * 7.567487e-03 774c0b746e5SOllivier Robert + up->bpf[7] * 3.156599e-03 775c0b746e5SOllivier Robert + up->bpf[8] * 6.176213e-03; 776c0b746e5SOllivier Robert 777c0b746e5SOllivier Robert up->monitor = signal / 4.; /* note monitor after filter */ 778c0b746e5SOllivier Robert 779c0b746e5SOllivier Robert /* 780c0b746e5SOllivier Robert * Soft limiter/discriminator. The 11-sample discriminator lag 781c0b746e5SOllivier Robert * interval corresponds to three cycles of 2125 Hz, which 782c0b746e5SOllivier Robert * requires the sample frequency to be 2125 * 11 / 3 = 7791.7 783c0b746e5SOllivier Robert * Hz. The discriminator output varies +-0.5 interval for input 784c0b746e5SOllivier Robert * frequency 2025-2225 Hz. However, we don't get to sample at 785c0b746e5SOllivier Robert * this frequency, so the discriminator output is biased. Life 786c0b746e5SOllivier Robert * at 8000 Hz sucks. 787c0b746e5SOllivier Robert */ 788c0b746e5SOllivier Robert limit = signal; 789c0b746e5SOllivier Robert if (limit > LIMIT) 790c0b746e5SOllivier Robert limit = LIMIT; 791c0b746e5SOllivier Robert else if (limit < -LIMIT) 792c0b746e5SOllivier Robert limit = -LIMIT; 793c0b746e5SOllivier Robert disc = up->disc[up->discptr] * -limit; 794c0b746e5SOllivier Robert up->disc[up->discptr] = limit; 795c0b746e5SOllivier Robert up->discptr = (up->discptr + 1 ) % LAG; 796c0b746e5SOllivier Robert if (disc >= 0) 797224ba2bdSOllivier Robert disc = SQRT(disc); 798c0b746e5SOllivier Robert else 799224ba2bdSOllivier Robert disc = -SQRT(-disc); 800c0b746e5SOllivier Robert 801c0b746e5SOllivier Robert /* 8022b15cb3dSCy Schubert * Lowpass filter. Raised cosine FIR, Ts = 1 / 300, beta = 0.1. 803c0b746e5SOllivier Robert */ 804c0b746e5SOllivier Robert lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02; 805c0b746e5SOllivier Robert lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01; 806c0b746e5SOllivier Robert lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01; 807c0b746e5SOllivier Robert lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01; 808c0b746e5SOllivier Robert lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01; 809c0b746e5SOllivier Robert lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01; 810c0b746e5SOllivier Robert lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01; 811c0b746e5SOllivier Robert lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01; 812c0b746e5SOllivier Robert lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01; 813c0b746e5SOllivier Robert lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01; 814c0b746e5SOllivier Robert lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01; 815c0b746e5SOllivier Robert lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01; 816c0b746e5SOllivier Robert lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01; 817c0b746e5SOllivier Robert lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00; 818c0b746e5SOllivier Robert lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01; 819c0b746e5SOllivier Robert lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01; 820c0b746e5SOllivier Robert lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01; 821c0b746e5SOllivier Robert lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01; 822c0b746e5SOllivier Robert lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01; 823c0b746e5SOllivier Robert lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01; 824c0b746e5SOllivier Robert lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01; 825c0b746e5SOllivier Robert lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01; 826c0b746e5SOllivier Robert lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01; 827c0b746e5SOllivier Robert lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01; 828c0b746e5SOllivier Robert lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01; 829c0b746e5SOllivier Robert lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01; 830c0b746e5SOllivier Robert lpf += up->lpf[0] = disc * 2.538771e-02; 831a151a66cSOllivier Robert 832c0b746e5SOllivier Robert /* 8332b15cb3dSCy Schubert * Maximum-likelihood decoder. The UART updates each of the 834c0b746e5SOllivier Robert * eight survivors and determines the span, slice level and 835c0b746e5SOllivier Robert * tentative decoded character. Valid 11-bit characters are 8362b15cb3dSCy Schubert * framed so that bit 10 and bit 11 (stop bits) are mark and bit 8372b15cb3dSCy Schubert * 1 (start bit) is space. When a valid character is found, the 838c0b746e5SOllivier Robert * survivor with maximum distance determines the final decoded 839c0b746e5SOllivier Robert * character. 840c0b746e5SOllivier Robert */ 841a151a66cSOllivier Robert up->baud += 1. / SECOND; 842c0b746e5SOllivier Robert if (up->baud > 1. / (BAUD * 8.)) { 843c0b746e5SOllivier Robert up->baud -= 1. / (BAUD * 8.); 8442b15cb3dSCy Schubert up->decptr = (up->decptr + 1) % 8; 845c0b746e5SOllivier Robert sp = &up->surv[up->decptr]; 8462b15cb3dSCy Schubert sp->cstamp = up->timestamp; 8472b15cb3dSCy Schubert chu_uart(sp, -lpf * AGAIN); 848c0b746e5SOllivier Robert if (up->dbrk > 0) { 849c0b746e5SOllivier Robert up->dbrk--; 8502b15cb3dSCy Schubert if (up->dbrk > 0) 8512b15cb3dSCy Schubert return; 8522b15cb3dSCy Schubert 8532b15cb3dSCy Schubert up->decpha = up->decptr; 8542b15cb3dSCy Schubert } 8552b15cb3dSCy Schubert if (up->decptr != up->decpha) 8562b15cb3dSCy Schubert return; 8572b15cb3dSCy Schubert 858c0b746e5SOllivier Robert dist = 0; 8592b15cb3dSCy Schubert j = -1; 860c0b746e5SOllivier Robert for (i = 0; i < 8; i++) { 8612b15cb3dSCy Schubert 8622b15cb3dSCy Schubert /* 8632b15cb3dSCy Schubert * The timestamp is taken at the last bit, so 8642b15cb3dSCy Schubert * for correct decoding we reqire sufficient 8652b15cb3dSCy Schubert * span and correct start bit and two stop bits. 8662b15cb3dSCy Schubert */ 8672b15cb3dSCy Schubert if ((up->surv[i].uart & 0x601) != 0x600 || 8682b15cb3dSCy Schubert up->surv[i].span < SPAN) 8692b15cb3dSCy Schubert continue; 8702b15cb3dSCy Schubert 871c0b746e5SOllivier Robert if (up->surv[i].dist > dist) { 872c0b746e5SOllivier Robert dist = up->surv[i].dist; 873c0b746e5SOllivier Robert j = i; 874c0b746e5SOllivier Robert } 875c0b746e5SOllivier Robert } 8762b15cb3dSCy Schubert if (j < 0) 8772b15cb3dSCy Schubert return; 8782b15cb3dSCy Schubert 8792b15cb3dSCy Schubert /* 8802b15cb3dSCy Schubert * Process the character, then blank the decoder until 8812b15cb3dSCy Schubert * the end of the next character.This sets the decoding 8822b15cb3dSCy Schubert * phase of the entire burst from the phase of the first 8832b15cb3dSCy Schubert * character. 8842b15cb3dSCy Schubert */ 8852b15cb3dSCy Schubert up->maxsignal = up->surv[j].span; 8862b15cb3dSCy Schubert chu_decode(peer, (up->surv[j].uart >> 1) & 0xff, 8872b15cb3dSCy Schubert up->surv[j].cstamp); 8882b15cb3dSCy Schubert up->dbrk = 88; 889c0b746e5SOllivier Robert } 890c0b746e5SOllivier Robert } 891c0b746e5SOllivier Robert 892c0b746e5SOllivier Robert 893c0b746e5SOllivier Robert /* 8942b15cb3dSCy Schubert * chu_uart - maximum-likelihood UART 895c0b746e5SOllivier Robert * 896c0b746e5SOllivier Robert * This routine updates a shift register holding the last 11 envelope 897c0b746e5SOllivier Robert * samples. It then computes the slice level and span over these samples 898c0b746e5SOllivier Robert * and determines the tentative data bits and distance. The calling 899c0b746e5SOllivier Robert * program selects over the last eight survivors the one with maximum 900c0b746e5SOllivier Robert * distance to determine the decoded character. 901c0b746e5SOllivier Robert */ 902a151a66cSOllivier Robert static void 903c0b746e5SOllivier Robert chu_uart( 904c0b746e5SOllivier Robert struct surv *sp, /* survivor structure pointer */ 905c0b746e5SOllivier Robert double sample /* baseband signal */ 906c0b746e5SOllivier Robert ) 907c0b746e5SOllivier Robert { 908a151a66cSOllivier Robert double es_max, es_min; /* max/min envelope */ 909c0b746e5SOllivier Robert double slice; /* slice level */ 910c0b746e5SOllivier Robert double dist; /* distance */ 911a151a66cSOllivier Robert double dtemp; 912a151a66cSOllivier Robert int i; 913c0b746e5SOllivier Robert 914c0b746e5SOllivier Robert /* 915c0b746e5SOllivier Robert * Save the sample and shift right. At the same time, measure 916c0b746e5SOllivier Robert * the maximum and minimum over all eleven samples. 917c0b746e5SOllivier Robert */ 918a151a66cSOllivier Robert es_max = -1e6; 919a151a66cSOllivier Robert es_min = 1e6; 920c0b746e5SOllivier Robert sp->shift[0] = sample; 921c0b746e5SOllivier Robert for (i = 11; i > 0; i--) { 922c0b746e5SOllivier Robert sp->shift[i] = sp->shift[i - 1]; 923a151a66cSOllivier Robert if (sp->shift[i] > es_max) 924a151a66cSOllivier Robert es_max = sp->shift[i]; 925a151a66cSOllivier Robert if (sp->shift[i] < es_min) 926a151a66cSOllivier Robert es_min = sp->shift[i]; 927c0b746e5SOllivier Robert } 928c0b746e5SOllivier Robert 929c0b746e5SOllivier Robert /* 9302b15cb3dSCy Schubert * Determine the span as the maximum less the minimum and the 9312b15cb3dSCy Schubert * slice level as the minimum plus a fraction of the span. Note 9322b15cb3dSCy Schubert * the slight bias toward mark to correct for the modem tendency 9332b15cb3dSCy Schubert * to make more mark than space errors. Compute the distance on 9342b15cb3dSCy Schubert * the assumption the last two bits must be mark, the first 9352b15cb3dSCy Schubert * space and the rest either mark or space. 936c0b746e5SOllivier Robert */ 9372b15cb3dSCy Schubert sp->span = es_max - es_min; 9382b15cb3dSCy Schubert slice = es_min + .45 * sp->span; 939c0b746e5SOllivier Robert dist = 0; 940c0b746e5SOllivier Robert sp->uart = 0; 941c0b746e5SOllivier Robert for (i = 1; i < 12; i++) { 942c0b746e5SOllivier Robert sp->uart <<= 1; 943c0b746e5SOllivier Robert dtemp = sp->shift[i]; 944c0b746e5SOllivier Robert if (dtemp > slice) 945c0b746e5SOllivier Robert sp->uart |= 0x1; 9462b15cb3dSCy Schubert if (i == 1 || i == 2) { 947a151a66cSOllivier Robert dist += dtemp - es_min; 9482b15cb3dSCy Schubert } else if (i == 11) { 949a151a66cSOllivier Robert dist += es_max - dtemp; 950c0b746e5SOllivier Robert } else { 951c0b746e5SOllivier Robert if (dtemp > slice) 952a151a66cSOllivier Robert dist += dtemp - es_min; 953c0b746e5SOllivier Robert else 954a151a66cSOllivier Robert dist += es_max - dtemp; 955c0b746e5SOllivier Robert } 956c0b746e5SOllivier Robert } 9572b15cb3dSCy Schubert sp->dist = dist / (11 * sp->span); 958c0b746e5SOllivier Robert } 959224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 960c0b746e5SOllivier Robert 961c0b746e5SOllivier Robert 962c0b746e5SOllivier Robert /* 963224ba2bdSOllivier Robert * chu_serial_receive - receive data from the serial device 964c0b746e5SOllivier Robert */ 965c0b746e5SOllivier Robert static void 966224ba2bdSOllivier Robert chu_serial_receive( 967c0b746e5SOllivier Robert struct recvbuf *rbufp /* receive buffer structure pointer */ 968c0b746e5SOllivier Robert ) 969c0b746e5SOllivier Robert { 970c0b746e5SOllivier Robert struct peer *peer; 971c0b746e5SOllivier Robert 972c0b746e5SOllivier Robert u_char *dpt; /* receive buffer pointer */ 973c0b746e5SOllivier Robert 9742b15cb3dSCy Schubert peer = rbufp->recv_peer; 975c0b746e5SOllivier Robert 976c0b746e5SOllivier Robert dpt = (u_char *)&rbufp->recv_space; 9772b15cb3dSCy Schubert chu_decode(peer, *dpt, rbufp->recv_time); 978c0b746e5SOllivier Robert } 979c0b746e5SOllivier Robert 980c0b746e5SOllivier Robert 981c0b746e5SOllivier Robert /* 982224ba2bdSOllivier Robert * chu_decode - decode the character data 983c0b746e5SOllivier Robert */ 984c0b746e5SOllivier Robert static void 985c0b746e5SOllivier Robert chu_decode( 986c0b746e5SOllivier Robert struct peer *peer, /* peer structure pointer */ 9872b15cb3dSCy Schubert int hexhex, /* data character */ 9882b15cb3dSCy Schubert l_fp cstamp /* data character timestamp */ 989c0b746e5SOllivier Robert ) 990c0b746e5SOllivier Robert { 991c0b746e5SOllivier Robert struct refclockproc *pp; 992c0b746e5SOllivier Robert struct chuunit *up; 993c0b746e5SOllivier Robert 994c0b746e5SOllivier Robert l_fp tstmp; /* timestamp temp */ 995a151a66cSOllivier Robert double dtemp; 996c0b746e5SOllivier Robert 997c0b746e5SOllivier Robert pp = peer->procptr; 9982b15cb3dSCy Schubert up = pp->unitptr; 999c0b746e5SOllivier Robert 1000c0b746e5SOllivier Robert /* 1001c0b746e5SOllivier Robert * If the interval since the last character is greater than the 1002c0b746e5SOllivier Robert * longest burst, process the last burst and start a new one. If 1003c0b746e5SOllivier Robert * the interval is less than this but greater than two 1004c0b746e5SOllivier Robert * characters, consider this a noise burst and reject it. 1005c0b746e5SOllivier Robert */ 1006c0b746e5SOllivier Robert tstmp = up->timestamp; 1007c0b746e5SOllivier Robert if (L_ISZERO(&up->laststamp)) 1008c0b746e5SOllivier Robert up->laststamp = up->timestamp; 1009c0b746e5SOllivier Robert L_SUB(&tstmp, &up->laststamp); 1010c0b746e5SOllivier Robert up->laststamp = up->timestamp; 1011c0b746e5SOllivier Robert LFPTOD(&tstmp, dtemp); 1012c0b746e5SOllivier Robert if (dtemp > BURST * CHAR) { 1013c0b746e5SOllivier Robert chu_burst(peer); 1014c0b746e5SOllivier Robert up->ndx = 0; 1015c0b746e5SOllivier Robert } else if (dtemp > 2.5 * CHAR) { 1016c0b746e5SOllivier Robert up->ndx = 0; 1017c0b746e5SOllivier Robert } 1018c0b746e5SOllivier Robert 1019c0b746e5SOllivier Robert /* 1020c0b746e5SOllivier Robert * Append the character to the current burst and append the 10212b15cb3dSCy Schubert * character timestamp to the timestamp list. 1022c0b746e5SOllivier Robert */ 1023c0b746e5SOllivier Robert if (up->ndx < BURST) { 1024c0b746e5SOllivier Robert up->cbuf[up->ndx] = hexhex & 0xff; 10252b15cb3dSCy Schubert up->cstamp[up->ndx] = cstamp; 1026c0b746e5SOllivier Robert up->ndx++; 1027c0b746e5SOllivier Robert 1028c0b746e5SOllivier Robert } 1029c0b746e5SOllivier Robert } 1030c0b746e5SOllivier Robert 1031c0b746e5SOllivier Robert 1032c0b746e5SOllivier Robert /* 1033c0b746e5SOllivier Robert * chu_burst - search for valid burst format 1034c0b746e5SOllivier Robert */ 1035c0b746e5SOllivier Robert static void 1036c0b746e5SOllivier Robert chu_burst( 1037c0b746e5SOllivier Robert struct peer *peer 1038c0b746e5SOllivier Robert ) 1039c0b746e5SOllivier Robert { 1040c0b746e5SOllivier Robert struct chuunit *up; 1041c0b746e5SOllivier Robert struct refclockproc *pp; 1042c0b746e5SOllivier Robert 1043a151a66cSOllivier Robert int i; 1044c0b746e5SOllivier Robert 1045c0b746e5SOllivier Robert pp = peer->procptr; 10462b15cb3dSCy Schubert up = pp->unitptr; 1047c0b746e5SOllivier Robert 1048c0b746e5SOllivier Robert /* 1049c0b746e5SOllivier Robert * Correlate a block of five characters with the next block of 1050c0b746e5SOllivier Robert * five characters. The burst distance is defined as the number 1051c0b746e5SOllivier Robert * of bits that match in the two blocks for format A and that 1052c0b746e5SOllivier Robert * match the inverse for format B. 1053c0b746e5SOllivier Robert */ 10542b15cb3dSCy Schubert if (up->ndx < MINCHARS) { 1055a151a66cSOllivier Robert up->status |= RUNT; 1056c0b746e5SOllivier Robert return; 1057c0b746e5SOllivier Robert } 1058c0b746e5SOllivier Robert up->burdist = 0; 1059c0b746e5SOllivier Robert for (i = 0; i < 5 && i < up->ndx - 5; i++) 1060c0b746e5SOllivier Robert up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]); 1061c0b746e5SOllivier Robert 1062c0b746e5SOllivier Robert /* 1063c0b746e5SOllivier Robert * If the burst distance is at least MINDIST, this must be a 1064c0b746e5SOllivier Robert * format A burst; if the value is not greater than -MINDIST, it 1065a151a66cSOllivier Robert * must be a format B burst. If the B burst is perfect, we 1066a151a66cSOllivier Robert * believe it; otherwise, it is a noise burst and of no use to 1067a151a66cSOllivier Robert * anybody. 1068c0b746e5SOllivier Robert */ 1069c0b746e5SOllivier Robert if (up->burdist >= MINDIST) { 1070a151a66cSOllivier Robert chu_a(peer, up->ndx); 1071c0b746e5SOllivier Robert } else if (up->burdist <= -MINDIST) { 1072a151a66cSOllivier Robert chu_b(peer, up->ndx); 1073c0b746e5SOllivier Robert } else { 1074a151a66cSOllivier Robert up->status |= NOISE; 1075c0b746e5SOllivier Robert return; 1076c0b746e5SOllivier Robert } 1077c0b746e5SOllivier Robert 1078c0b746e5SOllivier Robert /* 1079c0b746e5SOllivier Robert * If this is a valid burst, wait a guard time of ten seconds to 1080c0b746e5SOllivier Robert * allow for more bursts, then arm the poll update routine to 1081c0b746e5SOllivier Robert * process the minute. Don't do this if this is called from the 1082c0b746e5SOllivier Robert * timer interrupt routine. 1083c0b746e5SOllivier Robert */ 1084a151a66cSOllivier Robert if (peer->outdate != current_time) 1085c0b746e5SOllivier Robert peer->nextdate = current_time + 10; 1086c0b746e5SOllivier Robert } 1087c0b746e5SOllivier Robert 1088c0b746e5SOllivier Robert 1089c0b746e5SOllivier Robert /* 1090a151a66cSOllivier Robert * chu_b - decode format B burst 1091c0b746e5SOllivier Robert */ 1092c0b746e5SOllivier Robert static void 1093a151a66cSOllivier Robert chu_b( 1094c0b746e5SOllivier Robert struct peer *peer, 1095c0b746e5SOllivier Robert int nchar 1096c0b746e5SOllivier Robert ) 1097c0b746e5SOllivier Robert { 1098c0b746e5SOllivier Robert struct refclockproc *pp; 1099c0b746e5SOllivier Robert struct chuunit *up; 1100c0b746e5SOllivier Robert 1101c0b746e5SOllivier Robert u_char code[11]; /* decoded timecode */ 1102a151a66cSOllivier Robert char tbuf[80]; /* trace buffer */ 11032b15cb3dSCy Schubert char * p; 11042b15cb3dSCy Schubert size_t chars; 11052b15cb3dSCy Schubert size_t cb; 1106a151a66cSOllivier Robert int i; 1107c0b746e5SOllivier Robert 1108c0b746e5SOllivier Robert pp = peer->procptr; 11092b15cb3dSCy Schubert up = pp->unitptr; 1110c0b746e5SOllivier Robert 1111c0b746e5SOllivier Robert /* 1112c0b746e5SOllivier Robert * In a format B burst, a character is considered valid only if 11132b15cb3dSCy Schubert * the first occurence matches the last occurence. The burst is 11142b15cb3dSCy Schubert * considered valid only if all characters are valid; that is, 11152b15cb3dSCy Schubert * only if the distance is 40. Note that once a valid frame has 11162b15cb3dSCy Schubert * been found errors are ignored. 1117c0b746e5SOllivier Robert */ 11182b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), "chuB %04x %4.0f %2d %2d ", 11192b15cb3dSCy Schubert up->status, up->maxsignal, nchar, -up->burdist); 11202b15cb3dSCy Schubert cb = sizeof(tbuf); 11212b15cb3dSCy Schubert p = tbuf; 11222b15cb3dSCy Schubert for (i = 0; i < nchar; i++) { 11232b15cb3dSCy Schubert chars = strlen(p); 11242b15cb3dSCy Schubert if (cb < chars + 1) { 11252b15cb3dSCy Schubert msyslog(LOG_ERR, "chu_b() fatal out buffer"); 11262b15cb3dSCy Schubert exit(1); 11272b15cb3dSCy Schubert } 11282b15cb3dSCy Schubert cb -= chars; 11292b15cb3dSCy Schubert p += chars; 11302b15cb3dSCy Schubert snprintf(p, cb, "%02x", up->cbuf[i]); 11312b15cb3dSCy Schubert } 1132c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4) 1133a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1134c0b746e5SOllivier Robert #ifdef DEBUG 1135a151a66cSOllivier Robert if (debug) 1136a151a66cSOllivier Robert printf("%s\n", tbuf); 1137c0b746e5SOllivier Robert #endif 1138a151a66cSOllivier Robert if (up->burdist > -40) { 1139a151a66cSOllivier Robert up->status |= BFRAME; 1140c0b746e5SOllivier Robert return; 1141c0b746e5SOllivier Robert } 1142c0b746e5SOllivier Robert 1143c0b746e5SOllivier Robert /* 11442b15cb3dSCy Schubert * Convert the burst data to internal format. Don't bother with 11452b15cb3dSCy Schubert * the timestamps. 1146c0b746e5SOllivier Robert */ 1147c0b746e5SOllivier Robert for (i = 0; i < 5; i++) { 1148c0b746e5SOllivier Robert code[2 * i] = hexchar[up->cbuf[i] & 0xf]; 1149c0b746e5SOllivier Robert code[2 * i + 1] = hexchar[(up->cbuf[i] >> 1150c0b746e5SOllivier Robert 4) & 0xf]; 1151c0b746e5SOllivier Robert } 1152a151a66cSOllivier Robert if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut, 1153a151a66cSOllivier Robert &pp->year, &up->tai, &up->dst) != 5) { 1154a151a66cSOllivier Robert up->status |= BFORMAT; 1155c0b746e5SOllivier Robert return; 1156c0b746e5SOllivier Robert } 11572b15cb3dSCy Schubert up->status |= BVALID; 1158a151a66cSOllivier Robert if (up->leap & 0x8) 1159a151a66cSOllivier Robert up->dut = -up->dut; 1160c0b746e5SOllivier Robert } 1161c0b746e5SOllivier Robert 1162c0b746e5SOllivier Robert 1163c0b746e5SOllivier Robert /* 1164a151a66cSOllivier Robert * chu_a - decode format A burst 1165c0b746e5SOllivier Robert */ 1166c0b746e5SOllivier Robert static void 1167a151a66cSOllivier Robert chu_a( 1168c0b746e5SOllivier Robert struct peer *peer, 1169c0b746e5SOllivier Robert int nchar 1170c0b746e5SOllivier Robert ) 1171c0b746e5SOllivier Robert { 1172c0b746e5SOllivier Robert struct refclockproc *pp; 1173c0b746e5SOllivier Robert struct chuunit *up; 1174c0b746e5SOllivier Robert 1175a151a66cSOllivier Robert char tbuf[80]; /* trace buffer */ 11762b15cb3dSCy Schubert char * p; 11772b15cb3dSCy Schubert size_t chars; 11782b15cb3dSCy Schubert size_t cb; 1179c0b746e5SOllivier Robert l_fp offset; /* timestamp offset */ 1180c0b746e5SOllivier Robert int val; /* distance */ 1181a151a66cSOllivier Robert int temp; 1182a151a66cSOllivier Robert int i, j, k; 1183c0b746e5SOllivier Robert 1184c0b746e5SOllivier Robert pp = peer->procptr; 11852b15cb3dSCy Schubert up = pp->unitptr; 1186c0b746e5SOllivier Robert 1187c0b746e5SOllivier Robert /* 1188c0b746e5SOllivier Robert * Determine correct burst phase. There are three cases 1189c0b746e5SOllivier Robert * corresponding to in-phase, one character early or one 1190c0b746e5SOllivier Robert * character late. These cases are distinguished by the position 11912b15cb3dSCy Schubert * of the framing digits 0x6 at positions 0 and 5 and 0x3 at 1192c0b746e5SOllivier Robert * positions 4 and 9. The correct phase is when the distance 1193c0b746e5SOllivier Robert * relative to the framing digits is maximum. The burst is valid 1194c0b746e5SOllivier Robert * only if the maximum distance is at least MINSYNC. 1195c0b746e5SOllivier Robert */ 1196c0b746e5SOllivier Robert up->syndist = k = 0; 1197c0b746e5SOllivier Robert val = -16; 1198c0b746e5SOllivier Robert for (i = -1; i < 2; i++) { 1199c0b746e5SOllivier Robert temp = up->cbuf[i + 4] & 0xf; 1200c0b746e5SOllivier Robert if (i >= 0) 1201c0b746e5SOllivier Robert temp |= (up->cbuf[i] & 0xf) << 4; 1202c0b746e5SOllivier Robert val = chu_dist(temp, 0x63); 1203c0b746e5SOllivier Robert temp = (up->cbuf[i + 5] & 0xf) << 4; 1204c0b746e5SOllivier Robert if (i + 9 < nchar) 1205c0b746e5SOllivier Robert temp |= up->cbuf[i + 9] & 0xf; 1206c0b746e5SOllivier Robert val += chu_dist(temp, 0x63); 1207c0b746e5SOllivier Robert if (val > up->syndist) { 1208c0b746e5SOllivier Robert up->syndist = val; 1209c0b746e5SOllivier Robert k = i; 1210c0b746e5SOllivier Robert } 1211c0b746e5SOllivier Robert } 1212224ba2bdSOllivier Robert 12132b15cb3dSCy Schubert /* 12142b15cb3dSCy Schubert * Extract the second number; it must be in the range 2 through 12152b15cb3dSCy Schubert * 9 and the two repititions must be the same. 12162b15cb3dSCy Schubert */ 12172b15cb3dSCy Schubert temp = (up->cbuf[k + 4] >> 4) & 0xf; 12182b15cb3dSCy Schubert if (temp < 2 || temp > 9 || k + 9 >= nchar || temp != 12192b15cb3dSCy Schubert ((up->cbuf[k + 9] >> 4) & 0xf)) 12202b15cb3dSCy Schubert temp = 0; 12212b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 12222b15cb3dSCy Schubert "chuA %04x %4.0f %2d %2d %2d %2d %1d ", up->status, 12232b15cb3dSCy Schubert up->maxsignal, nchar, up->burdist, k, up->syndist, 12242b15cb3dSCy Schubert temp); 12252b15cb3dSCy Schubert cb = sizeof(tbuf); 12262b15cb3dSCy Schubert p = tbuf; 12272b15cb3dSCy Schubert for (i = 0; i < nchar; i++) { 12282b15cb3dSCy Schubert chars = strlen(p); 12292b15cb3dSCy Schubert if (cb < chars + 1) { 12302b15cb3dSCy Schubert msyslog(LOG_ERR, "chu_a() fatal out buffer"); 12312b15cb3dSCy Schubert exit(1); 12322b15cb3dSCy Schubert } 12332b15cb3dSCy Schubert cb -= chars; 12342b15cb3dSCy Schubert p += chars; 12352b15cb3dSCy Schubert snprintf(p, cb, "%02x", up->cbuf[i]); 12362b15cb3dSCy Schubert } 1237c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4) 1238a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1239c0b746e5SOllivier Robert #ifdef DEBUG 1240a151a66cSOllivier Robert if (debug) 1241a151a66cSOllivier Robert printf("%s\n", tbuf); 1242c0b746e5SOllivier Robert #endif 1243c0b746e5SOllivier Robert if (up->syndist < MINSYNC) { 1244a151a66cSOllivier Robert up->status |= AFRAME; 1245c0b746e5SOllivier Robert return; 1246c0b746e5SOllivier Robert } 1247c0b746e5SOllivier Robert 1248c0b746e5SOllivier Robert /* 1249c0b746e5SOllivier Robert * A valid burst requires the first seconds number to match the 1250c0b746e5SOllivier Robert * last seconds number. If so, the burst timestamps are 1251c0b746e5SOllivier Robert * corrected to the current minute and saved for later 1252c0b746e5SOllivier Robert * processing. In addition, the seconds decode is advanced from 1253c0b746e5SOllivier Robert * the previous burst to the current one. 1254c0b746e5SOllivier Robert */ 12552b15cb3dSCy Schubert if (temp == 0) { 12562b15cb3dSCy Schubert up->status |= AFORMAT; 12572b15cb3dSCy Schubert } else { 12582b15cb3dSCy Schubert up->status |= AVALID; 12592b15cb3dSCy Schubert up->second = pp->second = 30 + temp; 1260c0b746e5SOllivier Robert offset.l_ui = 30 + temp; 12612b15cb3dSCy Schubert offset.l_uf = 0; 1262c0b746e5SOllivier Robert i = 0; 1263c0b746e5SOllivier Robert if (k < 0) 1264c0b746e5SOllivier Robert offset = up->charstamp; 1265c0b746e5SOllivier Robert else if (k > 0) 1266c0b746e5SOllivier Robert i = 1; 1267c0b746e5SOllivier Robert for (; i < nchar && i < k + 10; i++) { 1268c0b746e5SOllivier Robert up->tstamp[up->ntstamp] = up->cstamp[i]; 1269c0b746e5SOllivier Robert L_SUB(&up->tstamp[up->ntstamp], &offset); 1270c0b746e5SOllivier Robert L_ADD(&offset, &up->charstamp); 1271ea906c41SOllivier Robert if (up->ntstamp < MAXSTAGE - 1) 1272c0b746e5SOllivier Robert up->ntstamp++; 1273c0b746e5SOllivier Robert } 1274c0b746e5SOllivier Robert while (temp > up->prevsec) { 1275c0b746e5SOllivier Robert for (j = 15; j > 0; j--) { 1276c0b746e5SOllivier Robert up->decode[9][j] = up->decode[9][j - 1]; 1277c0b746e5SOllivier Robert up->decode[19][j] = 1278c0b746e5SOllivier Robert up->decode[19][j - 1]; 1279c0b746e5SOllivier Robert } 1280c0b746e5SOllivier Robert up->decode[9][j] = up->decode[19][j] = 0; 1281c0b746e5SOllivier Robert up->prevsec++; 1282c0b746e5SOllivier Robert } 1283c0b746e5SOllivier Robert } 12842b15cb3dSCy Schubert 12852b15cb3dSCy Schubert /* 12862b15cb3dSCy Schubert * Stash the data in the decoding matrix. 12872b15cb3dSCy Schubert */ 1288c0b746e5SOllivier Robert i = -(2 * k); 1289c0b746e5SOllivier Robert for (j = 0; j < nchar; j++) { 1290ea906c41SOllivier Robert if (i < 0 || i > 18) { 1291c0b746e5SOllivier Robert i += 2; 1292c0b746e5SOllivier Robert continue; 1293c0b746e5SOllivier Robert } 1294a151a66cSOllivier Robert up->decode[i][up->cbuf[j] & 0xf]++; 1295a151a66cSOllivier Robert i++; 1296a151a66cSOllivier Robert up->decode[i][(up->cbuf[j] >> 4) & 0xf]++; 1297a151a66cSOllivier Robert i++; 1298c0b746e5SOllivier Robert } 1299c0b746e5SOllivier Robert up->burstcnt++; 1300c0b746e5SOllivier Robert } 1301c0b746e5SOllivier Robert 1302c0b746e5SOllivier Robert 1303c0b746e5SOllivier Robert /* 1304c0b746e5SOllivier Robert * chu_poll - called by the transmit procedure 1305c0b746e5SOllivier Robert */ 1306c0b746e5SOllivier Robert static void 1307c0b746e5SOllivier Robert chu_poll( 1308c0b746e5SOllivier Robert int unit, 1309a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 1310c0b746e5SOllivier Robert ) 1311c0b746e5SOllivier Robert { 1312c0b746e5SOllivier Robert struct refclockproc *pp; 13132b15cb3dSCy Schubert 13142b15cb3dSCy Schubert pp = peer->procptr; 13152b15cb3dSCy Schubert pp->polls++; 13162b15cb3dSCy Schubert } 13172b15cb3dSCy Schubert 13182b15cb3dSCy Schubert 13192b15cb3dSCy Schubert /* 13202b15cb3dSCy Schubert * chu_second - process minute data 13212b15cb3dSCy Schubert */ 13222b15cb3dSCy Schubert static void 13232b15cb3dSCy Schubert chu_second( 13242b15cb3dSCy Schubert int unit, 13252b15cb3dSCy Schubert struct peer *peer /* peer structure pointer */ 13262b15cb3dSCy Schubert ) 13272b15cb3dSCy Schubert { 13282b15cb3dSCy Schubert struct refclockproc *pp; 1329c0b746e5SOllivier Robert struct chuunit *up; 13309c2daa00SOllivier Robert l_fp offset; 1331a151a66cSOllivier Robert char synchar, qual, leapchar; 13329c2daa00SOllivier Robert int minset, i; 13339c2daa00SOllivier Robert double dtemp; 13349c2daa00SOllivier Robert 1335c0b746e5SOllivier Robert pp = peer->procptr; 13362b15cb3dSCy Schubert up = pp->unitptr; 13379c2daa00SOllivier Robert 13389c2daa00SOllivier Robert /* 13392b15cb3dSCy Schubert * This routine is called once per minute to process the 13402b15cb3dSCy Schubert * accumulated burst data. We do a bit of fancy footwork so that 13412b15cb3dSCy Schubert * this doesn't run while burst data are being accumulated. 13429c2daa00SOllivier Robert */ 13432b15cb3dSCy Schubert up->second = (up->second + 1) % 60; 13442b15cb3dSCy Schubert if (up->second != 0) 13452b15cb3dSCy Schubert return; 1346c0b746e5SOllivier Robert 1347c0b746e5SOllivier Robert /* 1348c0b746e5SOllivier Robert * Process the last burst, if still in the burst buffer. 13492b15cb3dSCy Schubert * If the minute contains a valid B frame with sufficient A 13502b15cb3dSCy Schubert * frame metric, it is considered valid. However, the timecode 13512b15cb3dSCy Schubert * is sent to clockstats even if invalid. 1352c0b746e5SOllivier Robert */ 1353c0b746e5SOllivier Robert chu_burst(peer); 13542b15cb3dSCy Schubert minset = ((current_time - peer->update) + 30) / 60; 13559c2daa00SOllivier Robert dtemp = chu_major(peer); 1356a151a66cSOllivier Robert qual = 0; 1357a151a66cSOllivier Robert if (up->status & (BFRAME | AFRAME)) 1358a151a66cSOllivier Robert qual |= SYNERR; 1359a151a66cSOllivier Robert if (up->status & (BFORMAT | AFORMAT)) 1360a151a66cSOllivier Robert qual |= FMTERR; 1361a151a66cSOllivier Robert if (up->status & DECODE) 1362a151a66cSOllivier Robert qual |= DECERR; 1363a151a66cSOllivier Robert if (up->status & STAMP) 1364a151a66cSOllivier Robert qual |= TSPERR; 13652b15cb3dSCy Schubert if (up->status & BVALID && dtemp >= MINMETRIC) 13669c2daa00SOllivier Robert up->status |= INSYNC; 1367a151a66cSOllivier Robert synchar = leapchar = ' '; 1368a151a66cSOllivier Robert if (!(up->status & INSYNC)) { 1369a151a66cSOllivier Robert pp->leap = LEAP_NOTINSYNC; 1370a151a66cSOllivier Robert synchar = '?'; 1371a151a66cSOllivier Robert } else if (up->leap & 0x2) { 1372a151a66cSOllivier Robert pp->leap = LEAP_ADDSECOND; 1373a151a66cSOllivier Robert leapchar = 'L'; 1374ce265a54SOllivier Robert } else if (up->leap & 0x4) { 1375ce265a54SOllivier Robert pp->leap = LEAP_DELSECOND; 1376ce265a54SOllivier Robert leapchar = 'l'; 1377a151a66cSOllivier Robert } else { 1378a151a66cSOllivier Robert pp->leap = LEAP_NOWARNING; 1379a151a66cSOllivier Robert } 13802b15cb3dSCy Schubert snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 13812b15cb3dSCy Schubert "%c%1X %04d %03d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d", 1382a151a66cSOllivier Robert synchar, qual, pp->year, pp->day, pp->hour, pp->minute, 13832b15cb3dSCy Schubert pp->second, leapchar, up->dst, up->dut, minset, up->gain, 13842b15cb3dSCy Schubert up->ident, dtemp, up->ntstamp); 1385a151a66cSOllivier Robert pp->lencode = strlen(pp->a_lastcode); 1386c0b746e5SOllivier Robert 1387c0b746e5SOllivier Robert /* 13889c2daa00SOllivier Robert * If in sync and the signal metric is above threshold, the 13899c2daa00SOllivier Robert * timecode is ipso fatso valid and can be selected to 13902b15cb3dSCy Schubert * discipline the clock. 1391c0b746e5SOllivier Robert */ 13922b15cb3dSCy Schubert if (up->status & INSYNC && !(up->status & (DECODE | STAMP)) && 13932b15cb3dSCy Schubert dtemp > MINMETRIC) { 13949c2daa00SOllivier Robert if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, 13959c2daa00SOllivier Robert up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { 13969c2daa00SOllivier Robert up->errflg = CEVNT_BADTIME; 13979c2daa00SOllivier Robert } else { 13989c2daa00SOllivier Robert offset.l_uf = 0; 13999c2daa00SOllivier Robert for (i = 0; i < up->ntstamp; i++) 14009c2daa00SOllivier Robert refclock_process_offset(pp, offset, 14012b15cb3dSCy Schubert up->tstamp[i], PDELAY + 14029c2daa00SOllivier Robert pp->fudgetime1); 14039c2daa00SOllivier Robert pp->lastref = up->timestamp; 1404a151a66cSOllivier Robert refclock_receive(peer); 14059c2daa00SOllivier Robert } 1406a151a66cSOllivier Robert } 14072b15cb3dSCy Schubert if (dtemp > 0) 14082b15cb3dSCy Schubert record_clock_stats(&peer->srcadr, pp->a_lastcode); 1409a151a66cSOllivier Robert #ifdef DEBUG 1410a151a66cSOllivier Robert if (debug) 1411a151a66cSOllivier Robert printf("chu: timecode %d %s\n", pp->lencode, 1412a151a66cSOllivier Robert pp->a_lastcode); 1413a151a66cSOllivier Robert #endif 14149c2daa00SOllivier Robert #ifdef ICOM 14159c2daa00SOllivier Robert chu_newchan(peer, dtemp); 14169c2daa00SOllivier Robert #endif /* ICOM */ 1417a151a66cSOllivier Robert chu_clear(peer); 1418a151a66cSOllivier Robert if (up->errflg) 1419a151a66cSOllivier Robert refclock_report(peer, up->errflg); 1420a151a66cSOllivier Robert up->errflg = 0; 1421a151a66cSOllivier Robert } 1422a151a66cSOllivier Robert 1423a151a66cSOllivier Robert 1424a151a66cSOllivier Robert /* 1425a151a66cSOllivier Robert * chu_major - majority decoder 1426a151a66cSOllivier Robert */ 14279c2daa00SOllivier Robert static double 1428a151a66cSOllivier Robert chu_major( 1429a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 1430a151a66cSOllivier Robert ) 1431a151a66cSOllivier Robert { 1432a151a66cSOllivier Robert struct refclockproc *pp; 1433a151a66cSOllivier Robert struct chuunit *up; 1434a151a66cSOllivier Robert 1435a151a66cSOllivier Robert u_char code[11]; /* decoded timecode */ 14362b15cb3dSCy Schubert int metric; /* distance metric */ 14372b15cb3dSCy Schubert int val1; /* maximum distance */ 1438a151a66cSOllivier Robert int synchar; /* stray cat */ 1439a151a66cSOllivier Robert int temp; 1440a151a66cSOllivier Robert int i, j, k; 1441a151a66cSOllivier Robert 1442a151a66cSOllivier Robert pp = peer->procptr; 14432b15cb3dSCy Schubert up = pp->unitptr; 1444a151a66cSOllivier Robert 1445a151a66cSOllivier Robert /* 1446a151a66cSOllivier Robert * Majority decoder. Each burst encodes two replications at each 1447a151a66cSOllivier Robert * digit position in the timecode. Each row of the decoding 14482b15cb3dSCy Schubert * matrix encodes the number of occurences of each digit found 1449a151a66cSOllivier Robert * at the corresponding position. The maximum over all 14509c2daa00SOllivier Robert * occurrences at each position is the distance for this 14512b15cb3dSCy Schubert * position and the corresponding digit is the maximum- 14522b15cb3dSCy Schubert * likelihood candidate. If the distance is not more than half 14532b15cb3dSCy Schubert * the total number of occurences, a majority has not been found 14542b15cb3dSCy Schubert * and the data are discarded. The decoding distance is defined 14552b15cb3dSCy Schubert * as the sum of the distances over the first nine digits. The 14562b15cb3dSCy Schubert * tenth digit varies over the seconds, so we don't count it. 1457a151a66cSOllivier Robert */ 14582b15cb3dSCy Schubert metric = 0; 1459a151a66cSOllivier Robert for (i = 0; i < 9; i++) { 14602b15cb3dSCy Schubert val1 = 0; 1461c0b746e5SOllivier Robert k = 0; 1462c0b746e5SOllivier Robert for (j = 0; j < 16; j++) { 1463c0b746e5SOllivier Robert temp = up->decode[i][j] + up->decode[i + 10][j]; 1464c0b746e5SOllivier Robert if (temp > val1) { 1465c0b746e5SOllivier Robert val1 = temp; 1466c0b746e5SOllivier Robert k = j; 1467c0b746e5SOllivier Robert } 1468c0b746e5SOllivier Robert } 14692b15cb3dSCy Schubert if (val1 <= up->burstcnt) 1470a151a66cSOllivier Robert up->status |= DECODE; 14712b15cb3dSCy Schubert metric += val1; 14722b15cb3dSCy Schubert code[i] = hexchar[k]; 14732b15cb3dSCy Schubert } 1474c0b746e5SOllivier Robert 1475c0b746e5SOllivier Robert /* 1476c0b746e5SOllivier Robert * Compute the timecode timestamp from the days, hours and 1477c0b746e5SOllivier Robert * minutes of the timecode. Use clocktime() for the aggregate 1478c0b746e5SOllivier Robert * minutes and the minute offset computed from the burst 1479c0b746e5SOllivier Robert * seconds. Note that this code relies on the filesystem time 1480c0b746e5SOllivier Robert * for the years and does not use the years of the timecode. 1481c0b746e5SOllivier Robert */ 1482a151a66cSOllivier Robert if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day, 14832b15cb3dSCy Schubert &pp->hour, &pp->minute) != 4) 14842b15cb3dSCy Schubert up->status |= DECODE; 14852b15cb3dSCy Schubert if (up->ntstamp < MINSTAMP) 14862b15cb3dSCy Schubert up->status |= STAMP; 14872b15cb3dSCy Schubert return (metric); 1488c0b746e5SOllivier Robert } 1489c0b746e5SOllivier Robert 1490c0b746e5SOllivier Robert 1491c0b746e5SOllivier Robert /* 1492c0b746e5SOllivier Robert * chu_clear - clear decoding matrix 1493c0b746e5SOllivier Robert */ 1494c0b746e5SOllivier Robert static void 1495c0b746e5SOllivier Robert chu_clear( 1496a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 1497c0b746e5SOllivier Robert ) 1498c0b746e5SOllivier Robert { 1499c0b746e5SOllivier Robert struct refclockproc *pp; 1500c0b746e5SOllivier Robert struct chuunit *up; 1501a151a66cSOllivier Robert int i, j; 1502c0b746e5SOllivier Robert 1503c0b746e5SOllivier Robert pp = peer->procptr; 15042b15cb3dSCy Schubert up = pp->unitptr; 1505c0b746e5SOllivier Robert 1506c0b746e5SOllivier Robert /* 1507a151a66cSOllivier Robert * Clear stuff for the minute. 1508c0b746e5SOllivier Robert */ 1509a151a66cSOllivier Robert up->ndx = up->prevsec = 0; 15109c2daa00SOllivier Robert up->burstcnt = up->ntstamp = 0; 15112b15cb3dSCy Schubert up->status &= INSYNC | METRIC; 1512c0b746e5SOllivier Robert for (i = 0; i < 20; i++) { 1513c0b746e5SOllivier Robert for (j = 0; j < 16; j++) 1514c0b746e5SOllivier Robert up->decode[i][j] = 0; 1515c0b746e5SOllivier Robert } 1516c0b746e5SOllivier Robert } 1517c0b746e5SOllivier Robert 15189c2daa00SOllivier Robert #ifdef ICOM 15199c2daa00SOllivier Robert /* 15209c2daa00SOllivier Robert * chu_newchan - called once per minute to find the best channel; 15219c2daa00SOllivier Robert * returns zero on success, nonzero if ICOM error. 15229c2daa00SOllivier Robert */ 15239c2daa00SOllivier Robert static int 15249c2daa00SOllivier Robert chu_newchan( 15259c2daa00SOllivier Robert struct peer *peer, 15269c2daa00SOllivier Robert double met 15279c2daa00SOllivier Robert ) 15289c2daa00SOllivier Robert { 15299c2daa00SOllivier Robert struct chuunit *up; 15309c2daa00SOllivier Robert struct refclockproc *pp; 15319c2daa00SOllivier Robert struct xmtr *sp; 15329c2daa00SOllivier Robert int rval; 15339c2daa00SOllivier Robert double metric; 15342b15cb3dSCy Schubert int i; 15359c2daa00SOllivier Robert 15369c2daa00SOllivier Robert pp = peer->procptr; 15372b15cb3dSCy Schubert up = pp->unitptr; 15389c2daa00SOllivier Robert 15399c2daa00SOllivier Robert /* 15409c2daa00SOllivier Robert * The radio can be tuned to three channels: 0 (3330 kHz), 1 15412b15cb3dSCy Schubert * (7850 kHz) and 2 (14670 kHz). There are five one-minute 15429c2daa00SOllivier Robert * dwells in each cycle. During the first dwell the radio is 15432b15cb3dSCy Schubert * tuned to one of the three channels to measure the channel 15442b15cb3dSCy Schubert * metric. The channel is selected as the one least recently 15452b15cb3dSCy Schubert * measured. During the remaining four dwells the radio is tuned 15462b15cb3dSCy Schubert * to the channel with the highest channel metric. 15479c2daa00SOllivier Robert */ 15489c2daa00SOllivier Robert if (up->fd_icom <= 0) 15499c2daa00SOllivier Robert return (0); 15509c2daa00SOllivier Robert 15512b15cb3dSCy Schubert /* 15522b15cb3dSCy Schubert * Update the current channel metric and age of all channels. 15532b15cb3dSCy Schubert * Scan all channels for the highest metric. 15542b15cb3dSCy Schubert */ 15552b15cb3dSCy Schubert sp = &up->xmtr[up->chan]; 15569c2daa00SOllivier Robert sp->metric -= sp->integ[sp->iptr]; 15579c2daa00SOllivier Robert sp->integ[sp->iptr] = met; 15589c2daa00SOllivier Robert sp->metric += sp->integ[sp->iptr]; 15592b15cb3dSCy Schubert sp->probe = 0; 15609c2daa00SOllivier Robert sp->iptr = (sp->iptr + 1) % ISTAGE; 15619c2daa00SOllivier Robert metric = 0; 15629c2daa00SOllivier Robert for (i = 0; i < NCHAN; i++) { 15639c2daa00SOllivier Robert up->xmtr[i].probe++; 15642b15cb3dSCy Schubert if (up->xmtr[i].metric > metric) { 15652b15cb3dSCy Schubert up->status |= METRIC; 15669c2daa00SOllivier Robert metric = up->xmtr[i].metric; 15672b15cb3dSCy Schubert up->chan = i; 15689c2daa00SOllivier Robert } 15699c2daa00SOllivier Robert } 15709c2daa00SOllivier Robert 15719c2daa00SOllivier Robert /* 15722b15cb3dSCy Schubert * Start the next dwell. If the first dwell or no stations have 15732b15cb3dSCy Schubert * been heard, continue round-robin scan. 15749c2daa00SOllivier Robert */ 15752b15cb3dSCy Schubert up->dwell = (up->dwell + 1) % DWELL; 15762b15cb3dSCy Schubert if (up->dwell == 0 || metric == 0) { 15779c2daa00SOllivier Robert rval = 0; 15789c2daa00SOllivier Robert for (i = 0; i < NCHAN; i++) { 15792b15cb3dSCy Schubert if (up->xmtr[i].probe > rval) { 15809c2daa00SOllivier Robert rval = up->xmtr[i].probe; 15812b15cb3dSCy Schubert up->chan = i; 15829c2daa00SOllivier Robert } 15839c2daa00SOllivier Robert } 15842b15cb3dSCy Schubert } 15852b15cb3dSCy Schubert 15862b15cb3dSCy Schubert /* Retune the radio at each dwell in case somebody nudges the 15872b15cb3dSCy Schubert * tuning knob. 15882b15cb3dSCy Schubert */ 15892b15cb3dSCy Schubert rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan] + 15902b15cb3dSCy Schubert TUNE); 15912b15cb3dSCy Schubert snprintf(up->ident, sizeof(up->ident), "CHU%d", up->chan); 15922b15cb3dSCy Schubert memcpy(&pp->refid, up->ident, 4); 15939c2daa00SOllivier Robert memcpy(&peer->refid, up->ident, 4); 15942b15cb3dSCy Schubert if (metric == 0 && up->status & METRIC) { 15952b15cb3dSCy Schubert up->status &= ~METRIC; 15962b15cb3dSCy Schubert refclock_report(peer, CEVNT_PROP); 15972b15cb3dSCy Schubert } 15989c2daa00SOllivier Robert return (rval); 15999c2daa00SOllivier Robert } 16009c2daa00SOllivier Robert #endif /* ICOM */ 1601c0b746e5SOllivier Robert 16022b15cb3dSCy Schubert 1603c0b746e5SOllivier Robert /* 1604c0b746e5SOllivier Robert * chu_dist - determine the distance of two octet arguments 1605c0b746e5SOllivier Robert */ 1606c0b746e5SOllivier Robert static int 1607c0b746e5SOllivier Robert chu_dist( 1608c0b746e5SOllivier Robert int x, /* an octet of bits */ 1609c0b746e5SOllivier Robert int y /* another octet of bits */ 1610c0b746e5SOllivier Robert ) 1611c0b746e5SOllivier Robert { 1612c0b746e5SOllivier Robert int val; /* bit count */ 1613a151a66cSOllivier Robert int temp; 1614a151a66cSOllivier Robert int i; 1615c0b746e5SOllivier Robert 1616c0b746e5SOllivier Robert /* 1617c0b746e5SOllivier Robert * The distance is determined as the weight of the exclusive OR 1618c0b746e5SOllivier Robert * of the two arguments. The weight is determined by the number 1619c0b746e5SOllivier Robert * of one bits in the result. Each one bit increases the weight, 1620c0b746e5SOllivier Robert * while each zero bit decreases it. 1621c0b746e5SOllivier Robert */ 1622c0b746e5SOllivier Robert temp = x ^ y; 1623c0b746e5SOllivier Robert val = 0; 1624c0b746e5SOllivier Robert for (i = 0; i < 8; i++) { 1625c0b746e5SOllivier Robert if ((temp & 0x1) == 0) 1626c0b746e5SOllivier Robert val++; 1627c0b746e5SOllivier Robert else 1628c0b746e5SOllivier Robert val--; 1629c0b746e5SOllivier Robert temp >>= 1; 1630c0b746e5SOllivier Robert } 1631c0b746e5SOllivier Robert return (val); 1632c0b746e5SOllivier Robert } 1633c0b746e5SOllivier Robert 1634c0b746e5SOllivier Robert 1635224ba2bdSOllivier Robert #ifdef HAVE_AUDIO 1636c0b746e5SOllivier Robert /* 1637c0b746e5SOllivier Robert * chu_gain - adjust codec gain 1638c0b746e5SOllivier Robert * 16392b15cb3dSCy Schubert * This routine is called at the end of each second. During the second 16402b15cb3dSCy Schubert * the number of signal clips above the MAXAMP threshold (6000). If 16412b15cb3dSCy Schubert * there are no clips, the gain is bumped up; if there are more than 16422b15cb3dSCy Schubert * MAXCLP clips (100), it is bumped down. The decoder is relatively 16432b15cb3dSCy Schubert * insensitive to amplitude, so this crudity works just peachy. The 16442b15cb3dSCy Schubert * routine also jiggles the input port and selectively mutes the 1645c0b746e5SOllivier Robert */ 1646c0b746e5SOllivier Robert static void 1647c0b746e5SOllivier Robert chu_gain( 1648c0b746e5SOllivier Robert struct peer *peer /* peer structure pointer */ 1649c0b746e5SOllivier Robert ) 1650c0b746e5SOllivier Robert { 1651c0b746e5SOllivier Robert struct refclockproc *pp; 1652c0b746e5SOllivier Robert struct chuunit *up; 1653c0b746e5SOllivier Robert 1654c0b746e5SOllivier Robert pp = peer->procptr; 16552b15cb3dSCy Schubert up = pp->unitptr; 1656c0b746e5SOllivier Robert 1657c0b746e5SOllivier Robert /* 1658c0b746e5SOllivier Robert * Apparently, the codec uses only the high order bits of the 1659c0b746e5SOllivier Robert * gain control field. Thus, it may take awhile for changes to 1660a151a66cSOllivier Robert * wiggle the hardware bits. 1661c0b746e5SOllivier Robert */ 1662c0b746e5SOllivier Robert if (up->clipcnt == 0) { 1663c0b746e5SOllivier Robert up->gain += 4; 16649c2daa00SOllivier Robert if (up->gain > MAXGAIN) 16659c2daa00SOllivier Robert up->gain = MAXGAIN; 16669c2daa00SOllivier Robert } else if (up->clipcnt > MAXCLP) { 1667c0b746e5SOllivier Robert up->gain -= 4; 1668a151a66cSOllivier Robert if (up->gain < 0) 1669a151a66cSOllivier Robert up->gain = 0; 1670c0b746e5SOllivier Robert } 16719c2daa00SOllivier Robert audio_gain(up->gain, up->mongain, up->port); 1672a151a66cSOllivier Robert up->clipcnt = 0; 1673c0b746e5SOllivier Robert } 1674224ba2bdSOllivier Robert #endif /* HAVE_AUDIO */ 1675c0b746e5SOllivier Robert 1676a151a66cSOllivier Robert 1677c0b746e5SOllivier Robert #else 16782b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT 1679c0b746e5SOllivier Robert #endif /* REFCLOCK */ 1680