1a151a66cSOllivier Robert /* 2a151a66cSOllivier Robert * refclock_wwv - clock driver for NIST WWV/H time/frequency station 3a151a66cSOllivier Robert */ 4a151a66cSOllivier Robert #ifdef HAVE_CONFIG_H 5a151a66cSOllivier Robert #include <config.h> 6a151a66cSOllivier Robert #endif 7a151a66cSOllivier Robert 8a151a66cSOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_WWV) 9a151a66cSOllivier Robert 10a151a66cSOllivier Robert #include "ntpd.h" 11a151a66cSOllivier Robert #include "ntp_io.h" 12a151a66cSOllivier Robert #include "ntp_refclock.h" 13a151a66cSOllivier Robert #include "ntp_calendar.h" 14a151a66cSOllivier Robert #include "ntp_stdlib.h" 15a151a66cSOllivier Robert #include "audio.h" 16a151a66cSOllivier Robert 17224ba2bdSOllivier Robert #include <stdio.h> 18224ba2bdSOllivier Robert #include <ctype.h> 19224ba2bdSOllivier Robert #include <math.h> 20224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H 21224ba2bdSOllivier Robert # include <sys/ioctl.h> 22224ba2bdSOllivier Robert #endif /* HAVE_SYS_IOCTL_H */ 23224ba2bdSOllivier Robert 249c2daa00SOllivier Robert #define ICOM 1 25a151a66cSOllivier Robert 26a151a66cSOllivier Robert #ifdef ICOM 27a151a66cSOllivier Robert #include "icom.h" 28a151a66cSOllivier Robert #endif /* ICOM */ 29a151a66cSOllivier Robert 30a151a66cSOllivier Robert /* 31a151a66cSOllivier Robert * Audio WWV/H demodulator/decoder 32a151a66cSOllivier Robert * 33a151a66cSOllivier Robert * This driver synchronizes the computer time using data encoded in 34a151a66cSOllivier Robert * radio transmissions from NIST time/frequency stations WWV in Boulder, 359c2daa00SOllivier Robert * CO, and WWVH in Kauai, HI. Transmissions are made continuously on 36ea906c41SOllivier Robert * 2.5, 5, 10 and 15 MHz from WWV and WWVH, and 20 MHz from WWV. An 37ea906c41SOllivier Robert * ordinary AM shortwave receiver can be tuned manually to one of these 38ea906c41SOllivier Robert * frequencies or, in the case of ICOM receivers, the receiver can be 39ea906c41SOllivier Robert * tuned automatically using this program as propagation conditions 40ea906c41SOllivier Robert * change throughout the weasons, both day and night. 41a151a66cSOllivier Robert * 422b15cb3dSCy Schubert * The driver requires an audio codec or sound card with sampling rate 8 432b15cb3dSCy Schubert * kHz and mu-law companding. This is the same standard as used by the 442b15cb3dSCy Schubert * telephone industry and is supported by most hardware and operating 452b15cb3dSCy Schubert * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this 462b15cb3dSCy Schubert * implementation, only one audio driver and codec can be supported on a 472b15cb3dSCy Schubert * single machine. 48a151a66cSOllivier Robert * 49a151a66cSOllivier Robert * The demodulation and decoding algorithms used in this driver are 50a151a66cSOllivier Robert * based on those developed for the TAPR DSP93 development board and the 51a151a66cSOllivier Robert * TI 320C25 digital signal processor described in: Mills, D.L. A 52a151a66cSOllivier Robert * precision radio clock for WWV transmissions. Electrical Engineering 539c2daa00SOllivier Robert * Report 97-8-1, University of Delaware, August 1997, 25 pp., available 54ea906c41SOllivier Robert * from www.eecis.udel.edu/~mills/reports.html. The algorithms described 55a151a66cSOllivier Robert * in this report have been modified somewhat to improve performance 56a151a66cSOllivier Robert * under weak signal conditions and to provide an automatic station 57a151a66cSOllivier Robert * identification feature. 58a151a66cSOllivier Robert * 59a151a66cSOllivier Robert * The ICOM code is normally compiled in the driver. It isn't used, 60a151a66cSOllivier Robert * unless the mode keyword on the server configuration command specifies 61a151a66cSOllivier Robert * a nonzero ICOM ID select code. The C-IV trace is turned on if the 62a151a66cSOllivier Robert * debug level is greater than one. 63ea906c41SOllivier Robert * 64ea906c41SOllivier Robert * Fudge factors 65ea906c41SOllivier Robert * 662b15cb3dSCy Schubert * Fudge flag4 causes the debugging output described above to be 67ea906c41SOllivier Robert * recorded in the clockstats file. Fudge flag2 selects the audio input 68ea906c41SOllivier Robert * port, where 0 is the mike port (default) and 1 is the line-in port. 69ea906c41SOllivier Robert * It does not seem useful to select the compact disc player port. Fudge 70ea906c41SOllivier Robert * flag3 enables audio monitoring of the input signal. For this purpose, 71ea906c41SOllivier Robert * the monitor gain is set to a default value. 722b15cb3dSCy Schubert * 732b15cb3dSCy Schubert * CEVNT_BADTIME invalid date or time 742b15cb3dSCy Schubert * CEVNT_PROP propagation failure - no stations heard 752b15cb3dSCy Schubert * CEVNT_TIMEOUT timeout (see newgame() below) 76a151a66cSOllivier Robert */ 77a151a66cSOllivier Robert /* 78ea906c41SOllivier Robert * General definitions. These ordinarily do not need to be changed. 79a151a66cSOllivier Robert */ 80224ba2bdSOllivier Robert #define DEVICE_AUDIO "/dev/audio" /* audio device name */ 819c2daa00SOllivier Robert #define AUDIO_BUFSIZ 320 /* audio buffer size (50 ms) */ 82a151a66cSOllivier Robert #define PRECISION (-10) /* precision assumed (about 1 ms) */ 83a151a66cSOllivier Robert #define DESCRIPTION "WWV/H Audio Demodulator/Decoder" /* WRU */ 842b15cb3dSCy Schubert #define WWV_SEC 8000 /* second epoch (sample rate) (Hz) */ 852b15cb3dSCy Schubert #define WWV_MIN (WWV_SEC * 60) /* minute epoch */ 86a151a66cSOllivier Robert #define OFFSET 128 /* companded sample offset */ 87a151a66cSOllivier Robert #define SIZE 256 /* decompanding table size */ 88ea906c41SOllivier Robert #define MAXAMP 6000. /* max signal level reference */ 899c2daa00SOllivier Robert #define MAXCLP 100 /* max clips above reference per s */ 90ea906c41SOllivier Robert #define MAXSNR 40. /* max SNR reference */ 91ea906c41SOllivier Robert #define MAXFREQ 1.5 /* max frequency tolerance (187 PPM) */ 92ea906c41SOllivier Robert #define DATCYC 170 /* data filter cycles */ 93ea906c41SOllivier Robert #define DATSIZ (DATCYC * MS) /* data filter size */ 94ea906c41SOllivier Robert #define SYNCYC 800 /* minute filter cycles */ 95ea906c41SOllivier Robert #define SYNSIZ (SYNCYC * MS) /* minute filter size */ 96ea906c41SOllivier Robert #define TCKCYC 5 /* tick filter cycles */ 97ea906c41SOllivier Robert #define TCKSIZ (TCKCYC * MS) /* tick filter size */ 989c2daa00SOllivier Robert #define NCHAN 5 /* number of radio channels */ 999c2daa00SOllivier Robert #define AUDIO_PHI 5e-6 /* dispersion growth factor */ 1002b15cb3dSCy Schubert #define TBUF 128 /* max monitor line length */ 101ea906c41SOllivier Robert 102ea906c41SOllivier Robert /* 103ea906c41SOllivier Robert * Tunable parameters. The DGAIN parameter can be changed to fit the 104ea906c41SOllivier Robert * audio response of the radio at 100 Hz. The WWV/WWVH data subcarrier 105ea906c41SOllivier Robert * is transmitted at about 20 percent percent modulation; the matched 106ea906c41SOllivier Robert * filter boosts it by a factor of 17 and the receiver response does 107ea906c41SOllivier Robert * what it does. The compromise value works for ICOM radios. If the 108ea906c41SOllivier Robert * radio is not tunable, the DCHAN parameter can be changed to fit the 109ea906c41SOllivier Robert * expected best propagation frequency: higher if further from the 110ea906c41SOllivier Robert * transmitter, lower if nearer. The compromise value works for the US 1112b15cb3dSCy Schubert * right coast. 112ea906c41SOllivier Robert */ 113ea906c41SOllivier Robert #define DCHAN 3 /* default radio channel (15 Mhz) */ 114ea906c41SOllivier Robert #define DGAIN 5. /* subcarrier gain */ 115a151a66cSOllivier Robert 116a151a66cSOllivier Robert /* 117a151a66cSOllivier Robert * General purpose status bits (status) 118a151a66cSOllivier Robert * 119ea906c41SOllivier Robert * SELV and/or SELH are set when WWV or WWVH have been heard and cleared 1209c2daa00SOllivier Robert * on signal loss. SSYNC is set when the second sync pulse has been 1219c2daa00SOllivier Robert * acquired and cleared by signal loss. MSYNC is set when the minute 122ea906c41SOllivier Robert * sync pulse has been acquired. DSYNC is set when the units digit has 123ea906c41SOllivier Robert * has reached the threshold and INSYNC is set when all nine digits have 124ea906c41SOllivier Robert * reached the threshold. The MSYNC, DSYNC and INSYNC bits are cleared 125ea906c41SOllivier Robert * only by timeout, upon which the driver starts over from scratch. 126a151a66cSOllivier Robert * 127ea906c41SOllivier Robert * DGATE is lit if the data bit amplitude or SNR is below thresholds and 128ea906c41SOllivier Robert * BGATE is lit if the pulse width amplitude or SNR is below thresolds. 129ea906c41SOllivier Robert * LEPSEC is set during the last minute of the leap day. At the end of 130ea906c41SOllivier Robert * this minute the driver inserts second 60 in the seconds state machine 131ea906c41SOllivier Robert * and the minute sync slips a second. 132a151a66cSOllivier Robert */ 133a151a66cSOllivier Robert #define MSYNC 0x0001 /* minute epoch sync */ 134a151a66cSOllivier Robert #define SSYNC 0x0002 /* second epoch sync */ 135a151a66cSOllivier Robert #define DSYNC 0x0004 /* minute units sync */ 136a151a66cSOllivier Robert #define INSYNC 0x0008 /* clock synchronized */ 1379c2daa00SOllivier Robert #define FGATE 0x0010 /* frequency gate */ 138ea906c41SOllivier Robert #define DGATE 0x0020 /* data pulse amplitude error */ 139ea906c41SOllivier Robert #define BGATE 0x0040 /* data pulse width error */ 1402b15cb3dSCy Schubert #define METRIC 0x0080 /* one or more stations heard */ 141ea906c41SOllivier Robert #define LEPSEC 0x1000 /* leap minute */ 142a151a66cSOllivier Robert 143a151a66cSOllivier Robert /* 1449c2daa00SOllivier Robert * Station scoreboard bits 145a151a66cSOllivier Robert * 146a151a66cSOllivier Robert * These are used to establish the signal quality for each of the five 147a151a66cSOllivier Robert * frequencies and two stations. 148a151a66cSOllivier Robert */ 149a151a66cSOllivier Robert #define SELV 0x0100 /* WWV station select */ 150a151a66cSOllivier Robert #define SELH 0x0200 /* WWVH station select */ 151a151a66cSOllivier Robert 152a151a66cSOllivier Robert /* 153a151a66cSOllivier Robert * Alarm status bits (alarm) 154a151a66cSOllivier Robert * 155a151a66cSOllivier Robert * These bits indicate various alarm conditions, which are decoded to 156ea906c41SOllivier Robert * form the quality character included in the timecode. 157a151a66cSOllivier Robert */ 1582b15cb3dSCy Schubert #define CMPERR 0x1 /* digit or misc bit compare error */ 1592b15cb3dSCy Schubert #define LOWERR 0x2 /* low bit or digit amplitude or SNR */ 1602b15cb3dSCy Schubert #define NINERR 0x4 /* less than nine digits in minute */ 1612b15cb3dSCy Schubert #define SYNERR 0x8 /* not tracking second sync */ 162a151a66cSOllivier Robert 163a151a66cSOllivier Robert /* 1649c2daa00SOllivier Robert * Watchcat timeouts (watch) 165a151a66cSOllivier Robert * 166a151a66cSOllivier Robert * If these timeouts expire, the status bits are mashed to zero and the 167a151a66cSOllivier Robert * driver starts from scratch. Suitably more refined procedures may be 168a151a66cSOllivier Robert * developed in future. All these are in minutes. 169a151a66cSOllivier Robert */ 170ea906c41SOllivier Robert #define ACQSN 6 /* station acquisition timeout */ 171ea906c41SOllivier Robert #define DATA 15 /* unit minutes timeout */ 172ea906c41SOllivier Robert #define SYNCH 40 /* station sync timeout */ 1739c2daa00SOllivier Robert #define PANIC (2 * 1440) /* panic timeout */ 174a151a66cSOllivier Robert 175a151a66cSOllivier Robert /* 176a151a66cSOllivier Robert * Thresholds. These establish the minimum signal level, minimum SNR and 177a151a66cSOllivier Robert * maximum jitter thresholds which establish the error and false alarm 1789c2daa00SOllivier Robert * rates of the driver. The values defined here may be on the 179a151a66cSOllivier Robert * adventurous side in the interest of the highest sensitivity. 180a151a66cSOllivier Robert */ 181ea906c41SOllivier Robert #define MTHR 13. /* minute sync gate (percent) */ 182ea906c41SOllivier Robert #define TTHR 50. /* minute sync threshold (percent) */ 183ea906c41SOllivier Robert #define AWND 20 /* minute sync jitter threshold (ms) */ 184ea906c41SOllivier Robert #define ATHR 2500. /* QRZ minute sync threshold */ 185ea906c41SOllivier Robert #define ASNR 20. /* QRZ minute sync SNR threshold (dB) */ 186ea906c41SOllivier Robert #define QTHR 2500. /* QSY minute sync threshold */ 187ea906c41SOllivier Robert #define QSNR 20. /* QSY minute sync SNR threshold (dB) */ 188ea906c41SOllivier Robert #define STHR 2500. /* second sync threshold */ 189ea906c41SOllivier Robert #define SSNR 15. /* second sync SNR threshold (dB) */ 190ea906c41SOllivier Robert #define SCMP 10 /* second sync compare threshold */ 191ea906c41SOllivier Robert #define DTHR 1000. /* bit threshold */ 192ea906c41SOllivier Robert #define DSNR 10. /* bit SNR threshold (dB) */ 1939c2daa00SOllivier Robert #define AMIN 3 /* min bit count */ 1949c2daa00SOllivier Robert #define AMAX 6 /* max bit count */ 195ea906c41SOllivier Robert #define BTHR 1000. /* digit threshold */ 1969c2daa00SOllivier Robert #define BSNR 3. /* digit likelihood threshold (dB) */ 197ea906c41SOllivier Robert #define BCMP 3 /* digit compare threshold */ 198ea906c41SOllivier Robert #define MAXERR 40 /* maximum error alarm */ 199a151a66cSOllivier Robert 200a151a66cSOllivier Robert /* 2019c2daa00SOllivier Robert * Tone frequency definitions. The increments are for 4.5-deg sine 2029c2daa00SOllivier Robert * table. 203a151a66cSOllivier Robert */ 2042b15cb3dSCy Schubert #define MS (WWV_SEC / 1000) /* samples per millisecond */ 2052b15cb3dSCy Schubert #define IN100 ((100 * 80) / WWV_SEC) /* 100 Hz increment */ 2062b15cb3dSCy Schubert #define IN1000 ((1000 * 80) / WWV_SEC) /* 1000 Hz increment */ 2072b15cb3dSCy Schubert #define IN1200 ((1200 * 80) / WWV_SEC) /* 1200 Hz increment */ 208a151a66cSOllivier Robert 209a151a66cSOllivier Robert /* 210ea906c41SOllivier Robert * Acquisition and tracking time constants 211a151a66cSOllivier Robert */ 212ea906c41SOllivier Robert #define MINAVG 8 /* min averaging time */ 213ea906c41SOllivier Robert #define MAXAVG 1024 /* max averaging time */ 214ea906c41SOllivier Robert #define FCONST 3 /* frequency time constant */ 2159c2daa00SOllivier Robert #define TCONST 16 /* data bit/digit time constant */ 216a151a66cSOllivier Robert 217a151a66cSOllivier Robert /* 218a151a66cSOllivier Robert * Miscellaneous status bits (misc) 219a151a66cSOllivier Robert * 220a151a66cSOllivier Robert * These bits correspond to designated bits in the WWV/H timecode. The 221a151a66cSOllivier Robert * bit probabilities are exponentially averaged over several minutes and 222a151a66cSOllivier Robert * processed by a integrator and threshold. 223a151a66cSOllivier Robert */ 224a151a66cSOllivier Robert #define DUT1 0x01 /* 56 DUT .1 */ 225a151a66cSOllivier Robert #define DUT2 0x02 /* 57 DUT .2 */ 226a151a66cSOllivier Robert #define DUT4 0x04 /* 58 DUT .4 */ 227a151a66cSOllivier Robert #define DUTS 0x08 /* 50 DUT sign */ 2289c2daa00SOllivier Robert #define DST1 0x10 /* 55 DST1 leap warning */ 2299c2daa00SOllivier Robert #define DST2 0x20 /* 2 DST2 DST1 delayed one day */ 230a151a66cSOllivier Robert #define SECWAR 0x40 /* 3 leap second warning */ 231a151a66cSOllivier Robert 232a151a66cSOllivier Robert /* 2332b15cb3dSCy Schubert * The on-time synchronization point is the positive-going zero crossing 2342b15cb3dSCy Schubert * of the first cycle of the 5-ms second pulse. The IIR baseband filter 2352b15cb3dSCy Schubert * phase delay is 0.91 ms, while the receiver delay is approximately 4.7 2362b15cb3dSCy Schubert * ms at 1000 Hz. The fudge value -0.45 ms due to the codec and other 2372b15cb3dSCy Schubert * causes was determined by calibrating to a PPS signal from a GPS 2382b15cb3dSCy Schubert * receiver. The additional propagation delay specific to each receiver 2392b15cb3dSCy Schubert * location can be programmed in the fudge time1 and time2 values for 2402b15cb3dSCy Schubert * WWV and WWVH, respectively. 2412b15cb3dSCy Schubert * 2422b15cb3dSCy Schubert * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are 2432b15cb3dSCy Schubert * generally within .02 ms short-term with .02 ms jitter. The long-term 2442b15cb3dSCy Schubert * offsets vary up to 0.3 ms due to ionosperhic layer height variations. 2452b15cb3dSCy Schubert * The processor load due to the driver is 5.8 percent. 246a151a66cSOllivier Robert */ 2472b15cb3dSCy Schubert #define PDELAY ((.91 + 4.7 - 0.45) / 1000) /* system delay (s) */ 248a151a66cSOllivier Robert 249a151a66cSOllivier Robert /* 250a151a66cSOllivier Robert * Table of sine values at 4.5-degree increments. This is used by the 251ea906c41SOllivier Robert * synchronous matched filter demodulators. 252a151a66cSOllivier Robert */ 253a151a66cSOllivier Robert double sintab[] = { 254ea906c41SOllivier Robert 0.000000e+00, 7.845910e-02, 1.564345e-01, 2.334454e-01, /* 0-3 */ 255ea906c41SOllivier Robert 3.090170e-01, 3.826834e-01, 4.539905e-01, 5.224986e-01, /* 4-7 */ 256ea906c41SOllivier Robert 5.877853e-01, 6.494480e-01, 7.071068e-01, 7.604060e-01, /* 8-11 */ 257ea906c41SOllivier Robert 8.090170e-01, 8.526402e-01, 8.910065e-01, 9.238795e-01, /* 12-15 */ 258ea906c41SOllivier Robert 9.510565e-01, 9.723699e-01, 9.876883e-01, 9.969173e-01, /* 16-19 */ 259ea906c41SOllivier Robert 1.000000e+00, 9.969173e-01, 9.876883e-01, 9.723699e-01, /* 20-23 */ 260ea906c41SOllivier Robert 9.510565e-01, 9.238795e-01, 8.910065e-01, 8.526402e-01, /* 24-27 */ 261ea906c41SOllivier Robert 8.090170e-01, 7.604060e-01, 7.071068e-01, 6.494480e-01, /* 28-31 */ 262ea906c41SOllivier Robert 5.877853e-01, 5.224986e-01, 4.539905e-01, 3.826834e-01, /* 32-35 */ 263ea906c41SOllivier Robert 3.090170e-01, 2.334454e-01, 1.564345e-01, 7.845910e-02, /* 36-39 */ 264ea906c41SOllivier Robert -0.000000e+00, -7.845910e-02, -1.564345e-01, -2.334454e-01, /* 40-43 */ 265ea906c41SOllivier Robert -3.090170e-01, -3.826834e-01, -4.539905e-01, -5.224986e-01, /* 44-47 */ 266ea906c41SOllivier Robert -5.877853e-01, -6.494480e-01, -7.071068e-01, -7.604060e-01, /* 48-51 */ 267ea906c41SOllivier Robert -8.090170e-01, -8.526402e-01, -8.910065e-01, -9.238795e-01, /* 52-55 */ 268ea906c41SOllivier Robert -9.510565e-01, -9.723699e-01, -9.876883e-01, -9.969173e-01, /* 56-59 */ 269ea906c41SOllivier Robert -1.000000e+00, -9.969173e-01, -9.876883e-01, -9.723699e-01, /* 60-63 */ 270ea906c41SOllivier Robert -9.510565e-01, -9.238795e-01, -8.910065e-01, -8.526402e-01, /* 64-67 */ 271ea906c41SOllivier Robert -8.090170e-01, -7.604060e-01, -7.071068e-01, -6.494480e-01, /* 68-71 */ 272ea906c41SOllivier Robert -5.877853e-01, -5.224986e-01, -4.539905e-01, -3.826834e-01, /* 72-75 */ 273ea906c41SOllivier Robert -3.090170e-01, -2.334454e-01, -1.564345e-01, -7.845910e-02, /* 76-79 */ 274a151a66cSOllivier Robert 0.000000e+00}; /* 80 */ 275a151a66cSOllivier Robert 276a151a66cSOllivier Robert /* 277a151a66cSOllivier Robert * Decoder operations at the end of each second are driven by a state 278a151a66cSOllivier Robert * machine. The transition matrix consists of a dispatch table indexed 279a151a66cSOllivier Robert * by second number. Each entry in the table contains a case switch 280a151a66cSOllivier Robert * number and argument. 281a151a66cSOllivier Robert */ 282a151a66cSOllivier Robert struct progx { 283a151a66cSOllivier Robert int sw; /* case switch number */ 284a151a66cSOllivier Robert int arg; /* argument */ 285a151a66cSOllivier Robert }; 286a151a66cSOllivier Robert 287a151a66cSOllivier Robert /* 288a151a66cSOllivier Robert * Case switch numbers 289a151a66cSOllivier Robert */ 290a151a66cSOllivier Robert #define IDLE 0 /* no operation */ 2919c2daa00SOllivier Robert #define COEF 1 /* BCD bit */ 292ea906c41SOllivier Robert #define COEF1 2 /* BCD bit for minute unit */ 293ea906c41SOllivier Robert #define COEF2 3 /* BCD bit not used */ 294ea906c41SOllivier Robert #define DECIM9 4 /* BCD digit 0-9 */ 295ea906c41SOllivier Robert #define DECIM6 5 /* BCD digit 0-6 */ 296ea906c41SOllivier Robert #define DECIM3 6 /* BCD digit 0-3 */ 297ea906c41SOllivier Robert #define DECIM2 7 /* BCD digit 0-2 */ 298ea906c41SOllivier Robert #define MSCBIT 8 /* miscellaneous bit */ 299ea906c41SOllivier Robert #define MSC20 9 /* miscellaneous bit */ 300ea906c41SOllivier Robert #define MSC21 10 /* QSY probe channel */ 301ea906c41SOllivier Robert #define MIN1 11 /* latch time */ 302ea906c41SOllivier Robert #define MIN2 12 /* leap second */ 303ea906c41SOllivier Robert #define SYNC2 13 /* latch minute sync pulse */ 304ea906c41SOllivier Robert #define SYNC3 14 /* latch data pulse */ 305a151a66cSOllivier Robert 306a151a66cSOllivier Robert /* 307a151a66cSOllivier Robert * Offsets in decoding matrix 308a151a66cSOllivier Robert */ 309a151a66cSOllivier Robert #define MN 0 /* minute digits (2) */ 310a151a66cSOllivier Robert #define HR 2 /* hour digits (2) */ 311a151a66cSOllivier Robert #define DA 4 /* day digits (3) */ 312a151a66cSOllivier Robert #define YR 7 /* year digits (2) */ 313a151a66cSOllivier Robert 314a151a66cSOllivier Robert struct progx progx[] = { 315ea906c41SOllivier Robert {SYNC2, 0}, /* 0 latch minute sync pulse */ 316ea906c41SOllivier Robert {SYNC3, 0}, /* 1 latch data pulse */ 317a151a66cSOllivier Robert {MSCBIT, DST2}, /* 2 dst2 */ 318a151a66cSOllivier Robert {MSCBIT, SECWAR}, /* 3 lw */ 319a151a66cSOllivier Robert {COEF, 0}, /* 4 1 year units */ 320a151a66cSOllivier Robert {COEF, 1}, /* 5 2 */ 321a151a66cSOllivier Robert {COEF, 2}, /* 6 4 */ 322a151a66cSOllivier Robert {COEF, 3}, /* 7 8 */ 323a151a66cSOllivier Robert {DECIM9, YR}, /* 8 */ 324a151a66cSOllivier Robert {IDLE, 0}, /* 9 p1 */ 325ea906c41SOllivier Robert {COEF1, 0}, /* 10 1 minute units */ 326ea906c41SOllivier Robert {COEF1, 1}, /* 11 2 */ 327ea906c41SOllivier Robert {COEF1, 2}, /* 12 4 */ 328ea906c41SOllivier Robert {COEF1, 3}, /* 13 8 */ 329a151a66cSOllivier Robert {DECIM9, MN}, /* 14 */ 330a151a66cSOllivier Robert {COEF, 0}, /* 15 10 minute tens */ 331a151a66cSOllivier Robert {COEF, 1}, /* 16 20 */ 332a151a66cSOllivier Robert {COEF, 2}, /* 17 40 */ 333a151a66cSOllivier Robert {COEF2, 3}, /* 18 80 (not used) */ 334a151a66cSOllivier Robert {DECIM6, MN + 1}, /* 19 p2 */ 335a151a66cSOllivier Robert {COEF, 0}, /* 20 1 hour units */ 336a151a66cSOllivier Robert {COEF, 1}, /* 21 2 */ 337a151a66cSOllivier Robert {COEF, 2}, /* 22 4 */ 338a151a66cSOllivier Robert {COEF, 3}, /* 23 8 */ 339a151a66cSOllivier Robert {DECIM9, HR}, /* 24 */ 340a151a66cSOllivier Robert {COEF, 0}, /* 25 10 hour tens */ 341a151a66cSOllivier Robert {COEF, 1}, /* 26 20 */ 342a151a66cSOllivier Robert {COEF2, 2}, /* 27 40 (not used) */ 343a151a66cSOllivier Robert {COEF2, 3}, /* 28 80 (not used) */ 344a151a66cSOllivier Robert {DECIM2, HR + 1}, /* 29 p3 */ 345a151a66cSOllivier Robert {COEF, 0}, /* 30 1 day units */ 346a151a66cSOllivier Robert {COEF, 1}, /* 31 2 */ 347a151a66cSOllivier Robert {COEF, 2}, /* 32 4 */ 348a151a66cSOllivier Robert {COEF, 3}, /* 33 8 */ 349a151a66cSOllivier Robert {DECIM9, DA}, /* 34 */ 350a151a66cSOllivier Robert {COEF, 0}, /* 35 10 day tens */ 351a151a66cSOllivier Robert {COEF, 1}, /* 36 20 */ 352a151a66cSOllivier Robert {COEF, 2}, /* 37 40 */ 353a151a66cSOllivier Robert {COEF, 3}, /* 38 80 */ 354a151a66cSOllivier Robert {DECIM9, DA + 1}, /* 39 p4 */ 355a151a66cSOllivier Robert {COEF, 0}, /* 40 100 day hundreds */ 356a151a66cSOllivier Robert {COEF, 1}, /* 41 200 */ 357a151a66cSOllivier Robert {COEF2, 2}, /* 42 400 (not used) */ 358a151a66cSOllivier Robert {COEF2, 3}, /* 43 800 (not used) */ 359a151a66cSOllivier Robert {DECIM3, DA + 2}, /* 44 */ 360a151a66cSOllivier Robert {IDLE, 0}, /* 45 */ 361a151a66cSOllivier Robert {IDLE, 0}, /* 46 */ 362a151a66cSOllivier Robert {IDLE, 0}, /* 47 */ 363a151a66cSOllivier Robert {IDLE, 0}, /* 48 */ 364a151a66cSOllivier Robert {IDLE, 0}, /* 49 p5 */ 365a151a66cSOllivier Robert {MSCBIT, DUTS}, /* 50 dut+- */ 366a151a66cSOllivier Robert {COEF, 0}, /* 51 10 year tens */ 367a151a66cSOllivier Robert {COEF, 1}, /* 52 20 */ 368a151a66cSOllivier Robert {COEF, 2}, /* 53 40 */ 369a151a66cSOllivier Robert {COEF, 3}, /* 54 80 */ 370a151a66cSOllivier Robert {MSC20, DST1}, /* 55 dst1 */ 371a151a66cSOllivier Robert {MSCBIT, DUT1}, /* 56 0.1 dut */ 372a151a66cSOllivier Robert {MSCBIT, DUT2}, /* 57 0.2 */ 373a151a66cSOllivier Robert {MSC21, DUT4}, /* 58 0.4 QSY probe channel */ 374ea906c41SOllivier Robert {MIN1, 0}, /* 59 p6 latch time */ 375a151a66cSOllivier Robert {MIN2, 0} /* 60 leap second */ 376a151a66cSOllivier Robert }; 377a151a66cSOllivier Robert 378a151a66cSOllivier Robert /* 3792b15cb3dSCy Schubert * BCD coefficients for maximum-likelihood digit decode 380a151a66cSOllivier Robert */ 381a151a66cSOllivier Robert #define P15 1. /* max positive number */ 382a151a66cSOllivier Robert #define N15 -1. /* max negative number */ 383a151a66cSOllivier Robert 384a151a66cSOllivier Robert /* 385a151a66cSOllivier Robert * Digits 0-9 386a151a66cSOllivier Robert */ 387a151a66cSOllivier Robert #define P9 (P15 / 4) /* mark (+1) */ 388a151a66cSOllivier Robert #define N9 (N15 / 4) /* space (-1) */ 389a151a66cSOllivier Robert 390a151a66cSOllivier Robert double bcd9[][4] = { 391a151a66cSOllivier Robert {N9, N9, N9, N9}, /* 0 */ 392a151a66cSOllivier Robert {P9, N9, N9, N9}, /* 1 */ 393a151a66cSOllivier Robert {N9, P9, N9, N9}, /* 2 */ 394a151a66cSOllivier Robert {P9, P9, N9, N9}, /* 3 */ 395a151a66cSOllivier Robert {N9, N9, P9, N9}, /* 4 */ 396a151a66cSOllivier Robert {P9, N9, P9, N9}, /* 5 */ 397a151a66cSOllivier Robert {N9, P9, P9, N9}, /* 6 */ 398a151a66cSOllivier Robert {P9, P9, P9, N9}, /* 7 */ 399a151a66cSOllivier Robert {N9, N9, N9, P9}, /* 8 */ 400a151a66cSOllivier Robert {P9, N9, N9, P9}, /* 9 */ 401a151a66cSOllivier Robert {0, 0, 0, 0} /* backstop */ 402a151a66cSOllivier Robert }; 403a151a66cSOllivier Robert 404a151a66cSOllivier Robert /* 405a151a66cSOllivier Robert * Digits 0-6 (minute tens) 406a151a66cSOllivier Robert */ 407a151a66cSOllivier Robert #define P6 (P15 / 3) /* mark (+1) */ 408a151a66cSOllivier Robert #define N6 (N15 / 3) /* space (-1) */ 409a151a66cSOllivier Robert 410a151a66cSOllivier Robert double bcd6[][4] = { 411a151a66cSOllivier Robert {N6, N6, N6, 0}, /* 0 */ 412a151a66cSOllivier Robert {P6, N6, N6, 0}, /* 1 */ 413a151a66cSOllivier Robert {N6, P6, N6, 0}, /* 2 */ 414a151a66cSOllivier Robert {P6, P6, N6, 0}, /* 3 */ 415a151a66cSOllivier Robert {N6, N6, P6, 0}, /* 4 */ 416a151a66cSOllivier Robert {P6, N6, P6, 0}, /* 5 */ 417a151a66cSOllivier Robert {N6, P6, P6, 0}, /* 6 */ 418a151a66cSOllivier Robert {0, 0, 0, 0} /* backstop */ 419a151a66cSOllivier Robert }; 420a151a66cSOllivier Robert 421a151a66cSOllivier Robert /* 422a151a66cSOllivier Robert * Digits 0-3 (day hundreds) 423a151a66cSOllivier Robert */ 424a151a66cSOllivier Robert #define P3 (P15 / 2) /* mark (+1) */ 425a151a66cSOllivier Robert #define N3 (N15 / 2) /* space (-1) */ 426a151a66cSOllivier Robert 427a151a66cSOllivier Robert double bcd3[][4] = { 428a151a66cSOllivier Robert {N3, N3, 0, 0}, /* 0 */ 429a151a66cSOllivier Robert {P3, N3, 0, 0}, /* 1 */ 430a151a66cSOllivier Robert {N3, P3, 0, 0}, /* 2 */ 431a151a66cSOllivier Robert {P3, P3, 0, 0}, /* 3 */ 432a151a66cSOllivier Robert {0, 0, 0, 0} /* backstop */ 433a151a66cSOllivier Robert }; 434a151a66cSOllivier Robert 435a151a66cSOllivier Robert /* 436a151a66cSOllivier Robert * Digits 0-2 (hour tens) 437a151a66cSOllivier Robert */ 438a151a66cSOllivier Robert #define P2 (P15 / 2) /* mark (+1) */ 439a151a66cSOllivier Robert #define N2 (N15 / 2) /* space (-1) */ 440a151a66cSOllivier Robert 441a151a66cSOllivier Robert double bcd2[][4] = { 442a151a66cSOllivier Robert {N2, N2, 0, 0}, /* 0 */ 443a151a66cSOllivier Robert {P2, N2, 0, 0}, /* 1 */ 444a151a66cSOllivier Robert {N2, P2, 0, 0}, /* 2 */ 445a151a66cSOllivier Robert {0, 0, 0, 0} /* backstop */ 446a151a66cSOllivier Robert }; 447a151a66cSOllivier Robert 448a151a66cSOllivier Robert /* 449a151a66cSOllivier Robert * DST decode (DST2 DST1) for prettyprint 450a151a66cSOllivier Robert */ 451a151a66cSOllivier Robert char dstcod[] = { 452a151a66cSOllivier Robert 'S', /* 00 standard time */ 4539c2daa00SOllivier Robert 'I', /* 01 set clock ahead at 0200 local */ 4549c2daa00SOllivier Robert 'O', /* 10 set clock back at 0200 local */ 455a151a66cSOllivier Robert 'D' /* 11 daylight time */ 456a151a66cSOllivier Robert }; 457a151a66cSOllivier Robert 458a151a66cSOllivier Robert /* 459a151a66cSOllivier Robert * The decoding matrix consists of nine row vectors, one for each digit 460a151a66cSOllivier Robert * of the timecode. The digits are stored from least to most significant 4612b15cb3dSCy Schubert * order. The maximum-likelihood timecode is formed from the digits 4622b15cb3dSCy Schubert * corresponding to the maximum-likelihood values reading in the 463a151a66cSOllivier Robert * opposite order: yy ddd hh:mm. 464a151a66cSOllivier Robert */ 465a151a66cSOllivier Robert struct decvec { 466a151a66cSOllivier Robert int radix; /* radix (3, 4, 6, 10) */ 467a151a66cSOllivier Robert int digit; /* current clock digit */ 468a151a66cSOllivier Robert int count; /* match count */ 469a151a66cSOllivier Robert double digprb; /* max digit probability */ 470a151a66cSOllivier Robert double digsnr; /* likelihood function (dB) */ 471a151a66cSOllivier Robert double like[10]; /* likelihood integrator 0-9 */ 472a151a66cSOllivier Robert }; 473a151a66cSOllivier Robert 474a151a66cSOllivier Robert /* 475ea906c41SOllivier Robert * The station structure (sp) is used to acquire the minute pulse from 476ea906c41SOllivier Robert * WWV and/or WWVH. These stations are distinguished by the frequency 477ea906c41SOllivier Robert * used for the second and minute sync pulses, 1000 Hz for WWV and 1200 478ea906c41SOllivier Robert * Hz for WWVH. Other than frequency, the format is the same. 479a151a66cSOllivier Robert */ 480a151a66cSOllivier Robert struct sync { 4819c2daa00SOllivier Robert double epoch; /* accumulated epoch differences */ 482ea906c41SOllivier Robert double maxeng; /* sync max energy */ 483ea906c41SOllivier Robert double noieng; /* sync noise energy */ 4849c2daa00SOllivier Robert long pos; /* max amplitude position */ 4859c2daa00SOllivier Robert long lastpos; /* last max position */ 486a151a66cSOllivier Robert long mepoch; /* minute synch epoch */ 4879c2daa00SOllivier Robert 488ea906c41SOllivier Robert double amp; /* sync signal */ 489ea906c41SOllivier Robert double syneng; /* sync signal max */ 490ea906c41SOllivier Robert double synmax; /* sync signal max latched at 0 s */ 4919c2daa00SOllivier Robert double synsnr; /* sync signal SNR */ 492ea906c41SOllivier Robert double metric; /* signal quality metric */ 4939c2daa00SOllivier Robert int reach; /* reachability register */ 494ea906c41SOllivier Robert int count; /* bit counter */ 495ea906c41SOllivier Robert int select; /* select bits */ 496ea906c41SOllivier Robert char refid[5]; /* reference identifier */ 497a151a66cSOllivier Robert }; 498a151a66cSOllivier Robert 499a151a66cSOllivier Robert /* 500ea906c41SOllivier Robert * The channel structure (cp) is used to mitigate between channels. 501a151a66cSOllivier Robert */ 502a151a66cSOllivier Robert struct chan { 503a151a66cSOllivier Robert int gain; /* audio gain */ 504a151a66cSOllivier Robert struct sync wwv; /* wwv station */ 505a151a66cSOllivier Robert struct sync wwvh; /* wwvh station */ 506a151a66cSOllivier Robert }; 507a151a66cSOllivier Robert 508a151a66cSOllivier Robert /* 509ea906c41SOllivier Robert * WWV unit control structure (up) 510a151a66cSOllivier Robert */ 511a151a66cSOllivier Robert struct wwvunit { 512a151a66cSOllivier Robert l_fp timestamp; /* audio sample timestamp */ 513a151a66cSOllivier Robert l_fp tick; /* audio sample increment */ 514a151a66cSOllivier Robert double phase, freq; /* logical clock phase and frequency */ 515a151a66cSOllivier Robert double monitor; /* audio monitor point */ 5162b15cb3dSCy Schubert double pdelay; /* propagation delay (s) */ 517ea906c41SOllivier Robert #ifdef ICOM 518a151a66cSOllivier Robert int fd_icom; /* ICOM file descriptor */ 519ea906c41SOllivier Robert #endif /* ICOM */ 520a151a66cSOllivier Robert int errflg; /* error flags */ 5219c2daa00SOllivier Robert int watch; /* watchcat */ 5229c2daa00SOllivier Robert 5239c2daa00SOllivier Robert /* 5249c2daa00SOllivier Robert * Audio codec variables 5259c2daa00SOllivier Robert */ 5269c2daa00SOllivier Robert double comp[SIZE]; /* decompanding table */ 527a151a66cSOllivier Robert int port; /* codec port */ 528a151a66cSOllivier Robert int gain; /* codec gain */ 5299c2daa00SOllivier Robert int mongain; /* codec monitor gain */ 530a151a66cSOllivier Robert int clipcnt; /* sample clipped count */ 531a151a66cSOllivier Robert 532a151a66cSOllivier Robert /* 533a151a66cSOllivier Robert * Variables used to establish basic system timing 534a151a66cSOllivier Robert */ 5359c2daa00SOllivier Robert int avgint; /* master time constant */ 5369c2daa00SOllivier Robert int yepoch; /* sync epoch */ 5379c2daa00SOllivier Robert int repoch; /* buffered sync epoch */ 538a151a66cSOllivier Robert double epomax; /* second sync amplitude */ 5399c2daa00SOllivier Robert double eposnr; /* second sync SNR */ 540a151a66cSOllivier Robert double irig; /* data I channel amplitude */ 541a151a66cSOllivier Robert double qrig; /* data Q channel amplitude */ 542a151a66cSOllivier Robert int datapt; /* 100 Hz ramp */ 543a151a66cSOllivier Robert double datpha; /* 100 Hz VFO control */ 5449c2daa00SOllivier Robert int rphase; /* second sample counter */ 545a151a66cSOllivier Robert long mphase; /* minute sample counter */ 546a151a66cSOllivier Robert 547a151a66cSOllivier Robert /* 548a151a66cSOllivier Robert * Variables used to mitigate which channel to use 549a151a66cSOllivier Robert */ 550a151a66cSOllivier Robert struct chan mitig[NCHAN]; /* channel data */ 551a151a66cSOllivier Robert struct sync *sptr; /* station pointer */ 552a151a66cSOllivier Robert int dchan; /* data channel */ 553a151a66cSOllivier Robert int schan; /* probe channel */ 554a151a66cSOllivier Robert int achan; /* active channel */ 555a151a66cSOllivier Robert 556a151a66cSOllivier Robert /* 557a151a66cSOllivier Robert * Variables used by the clock state machine 558a151a66cSOllivier Robert */ 559a151a66cSOllivier Robert struct decvec decvec[9]; /* decoding matrix */ 5609c2daa00SOllivier Robert int rsec; /* seconds counter */ 561a151a66cSOllivier Robert int digcnt; /* count of digits synchronized */ 562a151a66cSOllivier Robert 563a151a66cSOllivier Robert /* 564a151a66cSOllivier Robert * Variables used to estimate signal levels and bit/digit 565a151a66cSOllivier Robert * probabilities 566a151a66cSOllivier Robert */ 567ea906c41SOllivier Robert double datsig; /* data signal max */ 568ea906c41SOllivier Robert double datsnr; /* data signal SNR (dB) */ 569a151a66cSOllivier Robert 570a151a66cSOllivier Robert /* 571a151a66cSOllivier Robert * Variables used to establish status and alarm conditions 572a151a66cSOllivier Robert */ 573a151a66cSOllivier Robert int status; /* status bits */ 574a151a66cSOllivier Robert int alarm; /* alarm flashers */ 575a151a66cSOllivier Robert int misc; /* miscellaneous timecode bits */ 576a151a66cSOllivier Robert int errcnt; /* data bit error counter */ 577a151a66cSOllivier Robert }; 578a151a66cSOllivier Robert 579a151a66cSOllivier Robert /* 580a151a66cSOllivier Robert * Function prototypes 581a151a66cSOllivier Robert */ 5822b15cb3dSCy Schubert static int wwv_start (int, struct peer *); 5832b15cb3dSCy Schubert static void wwv_shutdown (int, struct peer *); 5842b15cb3dSCy Schubert static void wwv_receive (struct recvbuf *); 5852b15cb3dSCy Schubert static void wwv_poll (int, struct peer *); 586a151a66cSOllivier Robert 587a151a66cSOllivier Robert /* 588a151a66cSOllivier Robert * More function prototypes 589a151a66cSOllivier Robert */ 5902b15cb3dSCy Schubert static void wwv_epoch (struct peer *); 5912b15cb3dSCy Schubert static void wwv_rf (struct peer *, double); 5922b15cb3dSCy Schubert static void wwv_endpoc (struct peer *, int); 5932b15cb3dSCy Schubert static void wwv_rsec (struct peer *, double); 5942b15cb3dSCy Schubert static void wwv_qrz (struct peer *, struct sync *, int); 5952b15cb3dSCy Schubert static void wwv_corr4 (struct peer *, struct decvec *, 5962b15cb3dSCy Schubert double [], double [][4]); 5972b15cb3dSCy Schubert static void wwv_gain (struct peer *); 5982b15cb3dSCy Schubert static void wwv_tsec (struct peer *); 5992b15cb3dSCy Schubert static int timecode (struct wwvunit *, char *, size_t); 6002b15cb3dSCy Schubert static double wwv_snr (double, double); 6012b15cb3dSCy Schubert static int carry (struct decvec *); 6022b15cb3dSCy Schubert static int wwv_newchan (struct peer *); 6032b15cb3dSCy Schubert static void wwv_newgame (struct peer *); 6042b15cb3dSCy Schubert static double wwv_metric (struct sync *); 6052b15cb3dSCy Schubert static void wwv_clock (struct peer *); 6069c2daa00SOllivier Robert #ifdef ICOM 6072b15cb3dSCy Schubert static int wwv_qsy (struct peer *, int); 6089c2daa00SOllivier Robert #endif /* ICOM */ 6099c2daa00SOllivier Robert 610a151a66cSOllivier Robert static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */ 611a151a66cSOllivier Robert 612a151a66cSOllivier Robert /* 613a151a66cSOllivier Robert * Transfer vector 614a151a66cSOllivier Robert */ 615a151a66cSOllivier Robert struct refclock refclock_wwv = { 616a151a66cSOllivier Robert wwv_start, /* start up driver */ 617a151a66cSOllivier Robert wwv_shutdown, /* shut down driver */ 618a151a66cSOllivier Robert wwv_poll, /* transmit poll message */ 619a151a66cSOllivier Robert noentry, /* not used (old wwv_control) */ 620a151a66cSOllivier Robert noentry, /* initialize driver (not used) */ 621a151a66cSOllivier Robert noentry, /* not used (old wwv_buginfo) */ 622a151a66cSOllivier Robert NOFLAGS /* not used */ 623a151a66cSOllivier Robert }; 624a151a66cSOllivier Robert 625a151a66cSOllivier Robert 626a151a66cSOllivier Robert /* 627a151a66cSOllivier Robert * wwv_start - open the devices and initialize data for processing 628a151a66cSOllivier Robert */ 629a151a66cSOllivier Robert static int 630a151a66cSOllivier Robert wwv_start( 6319c2daa00SOllivier Robert int unit, /* instance number (used by PCM) */ 632a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 633a151a66cSOllivier Robert ) 634a151a66cSOllivier Robert { 635a151a66cSOllivier Robert struct refclockproc *pp; 636a151a66cSOllivier Robert struct wwvunit *up; 637a151a66cSOllivier Robert #ifdef ICOM 638a151a66cSOllivier Robert int temp; 639a151a66cSOllivier Robert #endif /* ICOM */ 640a151a66cSOllivier Robert 641a151a66cSOllivier Robert /* 642a151a66cSOllivier Robert * Local variables 643a151a66cSOllivier Robert */ 644a151a66cSOllivier Robert int fd; /* file descriptor */ 645a151a66cSOllivier Robert int i; /* index */ 646a151a66cSOllivier Robert double step; /* codec adjustment */ 647a151a66cSOllivier Robert 648a151a66cSOllivier Robert /* 649a151a66cSOllivier Robert * Open audio device 650a151a66cSOllivier Robert */ 6519c2daa00SOllivier Robert fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit); 652a151a66cSOllivier Robert if (fd < 0) 653a151a66cSOllivier Robert return (0); 654a151a66cSOllivier Robert #ifdef DEBUG 655a151a66cSOllivier Robert if (debug) 656a151a66cSOllivier Robert audio_show(); 657ea906c41SOllivier Robert #endif /* DEBUG */ 658a151a66cSOllivier Robert 659a151a66cSOllivier Robert /* 660a151a66cSOllivier Robert * Allocate and initialize unit structure 661a151a66cSOllivier Robert */ 6622b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 663a151a66cSOllivier Robert pp = peer->procptr; 664a151a66cSOllivier Robert pp->io.clock_recv = wwv_receive; 6652b15cb3dSCy Schubert pp->io.srcclock = peer; 666a151a66cSOllivier Robert pp->io.datalen = 0; 667a151a66cSOllivier Robert pp->io.fd = fd; 668a151a66cSOllivier Robert if (!io_addclock(&pp->io)) { 6699c2daa00SOllivier Robert close(fd); 670a151a66cSOllivier Robert free(up); 671a151a66cSOllivier Robert return (0); 672a151a66cSOllivier Robert } 6732b15cb3dSCy Schubert pp->unitptr = up; 674a151a66cSOllivier Robert 675a151a66cSOllivier Robert /* 676a151a66cSOllivier Robert * Initialize miscellaneous variables 677a151a66cSOllivier Robert */ 678a151a66cSOllivier Robert peer->precision = PRECISION; 679a151a66cSOllivier Robert pp->clockdesc = DESCRIPTION; 680a151a66cSOllivier Robert 681a151a66cSOllivier Robert /* 682a151a66cSOllivier Robert * The companded samples are encoded sign-magnitude. The table 683a151a66cSOllivier Robert * contains all the 256 values in the interest of speed. 684a151a66cSOllivier Robert */ 685a151a66cSOllivier Robert up->comp[0] = up->comp[OFFSET] = 0.; 686ea906c41SOllivier Robert up->comp[1] = 1.; up->comp[OFFSET + 1] = -1.; 687ea906c41SOllivier Robert up->comp[2] = 3.; up->comp[OFFSET + 2] = -3.; 688a151a66cSOllivier Robert step = 2.; 689a151a66cSOllivier Robert for (i = 3; i < OFFSET; i++) { 690a151a66cSOllivier Robert up->comp[i] = up->comp[i - 1] + step; 691a151a66cSOllivier Robert up->comp[OFFSET + i] = -up->comp[i]; 692a151a66cSOllivier Robert if (i % 16 == 0) 693a151a66cSOllivier Robert step *= 2.; 694a151a66cSOllivier Robert } 6952b15cb3dSCy Schubert DTOLFP(1. / WWV_SEC, &up->tick); 696a151a66cSOllivier Robert 697a151a66cSOllivier Robert /* 698a151a66cSOllivier Robert * Initialize the decoding matrix with the radix for each digit 699a151a66cSOllivier Robert * position. 700a151a66cSOllivier Robert */ 701a151a66cSOllivier Robert up->decvec[MN].radix = 10; /* minutes */ 702a151a66cSOllivier Robert up->decvec[MN + 1].radix = 6; 703a151a66cSOllivier Robert up->decvec[HR].radix = 10; /* hours */ 704a151a66cSOllivier Robert up->decvec[HR + 1].radix = 3; 705a151a66cSOllivier Robert up->decvec[DA].radix = 10; /* days */ 706a151a66cSOllivier Robert up->decvec[DA + 1].radix = 10; 707a151a66cSOllivier Robert up->decvec[DA + 2].radix = 4; 708a151a66cSOllivier Robert up->decvec[YR].radix = 10; /* years */ 709a151a66cSOllivier Robert up->decvec[YR + 1].radix = 10; 710a151a66cSOllivier Robert 711a151a66cSOllivier Robert #ifdef ICOM 712ea906c41SOllivier Robert /* 713ea906c41SOllivier Robert * Initialize autotune if available. Note that the ICOM select 714ea906c41SOllivier Robert * code must be less than 128, so the high order bit can be used 7152b15cb3dSCy Schubert * to select the line speed 0 (9600 bps) or 1 (1200 bps). Note 7162b15cb3dSCy Schubert * we don't complain if the ICOM device is not there; but, if it 7172b15cb3dSCy Schubert * is, the radio better be working. 718ea906c41SOllivier Robert */ 719a151a66cSOllivier Robert temp = 0; 720a151a66cSOllivier Robert #ifdef DEBUG 721a151a66cSOllivier Robert if (debug > 1) 722a151a66cSOllivier Robert temp = P_TRACE; 723ea906c41SOllivier Robert #endif /* DEBUG */ 7249c2daa00SOllivier Robert if (peer->ttl != 0) { 7259c2daa00SOllivier Robert if (peer->ttl & 0x80) 726a151a66cSOllivier Robert up->fd_icom = icom_init("/dev/icom", B1200, 727a151a66cSOllivier Robert temp); 728a151a66cSOllivier Robert else 729a151a66cSOllivier Robert up->fd_icom = icom_init("/dev/icom", B9600, 730a151a66cSOllivier Robert temp); 731a151a66cSOllivier Robert } 732a151a66cSOllivier Robert if (up->fd_icom > 0) { 733ea906c41SOllivier Robert if (wwv_qsy(peer, DCHAN) != 0) { 7342b15cb3dSCy Schubert msyslog(LOG_NOTICE, "icom: radio not found"); 735a151a66cSOllivier Robert close(up->fd_icom); 736a151a66cSOllivier Robert up->fd_icom = 0; 7379c2daa00SOllivier Robert } else { 7382b15cb3dSCy Schubert msyslog(LOG_NOTICE, "icom: autotune enabled"); 739a151a66cSOllivier Robert } 740a151a66cSOllivier Robert } 741a151a66cSOllivier Robert #endif /* ICOM */ 742ea906c41SOllivier Robert 743ea906c41SOllivier Robert /* 744ea906c41SOllivier Robert * Let the games begin. 745ea906c41SOllivier Robert */ 746ea906c41SOllivier Robert wwv_newgame(peer); 747a151a66cSOllivier Robert return (1); 748a151a66cSOllivier Robert } 749a151a66cSOllivier Robert 750a151a66cSOllivier Robert 751a151a66cSOllivier Robert /* 752a151a66cSOllivier Robert * wwv_shutdown - shut down the clock 753a151a66cSOllivier Robert */ 754a151a66cSOllivier Robert static void 755a151a66cSOllivier Robert wwv_shutdown( 756a151a66cSOllivier Robert int unit, /* instance number (not used) */ 757a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 758a151a66cSOllivier Robert ) 759a151a66cSOllivier Robert { 760a151a66cSOllivier Robert struct refclockproc *pp; 761a151a66cSOllivier Robert struct wwvunit *up; 762a151a66cSOllivier Robert 763a151a66cSOllivier Robert pp = peer->procptr; 7642b15cb3dSCy Schubert up = pp->unitptr; 765ea906c41SOllivier Robert if (up == NULL) 766ea906c41SOllivier Robert return; 767ea906c41SOllivier Robert 768a151a66cSOllivier Robert io_closeclock(&pp->io); 769ea906c41SOllivier Robert #ifdef ICOM 770a151a66cSOllivier Robert if (up->fd_icom > 0) 771a151a66cSOllivier Robert close(up->fd_icom); 772ea906c41SOllivier Robert #endif /* ICOM */ 773a151a66cSOllivier Robert free(up); 774a151a66cSOllivier Robert } 775a151a66cSOllivier Robert 776a151a66cSOllivier Robert 777a151a66cSOllivier Robert /* 778a151a66cSOllivier Robert * wwv_receive - receive data from the audio device 779a151a66cSOllivier Robert * 780a151a66cSOllivier Robert * This routine reads input samples and adjusts the logical clock to 781a151a66cSOllivier Robert * track the A/D sample clock by dropping or duplicating codec samples. 782a151a66cSOllivier Robert * It also controls the A/D signal level with an AGC loop to mimimize 783a151a66cSOllivier Robert * quantization noise and avoid overload. 784a151a66cSOllivier Robert */ 785a151a66cSOllivier Robert static void 786a151a66cSOllivier Robert wwv_receive( 787a151a66cSOllivier Robert struct recvbuf *rbufp /* receive buffer structure pointer */ 788a151a66cSOllivier Robert ) 789a151a66cSOllivier Robert { 790a151a66cSOllivier Robert struct peer *peer; 791a151a66cSOllivier Robert struct refclockproc *pp; 792a151a66cSOllivier Robert struct wwvunit *up; 793a151a66cSOllivier Robert 794a151a66cSOllivier Robert /* 795a151a66cSOllivier Robert * Local variables 796a151a66cSOllivier Robert */ 797a151a66cSOllivier Robert double sample; /* codec sample */ 798a151a66cSOllivier Robert u_char *dpt; /* buffer pointer */ 7999c2daa00SOllivier Robert int bufcnt; /* buffer counter */ 800a151a66cSOllivier Robert l_fp ltemp; 801a151a66cSOllivier Robert 8022b15cb3dSCy Schubert peer = rbufp->recv_peer; 803a151a66cSOllivier Robert pp = peer->procptr; 8042b15cb3dSCy Schubert up = pp->unitptr; 805a151a66cSOllivier Robert 806a151a66cSOllivier Robert /* 807a151a66cSOllivier Robert * Main loop - read until there ain't no more. Note codec 808a151a66cSOllivier Robert * samples are bit-inverted. 809a151a66cSOllivier Robert */ 8102b15cb3dSCy Schubert DTOLFP((double)rbufp->recv_length / WWV_SEC, <emp); 8119c2daa00SOllivier Robert L_SUB(&rbufp->recv_time, <emp); 812a151a66cSOllivier Robert up->timestamp = rbufp->recv_time; 813a151a66cSOllivier Robert dpt = rbufp->recv_buffer; 8149c2daa00SOllivier Robert for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) { 8159c2daa00SOllivier Robert sample = up->comp[~*dpt++ & 0xff]; 816a151a66cSOllivier Robert 817a151a66cSOllivier Robert /* 818ea906c41SOllivier Robert * Clip noise spikes greater than MAXAMP (6000) and 819ea906c41SOllivier Robert * record the number of clips to be used later by the 820ea906c41SOllivier Robert * AGC. 821a151a66cSOllivier Robert */ 822ea906c41SOllivier Robert if (sample > MAXAMP) { 823ea906c41SOllivier Robert sample = MAXAMP; 824a151a66cSOllivier Robert up->clipcnt++; 825ea906c41SOllivier Robert } else if (sample < -MAXAMP) { 826ea906c41SOllivier Robert sample = -MAXAMP; 827a151a66cSOllivier Robert up->clipcnt++; 828a151a66cSOllivier Robert } 829a151a66cSOllivier Robert 830a151a66cSOllivier Robert /* 8319c2daa00SOllivier Robert * Variable frequency oscillator. The codec oscillator 8329c2daa00SOllivier Robert * runs at the nominal rate of 8000 samples per second, 8339c2daa00SOllivier Robert * or 125 us per sample. A frequency change of one unit 8349c2daa00SOllivier Robert * results in either duplicating or deleting one sample 8359c2daa00SOllivier Robert * per second, which results in a frequency change of 8369c2daa00SOllivier Robert * 125 PPM. 837a151a66cSOllivier Robert */ 8382b15cb3dSCy Schubert up->phase += (up->freq + clock_codec) / WWV_SEC; 839a151a66cSOllivier Robert if (up->phase >= .5) { 840a151a66cSOllivier Robert up->phase -= 1.; 841a151a66cSOllivier Robert } else if (up->phase < -.5) { 842a151a66cSOllivier Robert up->phase += 1.; 843a151a66cSOllivier Robert wwv_rf(peer, sample); 844a151a66cSOllivier Robert wwv_rf(peer, sample); 845a151a66cSOllivier Robert } else { 846a151a66cSOllivier Robert wwv_rf(peer, sample); 847a151a66cSOllivier Robert } 848a151a66cSOllivier Robert L_ADD(&up->timestamp, &up->tick); 8499c2daa00SOllivier Robert } 850a151a66cSOllivier Robert 851a151a66cSOllivier Robert /* 8529c2daa00SOllivier Robert * Set the input port and monitor gain for the next buffer. 853a151a66cSOllivier Robert */ 854a151a66cSOllivier Robert if (pp->sloppyclockflag & CLK_FLAG2) 855a151a66cSOllivier Robert up->port = 2; 856a151a66cSOllivier Robert else 857a151a66cSOllivier Robert up->port = 1; 858a151a66cSOllivier Robert if (pp->sloppyclockflag & CLK_FLAG3) 8599c2daa00SOllivier Robert up->mongain = MONGAIN; 8609c2daa00SOllivier Robert else 8619c2daa00SOllivier Robert up->mongain = 0; 862a151a66cSOllivier Robert } 863a151a66cSOllivier Robert 864a151a66cSOllivier Robert 865a151a66cSOllivier Robert /* 866a151a66cSOllivier Robert * wwv_poll - called by the transmit procedure 867a151a66cSOllivier Robert * 8689c2daa00SOllivier Robert * This routine keeps track of status. If no offset samples have been 8699c2daa00SOllivier Robert * processed during a poll interval, a timeout event is declared. If 8709c2daa00SOllivier Robert * errors have have occurred during the interval, they are reported as 871ea906c41SOllivier Robert * well. 872a151a66cSOllivier Robert */ 873a151a66cSOllivier Robert static void 874a151a66cSOllivier Robert wwv_poll( 875a151a66cSOllivier Robert int unit, /* instance number (not used) */ 876a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 877a151a66cSOllivier Robert ) 878a151a66cSOllivier Robert { 879a151a66cSOllivier Robert struct refclockproc *pp; 880a151a66cSOllivier Robert struct wwvunit *up; 881a151a66cSOllivier Robert 882a151a66cSOllivier Robert pp = peer->procptr; 8832b15cb3dSCy Schubert up = pp->unitptr; 884a151a66cSOllivier Robert if (up->errflg) 885a151a66cSOllivier Robert refclock_report(peer, up->errflg); 886a151a66cSOllivier Robert up->errflg = 0; 8879c2daa00SOllivier Robert pp->polls++; 888a151a66cSOllivier Robert } 889a151a66cSOllivier Robert 890a151a66cSOllivier Robert 891a151a66cSOllivier Robert /* 892a151a66cSOllivier Robert * wwv_rf - process signals and demodulate to baseband 893a151a66cSOllivier Robert * 894a151a66cSOllivier Robert * This routine grooms and filters decompanded raw audio samples. The 895ea906c41SOllivier Robert * output signal is the 100-Hz filtered baseband data signal in 896ea906c41SOllivier Robert * quadrature phase. The routine also determines the minute synch epoch, 897ea906c41SOllivier Robert * as well as certain signal maxima, minima and related values. 898a151a66cSOllivier Robert * 8999c2daa00SOllivier Robert * There are two 1-s ramps used by this program. Both count the 8000 9009c2daa00SOllivier Robert * logical clock samples spanning exactly one second. The epoch ramp 9019c2daa00SOllivier Robert * counts the samples starting at an arbitrary time. The rphase ramp 9029c2daa00SOllivier Robert * counts the samples starting at the 5-ms second sync pulse found 9039c2daa00SOllivier Robert * during the epoch ramp. 904a151a66cSOllivier Robert * 9059c2daa00SOllivier Robert * There are two 1-m ramps used by this program. The mphase ramp counts 9069c2daa00SOllivier Robert * the 480,000 logical clock samples spanning exactly one minute and 9079c2daa00SOllivier Robert * starting at an arbitrary time. The rsec ramp counts the 60 seconds of 9089c2daa00SOllivier Robert * the minute starting at the 800-ms minute sync pulse found during the 9099c2daa00SOllivier Robert * mphase ramp. The rsec ramp drives the seconds state machine to 9109c2daa00SOllivier Robert * determine the bits and digits of the timecode. 911a151a66cSOllivier Robert * 912a151a66cSOllivier Robert * Demodulation operations are based on three synthesized quadrature 9139c2daa00SOllivier Robert * sinusoids: 100 Hz for the data signal, 1000 Hz for the WWV sync 9149c2daa00SOllivier Robert * signal and 1200 Hz for the WWVH sync signal. These drive synchronous 9159c2daa00SOllivier Robert * matched filters for the data signal (170 ms at 100 Hz), WWV minute 9169c2daa00SOllivier Robert * sync signal (800 ms at 1000 Hz) and WWVH minute sync signal (800 ms 9179c2daa00SOllivier Robert * at 1200 Hz). Two additional matched filters are switched in 918ea906c41SOllivier Robert * as required for the WWV second sync signal (5 cycles at 1000 Hz) and 919ea906c41SOllivier Robert * WWVH second sync signal (6 cycles at 1200 Hz). 920a151a66cSOllivier Robert */ 921a151a66cSOllivier Robert static void 922a151a66cSOllivier Robert wwv_rf( 923a151a66cSOllivier Robert struct peer *peer, /* peerstructure pointer */ 924a151a66cSOllivier Robert double isig /* input signal */ 925a151a66cSOllivier Robert ) 926a151a66cSOllivier Robert { 927a151a66cSOllivier Robert struct refclockproc *pp; 928a151a66cSOllivier Robert struct wwvunit *up; 929ea906c41SOllivier Robert struct sync *sp, *rp; 930a151a66cSOllivier Robert 931a151a66cSOllivier Robert static double lpf[5]; /* 150-Hz lpf delay line */ 932a151a66cSOllivier Robert double data; /* lpf output */ 933a151a66cSOllivier Robert static double bpf[9]; /* 1000/1200-Hz bpf delay line */ 934a151a66cSOllivier Robert double syncx; /* bpf output */ 935a151a66cSOllivier Robert static double mf[41]; /* 1000/1200-Hz mf delay line */ 936a151a66cSOllivier Robert double mfsync; /* mf output */ 937a151a66cSOllivier Robert 938a151a66cSOllivier Robert static int iptr; /* data channel pointer */ 939a151a66cSOllivier Robert static double ibuf[DATSIZ]; /* data I channel delay line */ 940a151a66cSOllivier Robert static double qbuf[DATSIZ]; /* data Q channel delay line */ 941a151a66cSOllivier Robert 942a151a66cSOllivier Robert static int jptr; /* sync channel pointer */ 943ea906c41SOllivier Robert static int kptr; /* tick channel pointer */ 944ea906c41SOllivier Robert 945ea906c41SOllivier Robert static int csinptr; /* wwv channel phase */ 946a151a66cSOllivier Robert static double cibuf[SYNSIZ]; /* wwv I channel delay line */ 947a151a66cSOllivier Robert static double cqbuf[SYNSIZ]; /* wwv Q channel delay line */ 948a151a66cSOllivier Robert static double ciamp; /* wwv I channel amplitude */ 949a151a66cSOllivier Robert static double cqamp; /* wwv Q channel amplitude */ 950ea906c41SOllivier Robert 951ea906c41SOllivier Robert static double csibuf[TCKSIZ]; /* wwv I tick delay line */ 952ea906c41SOllivier Robert static double csqbuf[TCKSIZ]; /* wwv Q tick delay line */ 953ea906c41SOllivier Robert static double csiamp; /* wwv I tick amplitude */ 954ea906c41SOllivier Robert static double csqamp; /* wwv Q tick amplitude */ 955ea906c41SOllivier Robert 956ea906c41SOllivier Robert static int hsinptr; /* wwvh channel phase */ 957a151a66cSOllivier Robert static double hibuf[SYNSIZ]; /* wwvh I channel delay line */ 958a151a66cSOllivier Robert static double hqbuf[SYNSIZ]; /* wwvh Q channel delay line */ 959a151a66cSOllivier Robert static double hiamp; /* wwvh I channel amplitude */ 960a151a66cSOllivier Robert static double hqamp; /* wwvh Q channel amplitude */ 961a151a66cSOllivier Robert 962ea906c41SOllivier Robert static double hsibuf[TCKSIZ]; /* wwvh I tick delay line */ 963ea906c41SOllivier Robert static double hsqbuf[TCKSIZ]; /* wwvh Q tick delay line */ 964ea906c41SOllivier Robert static double hsiamp; /* wwvh I tick amplitude */ 965ea906c41SOllivier Robert static double hsqamp; /* wwvh Q tick amplitude */ 966ea906c41SOllivier Robert 9672b15cb3dSCy Schubert static double epobuf[WWV_SEC]; /* second sync comb filter */ 968ea906c41SOllivier Robert static double epomax, nxtmax; /* second sync amplitude buffer */ 969ea906c41SOllivier Robert static int epopos; /* epoch second sync position buffer */ 970a151a66cSOllivier Robert 971a151a66cSOllivier Robert static int iniflg; /* initialization flag */ 972ea906c41SOllivier Robert int epoch; /* comb filter index */ 973a151a66cSOllivier Robert double dtemp; 974a151a66cSOllivier Robert int i; 975a151a66cSOllivier Robert 976a151a66cSOllivier Robert pp = peer->procptr; 9772b15cb3dSCy Schubert up = pp->unitptr; 9789c2daa00SOllivier Robert 979a151a66cSOllivier Robert if (!iniflg) { 980a151a66cSOllivier Robert iniflg = 1; 981a151a66cSOllivier Robert memset((char *)lpf, 0, sizeof(lpf)); 982a151a66cSOllivier Robert memset((char *)bpf, 0, sizeof(bpf)); 983a151a66cSOllivier Robert memset((char *)mf, 0, sizeof(mf)); 984a151a66cSOllivier Robert memset((char *)ibuf, 0, sizeof(ibuf)); 985a151a66cSOllivier Robert memset((char *)qbuf, 0, sizeof(qbuf)); 986a151a66cSOllivier Robert memset((char *)cibuf, 0, sizeof(cibuf)); 987a151a66cSOllivier Robert memset((char *)cqbuf, 0, sizeof(cqbuf)); 988ea906c41SOllivier Robert memset((char *)csibuf, 0, sizeof(csibuf)); 989ea906c41SOllivier Robert memset((char *)csqbuf, 0, sizeof(csqbuf)); 990a151a66cSOllivier Robert memset((char *)hibuf, 0, sizeof(hibuf)); 991a151a66cSOllivier Robert memset((char *)hqbuf, 0, sizeof(hqbuf)); 992ea906c41SOllivier Robert memset((char *)hsibuf, 0, sizeof(hsibuf)); 993ea906c41SOllivier Robert memset((char *)hsqbuf, 0, sizeof(hsqbuf)); 994a151a66cSOllivier Robert memset((char *)epobuf, 0, sizeof(epobuf)); 995a151a66cSOllivier Robert } 996a151a66cSOllivier Robert 997a151a66cSOllivier Robert /* 998a151a66cSOllivier Robert * Baseband data demodulation. The 100-Hz subcarrier is 999a151a66cSOllivier Robert * extracted using a 150-Hz IIR lowpass filter. This attenuates 1000a151a66cSOllivier Robert * the 1000/1200-Hz sync signals, as well as the 440-Hz and 1001a151a66cSOllivier Robert * 600-Hz tones and most of the noise and voice modulation 1002a151a66cSOllivier Robert * components. 1003a151a66cSOllivier Robert * 1004ea906c41SOllivier Robert * The subcarrier is transmitted 10 dB down from the carrier. 1005ea906c41SOllivier Robert * The DGAIN parameter can be adjusted for this and to 1006ea906c41SOllivier Robert * compensate for the radio audio response at 100 Hz. 1007ea906c41SOllivier Robert * 1008a151a66cSOllivier Robert * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB 10092b15cb3dSCy Schubert * passband ripple, -50 dB stopband ripple, phase delay 0.97 ms. 1010a151a66cSOllivier Robert */ 1011a151a66cSOllivier Robert data = (lpf[4] = lpf[3]) * 8.360961e-01; 1012a151a66cSOllivier Robert data += (lpf[3] = lpf[2]) * -3.481740e+00; 1013a151a66cSOllivier Robert data += (lpf[2] = lpf[1]) * 5.452988e+00; 1014a151a66cSOllivier Robert data += (lpf[1] = lpf[0]) * -3.807229e+00; 1015ea906c41SOllivier Robert lpf[0] = isig * DGAIN - data; 1016a151a66cSOllivier Robert data = lpf[0] * 3.281435e-03 1017a151a66cSOllivier Robert + lpf[1] * -1.149947e-02 1018a151a66cSOllivier Robert + lpf[2] * 1.654858e-02 1019a151a66cSOllivier Robert + lpf[3] * -1.149947e-02 1020a151a66cSOllivier Robert + lpf[4] * 3.281435e-03; 1021a151a66cSOllivier Robert 1022a151a66cSOllivier Robert /* 1023ea906c41SOllivier Robert * The 100-Hz data signal is demodulated using a pair of 1024ea906c41SOllivier Robert * quadrature multipliers, matched filters and a phase lock 1025ea906c41SOllivier Robert * loop. The I and Q quadrature data signals are produced by 1026a151a66cSOllivier Robert * multiplying the filtered signal by 100-Hz sine and cosine 1027ea906c41SOllivier Robert * signals, respectively. The signals are processed by 170-ms 1028ea906c41SOllivier Robert * synchronous matched filters to produce the amplitude and 1029ea906c41SOllivier Robert * phase signals used by the demodulator. The signals are scaled 1030ea906c41SOllivier Robert * to produce unit energy at the maximum value. 1031a151a66cSOllivier Robert */ 10329c2daa00SOllivier Robert i = up->datapt; 1033a151a66cSOllivier Robert up->datapt = (up->datapt + IN100) % 80; 1034ea906c41SOllivier Robert dtemp = sintab[i] * data / (MS / 2. * DATCYC); 1035a151a66cSOllivier Robert up->irig -= ibuf[iptr]; 1036a151a66cSOllivier Robert ibuf[iptr] = dtemp; 1037a151a66cSOllivier Robert up->irig += dtemp; 1038ea906c41SOllivier Robert 1039a151a66cSOllivier Robert i = (i + 20) % 80; 1040ea906c41SOllivier Robert dtemp = sintab[i] * data / (MS / 2. * DATCYC); 1041a151a66cSOllivier Robert up->qrig -= qbuf[iptr]; 1042a151a66cSOllivier Robert qbuf[iptr] = dtemp; 1043a151a66cSOllivier Robert up->qrig += dtemp; 1044a151a66cSOllivier Robert iptr = (iptr + 1) % DATSIZ; 1045a151a66cSOllivier Robert 1046a151a66cSOllivier Robert /* 1047a151a66cSOllivier Robert * Baseband sync demodulation. The 1000/1200 sync signals are 1048a151a66cSOllivier Robert * extracted using a 600-Hz IIR bandpass filter. This removes 1049a151a66cSOllivier Robert * the 100-Hz data subcarrier, as well as the 440-Hz and 600-Hz 1050a151a66cSOllivier Robert * tones and most of the noise and voice modulation components. 1051a151a66cSOllivier Robert * 1052a151a66cSOllivier Robert * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB 10532b15cb3dSCy Schubert * passband ripple, -50 dB stopband ripple, phase delay 0.91 ms. 1054a151a66cSOllivier Robert */ 1055a151a66cSOllivier Robert syncx = (bpf[8] = bpf[7]) * 4.897278e-01; 1056a151a66cSOllivier Robert syncx += (bpf[7] = bpf[6]) * -2.765914e+00; 1057a151a66cSOllivier Robert syncx += (bpf[6] = bpf[5]) * 8.110921e+00; 1058a151a66cSOllivier Robert syncx += (bpf[5] = bpf[4]) * -1.517732e+01; 1059a151a66cSOllivier Robert syncx += (bpf[4] = bpf[3]) * 1.975197e+01; 1060a151a66cSOllivier Robert syncx += (bpf[3] = bpf[2]) * -1.814365e+01; 1061a151a66cSOllivier Robert syncx += (bpf[2] = bpf[1]) * 1.159783e+01; 1062a151a66cSOllivier Robert syncx += (bpf[1] = bpf[0]) * -4.735040e+00; 1063a151a66cSOllivier Robert bpf[0] = isig - syncx; 1064a151a66cSOllivier Robert syncx = bpf[0] * 8.203628e-03 1065a151a66cSOllivier Robert + bpf[1] * -2.375732e-02 1066a151a66cSOllivier Robert + bpf[2] * 3.353214e-02 1067a151a66cSOllivier Robert + bpf[3] * -4.080258e-02 1068a151a66cSOllivier Robert + bpf[4] * 4.605479e-02 1069a151a66cSOllivier Robert + bpf[5] * -4.080258e-02 1070a151a66cSOllivier Robert + bpf[6] * 3.353214e-02 1071a151a66cSOllivier Robert + bpf[7] * -2.375732e-02 1072a151a66cSOllivier Robert + bpf[8] * 8.203628e-03; 1073a151a66cSOllivier Robert 1074a151a66cSOllivier Robert /* 1075ea906c41SOllivier Robert * The 1000/1200 sync signals are demodulated using a pair of 1076ea906c41SOllivier Robert * quadrature multipliers and matched filters. However, 1077ea906c41SOllivier Robert * synchronous demodulation at these frequencies is impractical, 1078ea906c41SOllivier Robert * so only the signal amplitude is used. The I and Q quadrature 1079ea906c41SOllivier Robert * sync signals are produced by multiplying the filtered signal 1080ea906c41SOllivier Robert * by 1000-Hz (WWV) and 1200-Hz (WWVH) sine and cosine signals, 1081ea906c41SOllivier Robert * respectively. The WWV and WWVH signals are processed by 800- 1082ea906c41SOllivier Robert * ms synchronous matched filters and combined to produce the 1083ea906c41SOllivier Robert * minute sync signal and detect which one (or both) the WWV or 1084ea906c41SOllivier Robert * WWVH signal is present. The WWV and WWVH signals are also 1085ea906c41SOllivier Robert * processed by 5-ms synchronous matched filters and combined to 1086ea906c41SOllivier Robert * produce the second sync signal. The signals are scaled to 1087ea906c41SOllivier Robert * produce unit energy at the maximum value. 10889c2daa00SOllivier Robert * 10899c2daa00SOllivier Robert * Note the master timing ramps, which run continuously. The 10909c2daa00SOllivier Robert * minute counter (mphase) counts the samples in the minute, 10919c2daa00SOllivier Robert * while the second counter (epoch) counts the samples in the 10929c2daa00SOllivier Robert * second. 1093a151a66cSOllivier Robert */ 10942b15cb3dSCy Schubert up->mphase = (up->mphase + 1) % WWV_MIN; 10952b15cb3dSCy Schubert epoch = up->mphase % WWV_SEC; 1096ea906c41SOllivier Robert 1097ea906c41SOllivier Robert /* 1098ea906c41SOllivier Robert * WWV 1099ea906c41SOllivier Robert */ 1100a151a66cSOllivier Robert i = csinptr; 1101a151a66cSOllivier Robert csinptr = (csinptr + IN1000) % 80; 1102ea906c41SOllivier Robert 1103ea906c41SOllivier Robert dtemp = sintab[i] * syncx / (MS / 2.); 1104ea906c41SOllivier Robert ciamp -= cibuf[jptr]; 1105a151a66cSOllivier Robert cibuf[jptr] = dtemp; 1106ea906c41SOllivier Robert ciamp += dtemp; 1107ea906c41SOllivier Robert csiamp -= csibuf[kptr]; 1108ea906c41SOllivier Robert csibuf[kptr] = dtemp; 1109ea906c41SOllivier Robert csiamp += dtemp; 1110ea906c41SOllivier Robert 1111a151a66cSOllivier Robert i = (i + 20) % 80; 1112ea906c41SOllivier Robert dtemp = sintab[i] * syncx / (MS / 2.); 1113ea906c41SOllivier Robert cqamp -= cqbuf[jptr]; 1114a151a66cSOllivier Robert cqbuf[jptr] = dtemp; 1115ea906c41SOllivier Robert cqamp += dtemp; 1116ea906c41SOllivier Robert csqamp -= csqbuf[kptr]; 1117ea906c41SOllivier Robert csqbuf[kptr] = dtemp; 1118ea906c41SOllivier Robert csqamp += dtemp; 1119ea906c41SOllivier Robert 1120ea906c41SOllivier Robert sp = &up->mitig[up->achan].wwv; 1121ea906c41SOllivier Robert sp->amp = sqrt(ciamp * ciamp + cqamp * cqamp) / SYNCYC; 11229c2daa00SOllivier Robert if (!(up->status & MSYNC)) 11232b15cb3dSCy Schubert wwv_qrz(peer, sp, (int)(pp->fudgetime1 * WWV_SEC)); 1124ea906c41SOllivier Robert 1125ea906c41SOllivier Robert /* 1126ea906c41SOllivier Robert * WWVH 1127ea906c41SOllivier Robert */ 1128a151a66cSOllivier Robert i = hsinptr; 1129a151a66cSOllivier Robert hsinptr = (hsinptr + IN1200) % 80; 1130ea906c41SOllivier Robert 1131ea906c41SOllivier Robert dtemp = sintab[i] * syncx / (MS / 2.); 1132ea906c41SOllivier Robert hiamp -= hibuf[jptr]; 1133a151a66cSOllivier Robert hibuf[jptr] = dtemp; 1134ea906c41SOllivier Robert hiamp += dtemp; 1135ea906c41SOllivier Robert hsiamp -= hsibuf[kptr]; 1136ea906c41SOllivier Robert hsibuf[kptr] = dtemp; 1137ea906c41SOllivier Robert hsiamp += dtemp; 1138ea906c41SOllivier Robert 1139a151a66cSOllivier Robert i = (i + 20) % 80; 1140ea906c41SOllivier Robert dtemp = sintab[i] * syncx / (MS / 2.); 1141ea906c41SOllivier Robert hqamp -= hqbuf[jptr]; 1142a151a66cSOllivier Robert hqbuf[jptr] = dtemp; 1143ea906c41SOllivier Robert hqamp += dtemp; 1144ea906c41SOllivier Robert hsqamp -= hsqbuf[kptr]; 1145ea906c41SOllivier Robert hsqbuf[kptr] = dtemp; 1146ea906c41SOllivier Robert hsqamp += dtemp; 1147ea906c41SOllivier Robert 1148ea906c41SOllivier Robert rp = &up->mitig[up->achan].wwvh; 1149ea906c41SOllivier Robert rp->amp = sqrt(hiamp * hiamp + hqamp * hqamp) / SYNCYC; 11509c2daa00SOllivier Robert if (!(up->status & MSYNC)) 11512b15cb3dSCy Schubert wwv_qrz(peer, rp, (int)(pp->fudgetime2 * WWV_SEC)); 1152a151a66cSOllivier Robert jptr = (jptr + 1) % SYNSIZ; 1153ea906c41SOllivier Robert kptr = (kptr + 1) % TCKSIZ; 1154a151a66cSOllivier Robert 11559c2daa00SOllivier Robert /* 11569c2daa00SOllivier Robert * The following section is called once per minute. It does 11579c2daa00SOllivier Robert * housekeeping and timeout functions and empties the dustbins. 11589c2daa00SOllivier Robert */ 1159a151a66cSOllivier Robert if (up->mphase == 0) { 1160a151a66cSOllivier Robert up->watch++; 11619c2daa00SOllivier Robert if (!(up->status & MSYNC)) { 1162a151a66cSOllivier Robert 1163a151a66cSOllivier Robert /* 11649c2daa00SOllivier Robert * If minute sync has not been acquired before 1165ea906c41SOllivier Robert * ACQSN timeout (6 min), or if no signal is 1166ea906c41SOllivier Robert * heard, the program cycles to the next 1167ea906c41SOllivier Robert * frequency and tries again. 1168a151a66cSOllivier Robert */ 1169ea906c41SOllivier Robert if (!wwv_newchan(peer)) 1170ea906c41SOllivier Robert up->watch = 0; 1171a151a66cSOllivier Robert } else { 1172a151a66cSOllivier Robert 1173a151a66cSOllivier Robert /* 11749c2daa00SOllivier Robert * If the leap bit is set, set the minute epoch 11759c2daa00SOllivier Robert * back one second so the station processes 11769c2daa00SOllivier Robert * don't miss a beat. 1177a151a66cSOllivier Robert */ 11789c2daa00SOllivier Robert if (up->status & LEPSEC) { 11792b15cb3dSCy Schubert up->mphase -= WWV_SEC; 11809c2daa00SOllivier Robert if (up->mphase < 0) 11812b15cb3dSCy Schubert up->mphase += WWV_MIN; 11829c2daa00SOllivier Robert } 11839c2daa00SOllivier Robert } 11849c2daa00SOllivier Robert } 1185a151a66cSOllivier Robert 1186a151a66cSOllivier Robert /* 11879c2daa00SOllivier Robert * When the channel metric reaches threshold and the second 11889c2daa00SOllivier Robert * counter matches the minute epoch within the second, the 11899c2daa00SOllivier Robert * driver has synchronized to the station. The second number is 11909c2daa00SOllivier Robert * the remaining seconds until the next minute epoch, while the 11919c2daa00SOllivier Robert * sync epoch is zero. Watch out for the first second; if 11929c2daa00SOllivier Robert * already synchronized to the second, the buffered sync epoch 11939c2daa00SOllivier Robert * must be set. 1194ea906c41SOllivier Robert * 1195ea906c41SOllivier Robert * Note the guard interval is 200 ms; if for some reason the 1196ea906c41SOllivier Robert * clock drifts more than that, it might wind up in the wrong 1197ea906c41SOllivier Robert * second. If the maximum frequency error is not more than about 1198ea906c41SOllivier Robert * 1 PPM, the clock can go as much as two days while still in 1199ea906c41SOllivier Robert * the same second. 1200a151a66cSOllivier Robert */ 12019c2daa00SOllivier Robert if (up->status & MSYNC) { 12029c2daa00SOllivier Robert wwv_epoch(peer); 1203ea906c41SOllivier Robert } else if (up->sptr != NULL) { 1204ea906c41SOllivier Robert sp = up->sptr; 12052b15cb3dSCy Schubert if (sp->metric >= TTHR && epoch == sp->mepoch % WWV_SEC) 12062b15cb3dSCy Schubert { 12072b15cb3dSCy Schubert up->rsec = (60 - sp->mepoch / WWV_SEC) % 60; 12089c2daa00SOllivier Robert up->rphase = 0; 12099c2daa00SOllivier Robert up->status |= MSYNC; 12109c2daa00SOllivier Robert up->watch = 0; 1211a151a66cSOllivier Robert if (!(up->status & SSYNC)) 12129c2daa00SOllivier Robert up->repoch = up->yepoch = epoch; 12139c2daa00SOllivier Robert else 12149c2daa00SOllivier Robert up->repoch = up->yepoch; 1215ea906c41SOllivier Robert 1216a151a66cSOllivier Robert } 1217a151a66cSOllivier Robert } 1218a151a66cSOllivier Robert 1219a151a66cSOllivier Robert /* 12209c2daa00SOllivier Robert * The second sync pulse is extracted using 5-ms (40 sample) FIR 12219c2daa00SOllivier Robert * matched filters at 1000 Hz for WWV or 1200 Hz for WWVH. This 12229c2daa00SOllivier Robert * pulse is used for the most precise synchronization, since if 12239c2daa00SOllivier Robert * provides a resolution of one sample (125 us). The filters run 12249c2daa00SOllivier Robert * only if the station has been reliably determined. 1225a151a66cSOllivier Robert */ 12262b15cb3dSCy Schubert if (up->status & SELV) 1227ea906c41SOllivier Robert mfsync = sqrt(csiamp * csiamp + csqamp * csqamp) / 1228ea906c41SOllivier Robert TCKCYC; 12292b15cb3dSCy Schubert else if (up->status & SELH) 1230ea906c41SOllivier Robert mfsync = sqrt(hsiamp * hsiamp + hsqamp * hsqamp) / 1231ea906c41SOllivier Robert TCKCYC; 12322b15cb3dSCy Schubert else 1233ea906c41SOllivier Robert mfsync = 0; 1234a151a66cSOllivier Robert 1235a151a66cSOllivier Robert /* 12369c2daa00SOllivier Robert * Enhance the seconds sync pulse using a 1-s (8000-sample) comb 12379c2daa00SOllivier Robert * filter. Correct for the FIR matched filter delay, which is 5 12389c2daa00SOllivier Robert * ms for both the WWV and WWVH filters, and also for the 12399c2daa00SOllivier Robert * propagation delay. Once each second look for second sync. If 12409c2daa00SOllivier Robert * not in minute sync, fiddle the codec gain. Note the SNR is 1241ea906c41SOllivier Robert * computed from the maximum sample and the envelope of the 1242ea906c41SOllivier Robert * sample 6 ms before it, so if we slip more than a cycle the 1243ea906c41SOllivier Robert * SNR should plummet. The signal is scaled to produce unit 1244ea906c41SOllivier Robert * energy at the maximum value. 1245a151a66cSOllivier Robert */ 12469c2daa00SOllivier Robert dtemp = (epobuf[epoch] += (mfsync - epobuf[epoch]) / 12479c2daa00SOllivier Robert up->avgint); 12489c2daa00SOllivier Robert if (dtemp > epomax) { 1249ea906c41SOllivier Robert int j; 1250ea906c41SOllivier Robert 12519c2daa00SOllivier Robert epomax = dtemp; 12529c2daa00SOllivier Robert epopos = epoch; 1253ea906c41SOllivier Robert j = epoch - 6 * MS; 1254ea906c41SOllivier Robert if (j < 0) 12552b15cb3dSCy Schubert j += WWV_SEC; 1256ea906c41SOllivier Robert nxtmax = fabs(epobuf[j]); 12579c2daa00SOllivier Robert } 12589c2daa00SOllivier Robert if (epoch == 0) { 1259a151a66cSOllivier Robert up->epomax = epomax; 1260ea906c41SOllivier Robert up->eposnr = wwv_snr(epomax, nxtmax); 12612b15cb3dSCy Schubert epopos -= TCKCYC * MS; 12629c2daa00SOllivier Robert if (epopos < 0) 12632b15cb3dSCy Schubert epopos += WWV_SEC; 12649c2daa00SOllivier Robert wwv_endpoc(peer, epopos); 12659c2daa00SOllivier Robert if (!(up->status & SSYNC)) 12669c2daa00SOllivier Robert up->alarm |= SYNERR; 1267a151a66cSOllivier Robert epomax = 0; 1268a151a66cSOllivier Robert if (!(up->status & MSYNC)) 1269a151a66cSOllivier Robert wwv_gain(peer); 1270a151a66cSOllivier Robert } 1271a151a66cSOllivier Robert } 1272a151a66cSOllivier Robert 1273a151a66cSOllivier Robert 1274a151a66cSOllivier Robert /* 1275a151a66cSOllivier Robert * wwv_qrz - identify and acquire WWV/WWVH minute sync pulse 1276a151a66cSOllivier Robert * 1277a151a66cSOllivier Robert * This routine implements a virtual station process used to acquire 1278a151a66cSOllivier Robert * minute sync and to mitigate among the ten frequency and station 12799c2daa00SOllivier Robert * combinations. During minute sync acquisition the process probes each 1280ea906c41SOllivier Robert * frequency and station in turn for the minute pulse, which 1281ea906c41SOllivier Robert * involves searching through the entire 480,000-sample minute. The 1282ea906c41SOllivier Robert * process finds the maximum signal and RMS noise plus signal. Then, the 1283ea906c41SOllivier Robert * actual noise is determined by subtracting the energy of the matched 1284ea906c41SOllivier Robert * filter. 1285a151a66cSOllivier Robert * 1286a151a66cSOllivier Robert * Students of radar receiver technology will discover this algorithm 1287ea906c41SOllivier Robert * amounts to a range-gate discriminator. A valid pulse must have peak 1288ea906c41SOllivier Robert * amplitude at least QTHR (2500) and SNR at least QSNR (20) dB and the 12899c2daa00SOllivier Robert * difference between the current and previous epoch must be less than 1290ea906c41SOllivier Robert * AWND (20 ms). Note that the discriminator peak occurs about 800 ms 1291ea906c41SOllivier Robert * into the second, so the timing is retarded to the previous second 1292ea906c41SOllivier Robert * epoch. 1293a151a66cSOllivier Robert */ 1294a151a66cSOllivier Robert static void 1295a151a66cSOllivier Robert wwv_qrz( 1296a151a66cSOllivier Robert struct peer *peer, /* peer structure pointer */ 1297a151a66cSOllivier Robert struct sync *sp, /* sync channel structure */ 12989c2daa00SOllivier Robert int pdelay /* propagation delay (samples) */ 1299a151a66cSOllivier Robert ) 1300a151a66cSOllivier Robert { 1301a151a66cSOllivier Robert struct refclockproc *pp; 1302a151a66cSOllivier Robert struct wwvunit *up; 13032b15cb3dSCy Schubert char tbuf[TBUF]; /* monitor buffer */ 1304ea906c41SOllivier Robert long epoch; 1305a151a66cSOllivier Robert 1306a151a66cSOllivier Robert pp = peer->procptr; 13072b15cb3dSCy Schubert up = pp->unitptr; 1308a151a66cSOllivier Robert 1309a151a66cSOllivier Robert /* 1310ea906c41SOllivier Robert * Find the sample with peak amplitude, which defines the minute 1311ea906c41SOllivier Robert * epoch. Accumulate all samples to determine the total noise 1312ea906c41SOllivier Robert * energy. 1313a151a66cSOllivier Robert */ 1314ea906c41SOllivier Robert epoch = up->mphase - pdelay - SYNSIZ; 13159c2daa00SOllivier Robert if (epoch < 0) 13162b15cb3dSCy Schubert epoch += WWV_MIN; 1317ea906c41SOllivier Robert if (sp->amp > sp->maxeng) { 1318ea906c41SOllivier Robert sp->maxeng = sp->amp; 13199c2daa00SOllivier Robert sp->pos = epoch; 1320a151a66cSOllivier Robert } 1321ea906c41SOllivier Robert sp->noieng += sp->amp; 1322a151a66cSOllivier Robert 1323a151a66cSOllivier Robert /* 1324ea906c41SOllivier Robert * At the end of the minute, determine the epoch of the minute 1325ea906c41SOllivier Robert * sync pulse, as well as the difference between the current and 1326ea906c41SOllivier Robert * previous epoches due to the intrinsic frequency error plus 1327ea906c41SOllivier Robert * jitter. When calculating the SNR, subtract the pulse energy 1328ea906c41SOllivier Robert * from the total noise energy and then normalize. 1329a151a66cSOllivier Robert */ 13309c2daa00SOllivier Robert if (up->mphase == 0) { 1331ea906c41SOllivier Robert sp->synmax = sp->maxeng; 1332ea906c41SOllivier Robert sp->synsnr = wwv_snr(sp->synmax, (sp->noieng - 13332b15cb3dSCy Schubert sp->synmax) / WWV_MIN); 1334ea906c41SOllivier Robert if (sp->count == 0) 1335ea906c41SOllivier Robert sp->lastpos = sp->pos; 13362b15cb3dSCy Schubert epoch = (sp->pos - sp->lastpos) % WWV_MIN; 1337ea906c41SOllivier Robert sp->reach <<= 1; 1338ea906c41SOllivier Robert if (sp->reach & (1 << AMAX)) 1339ea906c41SOllivier Robert sp->count--; 1340ea906c41SOllivier Robert if (sp->synmax > ATHR && sp->synsnr > ASNR) { 13412b15cb3dSCy Schubert if (labs(epoch) < AWND * MS) { 1342ea906c41SOllivier Robert sp->reach |= 1; 1343a151a66cSOllivier Robert sp->count++; 1344ea906c41SOllivier Robert sp->mepoch = sp->lastpos = sp->pos; 1345ea906c41SOllivier Robert } else if (sp->count == 1) { 1346ea906c41SOllivier Robert sp->lastpos = sp->pos; 1347a151a66cSOllivier Robert } 1348a151a66cSOllivier Robert } 1349ea906c41SOllivier Robert if (up->watch > ACQSN) 1350ea906c41SOllivier Robert sp->metric = 0; 1351ea906c41SOllivier Robert else 1352ea906c41SOllivier Robert sp->metric = wwv_metric(sp); 13539c2daa00SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4) { 13542b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 13552b15cb3dSCy Schubert "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %ld %ld", 1356ea906c41SOllivier Robert up->status, up->gain, sp->refid, 1357ea906c41SOllivier Robert sp->reach & 0xffff, sp->metric, sp->synmax, 13582b15cb3dSCy Schubert sp->synsnr, sp->pos % WWV_SEC, epoch); 1359a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1360a151a66cSOllivier Robert #ifdef DEBUG 1361a151a66cSOllivier Robert if (debug) 1362a151a66cSOllivier Robert printf("%s\n", tbuf); 1363ea906c41SOllivier Robert #endif /* DEBUG */ 1364a151a66cSOllivier Robert } 1365ea906c41SOllivier Robert sp->maxeng = sp->noieng = 0; 1366a151a66cSOllivier Robert } 1367a151a66cSOllivier Robert } 1368a151a66cSOllivier Robert 1369a151a66cSOllivier Robert 1370a151a66cSOllivier Robert /* 13719c2daa00SOllivier Robert * wwv_endpoc - identify and acquire second sync pulse 1372a151a66cSOllivier Robert * 13739c2daa00SOllivier Robert * This routine is called at the end of the second sync interval. It 1374ea906c41SOllivier Robert * determines the second sync epoch position within the second and 13759c2daa00SOllivier Robert * disciplines the sample clock using a frequency-lock loop (FLL). 1376a151a66cSOllivier Robert * 13779c2daa00SOllivier Robert * Second sync is determined in the RF input routine as the maximum 1378a151a66cSOllivier Robert * over all 8000 samples in the second comb filter. To assure accurate 1379a151a66cSOllivier Robert * and reliable time and frequency discipline, this routine performs a 13809c2daa00SOllivier Robert * great deal of heavy-handed heuristic data filtering and grooming. 1381a151a66cSOllivier Robert */ 1382a151a66cSOllivier Robert static void 1383a151a66cSOllivier Robert wwv_endpoc( 1384a151a66cSOllivier Robert struct peer *peer, /* peer structure pointer */ 1385a151a66cSOllivier Robert int epopos /* epoch max position */ 1386a151a66cSOllivier Robert ) 1387a151a66cSOllivier Robert { 1388a151a66cSOllivier Robert struct refclockproc *pp; 1389a151a66cSOllivier Robert struct wwvunit *up; 1390a151a66cSOllivier Robert static int epoch_mf[3]; /* epoch median filter */ 1391ea906c41SOllivier Robert static int tepoch; /* current second epoch */ 1392a151a66cSOllivier Robert static int xepoch; /* last second epoch */ 1393ea906c41SOllivier Robert static int zepoch; /* last run epoch */ 1394ea906c41SOllivier Robert static int zcount; /* last run end time */ 1395ea906c41SOllivier Robert static int scount; /* seconds counter */ 13969c2daa00SOllivier Robert static int syncnt; /* run length counter */ 13979c2daa00SOllivier Robert static int maxrun; /* longest run length */ 1398ea906c41SOllivier Robert static int mepoch; /* longest run end epoch */ 1399ea906c41SOllivier Robert static int mcount; /* longest run end time */ 1400a151a66cSOllivier Robert static int avgcnt; /* averaging interval counter */ 1401a151a66cSOllivier Robert static int avginc; /* averaging ratchet */ 1402a151a66cSOllivier Robert static int iniflg; /* initialization flag */ 14032b15cb3dSCy Schubert char tbuf[TBUF]; /* monitor buffer */ 1404a151a66cSOllivier Robert double dtemp; 14059c2daa00SOllivier Robert int tmp2; 1406a151a66cSOllivier Robert 1407a151a66cSOllivier Robert pp = peer->procptr; 14082b15cb3dSCy Schubert up = pp->unitptr; 1409a151a66cSOllivier Robert if (!iniflg) { 1410a151a66cSOllivier Robert iniflg = 1; 14112b15cb3dSCy Schubert ZERO(epoch_mf); 1412a151a66cSOllivier Robert } 1413a151a66cSOllivier Robert 1414a151a66cSOllivier Robert /* 1415ea906c41SOllivier Robert * If the signal amplitude or SNR fall below thresholds, dim the 1416ea906c41SOllivier Robert * second sync lamp and wait for hotter ions. If no stations are 1417ea906c41SOllivier Robert * heard, we are either in a probe cycle or the ions are really 1418ea906c41SOllivier Robert * cold. 1419ea906c41SOllivier Robert */ 1420ea906c41SOllivier Robert scount++; 1421ea906c41SOllivier Robert if (up->epomax < STHR || up->eposnr < SSNR) { 1422ea906c41SOllivier Robert up->status &= ~(SSYNC | FGATE); 1423ea906c41SOllivier Robert avgcnt = syncnt = maxrun = 0; 1424ea906c41SOllivier Robert return; 1425ea906c41SOllivier Robert } 1426ea906c41SOllivier Robert if (!(up->status & (SELV | SELH))) 1427ea906c41SOllivier Robert return; 1428ea906c41SOllivier Robert 1429ea906c41SOllivier Robert /* 1430a151a66cSOllivier Robert * A three-stage median filter is used to help denoise the 14319c2daa00SOllivier Robert * second sync pulse. The median sample becomes the candidate 14329c2daa00SOllivier Robert * epoch. 1433a151a66cSOllivier Robert */ 1434a151a66cSOllivier Robert epoch_mf[2] = epoch_mf[1]; 1435a151a66cSOllivier Robert epoch_mf[1] = epoch_mf[0]; 1436a151a66cSOllivier Robert epoch_mf[0] = epopos; 1437a151a66cSOllivier Robert if (epoch_mf[0] > epoch_mf[1]) { 14389c2daa00SOllivier Robert if (epoch_mf[1] > epoch_mf[2]) 1439ea906c41SOllivier Robert tepoch = epoch_mf[1]; /* 0 1 2 */ 14409c2daa00SOllivier Robert else if (epoch_mf[2] > epoch_mf[0]) 1441ea906c41SOllivier Robert tepoch = epoch_mf[0]; /* 2 0 1 */ 14429c2daa00SOllivier Robert else 1443ea906c41SOllivier Robert tepoch = epoch_mf[2]; /* 0 2 1 */ 1444a151a66cSOllivier Robert } else { 14459c2daa00SOllivier Robert if (epoch_mf[1] < epoch_mf[2]) 1446ea906c41SOllivier Robert tepoch = epoch_mf[1]; /* 2 1 0 */ 14479c2daa00SOllivier Robert else if (epoch_mf[2] < epoch_mf[0]) 1448ea906c41SOllivier Robert tepoch = epoch_mf[0]; /* 1 0 2 */ 14499c2daa00SOllivier Robert else 1450ea906c41SOllivier Robert tepoch = epoch_mf[2]; /* 1 2 0 */ 1451a151a66cSOllivier Robert } 1452a151a66cSOllivier Robert 14539c2daa00SOllivier Robert 14549c2daa00SOllivier Robert /* 14559c2daa00SOllivier Robert * If the epoch candidate is the same as the last one, increment 1456ea906c41SOllivier Robert * the run counter. If not, save the length, epoch and end 1457ea906c41SOllivier Robert * time of the current run for use later and reset the counter. 1458ea906c41SOllivier Robert * The epoch is considered valid if the run is at least SCMP 1459ea906c41SOllivier Robert * (10) s, the minute is synchronized and the interval since the 1460ea906c41SOllivier Robert * last epoch is not greater than the averaging interval. Thus, 1461ea906c41SOllivier Robert * after a long absence, the program will wait a full averaging 1462ea906c41SOllivier Robert * interval while the comb filter charges up and noise 1463ea906c41SOllivier Robert * dissapates.. 14649c2daa00SOllivier Robert */ 14652b15cb3dSCy Schubert tmp2 = (tepoch - xepoch) % WWV_SEC; 14669c2daa00SOllivier Robert if (tmp2 == 0) { 14679c2daa00SOllivier Robert syncnt++; 1468ea906c41SOllivier Robert if (syncnt > SCMP && up->status & MSYNC && (up->status & 1469ea906c41SOllivier Robert FGATE || scount - zcount <= up->avgint)) { 1470ea906c41SOllivier Robert up->status |= SSYNC; 1471ea906c41SOllivier Robert up->yepoch = tepoch; 1472a151a66cSOllivier Robert } 1473ea906c41SOllivier Robert } else if (syncnt >= maxrun) { 1474ea906c41SOllivier Robert maxrun = syncnt; 1475ea906c41SOllivier Robert mcount = scount; 1476ea906c41SOllivier Robert mepoch = xepoch; 14779c2daa00SOllivier Robert syncnt = 0; 14789c2daa00SOllivier Robert } 14792b15cb3dSCy Schubert if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & 14802b15cb3dSCy Schubert MSYNC)) { 14812b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 1482ea906c41SOllivier Robert "wwv1 %04x %3d %4d %5.0f %5.1f %5d %4d %4d %4d", 1483ea906c41SOllivier Robert up->status, up->gain, tepoch, up->epomax, 1484ea906c41SOllivier Robert up->eposnr, tmp2, avgcnt, syncnt, 1485ea906c41SOllivier Robert maxrun); 1486a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1487a151a66cSOllivier Robert #ifdef DEBUG 1488a151a66cSOllivier Robert if (debug) 1489a151a66cSOllivier Robert printf("%s\n", tbuf); 1490a151a66cSOllivier Robert #endif /* DEBUG */ 1491a151a66cSOllivier Robert } 1492ea906c41SOllivier Robert avgcnt++; 14939c2daa00SOllivier Robert if (avgcnt < up->avgint) { 1494ea906c41SOllivier Robert xepoch = tepoch; 14959c2daa00SOllivier Robert return; 14969c2daa00SOllivier Robert } 14979c2daa00SOllivier Robert 14989c2daa00SOllivier Robert /* 1499ea906c41SOllivier Robert * The sample clock frequency is disciplined using a first-order 1500ea906c41SOllivier Robert * feedback loop with time constant consistent with the Allan 1501ea906c41SOllivier Robert * intercept of typical computer clocks. During each averaging 1502ea906c41SOllivier Robert * interval the candidate epoch at the end of the longest run is 1503ea906c41SOllivier Robert * determined. If the longest run is zero, all epoches in the 1504ea906c41SOllivier Robert * interval are different, so the candidate epoch is the current 1505ea906c41SOllivier Robert * epoch. The frequency update is computed from the candidate 1506ea906c41SOllivier Robert * epoch difference (125-us units) and time difference (seconds) 1507ea906c41SOllivier Robert * between updates. 15089c2daa00SOllivier Robert */ 1509ea906c41SOllivier Robert if (syncnt >= maxrun) { 15109c2daa00SOllivier Robert maxrun = syncnt; 1511ea906c41SOllivier Robert mcount = scount; 15129c2daa00SOllivier Robert mepoch = xepoch; 15139c2daa00SOllivier Robert } 1514ea906c41SOllivier Robert xepoch = tepoch; 1515ea906c41SOllivier Robert if (maxrun == 0) { 1516ea906c41SOllivier Robert mepoch = tepoch; 1517ea906c41SOllivier Robert mcount = scount; 15189c2daa00SOllivier Robert } 15199c2daa00SOllivier Robert 15209c2daa00SOllivier Robert /* 1521ea906c41SOllivier Robert * The master clock runs at the codec sample frequency of 8000 1522ea906c41SOllivier Robert * Hz, so the intrinsic time resolution is 125 us. The frequency 1523ea906c41SOllivier Robert * resolution ranges from 18 PPM at the minimum averaging 1524ea906c41SOllivier Robert * interval of 8 s to 0.12 PPM at the maximum interval of 1024 1525ea906c41SOllivier Robert * s. An offset update is determined at the end of the longest 1526ea906c41SOllivier Robert * run in each averaging interval. The frequency adjustment is 1527ea906c41SOllivier Robert * computed from the difference between offset updates and the 1528ea906c41SOllivier Robert * interval between them. 15299c2daa00SOllivier Robert * 1530ea906c41SOllivier Robert * The maximum frequency adjustment ranges from 187 PPM at the 1531ea906c41SOllivier Robert * minimum interval to 1.5 PPM at the maximum. If the adjustment 1532ea906c41SOllivier Robert * exceeds the maximum, the update is discarded and the 1533ea906c41SOllivier Robert * hysteresis counter is decremented. Otherwise, the frequency 1534ea906c41SOllivier Robert * is incremented by the adjustment, but clamped to the maximum 1535ea906c41SOllivier Robert * 187.5 PPM. If the update is less than half the maximum, the 1536ea906c41SOllivier Robert * hysteresis counter is incremented. If the counter increments 1537ea906c41SOllivier Robert * to +3, the averaging interval is doubled and the counter set 1538ea906c41SOllivier Robert * to zero; if it decrements to -3, the interval is halved and 1539ea906c41SOllivier Robert * the counter set to zero. 15409c2daa00SOllivier Robert */ 15412b15cb3dSCy Schubert dtemp = (mepoch - zepoch) % WWV_SEC; 15429c2daa00SOllivier Robert if (up->status & FGATE) { 15432b15cb3dSCy Schubert if (fabs(dtemp) < MAXFREQ * MINAVG) { 1544ea906c41SOllivier Robert up->freq += (dtemp / 2.) / ((mcount - zcount) * 1545ea906c41SOllivier Robert FCONST); 1546a151a66cSOllivier Robert if (up->freq > MAXFREQ) 1547a151a66cSOllivier Robert up->freq = MAXFREQ; 1548a151a66cSOllivier Robert else if (up->freq < -MAXFREQ) 1549a151a66cSOllivier Robert up->freq = -MAXFREQ; 15502b15cb3dSCy Schubert if (fabs(dtemp) < MAXFREQ * MINAVG / 2.) { 15519c2daa00SOllivier Robert if (avginc < 3) { 1552a151a66cSOllivier Robert avginc++; 1553a151a66cSOllivier Robert } else { 1554a151a66cSOllivier Robert if (up->avgint < MAXAVG) { 15559c2daa00SOllivier Robert up->avgint <<= 1; 15569c2daa00SOllivier Robert avginc = 0; 15579c2daa00SOllivier Robert } 15589c2daa00SOllivier Robert } 15599c2daa00SOllivier Robert } 15609c2daa00SOllivier Robert } else { 15619c2daa00SOllivier Robert if (avginc > -3) { 15629c2daa00SOllivier Robert avginc--; 15639c2daa00SOllivier Robert } else { 15649c2daa00SOllivier Robert if (up->avgint > MINAVG) { 15659c2daa00SOllivier Robert up->avgint >>= 1; 15669c2daa00SOllivier Robert avginc = 0; 15679c2daa00SOllivier Robert } 15689c2daa00SOllivier Robert } 15699c2daa00SOllivier Robert } 15709c2daa00SOllivier Robert } 15719c2daa00SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4) { 15722b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 1573ea906c41SOllivier Robert "wwv2 %04x %5.0f %5.1f %5d %4d %4d %4d %4.0f %7.2f", 1574ea906c41SOllivier Robert up->status, up->epomax, up->eposnr, mepoch, 1575ea906c41SOllivier Robert up->avgint, maxrun, mcount - zcount, dtemp, 15762b15cb3dSCy Schubert up->freq * 1e6 / WWV_SEC); 15779c2daa00SOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1578a151a66cSOllivier Robert #ifdef DEBUG 1579a151a66cSOllivier Robert if (debug) 1580a151a66cSOllivier Robert printf("%s\n", tbuf); 1581a151a66cSOllivier Robert #endif /* DEBUG */ 1582a151a66cSOllivier Robert } 1583ea906c41SOllivier Robert 1584ea906c41SOllivier Robert /* 1585ea906c41SOllivier Robert * This is a valid update; set up for the next interval. 1586ea906c41SOllivier Robert */ 15879c2daa00SOllivier Robert up->status |= FGATE; 15889c2daa00SOllivier Robert zepoch = mepoch; 1589ea906c41SOllivier Robert zcount = mcount; 15909c2daa00SOllivier Robert avgcnt = syncnt = maxrun = 0; 1591a151a66cSOllivier Robert } 1592a151a66cSOllivier Robert 1593a151a66cSOllivier Robert 1594a151a66cSOllivier Robert /* 15959c2daa00SOllivier Robert * wwv_epoch - epoch scanner 1596a151a66cSOllivier Robert * 1597ea906c41SOllivier Robert * This routine extracts data signals from the 100-Hz subcarrier. It 1598ea906c41SOllivier Robert * scans the receiver second epoch to determine the signal amplitudes 1599ea906c41SOllivier Robert * and pulse timings. Receiver synchronization is determined by the 1600ea906c41SOllivier Robert * minute sync pulse detected in the wwv_rf() routine and the second 1601ea906c41SOllivier Robert * sync pulse detected in the wwv_epoch() routine. The transmitted 1602ea906c41SOllivier Robert * signals are delayed by the propagation delay, receiver delay and 1603ea906c41SOllivier Robert * filter delay of this program. Delay corrections are introduced 1604ea906c41SOllivier Robert * separately for WWV and WWVH. 1605a151a66cSOllivier Robert * 1606a151a66cSOllivier Robert * Most communications radios use a highpass filter in the audio stages, 1607a151a66cSOllivier Robert * which can do nasty things to the subcarrier phase relative to the 1608a151a66cSOllivier Robert * sync pulses. Therefore, the data subcarrier reference phase is 1609a151a66cSOllivier Robert * disciplined using the hardlimited quadrature-phase signal sampled at 1610a151a66cSOllivier Robert * the same time as the in-phase signal. The phase tracking loop uses 1611a151a66cSOllivier Robert * phase adjustments of plus-minus one sample (125 us). 1612a151a66cSOllivier Robert */ 1613a151a66cSOllivier Robert static void 1614a151a66cSOllivier Robert wwv_epoch( 1615a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 1616a151a66cSOllivier Robert ) 1617a151a66cSOllivier Robert { 1618a151a66cSOllivier Robert struct refclockproc *pp; 1619a151a66cSOllivier Robert struct wwvunit *up; 1620a151a66cSOllivier Robert struct chan *cp; 1621ea906c41SOllivier Robert static double sigmin, sigzer, sigone, engmax, engmin; 1622a151a66cSOllivier Robert 1623a151a66cSOllivier Robert pp = peer->procptr; 16242b15cb3dSCy Schubert up = pp->unitptr; 1625a151a66cSOllivier Robert 1626a151a66cSOllivier Robert /* 1627ea906c41SOllivier Robert * Find the maximum minute sync pulse energy for both the 1628ea906c41SOllivier Robert * WWV and WWVH stations. This will be used later for channel 1629ea906c41SOllivier Robert * and station mitigation. Also set the seconds epoch at 800 ms 1630ea906c41SOllivier Robert * well before the end of the second to make sure we never set 1631ea906c41SOllivier Robert * the epoch backwards. 1632a151a66cSOllivier Robert */ 16339c2daa00SOllivier Robert cp = &up->mitig[up->achan]; 1634ea906c41SOllivier Robert if (cp->wwv.amp > cp->wwv.syneng) 1635ea906c41SOllivier Robert cp->wwv.syneng = cp->wwv.amp; 1636ea906c41SOllivier Robert if (cp->wwvh.amp > cp->wwvh.syneng) 1637ea906c41SOllivier Robert cp->wwvh.syneng = cp->wwvh.amp; 1638ea906c41SOllivier Robert if (up->rphase == 800 * MS) 1639ea906c41SOllivier Robert up->repoch = up->yepoch; 1640a151a66cSOllivier Robert 16419c2daa00SOllivier Robert /* 1642ea906c41SOllivier Robert * Use the signal amplitude at epoch 15 ms as the noise floor. 1643ea906c41SOllivier Robert * This gives a guard time of +-15 ms from the beginning of the 1644ea906c41SOllivier Robert * second until the second pulse rises at 30 ms. There is a 16459c2daa00SOllivier Robert * compromise here; we want to delay the sample as long as 16469c2daa00SOllivier Robert * possible to give the radio time to change frequency and the 16479c2daa00SOllivier Robert * AGC to stabilize, but as early as possible if the second 16489c2daa00SOllivier Robert * epoch is not exact. 16499c2daa00SOllivier Robert */ 1650ea906c41SOllivier Robert if (up->rphase == 15 * MS) 1651ea906c41SOllivier Robert sigmin = sigzer = sigone = up->irig; 1652a151a66cSOllivier Robert 1653a151a66cSOllivier Robert /* 1654ea906c41SOllivier Robert * Latch the data signal at 200 ms. Keep this around until the 1655ea906c41SOllivier Robert * end of the second. Use the signal energy as the peak to 1656ea906c41SOllivier Robert * compute the SNR. Use the Q sample to adjust the 100-Hz 1657ea906c41SOllivier Robert * reference oscillator phase. 1658a151a66cSOllivier Robert */ 1659ea906c41SOllivier Robert if (up->rphase == 200 * MS) { 1660ea906c41SOllivier Robert sigzer = up->irig; 1661ea906c41SOllivier Robert engmax = sqrt(up->irig * up->irig + up->qrig * 1662ea906c41SOllivier Robert up->qrig); 16639c2daa00SOllivier Robert up->datpha = up->qrig / up->avgint; 1664a151a66cSOllivier Robert if (up->datpha >= 0) { 1665a151a66cSOllivier Robert up->datapt++; 1666a151a66cSOllivier Robert if (up->datapt >= 80) 1667a151a66cSOllivier Robert up->datapt -= 80; 1668a151a66cSOllivier Robert } else { 1669a151a66cSOllivier Robert up->datapt--; 1670a151a66cSOllivier Robert if (up->datapt < 0) 1671a151a66cSOllivier Robert up->datapt += 80; 1672a151a66cSOllivier Robert } 1673ea906c41SOllivier Robert } 1674ea906c41SOllivier Robert 1675a151a66cSOllivier Robert 1676a151a66cSOllivier Robert /* 1677ea906c41SOllivier Robert * Latch the data signal at 500 ms. Keep this around until the 1678ea906c41SOllivier Robert * end of the second. 1679a151a66cSOllivier Robert */ 1680ea906c41SOllivier Robert else if (up->rphase == 500 * MS) 1681ea906c41SOllivier Robert sigone = up->irig; 1682a151a66cSOllivier Robert 1683a151a66cSOllivier Robert /* 16849c2daa00SOllivier Robert * At the end of the second crank the clock state machine and 16859c2daa00SOllivier Robert * adjust the codec gain. Note the epoch is buffered from the 16869c2daa00SOllivier Robert * center of the second in order to avoid jitter while the 16879c2daa00SOllivier Robert * seconds synch is diddling the epoch. Then, determine the true 16889c2daa00SOllivier Robert * offset and update the median filter in the driver interface. 16899c2daa00SOllivier Robert * 1690ea906c41SOllivier Robert * Use the energy at the end of the second as the noise to 1691ea906c41SOllivier Robert * compute the SNR for the data pulse. This gives a better 1692ea906c41SOllivier Robert * measurement than the beginning of the second, especially when 1693ea906c41SOllivier Robert * returning from the probe channel. This gives a guard time of 1694ea906c41SOllivier Robert * 30 ms from the decay of the longest pulse to the rise of the 1695ea906c41SOllivier Robert * next pulse. 1696a151a66cSOllivier Robert */ 1697a151a66cSOllivier Robert up->rphase++; 16982b15cb3dSCy Schubert if (up->mphase % WWV_SEC == up->repoch) { 1699ea906c41SOllivier Robert up->status &= ~(DGATE | BGATE); 1700ea906c41SOllivier Robert engmin = sqrt(up->irig * up->irig + up->qrig * 1701ea906c41SOllivier Robert up->qrig); 1702ea906c41SOllivier Robert up->datsig = engmax; 1703ea906c41SOllivier Robert up->datsnr = wwv_snr(engmax, engmin); 1704ea906c41SOllivier Robert 1705ea906c41SOllivier Robert /* 1706ea906c41SOllivier Robert * If the amplitude or SNR is below threshold, average a 1707ea906c41SOllivier Robert * 0 in the the integrators; otherwise, average the 1708ea906c41SOllivier Robert * bipolar signal. This is done to avoid noise polution. 1709ea906c41SOllivier Robert */ 1710ea906c41SOllivier Robert if (engmax < DTHR || up->datsnr < DSNR) { 1711ea906c41SOllivier Robert up->status |= DGATE; 1712ea906c41SOllivier Robert wwv_rsec(peer, 0); 1713ea906c41SOllivier Robert } else { 1714ea906c41SOllivier Robert sigzer -= sigone; 1715ea906c41SOllivier Robert sigone -= sigmin; 1716ea906c41SOllivier Robert wwv_rsec(peer, sigone - sigzer); 1717ea906c41SOllivier Robert } 1718ea906c41SOllivier Robert if (up->status & (DGATE | BGATE)) 1719ea906c41SOllivier Robert up->errcnt++; 1720ea906c41SOllivier Robert if (up->errcnt > MAXERR) 1721ea906c41SOllivier Robert up->alarm |= LOWERR; 1722a151a66cSOllivier Robert wwv_gain(peer); 1723ea906c41SOllivier Robert cp = &up->mitig[up->achan]; 1724ea906c41SOllivier Robert cp->wwv.syneng = 0; 1725ea906c41SOllivier Robert cp->wwvh.syneng = 0; 1726ea906c41SOllivier Robert up->rphase = 0; 1727a151a66cSOllivier Robert } 1728a151a66cSOllivier Robert } 1729a151a66cSOllivier Robert 1730a151a66cSOllivier Robert 1731a151a66cSOllivier Robert /* 1732a151a66cSOllivier Robert * wwv_rsec - process receiver second 1733a151a66cSOllivier Robert * 1734a151a66cSOllivier Robert * This routine is called at the end of each receiver second to 1735a151a66cSOllivier Robert * implement the per-second state machine. The machine assembles BCD 1736a151a66cSOllivier Robert * digit bits, decodes miscellaneous bits and dances the leap seconds. 1737a151a66cSOllivier Robert * 1738a151a66cSOllivier Robert * Normally, the minute has 60 seconds numbered 0-59. If the leap 1739a151a66cSOllivier Robert * warning bit is set, the last minute (1439) of 30 June (day 181 or 182 1740a151a66cSOllivier Robert * for leap years) or 31 December (day 365 or 366 for leap years) is 1741a151a66cSOllivier Robert * augmented by one second numbered 60. This is accomplished by 1742a151a66cSOllivier Robert * extending the minute interval by one second and teaching the state 17439c2daa00SOllivier Robert * machine to ignore it. 1744a151a66cSOllivier Robert */ 1745a151a66cSOllivier Robert static void 1746a151a66cSOllivier Robert wwv_rsec( 1747a151a66cSOllivier Robert struct peer *peer, /* peer structure pointer */ 1748ea906c41SOllivier Robert double bit 1749a151a66cSOllivier Robert ) 1750a151a66cSOllivier Robert { 1751a151a66cSOllivier Robert static int iniflg; /* initialization flag */ 1752a151a66cSOllivier Robert static double bcddld[4]; /* BCD data bits */ 1753a151a66cSOllivier Robert static double bitvec[61]; /* bit integrator for misc bits */ 1754a151a66cSOllivier Robert struct refclockproc *pp; 1755a151a66cSOllivier Robert struct wwvunit *up; 1756a151a66cSOllivier Robert struct chan *cp; 1757a151a66cSOllivier Robert struct sync *sp, *rp; 17582b15cb3dSCy Schubert char tbuf[TBUF]; /* monitor buffer */ 1759a151a66cSOllivier Robert int sw, arg, nsec; 1760a151a66cSOllivier Robert 1761a151a66cSOllivier Robert pp = peer->procptr; 17622b15cb3dSCy Schubert up = pp->unitptr; 1763a151a66cSOllivier Robert if (!iniflg) { 1764a151a66cSOllivier Robert iniflg = 1; 17652b15cb3dSCy Schubert ZERO(bitvec); 1766a151a66cSOllivier Robert } 1767a151a66cSOllivier Robert 1768a151a66cSOllivier Robert /* 1769a151a66cSOllivier Robert * The bit represents the probability of a hit on zero (negative 1770a151a66cSOllivier Robert * values), a hit on one (positive values) or a miss (zero 1771a151a66cSOllivier Robert * value). The likelihood vector is the exponential average of 1772a151a66cSOllivier Robert * these probabilities. Only the bits of this vector 1773a151a66cSOllivier Robert * corresponding to the miscellaneous bits of the timecode are 1774a151a66cSOllivier Robert * used, but it's easier to do them all. After that, crank the 1775a151a66cSOllivier Robert * seconds state machine. 1776a151a66cSOllivier Robert */ 1777ea906c41SOllivier Robert nsec = up->rsec; 1778ea906c41SOllivier Robert up->rsec++; 1779ea906c41SOllivier Robert bitvec[nsec] += (bit - bitvec[nsec]) / TCONST; 1780ea906c41SOllivier Robert sw = progx[nsec].sw; 1781ea906c41SOllivier Robert arg = progx[nsec].arg; 1782ea906c41SOllivier Robert 1783ea906c41SOllivier Robert /* 1784ea906c41SOllivier Robert * The minute state machine. Fly off to a particular section as 1785ea906c41SOllivier Robert * directed by the transition matrix and second number. 1786ea906c41SOllivier Robert */ 1787a151a66cSOllivier Robert switch (sw) { 1788a151a66cSOllivier Robert 1789a151a66cSOllivier Robert /* 1790a151a66cSOllivier Robert * Ignore this second. 1791a151a66cSOllivier Robert */ 1792a151a66cSOllivier Robert case IDLE: /* 9, 45-49 */ 1793a151a66cSOllivier Robert break; 1794a151a66cSOllivier Robert 1795a151a66cSOllivier Robert /* 1796a151a66cSOllivier Robert * Probe channel stuff 1797a151a66cSOllivier Robert * 1798a151a66cSOllivier Robert * The WWV/H format contains data pulses in second 59 (position 1799ea906c41SOllivier Robert * identifier) and second 1, but not in second 0. The minute 1800ea906c41SOllivier Robert * sync pulse is contained in second 0. At the end of second 58 1801ea906c41SOllivier Robert * QSY to the probe channel, which rotates in turn over all 1802ea906c41SOllivier Robert * WWV/H frequencies. At the end of second 0 measure the minute 1803ea906c41SOllivier Robert * sync pulse. At the end of second 1 measure the data pulse and 1804ea906c41SOllivier Robert * QSY back to the data channel. Note that the actions commented 1805ea906c41SOllivier Robert * here happen at the end of the second numbered as shown. 18069c2daa00SOllivier Robert * 1807ea906c41SOllivier Robert * At the end of second 0 save the minute sync amplitude latched 1808ea906c41SOllivier Robert * at 800 ms as the signal later used to calculate the SNR. 1809a151a66cSOllivier Robert */ 1810a151a66cSOllivier Robert case SYNC2: /* 0 */ 1811a151a66cSOllivier Robert cp = &up->mitig[up->achan]; 1812ea906c41SOllivier Robert cp->wwv.synmax = cp->wwv.syneng; 1813ea906c41SOllivier Robert cp->wwvh.synmax = cp->wwvh.syneng; 1814a151a66cSOllivier Robert break; 1815a151a66cSOllivier Robert 1816a151a66cSOllivier Robert /* 1817ea906c41SOllivier Robert * At the end of second 1 use the minute sync amplitude latched 1818ea906c41SOllivier Robert * at 800 ms as the noise to calculate the SNR. If the minute 1819ea906c41SOllivier Robert * sync pulse and SNR are above thresholds and the data pulse 1820ea906c41SOllivier Robert * amplitude and SNR are above thresolds, shift a 1 into the 1821ea906c41SOllivier Robert * station reachability register; otherwise, shift a 0. The 1822ea906c41SOllivier Robert * number of 1 bits in the last six intervals is a component of 1823ea906c41SOllivier Robert * the channel metric computed by the wwv_metric() routine. 18249c2daa00SOllivier Robert * Finally, QSY back to the data channel. 1825a151a66cSOllivier Robert */ 1826a151a66cSOllivier Robert case SYNC3: /* 1 */ 1827a151a66cSOllivier Robert cp = &up->mitig[up->achan]; 1828a151a66cSOllivier Robert 18299c2daa00SOllivier Robert /* 18309c2daa00SOllivier Robert * WWV station 18319c2daa00SOllivier Robert */ 1832a151a66cSOllivier Robert sp = &cp->wwv; 1833ea906c41SOllivier Robert sp->synsnr = wwv_snr(sp->synmax, sp->amp); 18349c2daa00SOllivier Robert sp->reach <<= 1; 18359c2daa00SOllivier Robert if (sp->reach & (1 << AMAX)) 18369c2daa00SOllivier Robert sp->count--; 1837ea906c41SOllivier Robert if (sp->synmax >= QTHR && sp->synsnr >= QSNR && 1838ea906c41SOllivier Robert !(up->status & (DGATE | BGATE))) { 18399c2daa00SOllivier Robert sp->reach |= 1; 18409c2daa00SOllivier Robert sp->count++; 18419c2daa00SOllivier Robert } 1842ea906c41SOllivier Robert sp->metric = wwv_metric(sp); 1843a151a66cSOllivier Robert 18449c2daa00SOllivier Robert /* 18459c2daa00SOllivier Robert * WWVH station 18469c2daa00SOllivier Robert */ 1847a151a66cSOllivier Robert rp = &cp->wwvh; 1848ea906c41SOllivier Robert rp->synsnr = wwv_snr(rp->synmax, rp->amp); 18499c2daa00SOllivier Robert rp->reach <<= 1; 18509c2daa00SOllivier Robert if (rp->reach & (1 << AMAX)) 18519c2daa00SOllivier Robert rp->count--; 1852ea906c41SOllivier Robert if (rp->synmax >= QTHR && rp->synsnr >= QSNR && 1853ea906c41SOllivier Robert !(up->status & (DGATE | BGATE))) { 18549c2daa00SOllivier Robert rp->reach |= 1; 18559c2daa00SOllivier Robert rp->count++; 18569c2daa00SOllivier Robert } 1857ea906c41SOllivier Robert rp->metric = wwv_metric(rp); 18589c2daa00SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4) { 18592b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 1860ea906c41SOllivier Robert "wwv5 %04x %3d %4d %.0f/%.1f %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f", 1861ea906c41SOllivier Robert up->status, up->gain, up->yepoch, 1862ea906c41SOllivier Robert up->epomax, up->eposnr, up->datsig, 1863ea906c41SOllivier Robert up->datsnr, 18649c2daa00SOllivier Robert sp->refid, sp->reach & 0xffff, 1865ea906c41SOllivier Robert sp->metric, sp->synmax, sp->synsnr, 18669c2daa00SOllivier Robert rp->refid, rp->reach & 0xffff, 1867ea906c41SOllivier Robert rp->metric, rp->synmax, rp->synsnr); 1868a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 1869a151a66cSOllivier Robert #ifdef DEBUG 1870a151a66cSOllivier Robert if (debug) 1871a151a66cSOllivier Robert printf("%s\n", tbuf); 1872a151a66cSOllivier Robert #endif /* DEBUG */ 18739c2daa00SOllivier Robert } 1874ea906c41SOllivier Robert up->errcnt = up->digcnt = up->alarm = 0; 1875ea906c41SOllivier Robert 1876ea906c41SOllivier Robert /* 18772b15cb3dSCy Schubert * If synchronized to a station, restart if no stations 18782b15cb3dSCy Schubert * have been heard within the PANIC timeout (2 days). If 18792b15cb3dSCy Schubert * not and the minute digit has been found, restart if 18802b15cb3dSCy Schubert * not synchronized withing the SYNCH timeout (40 m). If 18812b15cb3dSCy Schubert * not, restart if the unit digit has not been found 18822b15cb3dSCy Schubert * within the DATA timeout (15 m). 1883ea906c41SOllivier Robert */ 1884ea906c41SOllivier Robert if (up->status & INSYNC) { 1885ea906c41SOllivier Robert if (up->watch > PANIC) { 1886ea906c41SOllivier Robert wwv_newgame(peer); 1887ea906c41SOllivier Robert return; 1888ea906c41SOllivier Robert } 18892b15cb3dSCy Schubert } else if (up->status & DSYNC) { 1890ea906c41SOllivier Robert if (up->watch > SYNCH) { 1891ea906c41SOllivier Robert wwv_newgame(peer); 1892ea906c41SOllivier Robert return; 1893ea906c41SOllivier Robert } 18942b15cb3dSCy Schubert } else if (up->watch > DATA) { 18952b15cb3dSCy Schubert wwv_newgame(peer); 18962b15cb3dSCy Schubert return; 1897ea906c41SOllivier Robert } 1898ea906c41SOllivier Robert wwv_newchan(peer); 1899a151a66cSOllivier Robert break; 1900a151a66cSOllivier Robert 1901a151a66cSOllivier Robert /* 1902a151a66cSOllivier Robert * Save the bit probability in the BCD data vector at the index 1903ea906c41SOllivier Robert * given by the argument. Bits not used in the digit are forced 1904ea906c41SOllivier Robert * to zero. 1905a151a66cSOllivier Robert */ 1906ea906c41SOllivier Robert case COEF1: /* 4-7 */ 1907a151a66cSOllivier Robert bcddld[arg] = bit; 1908a151a66cSOllivier Robert break; 1909a151a66cSOllivier Robert 1910ea906c41SOllivier Robert case COEF: /* 10-13, 15-17, 20-23, 25-26, 1911ea906c41SOllivier Robert 30-33, 35-38, 40-41, 51-54 */ 1912ea906c41SOllivier Robert if (up->status & DSYNC) 1913ea906c41SOllivier Robert bcddld[arg] = bit; 1914ea906c41SOllivier Robert else 1915ea906c41SOllivier Robert bcddld[arg] = 0; 1916ea906c41SOllivier Robert break; 1917ea906c41SOllivier Robert 1918a151a66cSOllivier Robert case COEF2: /* 18, 27-28, 42-43 */ 1919a151a66cSOllivier Robert bcddld[arg] = 0; 1920a151a66cSOllivier Robert break; 1921a151a66cSOllivier Robert 1922a151a66cSOllivier Robert /* 1923a151a66cSOllivier Robert * Correlate coefficient vector with each valid digit vector and 1924a151a66cSOllivier Robert * save in decoding matrix. We step through the decoding matrix 1925a151a66cSOllivier Robert * digits correlating each with the coefficients and saving the 1926a151a66cSOllivier Robert * greatest and the next lower for later SNR calculation. 1927a151a66cSOllivier Robert */ 1928a151a66cSOllivier Robert case DECIM2: /* 29 */ 1929a151a66cSOllivier Robert wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2); 1930a151a66cSOllivier Robert break; 1931a151a66cSOllivier Robert 1932a151a66cSOllivier Robert case DECIM3: /* 44 */ 1933a151a66cSOllivier Robert wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3); 1934a151a66cSOllivier Robert break; 1935a151a66cSOllivier Robert 1936a151a66cSOllivier Robert case DECIM6: /* 19 */ 1937a151a66cSOllivier Robert wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6); 1938a151a66cSOllivier Robert break; 1939a151a66cSOllivier Robert 1940a151a66cSOllivier Robert case DECIM9: /* 8, 14, 24, 34, 39 */ 1941a151a66cSOllivier Robert wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9); 1942a151a66cSOllivier Robert break; 1943a151a66cSOllivier Robert 1944a151a66cSOllivier Robert /* 1945a151a66cSOllivier Robert * Miscellaneous bits. If above the positive threshold, declare 1946a151a66cSOllivier Robert * 1; if below the negative threshold, declare 0; otherwise 1947ea906c41SOllivier Robert * raise the BGATE bit. The design is intended to avoid 1948ea906c41SOllivier Robert * integrating noise under low SNR conditions. 1949a151a66cSOllivier Robert */ 1950a151a66cSOllivier Robert case MSC20: /* 55 */ 1951a151a66cSOllivier Robert wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9); 1952a151a66cSOllivier Robert /* fall through */ 1953a151a66cSOllivier Robert 19549c2daa00SOllivier Robert case MSCBIT: /* 2-3, 50, 56-57 */ 1955ea906c41SOllivier Robert if (bitvec[nsec] > BTHR) { 1956ea906c41SOllivier Robert if (!(up->misc & arg)) 1957ea906c41SOllivier Robert up->alarm |= CMPERR; 1958a151a66cSOllivier Robert up->misc |= arg; 1959ea906c41SOllivier Robert } else if (bitvec[nsec] < -BTHR) { 1960ea906c41SOllivier Robert if (up->misc & arg) 1961ea906c41SOllivier Robert up->alarm |= CMPERR; 1962a151a66cSOllivier Robert up->misc &= ~arg; 1963ea906c41SOllivier Robert } else { 1964ea906c41SOllivier Robert up->status |= BGATE; 1965ea906c41SOllivier Robert } 1966a151a66cSOllivier Robert break; 1967a151a66cSOllivier Robert 19689c2daa00SOllivier Robert /* 1969ea906c41SOllivier Robert * Save the data channel gain, then QSY to the probe channel and 19702b15cb3dSCy Schubert * dim the seconds comb filters. The www_newchan() routine will 1971ea906c41SOllivier Robert * light them back up. 19729c2daa00SOllivier Robert */ 1973a151a66cSOllivier Robert case MSC21: /* 58 */ 1974ea906c41SOllivier Robert if (bitvec[nsec] > BTHR) { 1975ea906c41SOllivier Robert if (!(up->misc & arg)) 1976ea906c41SOllivier Robert up->alarm |= CMPERR; 1977a151a66cSOllivier Robert up->misc |= arg; 1978ea906c41SOllivier Robert } else if (bitvec[nsec] < -BTHR) { 1979ea906c41SOllivier Robert if (up->misc & arg) 1980ea906c41SOllivier Robert up->alarm |= CMPERR; 1981a151a66cSOllivier Robert up->misc &= ~arg; 1982ea906c41SOllivier Robert } else { 1983ea906c41SOllivier Robert up->status |= BGATE; 1984ea906c41SOllivier Robert } 1985ea906c41SOllivier Robert up->status &= ~(SELV | SELH); 19869c2daa00SOllivier Robert #ifdef ICOM 19879c2daa00SOllivier Robert if (up->fd_icom > 0) { 1988a151a66cSOllivier Robert up->schan = (up->schan + 1) % NCHAN; 1989a151a66cSOllivier Robert wwv_qsy(peer, up->schan); 1990ea906c41SOllivier Robert } else { 1991ea906c41SOllivier Robert up->mitig[up->achan].gain = up->gain; 19929c2daa00SOllivier Robert } 1993ea906c41SOllivier Robert #else 1994ea906c41SOllivier Robert up->mitig[up->achan].gain = up->gain; 19959c2daa00SOllivier Robert #endif /* ICOM */ 1996a151a66cSOllivier Robert break; 1997a151a66cSOllivier Robert 1998a151a66cSOllivier Robert /* 1999a151a66cSOllivier Robert * The endgames 2000a151a66cSOllivier Robert * 20019c2daa00SOllivier Robert * During second 59 the receiver and codec AGC are settling 2002ea906c41SOllivier Robert * down, so the data pulse is unusable as quality metric. If 2003ea906c41SOllivier Robert * LEPSEC is set on the last minute of 30 June or 31 December, 2004ea906c41SOllivier Robert * the transmitter and receiver insert an extra second (60) in 2005ea906c41SOllivier Robert * the timescale and the minute sync repeats the second. Once 2006ea906c41SOllivier Robert * leaps occurred at intervals of about 18 months, but the last 2007ea906c41SOllivier Robert * leap before the most recent leap in 1995 was in 1998. 2008a151a66cSOllivier Robert */ 2009a151a66cSOllivier Robert case MIN1: /* 59 */ 2010ea906c41SOllivier Robert if (up->status & LEPSEC) 2011ea906c41SOllivier Robert break; 2012a151a66cSOllivier Robert 2013ea906c41SOllivier Robert /* fall through */ 2014ea906c41SOllivier Robert 2015ea906c41SOllivier Robert case MIN2: /* 60 */ 2016ea906c41SOllivier Robert up->status &= ~LEPSEC; 2017ea906c41SOllivier Robert wwv_tsec(peer); 2018ea906c41SOllivier Robert up->rsec = 0; 2019ea906c41SOllivier Robert wwv_clock(peer); 2020ea906c41SOllivier Robert break; 2021a151a66cSOllivier Robert } 2022ea906c41SOllivier Robert if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & 2023ea906c41SOllivier Robert DSYNC)) { 20242b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 2025ea906c41SOllivier Robert "wwv3 %2d %04x %3d %4d %5.0f %5.1f %5.0f %5.1f %5.0f", 2026ea906c41SOllivier Robert nsec, up->status, up->gain, up->yepoch, up->epomax, 2027ea906c41SOllivier Robert up->eposnr, up->datsig, up->datsnr, bit); 2028ea906c41SOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 2029a151a66cSOllivier Robert #ifdef DEBUG 2030a151a66cSOllivier Robert if (debug) 2031ea906c41SOllivier Robert printf("%s\n", tbuf); 2032a151a66cSOllivier Robert #endif /* DEBUG */ 20339c2daa00SOllivier Robert } 20349c2daa00SOllivier Robert pp->disp += AUDIO_PHI; 20359c2daa00SOllivier Robert } 20369c2daa00SOllivier Robert 20379c2daa00SOllivier Robert /* 2038ea906c41SOllivier Robert * The radio clock is set if the alarm bits are all zero. After that, 2039ea906c41SOllivier Robert * the time is considered valid if the second sync bit is lit. It should 2040ea906c41SOllivier Robert * not be a surprise, especially if the radio is not tunable, that 2041ea906c41SOllivier Robert * sometimes no stations are above the noise and the integrators 2042ea906c41SOllivier Robert * discharge below the thresholds. We assume that, after a day of signal 2043ea906c41SOllivier Robert * loss, the minute sync epoch will be in the same second. This requires 2044ea906c41SOllivier Robert * the codec frequency be accurate within 6 PPM. Practical experience 2045ea906c41SOllivier Robert * shows the frequency typically within 0.1 PPM, so after a day of 2046ea906c41SOllivier Robert * signal loss, the time should be within 8.6 ms.. 20479c2daa00SOllivier Robert */ 2048ea906c41SOllivier Robert static void 2049ea906c41SOllivier Robert wwv_clock( 2050ea906c41SOllivier Robert struct peer *peer /* peer unit pointer */ 2051ea906c41SOllivier Robert ) 2052ea906c41SOllivier Robert { 2053ea906c41SOllivier Robert struct refclockproc *pp; 2054ea906c41SOllivier Robert struct wwvunit *up; 2055ea906c41SOllivier Robert l_fp offset; /* offset in NTP seconds */ 2056ea906c41SOllivier Robert 2057ea906c41SOllivier Robert pp = peer->procptr; 20582b15cb3dSCy Schubert up = pp->unitptr; 2059ea906c41SOllivier Robert if (!(up->status & SSYNC)) 2060ea906c41SOllivier Robert up->alarm |= SYNERR; 2061ea906c41SOllivier Robert if (up->digcnt < 9) 2062ea906c41SOllivier Robert up->alarm |= NINERR; 2063ea906c41SOllivier Robert if (!(up->alarm)) 2064ea906c41SOllivier Robert up->status |= INSYNC; 20659c2daa00SOllivier Robert if (up->status & INSYNC && up->status & SSYNC) { 2066ea906c41SOllivier Robert if (up->misc & SECWAR) 2067ea906c41SOllivier Robert pp->leap = LEAP_ADDSECOND; 2068ea906c41SOllivier Robert else 2069ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 20709c2daa00SOllivier Robert pp->second = up->rsec; 20719c2daa00SOllivier Robert pp->minute = up->decvec[MN].digit + up->decvec[MN + 20729c2daa00SOllivier Robert 1].digit * 10; 20739c2daa00SOllivier Robert pp->hour = up->decvec[HR].digit + up->decvec[HR + 20749c2daa00SOllivier Robert 1].digit * 10; 20759c2daa00SOllivier Robert pp->day = up->decvec[DA].digit + up->decvec[DA + 20769c2daa00SOllivier Robert 1].digit * 10 + up->decvec[DA + 2].digit * 100; 20779c2daa00SOllivier Robert pp->year = up->decvec[YR].digit + up->decvec[YR + 20789c2daa00SOllivier Robert 1].digit * 10; 20799c2daa00SOllivier Robert pp->year += 2000; 20809c2daa00SOllivier Robert L_CLR(&offset); 20819c2daa00SOllivier Robert if (!clocktime(pp->day, pp->hour, pp->minute, 20829c2daa00SOllivier Robert pp->second, GMT, up->timestamp.l_ui, 20839c2daa00SOllivier Robert &pp->yearstart, &offset.l_ui)) { 20849c2daa00SOllivier Robert up->errflg = CEVNT_BADTIME; 20859c2daa00SOllivier Robert } else { 20869c2daa00SOllivier Robert up->watch = 0; 20879c2daa00SOllivier Robert pp->disp = 0; 2088ea906c41SOllivier Robert pp->lastref = up->timestamp; 20899c2daa00SOllivier Robert refclock_process_offset(pp, offset, 20902b15cb3dSCy Schubert up->timestamp, PDELAY + up->pdelay); 2091ea906c41SOllivier Robert refclock_receive(peer); 20929c2daa00SOllivier Robert } 20939c2daa00SOllivier Robert } 20942b15cb3dSCy Schubert pp->lencode = timecode(up, pp->a_lastcode, 20952b15cb3dSCy Schubert sizeof(pp->a_lastcode)); 2096ea906c41SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 2097a151a66cSOllivier Robert #ifdef DEBUG 2098a151a66cSOllivier Robert if (debug) 2099ea906c41SOllivier Robert printf("wwv: timecode %d %s\n", pp->lencode, 2100ea906c41SOllivier Robert pp->a_lastcode); 2101a151a66cSOllivier Robert #endif /* DEBUG */ 2102a151a66cSOllivier Robert } 2103a151a66cSOllivier Robert 2104a151a66cSOllivier Robert 2105a151a66cSOllivier Robert /* 21062b15cb3dSCy Schubert * wwv_corr4 - determine maximum-likelihood digit 2107a151a66cSOllivier Robert * 2108a151a66cSOllivier Robert * This routine correlates the received digit vector with the BCD 2109a151a66cSOllivier Robert * coefficient vectors corresponding to all valid digits at the given 2110a151a66cSOllivier Robert * position in the decoding matrix. The maximum value corresponds to the 21112b15cb3dSCy Schubert * maximum-likelihood digit, while the ratio of this value to the next 2112a151a66cSOllivier Robert * lower value determines the likelihood function. Note that, if the 2113a151a66cSOllivier Robert * digit is invalid, the likelihood vector is averaged toward a miss. 2114a151a66cSOllivier Robert */ 2115a151a66cSOllivier Robert static void 2116a151a66cSOllivier Robert wwv_corr4( 2117a151a66cSOllivier Robert struct peer *peer, /* peer unit pointer */ 2118a151a66cSOllivier Robert struct decvec *vp, /* decoding table pointer */ 2119a151a66cSOllivier Robert double data[], /* received data vector */ 2120a151a66cSOllivier Robert double tab[][4] /* correlation vector array */ 2121a151a66cSOllivier Robert ) 2122a151a66cSOllivier Robert { 2123a151a66cSOllivier Robert struct refclockproc *pp; 2124a151a66cSOllivier Robert struct wwvunit *up; 2125a151a66cSOllivier Robert double topmax, nxtmax; /* metrics */ 2126a151a66cSOllivier Robert double acc; /* accumulator */ 21272b15cb3dSCy Schubert char tbuf[TBUF]; /* monitor buffer */ 2128a151a66cSOllivier Robert int mldigit; /* max likelihood digit */ 2129a151a66cSOllivier Robert int i, j; 2130a151a66cSOllivier Robert 2131a151a66cSOllivier Robert pp = peer->procptr; 21322b15cb3dSCy Schubert up = pp->unitptr; 2133a151a66cSOllivier Robert 2134a151a66cSOllivier Robert /* 2135a151a66cSOllivier Robert * Correlate digit vector with each BCD coefficient vector. If 2136ea906c41SOllivier Robert * any BCD digit bit is bad, consider all bits a miss. Until the 2137ea906c41SOllivier Robert * minute units digit has been resolved, don't to anything else. 2138ea906c41SOllivier Robert * Note the SNR is calculated as the ratio of the largest 2139ea906c41SOllivier Robert * likelihood value to the next largest likelihood value. 2140a151a66cSOllivier Robert */ 2141a151a66cSOllivier Robert mldigit = 0; 2142ea906c41SOllivier Robert topmax = nxtmax = -MAXAMP; 2143a151a66cSOllivier Robert for (i = 0; tab[i][0] != 0; i++) { 2144a151a66cSOllivier Robert acc = 0; 2145ea906c41SOllivier Robert for (j = 0; j < 4; j++) 2146a151a66cSOllivier Robert acc += data[j] * tab[i][j]; 2147a151a66cSOllivier Robert acc = (vp->like[i] += (acc - vp->like[i]) / TCONST); 2148a151a66cSOllivier Robert if (acc > topmax) { 2149a151a66cSOllivier Robert nxtmax = topmax; 2150a151a66cSOllivier Robert topmax = acc; 2151a151a66cSOllivier Robert mldigit = i; 2152a151a66cSOllivier Robert } else if (acc > nxtmax) { 2153a151a66cSOllivier Robert nxtmax = acc; 2154a151a66cSOllivier Robert } 2155a151a66cSOllivier Robert } 2156a151a66cSOllivier Robert vp->digprb = topmax; 2157a151a66cSOllivier Robert vp->digsnr = wwv_snr(topmax, nxtmax); 2158a151a66cSOllivier Robert 2159a151a66cSOllivier Robert /* 21602b15cb3dSCy Schubert * The current maximum-likelihood digit is compared to the last 21612b15cb3dSCy Schubert * maximum-likelihood digit. If different, the compare counter 21622b15cb3dSCy Schubert * and maximum-likelihood digit are reset. When the compare 2163ea906c41SOllivier Robert * counter reaches the BCMP threshold (3), the digit is assumed 2164ea906c41SOllivier Robert * correct. When the compare counter of all nine digits have 2165ea906c41SOllivier Robert * reached threshold, the clock is assumed correct. 2166ea906c41SOllivier Robert * 2167ea906c41SOllivier Robert * Note that the clock display digit is set before the compare 2168ea906c41SOllivier Robert * counter has reached threshold; however, the clock display is 2169ea906c41SOllivier Robert * not considered correct until all nine clock digits have 2170ea906c41SOllivier Robert * reached threshold. This is intended as eye candy, but avoids 2171ea906c41SOllivier Robert * mistakes when the signal is low and the SNR is very marginal. 2172a151a66cSOllivier Robert */ 2173ea906c41SOllivier Robert if (vp->digprb < BTHR || vp->digsnr < BSNR) { 2174ea906c41SOllivier Robert up->status |= BGATE; 2175a151a66cSOllivier Robert } else { 2176ea906c41SOllivier Robert if (vp->digit != mldigit) { 2177ea906c41SOllivier Robert up->alarm |= CMPERR; 21782b15cb3dSCy Schubert if (vp->count > 0) 21792b15cb3dSCy Schubert vp->count--; 21802b15cb3dSCy Schubert if (vp->count == 0) 2181a151a66cSOllivier Robert vp->digit = mldigit; 2182ea906c41SOllivier Robert } else { 2183ea906c41SOllivier Robert if (vp->count < BCMP) 2184ea906c41SOllivier Robert vp->count++; 21852b15cb3dSCy Schubert if (vp->count == BCMP) { 21862b15cb3dSCy Schubert up->status |= DSYNC; 2187a151a66cSOllivier Robert up->digcnt++; 2188a151a66cSOllivier Robert } 2189ea906c41SOllivier Robert } 21902b15cb3dSCy Schubert } 21919c2daa00SOllivier Robert if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & 21929c2daa00SOllivier Robert INSYNC)) { 21932b15cb3dSCy Schubert snprintf(tbuf, sizeof(tbuf), 2194ea906c41SOllivier Robert "wwv4 %2d %04x %3d %4d %5.0f %2d %d %d %d %5.0f %5.1f", 2195ea906c41SOllivier Robert up->rsec - 1, up->status, up->gain, up->yepoch, 21962b15cb3dSCy Schubert up->epomax, vp->radix, vp->digit, mldigit, 2197ea906c41SOllivier Robert vp->count, vp->digprb, vp->digsnr); 2198a151a66cSOllivier Robert record_clock_stats(&peer->srcadr, tbuf); 2199a151a66cSOllivier Robert #ifdef DEBUG 2200a151a66cSOllivier Robert if (debug) 2201a151a66cSOllivier Robert printf("%s\n", tbuf); 2202a151a66cSOllivier Robert #endif /* DEBUG */ 2203a151a66cSOllivier Robert } 2204a151a66cSOllivier Robert } 2205a151a66cSOllivier Robert 2206a151a66cSOllivier Robert 2207a151a66cSOllivier Robert /* 22089c2daa00SOllivier Robert * wwv_tsec - transmitter minute processing 2209a151a66cSOllivier Robert * 22109c2daa00SOllivier Robert * This routine is called at the end of the transmitter minute. It 2211a151a66cSOllivier Robert * implements a state machine that advances the logical clock subject to 22129c2daa00SOllivier Robert * the funny rules that govern the conventional clock and calendar. 2213a151a66cSOllivier Robert */ 2214a151a66cSOllivier Robert static void 2215a151a66cSOllivier Robert wwv_tsec( 2216ea906c41SOllivier Robert struct peer *peer /* driver structure pointer */ 2217a151a66cSOllivier Robert ) 2218a151a66cSOllivier Robert { 2219ea906c41SOllivier Robert struct refclockproc *pp; 2220ea906c41SOllivier Robert struct wwvunit *up; 2221a151a66cSOllivier Robert int minute, day, isleap; 2222a151a66cSOllivier Robert int temp; 2223a151a66cSOllivier Robert 2224ea906c41SOllivier Robert pp = peer->procptr; 22252b15cb3dSCy Schubert up = pp->unitptr; 2226ea906c41SOllivier Robert 2227a151a66cSOllivier Robert /* 2228ea906c41SOllivier Robert * Advance minute unit of the day. Don't propagate carries until 2229ea906c41SOllivier Robert * the unit minute digit has been found. 2230a151a66cSOllivier Robert */ 2231a151a66cSOllivier Robert temp = carry(&up->decvec[MN]); /* minute units */ 2232ea906c41SOllivier Robert if (!(up->status & DSYNC)) 2233ea906c41SOllivier Robert return; 2234a151a66cSOllivier Robert 2235a151a66cSOllivier Robert /* 2236a151a66cSOllivier Robert * Propagate carries through the day. 2237a151a66cSOllivier Robert */ 2238a151a66cSOllivier Robert if (temp == 0) /* carry minutes */ 2239a151a66cSOllivier Robert temp = carry(&up->decvec[MN + 1]); 2240a151a66cSOllivier Robert if (temp == 0) /* carry hours */ 2241a151a66cSOllivier Robert temp = carry(&up->decvec[HR]); 2242a151a66cSOllivier Robert if (temp == 0) 2243a151a66cSOllivier Robert temp = carry(&up->decvec[HR + 1]); 22449034852cSGleb Smirnoff // XXX: Does temp have an expected value here? 2245a151a66cSOllivier Robert 2246a151a66cSOllivier Robert /* 22479c2daa00SOllivier Robert * Decode the current minute and day. Set leap day if the 22489c2daa00SOllivier Robert * timecode leap bit is set on 30 June or 31 December. Set leap 2249ea906c41SOllivier Robert * minute if the last minute on leap day, but only if the clock 2250ea906c41SOllivier Robert * is syncrhronized. This code fails in 2400 AD. 2251a151a66cSOllivier Robert */ 2252a151a66cSOllivier Robert minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 2253a151a66cSOllivier Robert 10 + up->decvec[HR].digit * 60 + up->decvec[HR + 2254a151a66cSOllivier Robert 1].digit * 600; 2255a151a66cSOllivier Robert day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 + 2256a151a66cSOllivier Robert up->decvec[DA + 2].digit * 100; 2257ea906c41SOllivier Robert 2258ea906c41SOllivier Robert /* 2259ea906c41SOllivier Robert * Set the leap bit on the last minute of the leap day. 2260ea906c41SOllivier Robert */ 2261ea906c41SOllivier Robert isleap = up->decvec[YR].digit & 0x3; 2262ea906c41SOllivier Robert if (up->misc & SECWAR && up->status & INSYNC) { 2263ea906c41SOllivier Robert if ((day == (isleap ? 182 : 183) || day == (isleap ? 2264ea906c41SOllivier Robert 365 : 366)) && minute == 1439) 2265a151a66cSOllivier Robert up->status |= LEPSEC; 2266ea906c41SOllivier Robert } 2267a151a66cSOllivier Robert 2268a151a66cSOllivier Robert /* 2269a151a66cSOllivier Robert * Roll the day if this the first minute and propagate carries 2270a151a66cSOllivier Robert * through the year. 2271a151a66cSOllivier Robert */ 2272a151a66cSOllivier Robert if (minute != 1440) 2273a151a66cSOllivier Robert return; 2274ea906c41SOllivier Robert 22759034852cSGleb Smirnoff // minute = 0; 2276a151a66cSOllivier Robert while (carry(&up->decvec[HR]) != 0); /* advance to minute 0 */ 2277a151a66cSOllivier Robert while (carry(&up->decvec[HR + 1]) != 0); 2278a151a66cSOllivier Robert day++; 2279a151a66cSOllivier Robert temp = carry(&up->decvec[DA]); /* carry days */ 2280a151a66cSOllivier Robert if (temp == 0) 2281a151a66cSOllivier Robert temp = carry(&up->decvec[DA + 1]); 2282a151a66cSOllivier Robert if (temp == 0) 2283a151a66cSOllivier Robert temp = carry(&up->decvec[DA + 2]); 22849034852cSGleb Smirnoff // XXX: Is there an expected value of temp here? 2285a151a66cSOllivier Robert 2286a151a66cSOllivier Robert /* 2287a151a66cSOllivier Robert * Roll the year if this the first day and propagate carries 2288a151a66cSOllivier Robert * through the century. 2289a151a66cSOllivier Robert */ 2290a151a66cSOllivier Robert if (day != (isleap ? 365 : 366)) 2291a151a66cSOllivier Robert return; 2292ea906c41SOllivier Robert 22939034852cSGleb Smirnoff // day = 1; 2294a151a66cSOllivier Robert while (carry(&up->decvec[DA]) != 1); /* advance to day 1 */ 2295a151a66cSOllivier Robert while (carry(&up->decvec[DA + 1]) != 0); 2296a151a66cSOllivier Robert while (carry(&up->decvec[DA + 2]) != 0); 2297a151a66cSOllivier Robert temp = carry(&up->decvec[YR]); /* carry years */ 2298ea906c41SOllivier Robert if (temp == 0) 2299a151a66cSOllivier Robert carry(&up->decvec[YR + 1]); 2300a151a66cSOllivier Robert } 2301a151a66cSOllivier Robert 2302a151a66cSOllivier Robert 2303a151a66cSOllivier Robert /* 2304a151a66cSOllivier Robert * carry - process digit 2305a151a66cSOllivier Robert * 2306a151a66cSOllivier Robert * This routine rotates a likelihood vector one position and increments 23079c2daa00SOllivier Robert * the clock digit modulo the radix. It returns the new clock digit or 23089c2daa00SOllivier Robert * zero if a carry occurred. Once synchronized, the clock digit will 23092b15cb3dSCy Schubert * match the maximum-likelihood digit corresponding to that position. 2310a151a66cSOllivier Robert */ 2311a151a66cSOllivier Robert static int 2312a151a66cSOllivier Robert carry( 2313a151a66cSOllivier Robert struct decvec *dp /* decoding table pointer */ 2314a151a66cSOllivier Robert ) 2315a151a66cSOllivier Robert { 2316a151a66cSOllivier Robert int temp; 2317a151a66cSOllivier Robert int j; 2318a151a66cSOllivier Robert 2319ea906c41SOllivier Robert dp->digit++; 2320ea906c41SOllivier Robert if (dp->digit == dp->radix) 2321a151a66cSOllivier Robert dp->digit = 0; 2322ea906c41SOllivier Robert temp = dp->like[dp->radix - 1]; 2323a151a66cSOllivier Robert for (j = dp->radix - 1; j > 0; j--) 2324a151a66cSOllivier Robert dp->like[j] = dp->like[j - 1]; 2325a151a66cSOllivier Robert dp->like[0] = temp; 2326a151a66cSOllivier Robert return (dp->digit); 2327a151a66cSOllivier Robert } 2328a151a66cSOllivier Robert 2329a151a66cSOllivier Robert 2330a151a66cSOllivier Robert /* 2331a151a66cSOllivier Robert * wwv_snr - compute SNR or likelihood function 2332a151a66cSOllivier Robert */ 2333a151a66cSOllivier Robert static double 2334a151a66cSOllivier Robert wwv_snr( 2335a151a66cSOllivier Robert double signal, /* signal */ 2336a151a66cSOllivier Robert double noise /* noise */ 2337a151a66cSOllivier Robert ) 2338a151a66cSOllivier Robert { 2339a151a66cSOllivier Robert double rval; 2340a151a66cSOllivier Robert 2341a151a66cSOllivier Robert /* 2342a151a66cSOllivier Robert * This is a little tricky. Due to the way things are measured, 2343a151a66cSOllivier Robert * either or both the signal or noise amplitude can be negative 2344a151a66cSOllivier Robert * or zero. The intent is that, if the signal is negative or 2345a151a66cSOllivier Robert * zero, the SNR must always be zero. This can happen with the 2346a151a66cSOllivier Robert * subcarrier SNR before the phase has been aligned. On the 2347a151a66cSOllivier Robert * other hand, in the likelihood function the "noise" is the 2348a151a66cSOllivier Robert * next maximum down from the peak and this could be negative. 2349a151a66cSOllivier Robert * However, in this case the SNR is truly stupendous, so we 2350ea906c41SOllivier Robert * simply cap at MAXSNR dB (40). 2351a151a66cSOllivier Robert */ 2352a151a66cSOllivier Robert if (signal <= 0) { 2353a151a66cSOllivier Robert rval = 0; 2354a151a66cSOllivier Robert } else if (noise <= 0) { 2355a151a66cSOllivier Robert rval = MAXSNR; 2356a151a66cSOllivier Robert } else { 2357ea906c41SOllivier Robert rval = 20. * log10(signal / noise); 2358a151a66cSOllivier Robert if (rval > MAXSNR) 2359a151a66cSOllivier Robert rval = MAXSNR; 2360a151a66cSOllivier Robert } 2361a151a66cSOllivier Robert return (rval); 2362a151a66cSOllivier Robert } 2363a151a66cSOllivier Robert 23649c2daa00SOllivier Robert 2365a151a66cSOllivier Robert /* 2366a151a66cSOllivier Robert * wwv_newchan - change to new data channel 2367a151a66cSOllivier Robert * 23689c2daa00SOllivier Robert * The radio actually appears to have ten channels, one channel for each 23699c2daa00SOllivier Robert * of five frequencies and each of two stations (WWV and WWVH), although 2370ea906c41SOllivier Robert * if not tunable only the DCHAN channel appears live. While the radio 23719c2daa00SOllivier Robert * is tuned to the working data channel frequency and station for most 23729c2daa00SOllivier Robert * of the minute, during seconds 59, 0 and 1 the radio is tuned to a 23739c2daa00SOllivier Robert * probe frequency in order to search for minute sync pulse and data 23749c2daa00SOllivier Robert * subcarrier from other transmitters. 23759c2daa00SOllivier Robert * 23769c2daa00SOllivier Robert * The search for WWV and WWVH operates simultaneously, with WWV minute 23779c2daa00SOllivier Robert * sync pulse at 1000 Hz and WWVH at 1200 Hz. The probe frequency 23789c2daa00SOllivier Robert * rotates each minute over 2.5, 5, 10, 15 and 20 MHz in order and yes, 23799c2daa00SOllivier Robert * we all know WWVH is dark on 20 MHz, but few remember when WWV was lit 23809c2daa00SOllivier Robert * on 25 MHz. 23819c2daa00SOllivier Robert * 23829c2daa00SOllivier Robert * This routine selects the best channel using a metric computed from 23839c2daa00SOllivier Robert * the reachability register and minute pulse amplitude. Normally, the 23849c2daa00SOllivier Robert * award goes to the the channel with the highest metric; but, in case 23859c2daa00SOllivier Robert * of ties, the award goes to the channel with the highest minute sync 23869c2daa00SOllivier Robert * pulse amplitude and then to the highest frequency. 23879c2daa00SOllivier Robert * 23889c2daa00SOllivier Robert * The routine performs an important squelch function to keep dirty data 2389ea906c41SOllivier Robert * from polluting the integrators. In order to consider a station valid, 2390ea906c41SOllivier Robert * the metric must be at least MTHR (13); otherwise, the station select 2391ea906c41SOllivier Robert * bits are cleared so the second sync is disabled and the data bit 2392ea906c41SOllivier Robert * integrators averaged to a miss. 2393a151a66cSOllivier Robert */ 2394ea906c41SOllivier Robert static int 2395a151a66cSOllivier Robert wwv_newchan( 2396a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 2397a151a66cSOllivier Robert ) 2398a151a66cSOllivier Robert { 2399a151a66cSOllivier Robert struct refclockproc *pp; 2400a151a66cSOllivier Robert struct wwvunit *up; 2401a151a66cSOllivier Robert struct sync *sp, *rp; 24029c2daa00SOllivier Robert double rank, dtemp; 24032b15cb3dSCy Schubert int i, j, rval; 2404a151a66cSOllivier Robert 2405a151a66cSOllivier Robert pp = peer->procptr; 24062b15cb3dSCy Schubert up = pp->unitptr; 2407a151a66cSOllivier Robert 2408a151a66cSOllivier Robert /* 24099c2daa00SOllivier Robert * Search all five station pairs looking for the channel with 24102b15cb3dSCy Schubert * maximum metric. 2411a151a66cSOllivier Robert */ 24129c2daa00SOllivier Robert sp = NULL; 2413ea906c41SOllivier Robert j = 0; 2414a151a66cSOllivier Robert rank = 0; 2415a151a66cSOllivier Robert for (i = 0; i < NCHAN; i++) { 24169c2daa00SOllivier Robert rp = &up->mitig[i].wwvh; 2417ea906c41SOllivier Robert dtemp = rp->metric; 24189c2daa00SOllivier Robert if (dtemp >= rank) { 24199c2daa00SOllivier Robert rank = dtemp; 24209c2daa00SOllivier Robert sp = rp; 24219c2daa00SOllivier Robert j = i; 24229c2daa00SOllivier Robert } 24239c2daa00SOllivier Robert rp = &up->mitig[i].wwv; 2424ea906c41SOllivier Robert dtemp = rp->metric; 24259c2daa00SOllivier Robert if (dtemp >= rank) { 24269c2daa00SOllivier Robert rank = dtemp; 24279c2daa00SOllivier Robert sp = rp; 24289c2daa00SOllivier Robert j = i; 24299c2daa00SOllivier Robert } 24309c2daa00SOllivier Robert } 2431ea906c41SOllivier Robert 2432ea906c41SOllivier Robert /* 2433ea906c41SOllivier Robert * If the strongest signal is less than the MTHR threshold (13), 24342b15cb3dSCy Schubert * we are beneath the waves, so squelch the second sync and 24352b15cb3dSCy Schubert * advance to the next station. This makes sure all stations are 24362b15cb3dSCy Schubert * scanned when the ions grow dim. If the strongest signal is 24372b15cb3dSCy Schubert * greater than the threshold, tune to that frequency and 24382b15cb3dSCy Schubert * transmitter QTH. 2439ea906c41SOllivier Robert */ 24402b15cb3dSCy Schubert up->status &= ~(SELV | SELH); 2441ea906c41SOllivier Robert if (rank < MTHR) { 2442ea906c41SOllivier Robert up->dchan = (up->dchan + 1) % NCHAN; 24432b15cb3dSCy Schubert if (up->status & METRIC) { 24442b15cb3dSCy Schubert up->status &= ~METRIC; 24452b15cb3dSCy Schubert refclock_report(peer, CEVNT_PROP); 24469c2daa00SOllivier Robert } 24472b15cb3dSCy Schubert rval = FALSE; 24482b15cb3dSCy Schubert } else { 2449ea906c41SOllivier Robert up->dchan = j; 2450ea906c41SOllivier Robert up->sptr = sp; 2451ea906c41SOllivier Robert memcpy(&pp->refid, sp->refid, 4); 2452ea906c41SOllivier Robert peer->refid = pp->refid; 24532b15cb3dSCy Schubert up->status |= METRIC; 24542b15cb3dSCy Schubert if (sp->select & SELV) { 24552b15cb3dSCy Schubert up->status |= SELV; 24562b15cb3dSCy Schubert up->pdelay = pp->fudgetime1; 24572b15cb3dSCy Schubert } else if (sp->select & SELH) { 24582b15cb3dSCy Schubert up->status |= SELH; 24592b15cb3dSCy Schubert up->pdelay = pp->fudgetime2; 24602b15cb3dSCy Schubert } else { 24612b15cb3dSCy Schubert up->pdelay = 0; 24622b15cb3dSCy Schubert } 24632b15cb3dSCy Schubert rval = TRUE; 24642b15cb3dSCy Schubert } 24652b15cb3dSCy Schubert #ifdef ICOM 24662b15cb3dSCy Schubert if (up->fd_icom > 0) 24672b15cb3dSCy Schubert wwv_qsy(peer, up->dchan); 24682b15cb3dSCy Schubert #endif /* ICOM */ 24692b15cb3dSCy Schubert return (rval); 24709c2daa00SOllivier Robert } 24719c2daa00SOllivier Robert 24729c2daa00SOllivier Robert 24739c2daa00SOllivier Robert /* 2474ea906c41SOllivier Robert * wwv_newgame - reset and start over 2475ea906c41SOllivier Robert * 24762b15cb3dSCy Schubert * There are three conditions resulting in a new game: 2477ea906c41SOllivier Robert * 24782b15cb3dSCy Schubert * 1 After finding the minute pulse (MSYNC lit), going 15 minutes 2479ea906c41SOllivier Robert * (DATA) without finding the unit seconds digit. 2480ea906c41SOllivier Robert * 24812b15cb3dSCy Schubert * 2 After finding good data (DSYNC lit), going more than 40 minutes 2482ea906c41SOllivier Robert * (SYNCH) without finding station sync (INSYNC lit). 2483ea906c41SOllivier Robert * 24842b15cb3dSCy Schubert * 3 After finding station sync (INSYNC lit), going more than 2 days 2485ea906c41SOllivier Robert * (PANIC) without finding any station. 24869c2daa00SOllivier Robert */ 24879c2daa00SOllivier Robert static void 24889c2daa00SOllivier Robert wwv_newgame( 24899c2daa00SOllivier Robert struct peer *peer /* peer structure pointer */ 24909c2daa00SOllivier Robert ) 24919c2daa00SOllivier Robert { 24929c2daa00SOllivier Robert struct refclockproc *pp; 24939c2daa00SOllivier Robert struct wwvunit *up; 24949c2daa00SOllivier Robert struct chan *cp; 24959c2daa00SOllivier Robert int i; 24969c2daa00SOllivier Robert 24979c2daa00SOllivier Robert pp = peer->procptr; 24982b15cb3dSCy Schubert up = pp->unitptr; 24999c2daa00SOllivier Robert 25009c2daa00SOllivier Robert /* 25019c2daa00SOllivier Robert * Initialize strategic values. Note we set the leap bits 25029c2daa00SOllivier Robert * NOTINSYNC and the refid "NONE". 25039c2daa00SOllivier Robert */ 25042b15cb3dSCy Schubert if (up->status) 25052b15cb3dSCy Schubert up->errflg = CEVNT_TIMEOUT; 25069c2daa00SOllivier Robert peer->leap = LEAP_NOTINSYNC; 25079c2daa00SOllivier Robert up->watch = up->status = up->alarm = 0; 25089c2daa00SOllivier Robert up->avgint = MINAVG; 25099c2daa00SOllivier Robert up->freq = 0; 25109c2daa00SOllivier Robert up->gain = MAXGAIN / 2; 25119c2daa00SOllivier Robert 25129c2daa00SOllivier Robert /* 25139c2daa00SOllivier Robert * Initialize the station processes for audio gain, select bit, 2514ea906c41SOllivier Robert * station/frequency identifier and reference identifier. Start 25152b15cb3dSCy Schubert * probing at the strongest channel or the default channel if 25162b15cb3dSCy Schubert * nothing heard. 25179c2daa00SOllivier Robert */ 25189c2daa00SOllivier Robert memset(up->mitig, 0, sizeof(up->mitig)); 25199c2daa00SOllivier Robert for (i = 0; i < NCHAN; i++) { 2520a151a66cSOllivier Robert cp = &up->mitig[i]; 25219c2daa00SOllivier Robert cp->gain = up->gain; 25229c2daa00SOllivier Robert cp->wwv.select = SELV; 25232b15cb3dSCy Schubert snprintf(cp->wwv.refid, sizeof(cp->wwv.refid), "WV%.0f", 25242b15cb3dSCy Schubert floor(qsy[i])); 25259c2daa00SOllivier Robert cp->wwvh.select = SELH; 25262b15cb3dSCy Schubert snprintf(cp->wwvh.refid, sizeof(cp->wwvh.refid), "WH%.0f", 25272b15cb3dSCy Schubert floor(qsy[i])); 2528a151a66cSOllivier Robert } 25292b15cb3dSCy Schubert up->dchan = (DCHAN + NCHAN - 1) % NCHAN; 25309c2daa00SOllivier Robert wwv_newchan(peer); 25312b15cb3dSCy Schubert up->schan = up->dchan; 2532a151a66cSOllivier Robert } 2533a151a66cSOllivier Robert 2534a151a66cSOllivier Robert /* 25359c2daa00SOllivier Robert * wwv_metric - compute station metric 25369c2daa00SOllivier Robert * 25379c2daa00SOllivier Robert * The most significant bits represent the number of ones in the 2538ea906c41SOllivier Robert * station reachability register. The least significant bits represent 2539ea906c41SOllivier Robert * the minute sync pulse amplitude. The combined value is scaled 0-100. 2540a151a66cSOllivier Robert */ 25419c2daa00SOllivier Robert double 25429c2daa00SOllivier Robert wwv_metric( 25439c2daa00SOllivier Robert struct sync *sp /* station pointer */ 25449c2daa00SOllivier Robert ) 25459c2daa00SOllivier Robert { 25469c2daa00SOllivier Robert double dtemp; 25479c2daa00SOllivier Robert 2548ea906c41SOllivier Robert dtemp = sp->count * MAXAMP; 2549ea906c41SOllivier Robert if (sp->synmax < MAXAMP) 25509c2daa00SOllivier Robert dtemp += sp->synmax; 25519c2daa00SOllivier Robert else 2552ea906c41SOllivier Robert dtemp += MAXAMP - 1; 2553ea906c41SOllivier Robert dtemp /= (AMAX + 1) * MAXAMP; 25549c2daa00SOllivier Robert return (dtemp * 100.); 2555a151a66cSOllivier Robert } 2556a151a66cSOllivier Robert 2557a151a66cSOllivier Robert 25589c2daa00SOllivier Robert #ifdef ICOM 2559a151a66cSOllivier Robert /* 2560a151a66cSOllivier Robert * wwv_qsy - Tune ICOM receiver 2561a151a66cSOllivier Robert * 2562a151a66cSOllivier Robert * This routine saves the AGC for the current channel, switches to a new 2563a151a66cSOllivier Robert * channel and restores the AGC for that channel. If a tunable receiver 2564a151a66cSOllivier Robert * is not available, just fake it. 2565a151a66cSOllivier Robert */ 2566a151a66cSOllivier Robert static int 2567a151a66cSOllivier Robert wwv_qsy( 2568a151a66cSOllivier Robert struct peer *peer, /* peer structure pointer */ 2569a151a66cSOllivier Robert int chan /* channel */ 2570a151a66cSOllivier Robert ) 2571a151a66cSOllivier Robert { 25729c2daa00SOllivier Robert int rval = 0; 2573a151a66cSOllivier Robert struct refclockproc *pp; 2574a151a66cSOllivier Robert struct wwvunit *up; 2575a151a66cSOllivier Robert 2576a151a66cSOllivier Robert pp = peer->procptr; 25772b15cb3dSCy Schubert up = pp->unitptr; 25789c2daa00SOllivier Robert if (up->fd_icom > 0) { 2579a151a66cSOllivier Robert up->mitig[up->achan].gain = up->gain; 25809c2daa00SOllivier Robert rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, 2581a151a66cSOllivier Robert qsy[chan]); 2582a151a66cSOllivier Robert up->achan = chan; 2583a151a66cSOllivier Robert up->gain = up->mitig[up->achan].gain; 25849c2daa00SOllivier Robert } 2585a151a66cSOllivier Robert return (rval); 2586a151a66cSOllivier Robert } 25879c2daa00SOllivier Robert #endif /* ICOM */ 2588a151a66cSOllivier Robert 2589a151a66cSOllivier Robert 2590a151a66cSOllivier Robert /* 2591a151a66cSOllivier Robert * timecode - assemble timecode string and length 2592a151a66cSOllivier Robert * 2593a151a66cSOllivier Robert * Prettytime format - similar to Spectracom 2594a151a66cSOllivier Robert * 25959c2daa00SOllivier Robert * sq yy ddd hh:mm:ss ld dut lset agc iden sig errs freq avgt 2596a151a66cSOllivier Robert * 2597a151a66cSOllivier Robert * s sync indicator ('?' or ' ') 25989c2daa00SOllivier Robert * q error bits (hex 0-F) 2599a151a66cSOllivier Robert * yyyy year of century 2600a151a66cSOllivier Robert * ddd day of year 2601a151a66cSOllivier Robert * hh hour of day 2602a151a66cSOllivier Robert * mm minute of hour 26039c2daa00SOllivier Robert * ss second of minute) 26049c2daa00SOllivier Robert * l leap second warning (' ' or 'L') 26059c2daa00SOllivier Robert * d DST state ('S', 'D', 'I', or 'O') 26069c2daa00SOllivier Robert * dut DUT sign and magnitude (0.1 s) 2607a151a66cSOllivier Robert * lset minutes since last clock update 2608a151a66cSOllivier Robert * agc audio gain (0-255) 26099c2daa00SOllivier Robert * iden reference identifier (station and frequency) 26109c2daa00SOllivier Robert * sig signal quality (0-100) 2611224ba2bdSOllivier Robert * errs bit errors in last minute 2612224ba2bdSOllivier Robert * freq frequency offset (PPM) 2613224ba2bdSOllivier Robert * avgt averaging time (s) 2614224ba2bdSOllivier Robert */ 2615a151a66cSOllivier Robert static int 2616a151a66cSOllivier Robert timecode( 2617a151a66cSOllivier Robert struct wwvunit *up, /* driver structure pointer */ 26182b15cb3dSCy Schubert char * tc, /* target string */ 26192b15cb3dSCy Schubert size_t tcsiz /* target max chars */ 2620a151a66cSOllivier Robert ) 2621a151a66cSOllivier Robert { 2622a151a66cSOllivier Robert struct sync *sp; 26239c2daa00SOllivier Robert int year, day, hour, minute, second, dut; 26249c2daa00SOllivier Robert char synchar, leapchar, dst; 2625a151a66cSOllivier Robert char cptr[50]; 2626a151a66cSOllivier Robert 2627a151a66cSOllivier Robert 2628a151a66cSOllivier Robert /* 2629a151a66cSOllivier Robert * Common fixed-format fields 2630a151a66cSOllivier Robert */ 2631a151a66cSOllivier Robert synchar = (up->status & INSYNC) ? ' ' : '?'; 26329c2daa00SOllivier Robert year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10 + 26339c2daa00SOllivier Robert 2000; 26349c2daa00SOllivier Robert day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 + 26359c2daa00SOllivier Robert up->decvec[DA + 2].digit * 100; 26369c2daa00SOllivier Robert hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10; 26379c2daa00SOllivier Robert minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10; 26389c2daa00SOllivier Robert second = 0; 2639a151a66cSOllivier Robert leapchar = (up->misc & SECWAR) ? 'L' : ' '; 2640a151a66cSOllivier Robert dst = dstcod[(up->misc >> 4) & 0x3]; 2641a151a66cSOllivier Robert dut = up->misc & 0x7; 2642a151a66cSOllivier Robert if (!(up->misc & DUTS)) 2643a151a66cSOllivier Robert dut = -dut; 26442b15cb3dSCy Schubert snprintf(tc, tcsiz, "%c%1X", synchar, up->alarm); 26452b15cb3dSCy Schubert snprintf(cptr, sizeof(cptr), 26462b15cb3dSCy Schubert " %4d %03d %02d:%02d:%02d %c%c %+d", 26479c2daa00SOllivier Robert year, day, hour, minute, second, leapchar, dst, dut); 26482b15cb3dSCy Schubert strlcat(tc, cptr, tcsiz); 2649a151a66cSOllivier Robert 2650a151a66cSOllivier Robert /* 2651a151a66cSOllivier Robert * Specific variable-format fields 2652a151a66cSOllivier Robert */ 2653a151a66cSOllivier Robert sp = up->sptr; 26542b15cb3dSCy Schubert snprintf(cptr, sizeof(cptr), " %d %d %s %.0f %d %.1f %d", 26552b15cb3dSCy Schubert up->watch, up->mitig[up->dchan].gain, sp->refid, 26562b15cb3dSCy Schubert sp->metric, up->errcnt, up->freq / WWV_SEC * 1e6, 26572b15cb3dSCy Schubert up->avgint); 26582b15cb3dSCy Schubert strlcat(tc, cptr, tcsiz); 26592b15cb3dSCy Schubert 26602b15cb3dSCy Schubert return strlen(tc); 2661a151a66cSOllivier Robert } 2662a151a66cSOllivier Robert 2663a151a66cSOllivier Robert 2664a151a66cSOllivier Robert /* 2665a151a66cSOllivier Robert * wwv_gain - adjust codec gain 2666a151a66cSOllivier Robert * 2667ea906c41SOllivier Robert * This routine is called at the end of each second. During the second 2668ea906c41SOllivier Robert * the number of signal clips above the MAXAMP threshold (6000). If 2669ea906c41SOllivier Robert * there are no clips, the gain is bumped up; if there are more than 2670ea906c41SOllivier Robert * MAXCLP clips (100), it is bumped down. The decoder is relatively 2671ea906c41SOllivier Robert * insensitive to amplitude, so this crudity works just peachy. The 26722b15cb3dSCy Schubert * routine also jiggles the input port and selectively mutes the 26732b15cb3dSCy Schubert * monitor. 2674a151a66cSOllivier Robert */ 2675a151a66cSOllivier Robert static void 2676a151a66cSOllivier Robert wwv_gain( 2677a151a66cSOllivier Robert struct peer *peer /* peer structure pointer */ 2678a151a66cSOllivier Robert ) 2679a151a66cSOllivier Robert { 2680a151a66cSOllivier Robert struct refclockproc *pp; 2681a151a66cSOllivier Robert struct wwvunit *up; 2682a151a66cSOllivier Robert 2683a151a66cSOllivier Robert pp = peer->procptr; 26842b15cb3dSCy Schubert up = pp->unitptr; 2685a151a66cSOllivier Robert 2686a151a66cSOllivier Robert /* 2687a151a66cSOllivier Robert * Apparently, the codec uses only the high order bits of the 2688a151a66cSOllivier Robert * gain control field. Thus, it may take awhile for changes to 2689a151a66cSOllivier Robert * wiggle the hardware bits. 2690a151a66cSOllivier Robert */ 2691a151a66cSOllivier Robert if (up->clipcnt == 0) { 2692a151a66cSOllivier Robert up->gain += 4; 26939c2daa00SOllivier Robert if (up->gain > MAXGAIN) 26949c2daa00SOllivier Robert up->gain = MAXGAIN; 26959c2daa00SOllivier Robert } else if (up->clipcnt > MAXCLP) { 2696a151a66cSOllivier Robert up->gain -= 4; 2697a151a66cSOllivier Robert if (up->gain < 0) 2698a151a66cSOllivier Robert up->gain = 0; 2699a151a66cSOllivier Robert } 27009c2daa00SOllivier Robert audio_gain(up->gain, up->mongain, up->port); 2701a151a66cSOllivier Robert up->clipcnt = 0; 27029c2daa00SOllivier Robert #if DEBUG 27039c2daa00SOllivier Robert if (debug > 1) 27049c2daa00SOllivier Robert audio_show(); 27059c2daa00SOllivier Robert #endif 2706a151a66cSOllivier Robert } 2707a151a66cSOllivier Robert 2708a151a66cSOllivier Robert 2709a151a66cSOllivier Robert #else 2710*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 2711a151a66cSOllivier Robert #endif /* REFCLOCK */ 2712