/src/ntp-dev/ntpd/refclock_wwv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_wwv - clock driver for NIST WWV/H time/frequency station |
3 | | */ |
4 | | #ifdef HAVE_CONFIG_H |
5 | | #include <config.h> |
6 | | #endif |
7 | | |
8 | | #if defined(REFCLOCK) && defined(CLOCK_WWV) |
9 | | |
10 | | #include "ntpd.h" |
11 | | #include "ntp_io.h" |
12 | | #include "ntp_refclock.h" |
13 | | #include "ntp_calendar.h" |
14 | | #include "ntp_stdlib.h" |
15 | | #include "audio.h" |
16 | | |
17 | | #include <stdio.h> |
18 | | #include <ctype.h> |
19 | | #include <math.h> |
20 | | #ifdef HAVE_SYS_IOCTL_H |
21 | | # include <sys/ioctl.h> |
22 | | #endif /* HAVE_SYS_IOCTL_H */ |
23 | | |
24 | | #define ICOM 1 |
25 | | |
26 | | #ifdef ICOM |
27 | | #include "icom.h" |
28 | | #endif /* ICOM */ |
29 | | |
30 | | /* |
31 | | * Audio WWV/H demodulator/decoder |
32 | | * |
33 | | * This driver synchronizes the computer time using data encoded in |
34 | | * radio transmissions from NIST time/frequency stations WWV in Boulder, |
35 | | * CO, and WWVH in Kauai, HI. Transmissions are made continuously on |
36 | | * 2.5, 5, 10 and 15 MHz from WWV and WWVH, and 20 MHz from WWV. An |
37 | | * ordinary AM shortwave receiver can be tuned manually to one of these |
38 | | * frequencies or, in the case of ICOM receivers, the receiver can be |
39 | | * tuned automatically using this program as propagation conditions |
40 | | * change throughout the weasons, both day and night. |
41 | | * |
42 | | * The driver requires an audio codec or sound card with sampling rate 8 |
43 | | * kHz and mu-law companding. This is the same standard as used by the |
44 | | * telephone industry and is supported by most hardware and operating |
45 | | * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this |
46 | | * implementation, only one audio driver and codec can be supported on a |
47 | | * single machine. |
48 | | * |
49 | | * The demodulation and decoding algorithms used in this driver are |
50 | | * based on those developed for the TAPR DSP93 development board and the |
51 | | * TI 320C25 digital signal processor described in: Mills, D.L. A |
52 | | * precision radio clock for WWV transmissions. Electrical Engineering |
53 | | * Report 97-8-1, University of Delaware, August 1997, 25 pp., available |
54 | | * from www.eecis.udel.edu/~mills/reports.html. The algorithms described |
55 | | * in this report have been modified somewhat to improve performance |
56 | | * under weak signal conditions and to provide an automatic station |
57 | | * identification feature. |
58 | | * |
59 | | * The ICOM code is normally compiled in the driver. It isn't used, |
60 | | * unless the mode keyword on the server configuration command specifies |
61 | | * a nonzero ICOM ID select code. The C-IV trace is turned on if the |
62 | | * debug level is greater than one. |
63 | | * |
64 | | * Fudge factors |
65 | | * |
66 | | * Fudge flag4 causes the debugging output described above to be |
67 | | * recorded in the clockstats file. Fudge flag2 selects the audio input |
68 | | * port, where 0 is the mike port (default) and 1 is the line-in port. |
69 | | * It does not seem useful to select the compact disc player port. Fudge |
70 | | * flag3 enables audio monitoring of the input signal. For this purpose, |
71 | | * the monitor gain is set to a default value. |
72 | | * |
73 | | * CEVNT_BADTIME invalid date or time |
74 | | * CEVNT_PROP propagation failure - no stations heard |
75 | | * CEVNT_TIMEOUT timeout (see newgame() below) |
76 | | */ |
77 | | /* |
78 | | * General definitions. These ordinarily do not need to be changed. |
79 | | */ |
80 | 0 | #define DEVICE_AUDIO "/dev/audio" /* audio device name */ |
81 | 0 | #define AUDIO_BUFSIZ 320 /* audio buffer size (50 ms) */ |
82 | 0 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
83 | 0 | #define DESCRIPTION "WWV/H Audio Demodulator/Decoder" /* WRU */ |
84 | 0 | #define WWV_SEC 8000 /* second epoch (sample rate) (Hz) */ |
85 | 0 | #define WWV_MIN (WWV_SEC * 60) /* minute epoch */ |
86 | 0 | #define OFFSET 128 /* companded sample offset */ |
87 | | #define SIZE 256 /* decompanding table size */ |
88 | 0 | #define MAXAMP 6000. /* max signal level reference */ |
89 | 0 | #define MAXCLP 100 /* max clips above reference per s */ |
90 | 0 | #define MAXSNR 40. /* max SNR reference */ |
91 | 0 | #define MAXFREQ 1.5 /* max frequency tolerance (187 PPM) */ |
92 | 0 | #define DATCYC 170 /* data filter cycles */ |
93 | 0 | #define DATSIZ (DATCYC * MS) /* data filter size */ |
94 | 0 | #define SYNCYC 800 /* minute filter cycles */ |
95 | 0 | #define SYNSIZ (SYNCYC * MS) /* minute filter size */ |
96 | 0 | #define TCKCYC 5 /* tick filter cycles */ |
97 | 0 | #define TCKSIZ (TCKCYC * MS) /* tick filter size */ |
98 | 0 | #define NCHAN 5 /* number of radio channels */ |
99 | 0 | #define AUDIO_PHI 5e-6 /* dispersion growth factor */ |
100 | | #define TBUF 128 /* max monitor line length */ |
101 | | |
102 | | /* |
103 | | * Tunable parameters. The DGAIN parameter can be changed to fit the |
104 | | * audio response of the radio at 100 Hz. The WWV/WWVH data subcarrier |
105 | | * is transmitted at about 20 percent percent modulation; the matched |
106 | | * filter boosts it by a factor of 17 and the receiver response does |
107 | | * what it does. The compromise value works for ICOM radios. If the |
108 | | * radio is not tunable, the DCHAN parameter can be changed to fit the |
109 | | * expected best propagation frequency: higher if further from the |
110 | | * transmitter, lower if nearer. The compromise value works for the US |
111 | | * right coast. |
112 | | */ |
113 | 0 | #define DCHAN 3 /* default radio channel (15 Mhz) */ |
114 | 0 | #define DGAIN 5. /* subcarrier gain */ |
115 | | |
116 | | /* |
117 | | * General purpose status bits (status) |
118 | | * |
119 | | * SELV and/or SELH are set when WWV or WWVH have been heard and cleared |
120 | | * on signal loss. SSYNC is set when the second sync pulse has been |
121 | | * acquired and cleared by signal loss. MSYNC is set when the minute |
122 | | * sync pulse has been acquired. DSYNC is set when the units digit has |
123 | | * has reached the threshold and INSYNC is set when all nine digits have |
124 | | * reached the threshold. The MSYNC, DSYNC and INSYNC bits are cleared |
125 | | * only by timeout, upon which the driver starts over from scratch. |
126 | | * |
127 | | * DGATE is lit if the data bit amplitude or SNR is below thresholds and |
128 | | * BGATE is lit if the pulse width amplitude or SNR is below thresolds. |
129 | | * LEPSEC is set during the last minute of the leap day. At the end of |
130 | | * this minute the driver inserts second 60 in the seconds state machine |
131 | | * and the minute sync slips a second. |
132 | | */ |
133 | 0 | #define MSYNC 0x0001 /* minute epoch sync */ |
134 | 0 | #define SSYNC 0x0002 /* second epoch sync */ |
135 | 0 | #define DSYNC 0x0004 /* minute units sync */ |
136 | 0 | #define INSYNC 0x0008 /* clock synchronized */ |
137 | 0 | #define FGATE 0x0010 /* frequency gate */ |
138 | 0 | #define DGATE 0x0020 /* data pulse amplitude error */ |
139 | 0 | #define BGATE 0x0040 /* data pulse width error */ |
140 | 0 | #define METRIC 0x0080 /* one or more stations heard */ |
141 | 0 | #define LEPSEC 0x1000 /* leap minute */ |
142 | | |
143 | | /* |
144 | | * Station scoreboard bits |
145 | | * |
146 | | * These are used to establish the signal quality for each of the five |
147 | | * frequencies and two stations. |
148 | | */ |
149 | 0 | #define SELV 0x0100 /* WWV station select */ |
150 | 0 | #define SELH 0x0200 /* WWVH station select */ |
151 | | |
152 | | /* |
153 | | * Alarm status bits (alarm) |
154 | | * |
155 | | * These bits indicate various alarm conditions, which are decoded to |
156 | | * form the quality character included in the timecode. |
157 | | */ |
158 | 0 | #define CMPERR 0x1 /* digit or misc bit compare error */ |
159 | 0 | #define LOWERR 0x2 /* low bit or digit amplitude or SNR */ |
160 | 0 | #define NINERR 0x4 /* less than nine digits in minute */ |
161 | 0 | #define SYNERR 0x8 /* not tracking second sync */ |
162 | | |
163 | | /* |
164 | | * Watchcat timeouts (watch) |
165 | | * |
166 | | * If these timeouts expire, the status bits are mashed to zero and the |
167 | | * driver starts from scratch. Suitably more refined procedures may be |
168 | | * developed in future. All these are in minutes. |
169 | | */ |
170 | 0 | #define ACQSN 6 /* station acquisition timeout */ |
171 | 0 | #define DATA 15 /* unit minutes timeout */ |
172 | 0 | #define SYNCH 40 /* station sync timeout */ |
173 | 0 | #define PANIC (2 * 1440) /* panic timeout */ |
174 | | |
175 | | /* |
176 | | * Thresholds. These establish the minimum signal level, minimum SNR and |
177 | | * maximum jitter thresholds which establish the error and false alarm |
178 | | * rates of the driver. The values defined here may be on the |
179 | | * adventurous side in the interest of the highest sensitivity. |
180 | | */ |
181 | 0 | #define MTHR 13. /* minute sync gate (percent) */ |
182 | 0 | #define TTHR 50. /* minute sync threshold (percent) */ |
183 | 0 | #define AWND 20 /* minute sync jitter threshold (ms) */ |
184 | 0 | #define ATHR 2500. /* QRZ minute sync threshold */ |
185 | 0 | #define ASNR 20. /* QRZ minute sync SNR threshold (dB) */ |
186 | 0 | #define QTHR 2500. /* QSY minute sync threshold */ |
187 | 0 | #define QSNR 20. /* QSY minute sync SNR threshold (dB) */ |
188 | 0 | #define STHR 2500. /* second sync threshold */ |
189 | 0 | #define SSNR 15. /* second sync SNR threshold (dB) */ |
190 | 0 | #define SCMP 10 /* second sync compare threshold */ |
191 | 0 | #define DTHR 1000. /* bit threshold */ |
192 | 0 | #define DSNR 10. /* bit SNR threshold (dB) */ |
193 | | #define AMIN 3 /* min bit count */ |
194 | 0 | #define AMAX 6 /* max bit count */ |
195 | 0 | #define BTHR 1000. /* digit threshold */ |
196 | 0 | #define BSNR 3. /* digit likelihood threshold (dB) */ |
197 | 0 | #define BCMP 3 /* digit compare threshold */ |
198 | 0 | #define MAXERR 40 /* maximum error alarm */ |
199 | | |
200 | | /* |
201 | | * Tone frequency definitions. The increments are for 4.5-deg sine |
202 | | * table. |
203 | | */ |
204 | 0 | #define MS (WWV_SEC / 1000) /* samples per millisecond */ |
205 | 0 | #define IN100 ((100 * 80) / WWV_SEC) /* 100 Hz increment */ |
206 | 0 | #define IN1000 ((1000 * 80) / WWV_SEC) /* 1000 Hz increment */ |
207 | 0 | #define IN1200 ((1200 * 80) / WWV_SEC) /* 1200 Hz increment */ |
208 | | |
209 | | /* |
210 | | * Acquisition and tracking time constants |
211 | | */ |
212 | 0 | #define MINAVG 8 /* min averaging time */ |
213 | 0 | #define MAXAVG 1024 /* max averaging time */ |
214 | 0 | #define FCONST 3 /* frequency time constant */ |
215 | 0 | #define TCONST 16 /* data bit/digit time constant */ |
216 | | |
217 | | /* |
218 | | * Miscellaneous status bits (misc) |
219 | | * |
220 | | * These bits correspond to designated bits in the WWV/H timecode. The |
221 | | * bit probabilities are exponentially averaged over several minutes and |
222 | | * processed by a integrator and threshold. |
223 | | */ |
224 | | #define DUT1 0x01 /* 56 DUT .1 */ |
225 | | #define DUT2 0x02 /* 57 DUT .2 */ |
226 | | #define DUT4 0x04 /* 58 DUT .4 */ |
227 | 0 | #define DUTS 0x08 /* 50 DUT sign */ |
228 | | #define DST1 0x10 /* 55 DST1 leap warning */ |
229 | | #define DST2 0x20 /* 2 DST2 DST1 delayed one day */ |
230 | 0 | #define SECWAR 0x40 /* 3 leap second warning */ |
231 | | |
232 | | /* |
233 | | * The on-time synchronization point is the positive-going zero crossing |
234 | | * of the first cycle of the 5-ms second pulse. The IIR baseband filter |
235 | | * phase delay is 0.91 ms, while the receiver delay is approximately 4.7 |
236 | | * ms at 1000 Hz. The fudge value -0.45 ms due to the codec and other |
237 | | * causes was determined by calibrating to a PPS signal from a GPS |
238 | | * receiver. The additional propagation delay specific to each receiver |
239 | | * location can be programmed in the fudge time1 and time2 values for |
240 | | * WWV and WWVH, respectively. |
241 | | * |
242 | | * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are |
243 | | * generally within .02 ms short-term with .02 ms jitter. The long-term |
244 | | * offsets vary up to 0.3 ms due to ionosperhic layer height variations. |
245 | | * The processor load due to the driver is 5.8 percent. |
246 | | */ |
247 | 0 | #define PDELAY ((.91 + 4.7 - 0.45) / 1000) /* system delay (s) */ |
248 | | |
249 | | /* |
250 | | * Table of sine values at 4.5-degree increments. This is used by the |
251 | | * synchronous matched filter demodulators. |
252 | | */ |
253 | | double sintab[] = { |
254 | | 0.000000e+00, 7.845910e-02, 1.564345e-01, 2.334454e-01, /* 0-3 */ |
255 | | 3.090170e-01, 3.826834e-01, 4.539905e-01, 5.224986e-01, /* 4-7 */ |
256 | | 5.877853e-01, 6.494480e-01, 7.071068e-01, 7.604060e-01, /* 8-11 */ |
257 | | 8.090170e-01, 8.526402e-01, 8.910065e-01, 9.238795e-01, /* 12-15 */ |
258 | | 9.510565e-01, 9.723699e-01, 9.876883e-01, 9.969173e-01, /* 16-19 */ |
259 | | 1.000000e+00, 9.969173e-01, 9.876883e-01, 9.723699e-01, /* 20-23 */ |
260 | | 9.510565e-01, 9.238795e-01, 8.910065e-01, 8.526402e-01, /* 24-27 */ |
261 | | 8.090170e-01, 7.604060e-01, 7.071068e-01, 6.494480e-01, /* 28-31 */ |
262 | | 5.877853e-01, 5.224986e-01, 4.539905e-01, 3.826834e-01, /* 32-35 */ |
263 | | 3.090170e-01, 2.334454e-01, 1.564345e-01, 7.845910e-02, /* 36-39 */ |
264 | | -0.000000e+00, -7.845910e-02, -1.564345e-01, -2.334454e-01, /* 40-43 */ |
265 | | -3.090170e-01, -3.826834e-01, -4.539905e-01, -5.224986e-01, /* 44-47 */ |
266 | | -5.877853e-01, -6.494480e-01, -7.071068e-01, -7.604060e-01, /* 48-51 */ |
267 | | -8.090170e-01, -8.526402e-01, -8.910065e-01, -9.238795e-01, /* 52-55 */ |
268 | | -9.510565e-01, -9.723699e-01, -9.876883e-01, -9.969173e-01, /* 56-59 */ |
269 | | -1.000000e+00, -9.969173e-01, -9.876883e-01, -9.723699e-01, /* 60-63 */ |
270 | | -9.510565e-01, -9.238795e-01, -8.910065e-01, -8.526402e-01, /* 64-67 */ |
271 | | -8.090170e-01, -7.604060e-01, -7.071068e-01, -6.494480e-01, /* 68-71 */ |
272 | | -5.877853e-01, -5.224986e-01, -4.539905e-01, -3.826834e-01, /* 72-75 */ |
273 | | -3.090170e-01, -2.334454e-01, -1.564345e-01, -7.845910e-02, /* 76-79 */ |
274 | | 0.000000e+00}; /* 80 */ |
275 | | |
276 | | /* |
277 | | * Decoder operations at the end of each second are driven by a state |
278 | | * machine. The transition matrix consists of a dispatch table indexed |
279 | | * by second number. Each entry in the table contains a case switch |
280 | | * number and argument. |
281 | | */ |
282 | | struct progx { |
283 | | int sw; /* case switch number */ |
284 | | int arg; /* argument */ |
285 | | }; |
286 | | |
287 | | /* |
288 | | * Case switch numbers |
289 | | */ |
290 | 0 | #define IDLE 0 /* no operation */ |
291 | 0 | #define COEF 1 /* BCD bit */ |
292 | 0 | #define COEF1 2 /* BCD bit for minute unit */ |
293 | 0 | #define COEF2 3 /* BCD bit not used */ |
294 | 0 | #define DECIM9 4 /* BCD digit 0-9 */ |
295 | 0 | #define DECIM6 5 /* BCD digit 0-6 */ |
296 | 0 | #define DECIM3 6 /* BCD digit 0-3 */ |
297 | 0 | #define DECIM2 7 /* BCD digit 0-2 */ |
298 | 0 | #define MSCBIT 8 /* miscellaneous bit */ |
299 | 0 | #define MSC20 9 /* miscellaneous bit */ |
300 | 0 | #define MSC21 10 /* QSY probe channel */ |
301 | 0 | #define MIN1 11 /* latch time */ |
302 | 0 | #define MIN2 12 /* leap second */ |
303 | 0 | #define SYNC2 13 /* latch minute sync pulse */ |
304 | 0 | #define SYNC3 14 /* latch data pulse */ |
305 | | |
306 | | /* |
307 | | * Offsets in decoding matrix |
308 | | */ |
309 | 0 | #define MN 0 /* minute digits (2) */ |
310 | 0 | #define HR 2 /* hour digits (2) */ |
311 | 0 | #define DA 4 /* day digits (3) */ |
312 | 0 | #define YR 7 /* year digits (2) */ |
313 | | |
314 | | struct progx progx[] = { |
315 | | {SYNC2, 0}, /* 0 latch minute sync pulse */ |
316 | | {SYNC3, 0}, /* 1 latch data pulse */ |
317 | | {MSCBIT, DST2}, /* 2 dst2 */ |
318 | | {MSCBIT, SECWAR}, /* 3 lw */ |
319 | | {COEF, 0}, /* 4 1 year units */ |
320 | | {COEF, 1}, /* 5 2 */ |
321 | | {COEF, 2}, /* 6 4 */ |
322 | | {COEF, 3}, /* 7 8 */ |
323 | | {DECIM9, YR}, /* 8 */ |
324 | | {IDLE, 0}, /* 9 p1 */ |
325 | | {COEF1, 0}, /* 10 1 minute units */ |
326 | | {COEF1, 1}, /* 11 2 */ |
327 | | {COEF1, 2}, /* 12 4 */ |
328 | | {COEF1, 3}, /* 13 8 */ |
329 | | {DECIM9, MN}, /* 14 */ |
330 | | {COEF, 0}, /* 15 10 minute tens */ |
331 | | {COEF, 1}, /* 16 20 */ |
332 | | {COEF, 2}, /* 17 40 */ |
333 | | {COEF2, 3}, /* 18 80 (not used) */ |
334 | | {DECIM6, MN + 1}, /* 19 p2 */ |
335 | | {COEF, 0}, /* 20 1 hour units */ |
336 | | {COEF, 1}, /* 21 2 */ |
337 | | {COEF, 2}, /* 22 4 */ |
338 | | {COEF, 3}, /* 23 8 */ |
339 | | {DECIM9, HR}, /* 24 */ |
340 | | {COEF, 0}, /* 25 10 hour tens */ |
341 | | {COEF, 1}, /* 26 20 */ |
342 | | {COEF2, 2}, /* 27 40 (not used) */ |
343 | | {COEF2, 3}, /* 28 80 (not used) */ |
344 | | {DECIM2, HR + 1}, /* 29 p3 */ |
345 | | {COEF, 0}, /* 30 1 day units */ |
346 | | {COEF, 1}, /* 31 2 */ |
347 | | {COEF, 2}, /* 32 4 */ |
348 | | {COEF, 3}, /* 33 8 */ |
349 | | {DECIM9, DA}, /* 34 */ |
350 | | {COEF, 0}, /* 35 10 day tens */ |
351 | | {COEF, 1}, /* 36 20 */ |
352 | | {COEF, 2}, /* 37 40 */ |
353 | | {COEF, 3}, /* 38 80 */ |
354 | | {DECIM9, DA + 1}, /* 39 p4 */ |
355 | | {COEF, 0}, /* 40 100 day hundreds */ |
356 | | {COEF, 1}, /* 41 200 */ |
357 | | {COEF2, 2}, /* 42 400 (not used) */ |
358 | | {COEF2, 3}, /* 43 800 (not used) */ |
359 | | {DECIM3, DA + 2}, /* 44 */ |
360 | | {IDLE, 0}, /* 45 */ |
361 | | {IDLE, 0}, /* 46 */ |
362 | | {IDLE, 0}, /* 47 */ |
363 | | {IDLE, 0}, /* 48 */ |
364 | | {IDLE, 0}, /* 49 p5 */ |
365 | | {MSCBIT, DUTS}, /* 50 dut+- */ |
366 | | {COEF, 0}, /* 51 10 year tens */ |
367 | | {COEF, 1}, /* 52 20 */ |
368 | | {COEF, 2}, /* 53 40 */ |
369 | | {COEF, 3}, /* 54 80 */ |
370 | | {MSC20, DST1}, /* 55 dst1 */ |
371 | | {MSCBIT, DUT1}, /* 56 0.1 dut */ |
372 | | {MSCBIT, DUT2}, /* 57 0.2 */ |
373 | | {MSC21, DUT4}, /* 58 0.4 QSY probe channel */ |
374 | | {MIN1, 0}, /* 59 p6 latch time */ |
375 | | {MIN2, 0} /* 60 leap second */ |
376 | | }; |
377 | | |
378 | | /* |
379 | | * BCD coefficients for maximum-likelihood digit decode |
380 | | */ |
381 | | #define P15 1. /* max positive number */ |
382 | | #define N15 -1. /* max negative number */ |
383 | | |
384 | | /* |
385 | | * Digits 0-9 |
386 | | */ |
387 | | #define P9 (P15 / 4) /* mark (+1) */ |
388 | | #define N9 (N15 / 4) /* space (-1) */ |
389 | | |
390 | | double bcd9[][4] = { |
391 | | {N9, N9, N9, N9}, /* 0 */ |
392 | | {P9, N9, N9, N9}, /* 1 */ |
393 | | {N9, P9, N9, N9}, /* 2 */ |
394 | | {P9, P9, N9, N9}, /* 3 */ |
395 | | {N9, N9, P9, N9}, /* 4 */ |
396 | | {P9, N9, P9, N9}, /* 5 */ |
397 | | {N9, P9, P9, N9}, /* 6 */ |
398 | | {P9, P9, P9, N9}, /* 7 */ |
399 | | {N9, N9, N9, P9}, /* 8 */ |
400 | | {P9, N9, N9, P9}, /* 9 */ |
401 | | {0, 0, 0, 0} /* backstop */ |
402 | | }; |
403 | | |
404 | | /* |
405 | | * Digits 0-6 (minute tens) |
406 | | */ |
407 | | #define P6 (P15 / 3) /* mark (+1) */ |
408 | | #define N6 (N15 / 3) /* space (-1) */ |
409 | | |
410 | | double bcd6[][4] = { |
411 | | {N6, N6, N6, 0}, /* 0 */ |
412 | | {P6, N6, N6, 0}, /* 1 */ |
413 | | {N6, P6, N6, 0}, /* 2 */ |
414 | | {P6, P6, N6, 0}, /* 3 */ |
415 | | {N6, N6, P6, 0}, /* 4 */ |
416 | | {P6, N6, P6, 0}, /* 5 */ |
417 | | {N6, P6, P6, 0}, /* 6 */ |
418 | | {0, 0, 0, 0} /* backstop */ |
419 | | }; |
420 | | |
421 | | /* |
422 | | * Digits 0-3 (day hundreds) |
423 | | */ |
424 | | #define P3 (P15 / 2) /* mark (+1) */ |
425 | | #define N3 (N15 / 2) /* space (-1) */ |
426 | | |
427 | | double bcd3[][4] = { |
428 | | {N3, N3, 0, 0}, /* 0 */ |
429 | | {P3, N3, 0, 0}, /* 1 */ |
430 | | {N3, P3, 0, 0}, /* 2 */ |
431 | | {P3, P3, 0, 0}, /* 3 */ |
432 | | {0, 0, 0, 0} /* backstop */ |
433 | | }; |
434 | | |
435 | | /* |
436 | | * Digits 0-2 (hour tens) |
437 | | */ |
438 | | #define P2 (P15 / 2) /* mark (+1) */ |
439 | | #define N2 (N15 / 2) /* space (-1) */ |
440 | | |
441 | | double bcd2[][4] = { |
442 | | {N2, N2, 0, 0}, /* 0 */ |
443 | | {P2, N2, 0, 0}, /* 1 */ |
444 | | {N2, P2, 0, 0}, /* 2 */ |
445 | | {0, 0, 0, 0} /* backstop */ |
446 | | }; |
447 | | |
448 | | /* |
449 | | * DST decode (DST2 DST1) for prettyprint |
450 | | */ |
451 | | char dstcod[] = { |
452 | | 'S', /* 00 standard time */ |
453 | | 'I', /* 01 set clock ahead at 0200 local */ |
454 | | 'O', /* 10 set clock back at 0200 local */ |
455 | | 'D' /* 11 daylight time */ |
456 | | }; |
457 | | |
458 | | /* |
459 | | * The decoding matrix consists of nine row vectors, one for each digit |
460 | | * of the timecode. The digits are stored from least to most significant |
461 | | * order. The maximum-likelihood timecode is formed from the digits |
462 | | * corresponding to the maximum-likelihood values reading in the |
463 | | * opposite order: yy ddd hh:mm. |
464 | | */ |
465 | | struct decvec { |
466 | | int radix; /* radix (3, 4, 6, 10) */ |
467 | | int digit; /* current clock digit */ |
468 | | int count; /* match count */ |
469 | | double digprb; /* max digit probability */ |
470 | | double digsnr; /* likelihood function (dB) */ |
471 | | double like[10]; /* likelihood integrator 0-9 */ |
472 | | }; |
473 | | |
474 | | /* |
475 | | * The station structure (sp) is used to acquire the minute pulse from |
476 | | * WWV and/or WWVH. These stations are distinguished by the frequency |
477 | | * used for the second and minute sync pulses, 1000 Hz for WWV and 1200 |
478 | | * Hz for WWVH. Other than frequency, the format is the same. |
479 | | */ |
480 | | struct sync { |
481 | | double epoch; /* accumulated epoch differences */ |
482 | | double maxeng; /* sync max energy */ |
483 | | double noieng; /* sync noise energy */ |
484 | | long pos; /* max amplitude position */ |
485 | | long lastpos; /* last max position */ |
486 | | long mepoch; /* minute synch epoch */ |
487 | | |
488 | | double amp; /* sync signal */ |
489 | | double syneng; /* sync signal max */ |
490 | | double synmax; /* sync signal max latched at 0 s */ |
491 | | double synsnr; /* sync signal SNR */ |
492 | | double metric; /* signal quality metric */ |
493 | | int reach; /* reachability register */ |
494 | | int count; /* bit counter */ |
495 | | int select; /* select bits */ |
496 | | char refid[5]; /* reference identifier */ |
497 | | }; |
498 | | |
499 | | /* |
500 | | * The channel structure (cp) is used to mitigate between channels. |
501 | | */ |
502 | | struct chan { |
503 | | int gain; /* audio gain */ |
504 | | struct sync wwv; /* wwv station */ |
505 | | struct sync wwvh; /* wwvh station */ |
506 | | }; |
507 | | |
508 | | /* |
509 | | * WWV unit control structure (up) |
510 | | */ |
511 | | struct wwvunit { |
512 | | l_fp timestamp; /* audio sample timestamp */ |
513 | | l_fp tick; /* audio sample increment */ |
514 | | double phase, freq; /* logical clock phase and frequency */ |
515 | | double monitor; /* audio monitor point */ |
516 | | double pdelay; /* propagation delay (s) */ |
517 | | #ifdef ICOM |
518 | | int fd_icom; /* ICOM file descriptor */ |
519 | | #endif /* ICOM */ |
520 | | int errflg; /* error flags */ |
521 | | int watch; /* watchcat */ |
522 | | |
523 | | /* |
524 | | * Audio codec variables |
525 | | */ |
526 | | double comp[SIZE]; /* decompanding table */ |
527 | | int port; /* codec port */ |
528 | | int gain; /* codec gain */ |
529 | | int mongain; /* codec monitor gain */ |
530 | | int clipcnt; /* sample clipped count */ |
531 | | |
532 | | /* |
533 | | * Variables used to establish basic system timing |
534 | | */ |
535 | | int avgint; /* master time constant */ |
536 | | int yepoch; /* sync epoch */ |
537 | | int repoch; /* buffered sync epoch */ |
538 | | double epomax; /* second sync amplitude */ |
539 | | double eposnr; /* second sync SNR */ |
540 | | double irig; /* data I channel amplitude */ |
541 | | double qrig; /* data Q channel amplitude */ |
542 | | int datapt; /* 100 Hz ramp */ |
543 | | double datpha; /* 100 Hz VFO control */ |
544 | | int rphase; /* second sample counter */ |
545 | | long mphase; /* minute sample counter */ |
546 | | |
547 | | /* |
548 | | * Variables used to mitigate which channel to use |
549 | | */ |
550 | | struct chan mitig[NCHAN]; /* channel data */ |
551 | | struct sync *sptr; /* station pointer */ |
552 | | int dchan; /* data channel */ |
553 | | int schan; /* probe channel */ |
554 | | int achan; /* active channel */ |
555 | | |
556 | | /* |
557 | | * Variables used by the clock state machine |
558 | | */ |
559 | | struct decvec decvec[9]; /* decoding matrix */ |
560 | | int rsec; /* seconds counter */ |
561 | | int digcnt; /* count of digits synchronized */ |
562 | | |
563 | | /* |
564 | | * Variables used to estimate signal levels and bit/digit |
565 | | * probabilities |
566 | | */ |
567 | | double datsig; /* data signal max */ |
568 | | double datsnr; /* data signal SNR (dB) */ |
569 | | |
570 | | /* |
571 | | * Variables used to establish status and alarm conditions |
572 | | */ |
573 | | int status; /* status bits */ |
574 | | int alarm; /* alarm flashers */ |
575 | | int misc; /* miscellaneous timecode bits */ |
576 | | int errcnt; /* data bit error counter */ |
577 | | }; |
578 | | |
579 | | /* |
580 | | * Function prototypes |
581 | | */ |
582 | | static int wwv_start (int, struct peer *); |
583 | | static void wwv_shutdown (int, struct peer *); |
584 | | static void wwv_receive (struct recvbuf *); |
585 | | static void wwv_poll (int, struct peer *); |
586 | | |
587 | | /* |
588 | | * More function prototypes |
589 | | */ |
590 | | static void wwv_epoch (struct peer *); |
591 | | static void wwv_rf (struct peer *, double); |
592 | | static void wwv_endpoc (struct peer *, int); |
593 | | static void wwv_rsec (struct peer *, double); |
594 | | static void wwv_qrz (struct peer *, struct sync *, int); |
595 | | static void wwv_corr4 (struct peer *, struct decvec *, |
596 | | double [], double [][4]); |
597 | | static void wwv_gain (struct peer *); |
598 | | static void wwv_tsec (struct peer *); |
599 | | static int timecode (struct wwvunit *, char *, size_t); |
600 | | static double wwv_snr (double, double); |
601 | | static int carry (struct decvec *); |
602 | | static int wwv_newchan (struct peer *); |
603 | | static void wwv_newgame (struct peer *); |
604 | | static double wwv_metric (struct sync *); |
605 | | static void wwv_clock (struct peer *); |
606 | | #ifdef ICOM |
607 | | static int wwv_qsy (struct peer *, int); |
608 | | #endif /* ICOM */ |
609 | | |
610 | | static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */ |
611 | | |
612 | | /* |
613 | | * Transfer vector |
614 | | */ |
615 | | struct refclock refclock_wwv = { |
616 | | wwv_start, /* start up driver */ |
617 | | wwv_shutdown, /* shut down driver */ |
618 | | wwv_poll, /* transmit poll message */ |
619 | | noentry, /* not used (old wwv_control) */ |
620 | | noentry, /* initialize driver (not used) */ |
621 | | noentry, /* not used (old wwv_buginfo) */ |
622 | | NOFLAGS /* not used */ |
623 | | }; |
624 | | |
625 | | |
626 | | /* |
627 | | * wwv_start - open the devices and initialize data for processing |
628 | | */ |
629 | | static int |
630 | | wwv_start( |
631 | | int unit, /* instance number (used by PCM) */ |
632 | | struct peer *peer /* peer structure pointer */ |
633 | | ) |
634 | 0 | { |
635 | 0 | struct refclockproc *pp; |
636 | 0 | struct wwvunit *up; |
637 | 0 | #ifdef ICOM |
638 | 0 | int temp; |
639 | 0 | #endif /* ICOM */ |
640 | | |
641 | | /* |
642 | | * Local variables |
643 | | */ |
644 | 0 | int fd; /* file descriptor */ |
645 | 0 | int i; /* index */ |
646 | 0 | double step; /* codec adjustment */ |
647 | | |
648 | | /* |
649 | | * Open audio device |
650 | | */ |
651 | 0 | fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit); |
652 | 0 | if (fd < 0) |
653 | 0 | return (0); |
654 | 0 | #ifdef DEBUG |
655 | 0 | if (debug) |
656 | 0 | audio_show(); |
657 | 0 | #endif /* DEBUG */ |
658 | | |
659 | | /* |
660 | | * Allocate and initialize unit structure |
661 | | */ |
662 | 0 | up = emalloc_zero(sizeof(*up)); |
663 | 0 | pp = peer->procptr; |
664 | 0 | pp->io.clock_recv = wwv_receive; |
665 | 0 | pp->io.srcclock = peer; |
666 | 0 | pp->io.datalen = 0; |
667 | 0 | pp->io.fd = fd; |
668 | 0 | if (!io_addclock(&pp->io)) { |
669 | 0 | close(fd); |
670 | 0 | free(up); |
671 | 0 | return (0); |
672 | 0 | } |
673 | 0 | pp->unitptr = up; |
674 | | |
675 | | /* |
676 | | * Initialize miscellaneous variables |
677 | | */ |
678 | 0 | peer->precision = PRECISION; |
679 | 0 | pp->clockdesc = DESCRIPTION; |
680 | | |
681 | | /* |
682 | | * The companded samples are encoded sign-magnitude. The table |
683 | | * contains all the 256 values in the interest of speed. |
684 | | */ |
685 | 0 | up->comp[0] = up->comp[OFFSET] = 0.; |
686 | 0 | up->comp[1] = 1.; up->comp[OFFSET + 1] = -1.; |
687 | 0 | up->comp[2] = 3.; up->comp[OFFSET + 2] = -3.; |
688 | 0 | step = 2.; |
689 | 0 | for (i = 3; i < OFFSET; i++) { |
690 | 0 | up->comp[i] = up->comp[i - 1] + step; |
691 | 0 | up->comp[OFFSET + i] = -up->comp[i]; |
692 | 0 | if (i % 16 == 0) |
693 | 0 | step *= 2.; |
694 | 0 | } |
695 | 0 | DTOLFP(1. / WWV_SEC, &up->tick); |
696 | | |
697 | | /* |
698 | | * Initialize the decoding matrix with the radix for each digit |
699 | | * position. |
700 | | */ |
701 | 0 | up->decvec[MN].radix = 10; /* minutes */ |
702 | 0 | up->decvec[MN + 1].radix = 6; |
703 | 0 | up->decvec[HR].radix = 10; /* hours */ |
704 | 0 | up->decvec[HR + 1].radix = 3; |
705 | 0 | up->decvec[DA].radix = 10; /* days */ |
706 | 0 | up->decvec[DA + 1].radix = 10; |
707 | 0 | up->decvec[DA + 2].radix = 4; |
708 | 0 | up->decvec[YR].radix = 10; /* years */ |
709 | 0 | up->decvec[YR + 1].radix = 10; |
710 | |
|
711 | 0 | #ifdef ICOM |
712 | | /* |
713 | | * Initialize autotune if available. Note that the ICOM select |
714 | | * code must be less than 128, so the high order bit can be used |
715 | | * to select the line speed 0 (9600 bps) or 1 (1200 bps). Note |
716 | | * we don't complain if the ICOM device is not there; but, if it |
717 | | * is, the radio better be working. |
718 | | */ |
719 | 0 | temp = 0; |
720 | 0 | #ifdef DEBUG |
721 | 0 | if (debug > 1) |
722 | 0 | temp = P_TRACE; |
723 | 0 | #endif /* DEBUG */ |
724 | 0 | if (peer->ttl != 0) { |
725 | 0 | if (peer->ttl & 0x80) |
726 | 0 | up->fd_icom = icom_init("/dev/icom", B1200, |
727 | 0 | temp); |
728 | 0 | else |
729 | 0 | up->fd_icom = icom_init("/dev/icom", B9600, |
730 | 0 | temp); |
731 | 0 | } |
732 | 0 | if (up->fd_icom > 0) { |
733 | 0 | if (wwv_qsy(peer, DCHAN) != 0) { |
734 | 0 | msyslog(LOG_NOTICE, "icom: radio not found"); |
735 | 0 | close(up->fd_icom); |
736 | 0 | up->fd_icom = 0; |
737 | 0 | } else { |
738 | 0 | msyslog(LOG_NOTICE, "icom: autotune enabled"); |
739 | 0 | } |
740 | 0 | } |
741 | 0 | #endif /* ICOM */ |
742 | | |
743 | | /* |
744 | | * Let the games begin. |
745 | | */ |
746 | 0 | wwv_newgame(peer); |
747 | 0 | return (1); |
748 | 0 | } |
749 | | |
750 | | |
751 | | /* |
752 | | * wwv_shutdown - shut down the clock |
753 | | */ |
754 | | static void |
755 | | wwv_shutdown( |
756 | | int unit, /* instance number (not used) */ |
757 | | struct peer *peer /* peer structure pointer */ |
758 | | ) |
759 | 0 | { |
760 | 0 | struct refclockproc *pp; |
761 | 0 | struct wwvunit *up; |
762 | |
|
763 | 0 | pp = peer->procptr; |
764 | 0 | up = pp->unitptr; |
765 | 0 | if (up == NULL) |
766 | 0 | return; |
767 | | |
768 | 0 | io_closeclock(&pp->io); |
769 | 0 | #ifdef ICOM |
770 | 0 | if (up->fd_icom > 0) |
771 | 0 | close(up->fd_icom); |
772 | 0 | #endif /* ICOM */ |
773 | 0 | free(up); |
774 | 0 | } |
775 | | |
776 | | |
777 | | /* |
778 | | * wwv_receive - receive data from the audio device |
779 | | * |
780 | | * This routine reads input samples and adjusts the logical clock to |
781 | | * track the A/D sample clock by dropping or duplicating codec samples. |
782 | | * It also controls the A/D signal level with an AGC loop to mimimize |
783 | | * quantization noise and avoid overload. |
784 | | */ |
785 | | static void |
786 | | wwv_receive( |
787 | | struct recvbuf *rbufp /* receive buffer structure pointer */ |
788 | | ) |
789 | 0 | { |
790 | 0 | struct peer *peer; |
791 | 0 | struct refclockproc *pp; |
792 | 0 | struct wwvunit *up; |
793 | | |
794 | | /* |
795 | | * Local variables |
796 | | */ |
797 | 0 | double sample; /* codec sample */ |
798 | 0 | u_char *dpt; /* buffer pointer */ |
799 | 0 | int bufcnt; /* buffer counter */ |
800 | 0 | l_fp ltemp; |
801 | |
|
802 | 0 | peer = rbufp->recv_peer; |
803 | 0 | pp = peer->procptr; |
804 | 0 | up = pp->unitptr; |
805 | | |
806 | | /* |
807 | | * Main loop - read until there ain't no more. Note codec |
808 | | * samples are bit-inverted. |
809 | | */ |
810 | 0 | DTOLFP((double)rbufp->recv_length / WWV_SEC, <emp); |
811 | 0 | L_SUB(&rbufp->recv_time, <emp); |
812 | 0 | up->timestamp = rbufp->recv_time; |
813 | 0 | dpt = rbufp->recv_buffer; |
814 | 0 | for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) { |
815 | 0 | sample = up->comp[~*dpt++ & 0xff]; |
816 | | |
817 | | /* |
818 | | * Clip noise spikes greater than MAXAMP (6000) and |
819 | | * record the number of clips to be used later by the |
820 | | * AGC. |
821 | | */ |
822 | 0 | if (sample > MAXAMP) { |
823 | 0 | sample = MAXAMP; |
824 | 0 | up->clipcnt++; |
825 | 0 | } else if (sample < -MAXAMP) { |
826 | 0 | sample = -MAXAMP; |
827 | 0 | up->clipcnt++; |
828 | 0 | } |
829 | | |
830 | | /* |
831 | | * Variable frequency oscillator. The codec oscillator |
832 | | * runs at the nominal rate of 8000 samples per second, |
833 | | * or 125 us per sample. A frequency change of one unit |
834 | | * results in either duplicating or deleting one sample |
835 | | * per second, which results in a frequency change of |
836 | | * 125 PPM. |
837 | | */ |
838 | 0 | up->phase += (up->freq + clock_codec) / WWV_SEC; |
839 | 0 | if (up->phase >= .5) { |
840 | 0 | up->phase -= 1.; |
841 | 0 | } else if (up->phase < -.5) { |
842 | 0 | up->phase += 1.; |
843 | 0 | wwv_rf(peer, sample); |
844 | 0 | wwv_rf(peer, sample); |
845 | 0 | } else { |
846 | 0 | wwv_rf(peer, sample); |
847 | 0 | } |
848 | 0 | L_ADD(&up->timestamp, &up->tick); |
849 | 0 | } |
850 | | |
851 | | /* |
852 | | * Set the input port and monitor gain for the next buffer. |
853 | | */ |
854 | 0 | if (pp->sloppyclockflag & CLK_FLAG2) |
855 | 0 | up->port = 2; |
856 | 0 | else |
857 | 0 | up->port = 1; |
858 | 0 | if (pp->sloppyclockflag & CLK_FLAG3) |
859 | 0 | up->mongain = MONGAIN; |
860 | 0 | else |
861 | 0 | up->mongain = 0; |
862 | 0 | } |
863 | | |
864 | | |
865 | | /* |
866 | | * wwv_poll - called by the transmit procedure |
867 | | * |
868 | | * This routine keeps track of status. If no offset samples have been |
869 | | * processed during a poll interval, a timeout event is declared. If |
870 | | * errors have have occurred during the interval, they are reported as |
871 | | * well. |
872 | | */ |
873 | | static void |
874 | | wwv_poll( |
875 | | int unit, /* instance number (not used) */ |
876 | | struct peer *peer /* peer structure pointer */ |
877 | | ) |
878 | 0 | { |
879 | 0 | struct refclockproc *pp; |
880 | 0 | struct wwvunit *up; |
881 | |
|
882 | 0 | pp = peer->procptr; |
883 | 0 | up = pp->unitptr; |
884 | 0 | if (up->errflg) |
885 | 0 | refclock_report(peer, up->errflg); |
886 | 0 | up->errflg = 0; |
887 | 0 | pp->polls++; |
888 | 0 | } |
889 | | |
890 | | |
891 | | /* |
892 | | * wwv_rf - process signals and demodulate to baseband |
893 | | * |
894 | | * This routine grooms and filters decompanded raw audio samples. The |
895 | | * output signal is the 100-Hz filtered baseband data signal in |
896 | | * quadrature phase. The routine also determines the minute synch epoch, |
897 | | * as well as certain signal maxima, minima and related values. |
898 | | * |
899 | | * There are two 1-s ramps used by this program. Both count the 8000 |
900 | | * logical clock samples spanning exactly one second. The epoch ramp |
901 | | * counts the samples starting at an arbitrary time. The rphase ramp |
902 | | * counts the samples starting at the 5-ms second sync pulse found |
903 | | * during the epoch ramp. |
904 | | * |
905 | | * There are two 1-m ramps used by this program. The mphase ramp counts |
906 | | * the 480,000 logical clock samples spanning exactly one minute and |
907 | | * starting at an arbitrary time. The rsec ramp counts the 60 seconds of |
908 | | * the minute starting at the 800-ms minute sync pulse found during the |
909 | | * mphase ramp. The rsec ramp drives the seconds state machine to |
910 | | * determine the bits and digits of the timecode. |
911 | | * |
912 | | * Demodulation operations are based on three synthesized quadrature |
913 | | * sinusoids: 100 Hz for the data signal, 1000 Hz for the WWV sync |
914 | | * signal and 1200 Hz for the WWVH sync signal. These drive synchronous |
915 | | * matched filters for the data signal (170 ms at 100 Hz), WWV minute |
916 | | * sync signal (800 ms at 1000 Hz) and WWVH minute sync signal (800 ms |
917 | | * at 1200 Hz). Two additional matched filters are switched in |
918 | | * as required for the WWV second sync signal (5 cycles at 1000 Hz) and |
919 | | * WWVH second sync signal (6 cycles at 1200 Hz). |
920 | | */ |
921 | | static void |
922 | | wwv_rf( |
923 | | struct peer *peer, /* peerstructure pointer */ |
924 | | double isig /* input signal */ |
925 | | ) |
926 | 0 | { |
927 | 0 | struct refclockproc *pp; |
928 | 0 | struct wwvunit *up; |
929 | 0 | struct sync *sp, *rp; |
930 | |
|
931 | 0 | static double lpf[5]; /* 150-Hz lpf delay line */ |
932 | 0 | double data; /* lpf output */ |
933 | 0 | static double bpf[9]; /* 1000/1200-Hz bpf delay line */ |
934 | 0 | double syncx; /* bpf output */ |
935 | 0 | static double mf[41]; /* 1000/1200-Hz mf delay line */ |
936 | 0 | double mfsync; /* mf output */ |
937 | |
|
938 | 0 | static int iptr; /* data channel pointer */ |
939 | 0 | static double ibuf[DATSIZ]; /* data I channel delay line */ |
940 | 0 | static double qbuf[DATSIZ]; /* data Q channel delay line */ |
941 | |
|
942 | 0 | static int jptr; /* sync channel pointer */ |
943 | 0 | static int kptr; /* tick channel pointer */ |
944 | |
|
945 | 0 | static int csinptr; /* wwv channel phase */ |
946 | 0 | static double cibuf[SYNSIZ]; /* wwv I channel delay line */ |
947 | 0 | static double cqbuf[SYNSIZ]; /* wwv Q channel delay line */ |
948 | 0 | static double ciamp; /* wwv I channel amplitude */ |
949 | 0 | static double cqamp; /* wwv Q channel amplitude */ |
950 | |
|
951 | 0 | static double csibuf[TCKSIZ]; /* wwv I tick delay line */ |
952 | 0 | static double csqbuf[TCKSIZ]; /* wwv Q tick delay line */ |
953 | 0 | static double csiamp; /* wwv I tick amplitude */ |
954 | 0 | static double csqamp; /* wwv Q tick amplitude */ |
955 | |
|
956 | 0 | static int hsinptr; /* wwvh channel phase */ |
957 | 0 | static double hibuf[SYNSIZ]; /* wwvh I channel delay line */ |
958 | 0 | static double hqbuf[SYNSIZ]; /* wwvh Q channel delay line */ |
959 | 0 | static double hiamp; /* wwvh I channel amplitude */ |
960 | 0 | static double hqamp; /* wwvh Q channel amplitude */ |
961 | |
|
962 | 0 | static double hsibuf[TCKSIZ]; /* wwvh I tick delay line */ |
963 | 0 | static double hsqbuf[TCKSIZ]; /* wwvh Q tick delay line */ |
964 | 0 | static double hsiamp; /* wwvh I tick amplitude */ |
965 | 0 | static double hsqamp; /* wwvh Q tick amplitude */ |
966 | |
|
967 | 0 | static double epobuf[WWV_SEC]; /* second sync comb filter */ |
968 | 0 | static double epomax, nxtmax; /* second sync amplitude buffer */ |
969 | 0 | static int epopos; /* epoch second sync position buffer */ |
970 | |
|
971 | 0 | static int iniflg; /* initialization flag */ |
972 | 0 | int epoch; /* comb filter index */ |
973 | 0 | double dtemp; |
974 | 0 | int i; |
975 | |
|
976 | 0 | pp = peer->procptr; |
977 | 0 | up = pp->unitptr; |
978 | |
|
979 | 0 | if (!iniflg) { |
980 | 0 | iniflg = 1; |
981 | 0 | memset((char *)lpf, 0, sizeof(lpf)); |
982 | 0 | memset((char *)bpf, 0, sizeof(bpf)); |
983 | 0 | memset((char *)mf, 0, sizeof(mf)); |
984 | 0 | memset((char *)ibuf, 0, sizeof(ibuf)); |
985 | 0 | memset((char *)qbuf, 0, sizeof(qbuf)); |
986 | 0 | memset((char *)cibuf, 0, sizeof(cibuf)); |
987 | 0 | memset((char *)cqbuf, 0, sizeof(cqbuf)); |
988 | 0 | memset((char *)csibuf, 0, sizeof(csibuf)); |
989 | 0 | memset((char *)csqbuf, 0, sizeof(csqbuf)); |
990 | 0 | memset((char *)hibuf, 0, sizeof(hibuf)); |
991 | 0 | memset((char *)hqbuf, 0, sizeof(hqbuf)); |
992 | 0 | memset((char *)hsibuf, 0, sizeof(hsibuf)); |
993 | 0 | memset((char *)hsqbuf, 0, sizeof(hsqbuf)); |
994 | 0 | memset((char *)epobuf, 0, sizeof(epobuf)); |
995 | 0 | } |
996 | | |
997 | | /* |
998 | | * Baseband data demodulation. The 100-Hz subcarrier is |
999 | | * extracted using a 150-Hz IIR lowpass filter. This attenuates |
1000 | | * the 1000/1200-Hz sync signals, as well as the 440-Hz and |
1001 | | * 600-Hz tones and most of the noise and voice modulation |
1002 | | * components. |
1003 | | * |
1004 | | * The subcarrier is transmitted 10 dB down from the carrier. |
1005 | | * The DGAIN parameter can be adjusted for this and to |
1006 | | * compensate for the radio audio response at 100 Hz. |
1007 | | * |
1008 | | * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB |
1009 | | * passband ripple, -50 dB stopband ripple, phase delay 0.97 ms. |
1010 | | */ |
1011 | 0 | data = (lpf[4] = lpf[3]) * 8.360961e-01; |
1012 | 0 | data += (lpf[3] = lpf[2]) * -3.481740e+00; |
1013 | 0 | data += (lpf[2] = lpf[1]) * 5.452988e+00; |
1014 | 0 | data += (lpf[1] = lpf[0]) * -3.807229e+00; |
1015 | 0 | lpf[0] = isig * DGAIN - data; |
1016 | 0 | data = lpf[0] * 3.281435e-03 |
1017 | 0 | + lpf[1] * -1.149947e-02 |
1018 | 0 | + lpf[2] * 1.654858e-02 |
1019 | 0 | + lpf[3] * -1.149947e-02 |
1020 | 0 | + lpf[4] * 3.281435e-03; |
1021 | | |
1022 | | /* |
1023 | | * The 100-Hz data signal is demodulated using a pair of |
1024 | | * quadrature multipliers, matched filters and a phase lock |
1025 | | * loop. The I and Q quadrature data signals are produced by |
1026 | | * multiplying the filtered signal by 100-Hz sine and cosine |
1027 | | * signals, respectively. The signals are processed by 170-ms |
1028 | | * synchronous matched filters to produce the amplitude and |
1029 | | * phase signals used by the demodulator. The signals are scaled |
1030 | | * to produce unit energy at the maximum value. |
1031 | | */ |
1032 | 0 | i = up->datapt; |
1033 | 0 | up->datapt = (up->datapt + IN100) % 80; |
1034 | 0 | dtemp = sintab[i] * data / (MS / 2. * DATCYC); |
1035 | 0 | up->irig -= ibuf[iptr]; |
1036 | 0 | ibuf[iptr] = dtemp; |
1037 | 0 | up->irig += dtemp; |
1038 | |
|
1039 | 0 | i = (i + 20) % 80; |
1040 | 0 | dtemp = sintab[i] * data / (MS / 2. * DATCYC); |
1041 | 0 | up->qrig -= qbuf[iptr]; |
1042 | 0 | qbuf[iptr] = dtemp; |
1043 | 0 | up->qrig += dtemp; |
1044 | 0 | iptr = (iptr + 1) % DATSIZ; |
1045 | | |
1046 | | /* |
1047 | | * Baseband sync demodulation. The 1000/1200 sync signals are |
1048 | | * extracted using a 600-Hz IIR bandpass filter. This removes |
1049 | | * the 100-Hz data subcarrier, as well as the 440-Hz and 600-Hz |
1050 | | * tones and most of the noise and voice modulation components. |
1051 | | * |
1052 | | * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB |
1053 | | * passband ripple, -50 dB stopband ripple, phase delay 0.91 ms. |
1054 | | */ |
1055 | 0 | syncx = (bpf[8] = bpf[7]) * 4.897278e-01; |
1056 | 0 | syncx += (bpf[7] = bpf[6]) * -2.765914e+00; |
1057 | 0 | syncx += (bpf[6] = bpf[5]) * 8.110921e+00; |
1058 | 0 | syncx += (bpf[5] = bpf[4]) * -1.517732e+01; |
1059 | 0 | syncx += (bpf[4] = bpf[3]) * 1.975197e+01; |
1060 | 0 | syncx += (bpf[3] = bpf[2]) * -1.814365e+01; |
1061 | 0 | syncx += (bpf[2] = bpf[1]) * 1.159783e+01; |
1062 | 0 | syncx += (bpf[1] = bpf[0]) * -4.735040e+00; |
1063 | 0 | bpf[0] = isig - syncx; |
1064 | 0 | syncx = bpf[0] * 8.203628e-03 |
1065 | 0 | + bpf[1] * -2.375732e-02 |
1066 | 0 | + bpf[2] * 3.353214e-02 |
1067 | 0 | + bpf[3] * -4.080258e-02 |
1068 | 0 | + bpf[4] * 4.605479e-02 |
1069 | 0 | + bpf[5] * -4.080258e-02 |
1070 | 0 | + bpf[6] * 3.353214e-02 |
1071 | 0 | + bpf[7] * -2.375732e-02 |
1072 | 0 | + bpf[8] * 8.203628e-03; |
1073 | | |
1074 | | /* |
1075 | | * The 1000/1200 sync signals are demodulated using a pair of |
1076 | | * quadrature multipliers and matched filters. However, |
1077 | | * synchronous demodulation at these frequencies is impractical, |
1078 | | * so only the signal amplitude is used. The I and Q quadrature |
1079 | | * sync signals are produced by multiplying the filtered signal |
1080 | | * by 1000-Hz (WWV) and 1200-Hz (WWVH) sine and cosine signals, |
1081 | | * respectively. The WWV and WWVH signals are processed by 800- |
1082 | | * ms synchronous matched filters and combined to produce the |
1083 | | * minute sync signal and detect which one (or both) the WWV or |
1084 | | * WWVH signal is present. The WWV and WWVH signals are also |
1085 | | * processed by 5-ms synchronous matched filters and combined to |
1086 | | * produce the second sync signal. The signals are scaled to |
1087 | | * produce unit energy at the maximum value. |
1088 | | * |
1089 | | * Note the master timing ramps, which run continuously. The |
1090 | | * minute counter (mphase) counts the samples in the minute, |
1091 | | * while the second counter (epoch) counts the samples in the |
1092 | | * second. |
1093 | | */ |
1094 | 0 | up->mphase = (up->mphase + 1) % WWV_MIN; |
1095 | 0 | epoch = up->mphase % WWV_SEC; |
1096 | | |
1097 | | /* |
1098 | | * WWV |
1099 | | */ |
1100 | 0 | i = csinptr; |
1101 | 0 | csinptr = (csinptr + IN1000) % 80; |
1102 | |
|
1103 | 0 | dtemp = sintab[i] * syncx / (MS / 2.); |
1104 | 0 | ciamp -= cibuf[jptr]; |
1105 | 0 | cibuf[jptr] = dtemp; |
1106 | 0 | ciamp += dtemp; |
1107 | 0 | csiamp -= csibuf[kptr]; |
1108 | 0 | csibuf[kptr] = dtemp; |
1109 | 0 | csiamp += dtemp; |
1110 | |
|
1111 | 0 | i = (i + 20) % 80; |
1112 | 0 | dtemp = sintab[i] * syncx / (MS / 2.); |
1113 | 0 | cqamp -= cqbuf[jptr]; |
1114 | 0 | cqbuf[jptr] = dtemp; |
1115 | 0 | cqamp += dtemp; |
1116 | 0 | csqamp -= csqbuf[kptr]; |
1117 | 0 | csqbuf[kptr] = dtemp; |
1118 | 0 | csqamp += dtemp; |
1119 | |
|
1120 | 0 | sp = &up->mitig[up->achan].wwv; |
1121 | 0 | sp->amp = sqrt(ciamp * ciamp + cqamp * cqamp) / SYNCYC; |
1122 | 0 | if (!(up->status & MSYNC)) |
1123 | 0 | wwv_qrz(peer, sp, (int)(pp->fudgetime1 * WWV_SEC)); |
1124 | | |
1125 | | /* |
1126 | | * WWVH |
1127 | | */ |
1128 | 0 | i = hsinptr; |
1129 | 0 | hsinptr = (hsinptr + IN1200) % 80; |
1130 | |
|
1131 | 0 | dtemp = sintab[i] * syncx / (MS / 2.); |
1132 | 0 | hiamp -= hibuf[jptr]; |
1133 | 0 | hibuf[jptr] = dtemp; |
1134 | 0 | hiamp += dtemp; |
1135 | 0 | hsiamp -= hsibuf[kptr]; |
1136 | 0 | hsibuf[kptr] = dtemp; |
1137 | 0 | hsiamp += dtemp; |
1138 | |
|
1139 | 0 | i = (i + 20) % 80; |
1140 | 0 | dtemp = sintab[i] * syncx / (MS / 2.); |
1141 | 0 | hqamp -= hqbuf[jptr]; |
1142 | 0 | hqbuf[jptr] = dtemp; |
1143 | 0 | hqamp += dtemp; |
1144 | 0 | hsqamp -= hsqbuf[kptr]; |
1145 | 0 | hsqbuf[kptr] = dtemp; |
1146 | 0 | hsqamp += dtemp; |
1147 | |
|
1148 | 0 | rp = &up->mitig[up->achan].wwvh; |
1149 | 0 | rp->amp = sqrt(hiamp * hiamp + hqamp * hqamp) / SYNCYC; |
1150 | 0 | if (!(up->status & MSYNC)) |
1151 | 0 | wwv_qrz(peer, rp, (int)(pp->fudgetime2 * WWV_SEC)); |
1152 | 0 | jptr = (jptr + 1) % SYNSIZ; |
1153 | 0 | kptr = (kptr + 1) % TCKSIZ; |
1154 | | |
1155 | | /* |
1156 | | * The following section is called once per minute. It does |
1157 | | * housekeeping and timeout functions and empties the dustbins. |
1158 | | */ |
1159 | 0 | if (up->mphase == 0) { |
1160 | 0 | up->watch++; |
1161 | 0 | if (!(up->status & MSYNC)) { |
1162 | | |
1163 | | /* |
1164 | | * If minute sync has not been acquired before |
1165 | | * ACQSN timeout (6 min), or if no signal is |
1166 | | * heard, the program cycles to the next |
1167 | | * frequency and tries again. |
1168 | | */ |
1169 | 0 | if (!wwv_newchan(peer)) |
1170 | 0 | up->watch = 0; |
1171 | 0 | } else { |
1172 | | |
1173 | | /* |
1174 | | * If the leap bit is set, set the minute epoch |
1175 | | * back one second so the station processes |
1176 | | * don't miss a beat. |
1177 | | */ |
1178 | 0 | if (up->status & LEPSEC) { |
1179 | 0 | up->mphase -= WWV_SEC; |
1180 | 0 | if (up->mphase < 0) |
1181 | 0 | up->mphase += WWV_MIN; |
1182 | 0 | } |
1183 | 0 | } |
1184 | 0 | } |
1185 | | |
1186 | | /* |
1187 | | * When the channel metric reaches threshold and the second |
1188 | | * counter matches the minute epoch within the second, the |
1189 | | * driver has synchronized to the station. The second number is |
1190 | | * the remaining seconds until the next minute epoch, while the |
1191 | | * sync epoch is zero. Watch out for the first second; if |
1192 | | * already synchronized to the second, the buffered sync epoch |
1193 | | * must be set. |
1194 | | * |
1195 | | * Note the guard interval is 200 ms; if for some reason the |
1196 | | * clock drifts more than that, it might wind up in the wrong |
1197 | | * second. If the maximum frequency error is not more than about |
1198 | | * 1 PPM, the clock can go as much as two days while still in |
1199 | | * the same second. |
1200 | | */ |
1201 | 0 | if (up->status & MSYNC) { |
1202 | 0 | wwv_epoch(peer); |
1203 | 0 | } else if (up->sptr != NULL) { |
1204 | 0 | sp = up->sptr; |
1205 | 0 | if (sp->metric >= TTHR && epoch == sp->mepoch % WWV_SEC) |
1206 | 0 | { |
1207 | 0 | up->rsec = (60 - sp->mepoch / WWV_SEC) % 60; |
1208 | 0 | up->rphase = 0; |
1209 | 0 | up->status |= MSYNC; |
1210 | 0 | up->watch = 0; |
1211 | 0 | if (!(up->status & SSYNC)) |
1212 | 0 | up->repoch = up->yepoch = epoch; |
1213 | 0 | else |
1214 | 0 | up->repoch = up->yepoch; |
1215 | | |
1216 | 0 | } |
1217 | 0 | } |
1218 | | |
1219 | | /* |
1220 | | * The second sync pulse is extracted using 5-ms (40 sample) FIR |
1221 | | * matched filters at 1000 Hz for WWV or 1200 Hz for WWVH. This |
1222 | | * pulse is used for the most precise synchronization, since if |
1223 | | * provides a resolution of one sample (125 us). The filters run |
1224 | | * only if the station has been reliably determined. |
1225 | | */ |
1226 | 0 | if (up->status & SELV) |
1227 | 0 | mfsync = sqrt(csiamp * csiamp + csqamp * csqamp) / |
1228 | 0 | TCKCYC; |
1229 | 0 | else if (up->status & SELH) |
1230 | 0 | mfsync = sqrt(hsiamp * hsiamp + hsqamp * hsqamp) / |
1231 | 0 | TCKCYC; |
1232 | 0 | else |
1233 | 0 | mfsync = 0; |
1234 | | |
1235 | | /* |
1236 | | * Enhance the seconds sync pulse using a 1-s (8000-sample) comb |
1237 | | * filter. Correct for the FIR matched filter delay, which is 5 |
1238 | | * ms for both the WWV and WWVH filters, and also for the |
1239 | | * propagation delay. Once each second look for second sync. If |
1240 | | * not in minute sync, fiddle the codec gain. Note the SNR is |
1241 | | * computed from the maximum sample and the envelope of the |
1242 | | * sample 6 ms before it, so if we slip more than a cycle the |
1243 | | * SNR should plummet. The signal is scaled to produce unit |
1244 | | * energy at the maximum value. |
1245 | | */ |
1246 | 0 | dtemp = (epobuf[epoch] += (mfsync - epobuf[epoch]) / |
1247 | 0 | up->avgint); |
1248 | 0 | if (dtemp > epomax) { |
1249 | 0 | int j; |
1250 | |
|
1251 | 0 | epomax = dtemp; |
1252 | 0 | epopos = epoch; |
1253 | 0 | j = epoch - 6 * MS; |
1254 | 0 | if (j < 0) |
1255 | 0 | j += WWV_SEC; |
1256 | 0 | nxtmax = fabs(epobuf[j]); |
1257 | 0 | } |
1258 | 0 | if (epoch == 0) { |
1259 | 0 | up->epomax = epomax; |
1260 | 0 | up->eposnr = wwv_snr(epomax, nxtmax); |
1261 | 0 | epopos -= TCKCYC * MS; |
1262 | 0 | if (epopos < 0) |
1263 | 0 | epopos += WWV_SEC; |
1264 | 0 | wwv_endpoc(peer, epopos); |
1265 | 0 | if (!(up->status & SSYNC)) |
1266 | 0 | up->alarm |= SYNERR; |
1267 | 0 | epomax = 0; |
1268 | 0 | if (!(up->status & MSYNC)) |
1269 | 0 | wwv_gain(peer); |
1270 | 0 | } |
1271 | 0 | } |
1272 | | |
1273 | | |
1274 | | /* |
1275 | | * wwv_qrz - identify and acquire WWV/WWVH minute sync pulse |
1276 | | * |
1277 | | * This routine implements a virtual station process used to acquire |
1278 | | * minute sync and to mitigate among the ten frequency and station |
1279 | | * combinations. During minute sync acquisition the process probes each |
1280 | | * frequency and station in turn for the minute pulse, which |
1281 | | * involves searching through the entire 480,000-sample minute. The |
1282 | | * process finds the maximum signal and RMS noise plus signal. Then, the |
1283 | | * actual noise is determined by subtracting the energy of the matched |
1284 | | * filter. |
1285 | | * |
1286 | | * Students of radar receiver technology will discover this algorithm |
1287 | | * amounts to a range-gate discriminator. A valid pulse must have peak |
1288 | | * amplitude at least QTHR (2500) and SNR at least QSNR (20) dB and the |
1289 | | * difference between the current and previous epoch must be less than |
1290 | | * AWND (20 ms). Note that the discriminator peak occurs about 800 ms |
1291 | | * into the second, so the timing is retarded to the previous second |
1292 | | * epoch. |
1293 | | */ |
1294 | | static void |
1295 | | wwv_qrz( |
1296 | | struct peer *peer, /* peer structure pointer */ |
1297 | | struct sync *sp, /* sync channel structure */ |
1298 | | int pdelay /* propagation delay (samples) */ |
1299 | | ) |
1300 | 0 | { |
1301 | 0 | struct refclockproc *pp; |
1302 | 0 | struct wwvunit *up; |
1303 | 0 | char tbuf[TBUF]; /* monitor buffer */ |
1304 | 0 | long epoch; |
1305 | |
|
1306 | 0 | pp = peer->procptr; |
1307 | 0 | up = pp->unitptr; |
1308 | | |
1309 | | /* |
1310 | | * Find the sample with peak amplitude, which defines the minute |
1311 | | * epoch. Accumulate all samples to determine the total noise |
1312 | | * energy. |
1313 | | */ |
1314 | 0 | epoch = up->mphase - pdelay - SYNSIZ; |
1315 | 0 | if (epoch < 0) |
1316 | 0 | epoch += WWV_MIN; |
1317 | 0 | if (sp->amp > sp->maxeng) { |
1318 | 0 | sp->maxeng = sp->amp; |
1319 | 0 | sp->pos = epoch; |
1320 | 0 | } |
1321 | 0 | sp->noieng += sp->amp; |
1322 | | |
1323 | | /* |
1324 | | * At the end of the minute, determine the epoch of the minute |
1325 | | * sync pulse, as well as the difference between the current and |
1326 | | * previous epoches due to the intrinsic frequency error plus |
1327 | | * jitter. When calculating the SNR, subtract the pulse energy |
1328 | | * from the total noise energy and then normalize. |
1329 | | */ |
1330 | 0 | if (up->mphase == 0) { |
1331 | 0 | sp->synmax = sp->maxeng; |
1332 | 0 | sp->synsnr = wwv_snr(sp->synmax, (sp->noieng - |
1333 | 0 | sp->synmax) / WWV_MIN); |
1334 | 0 | if (sp->count == 0) |
1335 | 0 | sp->lastpos = sp->pos; |
1336 | 0 | epoch = (sp->pos - sp->lastpos) % WWV_MIN; |
1337 | 0 | sp->reach <<= 1; |
1338 | 0 | if (sp->reach & (1 << AMAX)) |
1339 | 0 | sp->count--; |
1340 | 0 | if (sp->synmax > ATHR && sp->synsnr > ASNR) { |
1341 | 0 | if (labs(epoch) < AWND * MS) { |
1342 | 0 | sp->reach |= 1; |
1343 | 0 | sp->count++; |
1344 | 0 | sp->mepoch = sp->lastpos = sp->pos; |
1345 | 0 | } else if (sp->count == 1) { |
1346 | 0 | sp->lastpos = sp->pos; |
1347 | 0 | } |
1348 | 0 | } |
1349 | 0 | if (up->watch > ACQSN) |
1350 | 0 | sp->metric = 0; |
1351 | 0 | else |
1352 | 0 | sp->metric = wwv_metric(sp); |
1353 | 0 | if (pp->sloppyclockflag & CLK_FLAG4) { |
1354 | 0 | snprintf(tbuf, sizeof(tbuf), |
1355 | 0 | "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %ld %ld", |
1356 | 0 | up->status, up->gain, sp->refid, |
1357 | 0 | sp->reach & 0xffff, sp->metric, sp->synmax, |
1358 | 0 | sp->synsnr, sp->pos % WWV_SEC, epoch); |
1359 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
1360 | 0 | #ifdef DEBUG |
1361 | 0 | if (debug) |
1362 | 0 | printf("%s\n", tbuf); |
1363 | 0 | #endif /* DEBUG */ |
1364 | 0 | } |
1365 | 0 | sp->maxeng = sp->noieng = 0; |
1366 | 0 | } |
1367 | 0 | } |
1368 | | |
1369 | | |
1370 | | /* |
1371 | | * wwv_endpoc - identify and acquire second sync pulse |
1372 | | * |
1373 | | * This routine is called at the end of the second sync interval. It |
1374 | | * determines the second sync epoch position within the second and |
1375 | | * disciplines the sample clock using a frequency-lock loop (FLL). |
1376 | | * |
1377 | | * Second sync is determined in the RF input routine as the maximum |
1378 | | * over all 8000 samples in the second comb filter. To assure accurate |
1379 | | * and reliable time and frequency discipline, this routine performs a |
1380 | | * great deal of heavy-handed heuristic data filtering and grooming. |
1381 | | */ |
1382 | | static void |
1383 | | wwv_endpoc( |
1384 | | struct peer *peer, /* peer structure pointer */ |
1385 | | int epopos /* epoch max position */ |
1386 | | ) |
1387 | 0 | { |
1388 | 0 | struct refclockproc *pp; |
1389 | 0 | struct wwvunit *up; |
1390 | 0 | static int epoch_mf[3]; /* epoch median filter */ |
1391 | 0 | static int tepoch; /* current second epoch */ |
1392 | 0 | static int xepoch; /* last second epoch */ |
1393 | 0 | static int zepoch; /* last run epoch */ |
1394 | 0 | static int zcount; /* last run end time */ |
1395 | 0 | static int scount; /* seconds counter */ |
1396 | 0 | static int syncnt; /* run length counter */ |
1397 | 0 | static int maxrun; /* longest run length */ |
1398 | 0 | static int mepoch; /* longest run end epoch */ |
1399 | 0 | static int mcount; /* longest run end time */ |
1400 | 0 | static int avgcnt; /* averaging interval counter */ |
1401 | 0 | static int avginc; /* averaging ratchet */ |
1402 | 0 | static int iniflg; /* initialization flag */ |
1403 | 0 | char tbuf[TBUF]; /* monitor buffer */ |
1404 | 0 | double dtemp; |
1405 | 0 | int tmp2; |
1406 | |
|
1407 | 0 | pp = peer->procptr; |
1408 | 0 | up = pp->unitptr; |
1409 | 0 | if (!iniflg) { |
1410 | 0 | iniflg = 1; |
1411 | 0 | ZERO(epoch_mf); |
1412 | 0 | } |
1413 | | |
1414 | | /* |
1415 | | * If the signal amplitude or SNR fall below thresholds, dim the |
1416 | | * second sync lamp and wait for hotter ions. If no stations are |
1417 | | * heard, we are either in a probe cycle or the ions are really |
1418 | | * cold. |
1419 | | */ |
1420 | 0 | scount++; |
1421 | 0 | if (up->epomax < STHR || up->eposnr < SSNR) { |
1422 | 0 | up->status &= ~(SSYNC | FGATE); |
1423 | 0 | avgcnt = syncnt = maxrun = 0; |
1424 | 0 | return; |
1425 | 0 | } |
1426 | 0 | if (!(up->status & (SELV | SELH))) |
1427 | 0 | return; |
1428 | | |
1429 | | /* |
1430 | | * A three-stage median filter is used to help denoise the |
1431 | | * second sync pulse. The median sample becomes the candidate |
1432 | | * epoch. |
1433 | | */ |
1434 | 0 | epoch_mf[2] = epoch_mf[1]; |
1435 | 0 | epoch_mf[1] = epoch_mf[0]; |
1436 | 0 | epoch_mf[0] = epopos; |
1437 | 0 | if (epoch_mf[0] > epoch_mf[1]) { |
1438 | 0 | if (epoch_mf[1] > epoch_mf[2]) |
1439 | 0 | tepoch = epoch_mf[1]; /* 0 1 2 */ |
1440 | 0 | else if (epoch_mf[2] > epoch_mf[0]) |
1441 | 0 | tepoch = epoch_mf[0]; /* 2 0 1 */ |
1442 | 0 | else |
1443 | 0 | tepoch = epoch_mf[2]; /* 0 2 1 */ |
1444 | 0 | } else { |
1445 | 0 | if (epoch_mf[1] < epoch_mf[2]) |
1446 | 0 | tepoch = epoch_mf[1]; /* 2 1 0 */ |
1447 | 0 | else if (epoch_mf[2] < epoch_mf[0]) |
1448 | 0 | tepoch = epoch_mf[0]; /* 1 0 2 */ |
1449 | 0 | else |
1450 | 0 | tepoch = epoch_mf[2]; /* 1 2 0 */ |
1451 | 0 | } |
1452 | | |
1453 | | |
1454 | | /* |
1455 | | * If the epoch candidate is the same as the last one, increment |
1456 | | * the run counter. If not, save the length, epoch and end |
1457 | | * time of the current run for use later and reset the counter. |
1458 | | * The epoch is considered valid if the run is at least SCMP |
1459 | | * (10) s, the minute is synchronized and the interval since the |
1460 | | * last epoch is not greater than the averaging interval. Thus, |
1461 | | * after a long absence, the program will wait a full averaging |
1462 | | * interval while the comb filter charges up and noise |
1463 | | * dissapates.. |
1464 | | */ |
1465 | 0 | tmp2 = (tepoch - xepoch) % WWV_SEC; |
1466 | 0 | if (tmp2 == 0) { |
1467 | 0 | syncnt++; |
1468 | 0 | if (syncnt > SCMP && up->status & MSYNC && (up->status & |
1469 | 0 | FGATE || scount - zcount <= up->avgint)) { |
1470 | 0 | up->status |= SSYNC; |
1471 | 0 | up->yepoch = tepoch; |
1472 | 0 | } |
1473 | 0 | } else if (syncnt >= maxrun) { |
1474 | 0 | maxrun = syncnt; |
1475 | 0 | mcount = scount; |
1476 | 0 | mepoch = xepoch; |
1477 | 0 | syncnt = 0; |
1478 | 0 | } |
1479 | 0 | if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & |
1480 | 0 | MSYNC)) { |
1481 | 0 | snprintf(tbuf, sizeof(tbuf), |
1482 | 0 | "wwv1 %04x %3d %4d %5.0f %5.1f %5d %4d %4d %4d", |
1483 | 0 | up->status, up->gain, tepoch, up->epomax, |
1484 | 0 | up->eposnr, tmp2, avgcnt, syncnt, |
1485 | 0 | maxrun); |
1486 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
1487 | 0 | #ifdef DEBUG |
1488 | 0 | if (debug) |
1489 | 0 | printf("%s\n", tbuf); |
1490 | 0 | #endif /* DEBUG */ |
1491 | 0 | } |
1492 | 0 | avgcnt++; |
1493 | 0 | if (avgcnt < up->avgint) { |
1494 | 0 | xepoch = tepoch; |
1495 | 0 | return; |
1496 | 0 | } |
1497 | | |
1498 | | /* |
1499 | | * The sample clock frequency is disciplined using a first-order |
1500 | | * feedback loop with time constant consistent with the Allan |
1501 | | * intercept of typical computer clocks. During each averaging |
1502 | | * interval the candidate epoch at the end of the longest run is |
1503 | | * determined. If the longest run is zero, all epoches in the |
1504 | | * interval are different, so the candidate epoch is the current |
1505 | | * epoch. The frequency update is computed from the candidate |
1506 | | * epoch difference (125-us units) and time difference (seconds) |
1507 | | * between updates. |
1508 | | */ |
1509 | 0 | if (syncnt >= maxrun) { |
1510 | 0 | maxrun = syncnt; |
1511 | 0 | mcount = scount; |
1512 | 0 | mepoch = xepoch; |
1513 | 0 | } |
1514 | 0 | xepoch = tepoch; |
1515 | 0 | if (maxrun == 0) { |
1516 | 0 | mepoch = tepoch; |
1517 | 0 | mcount = scount; |
1518 | 0 | } |
1519 | | |
1520 | | /* |
1521 | | * The master clock runs at the codec sample frequency of 8000 |
1522 | | * Hz, so the intrinsic time resolution is 125 us. The frequency |
1523 | | * resolution ranges from 18 PPM at the minimum averaging |
1524 | | * interval of 8 s to 0.12 PPM at the maximum interval of 1024 |
1525 | | * s. An offset update is determined at the end of the longest |
1526 | | * run in each averaging interval. The frequency adjustment is |
1527 | | * computed from the difference between offset updates and the |
1528 | | * interval between them. |
1529 | | * |
1530 | | * The maximum frequency adjustment ranges from 187 PPM at the |
1531 | | * minimum interval to 1.5 PPM at the maximum. If the adjustment |
1532 | | * exceeds the maximum, the update is discarded and the |
1533 | | * hysteresis counter is decremented. Otherwise, the frequency |
1534 | | * is incremented by the adjustment, but clamped to the maximum |
1535 | | * 187.5 PPM. If the update is less than half the maximum, the |
1536 | | * hysteresis counter is incremented. If the counter increments |
1537 | | * to +3, the averaging interval is doubled and the counter set |
1538 | | * to zero; if it decrements to -3, the interval is halved and |
1539 | | * the counter set to zero. |
1540 | | */ |
1541 | 0 | dtemp = (mepoch - zepoch) % WWV_SEC; |
1542 | 0 | if (up->status & FGATE) { |
1543 | 0 | if (fabs(dtemp) < MAXFREQ * MINAVG) { |
1544 | 0 | up->freq += (dtemp / 2.) / ((mcount - zcount) * |
1545 | 0 | FCONST); |
1546 | 0 | if (up->freq > MAXFREQ) |
1547 | 0 | up->freq = MAXFREQ; |
1548 | 0 | else if (up->freq < -MAXFREQ) |
1549 | 0 | up->freq = -MAXFREQ; |
1550 | 0 | if (fabs(dtemp) < MAXFREQ * MINAVG / 2.) { |
1551 | 0 | if (avginc < 3) { |
1552 | 0 | avginc++; |
1553 | 0 | } else { |
1554 | 0 | if (up->avgint < MAXAVG) { |
1555 | 0 | up->avgint <<= 1; |
1556 | 0 | avginc = 0; |
1557 | 0 | } |
1558 | 0 | } |
1559 | 0 | } |
1560 | 0 | } else { |
1561 | 0 | if (avginc > -3) { |
1562 | 0 | avginc--; |
1563 | 0 | } else { |
1564 | 0 | if (up->avgint > MINAVG) { |
1565 | 0 | up->avgint >>= 1; |
1566 | 0 | avginc = 0; |
1567 | 0 | } |
1568 | 0 | } |
1569 | 0 | } |
1570 | 0 | } |
1571 | 0 | if (pp->sloppyclockflag & CLK_FLAG4) { |
1572 | 0 | snprintf(tbuf, sizeof(tbuf), |
1573 | 0 | "wwv2 %04x %5.0f %5.1f %5d %4d %4d %4d %4.0f %7.2f", |
1574 | 0 | up->status, up->epomax, up->eposnr, mepoch, |
1575 | 0 | up->avgint, maxrun, mcount - zcount, dtemp, |
1576 | 0 | up->freq * 1e6 / WWV_SEC); |
1577 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
1578 | 0 | #ifdef DEBUG |
1579 | 0 | if (debug) |
1580 | 0 | printf("%s\n", tbuf); |
1581 | 0 | #endif /* DEBUG */ |
1582 | 0 | } |
1583 | | |
1584 | | /* |
1585 | | * This is a valid update; set up for the next interval. |
1586 | | */ |
1587 | 0 | up->status |= FGATE; |
1588 | 0 | zepoch = mepoch; |
1589 | 0 | zcount = mcount; |
1590 | 0 | avgcnt = syncnt = maxrun = 0; |
1591 | 0 | } |
1592 | | |
1593 | | |
1594 | | /* |
1595 | | * wwv_epoch - epoch scanner |
1596 | | * |
1597 | | * This routine extracts data signals from the 100-Hz subcarrier. It |
1598 | | * scans the receiver second epoch to determine the signal amplitudes |
1599 | | * and pulse timings. Receiver synchronization is determined by the |
1600 | | * minute sync pulse detected in the wwv_rf() routine and the second |
1601 | | * sync pulse detected in the wwv_epoch() routine. The transmitted |
1602 | | * signals are delayed by the propagation delay, receiver delay and |
1603 | | * filter delay of this program. Delay corrections are introduced |
1604 | | * separately for WWV and WWVH. |
1605 | | * |
1606 | | * Most communications radios use a highpass filter in the audio stages, |
1607 | | * which can do nasty things to the subcarrier phase relative to the |
1608 | | * sync pulses. Therefore, the data subcarrier reference phase is |
1609 | | * disciplined using the hardlimited quadrature-phase signal sampled at |
1610 | | * the same time as the in-phase signal. The phase tracking loop uses |
1611 | | * phase adjustments of plus-minus one sample (125 us). |
1612 | | */ |
1613 | | static void |
1614 | | wwv_epoch( |
1615 | | struct peer *peer /* peer structure pointer */ |
1616 | | ) |
1617 | 0 | { |
1618 | 0 | struct refclockproc *pp; |
1619 | 0 | struct wwvunit *up; |
1620 | 0 | struct chan *cp; |
1621 | 0 | static double sigmin, sigzer, sigone, engmax, engmin; |
1622 | |
|
1623 | 0 | pp = peer->procptr; |
1624 | 0 | up = pp->unitptr; |
1625 | | |
1626 | | /* |
1627 | | * Find the maximum minute sync pulse energy for both the |
1628 | | * WWV and WWVH stations. This will be used later for channel |
1629 | | * and station mitigation. Also set the seconds epoch at 800 ms |
1630 | | * well before the end of the second to make sure we never set |
1631 | | * the epoch backwards. |
1632 | | */ |
1633 | 0 | cp = &up->mitig[up->achan]; |
1634 | 0 | if (cp->wwv.amp > cp->wwv.syneng) |
1635 | 0 | cp->wwv.syneng = cp->wwv.amp; |
1636 | 0 | if (cp->wwvh.amp > cp->wwvh.syneng) |
1637 | 0 | cp->wwvh.syneng = cp->wwvh.amp; |
1638 | 0 | if (up->rphase == 800 * MS) |
1639 | 0 | up->repoch = up->yepoch; |
1640 | | |
1641 | | /* |
1642 | | * Use the signal amplitude at epoch 15 ms as the noise floor. |
1643 | | * This gives a guard time of +-15 ms from the beginning of the |
1644 | | * second until the second pulse rises at 30 ms. There is a |
1645 | | * compromise here; we want to delay the sample as long as |
1646 | | * possible to give the radio time to change frequency and the |
1647 | | * AGC to stabilize, but as early as possible if the second |
1648 | | * epoch is not exact. |
1649 | | */ |
1650 | 0 | if (up->rphase == 15 * MS) |
1651 | 0 | sigmin = sigzer = sigone = up->irig; |
1652 | | |
1653 | | /* |
1654 | | * Latch the data signal at 200 ms. Keep this around until the |
1655 | | * end of the second. Use the signal energy as the peak to |
1656 | | * compute the SNR. Use the Q sample to adjust the 100-Hz |
1657 | | * reference oscillator phase. |
1658 | | */ |
1659 | 0 | if (up->rphase == 200 * MS) { |
1660 | 0 | sigzer = up->irig; |
1661 | 0 | engmax = sqrt(up->irig * up->irig + up->qrig * |
1662 | 0 | up->qrig); |
1663 | 0 | up->datpha = up->qrig / up->avgint; |
1664 | 0 | if (up->datpha >= 0) { |
1665 | 0 | up->datapt++; |
1666 | 0 | if (up->datapt >= 80) |
1667 | 0 | up->datapt -= 80; |
1668 | 0 | } else { |
1669 | 0 | up->datapt--; |
1670 | 0 | if (up->datapt < 0) |
1671 | 0 | up->datapt += 80; |
1672 | 0 | } |
1673 | 0 | } |
1674 | | |
1675 | | |
1676 | | /* |
1677 | | * Latch the data signal at 500 ms. Keep this around until the |
1678 | | * end of the second. |
1679 | | */ |
1680 | 0 | else if (up->rphase == 500 * MS) |
1681 | 0 | sigone = up->irig; |
1682 | | |
1683 | | /* |
1684 | | * At the end of the second crank the clock state machine and |
1685 | | * adjust the codec gain. Note the epoch is buffered from the |
1686 | | * center of the second in order to avoid jitter while the |
1687 | | * seconds synch is diddling the epoch. Then, determine the true |
1688 | | * offset and update the median filter in the driver interface. |
1689 | | * |
1690 | | * Use the energy at the end of the second as the noise to |
1691 | | * compute the SNR for the data pulse. This gives a better |
1692 | | * measurement than the beginning of the second, especially when |
1693 | | * returning from the probe channel. This gives a guard time of |
1694 | | * 30 ms from the decay of the longest pulse to the rise of the |
1695 | | * next pulse. |
1696 | | */ |
1697 | 0 | up->rphase++; |
1698 | 0 | if (up->mphase % WWV_SEC == up->repoch) { |
1699 | 0 | up->status &= ~(DGATE | BGATE); |
1700 | 0 | engmin = sqrt(up->irig * up->irig + up->qrig * |
1701 | 0 | up->qrig); |
1702 | 0 | up->datsig = engmax; |
1703 | 0 | up->datsnr = wwv_snr(engmax, engmin); |
1704 | | |
1705 | | /* |
1706 | | * If the amplitude or SNR is below threshold, average a |
1707 | | * 0 in the the integrators; otherwise, average the |
1708 | | * bipolar signal. This is done to avoid noise polution. |
1709 | | */ |
1710 | 0 | if (engmax < DTHR || up->datsnr < DSNR) { |
1711 | 0 | up->status |= DGATE; |
1712 | 0 | wwv_rsec(peer, 0); |
1713 | 0 | } else { |
1714 | 0 | sigzer -= sigone; |
1715 | 0 | sigone -= sigmin; |
1716 | 0 | wwv_rsec(peer, sigone - sigzer); |
1717 | 0 | } |
1718 | 0 | if (up->status & (DGATE | BGATE)) |
1719 | 0 | up->errcnt++; |
1720 | 0 | if (up->errcnt > MAXERR) |
1721 | 0 | up->alarm |= LOWERR; |
1722 | 0 | wwv_gain(peer); |
1723 | 0 | cp = &up->mitig[up->achan]; |
1724 | 0 | cp->wwv.syneng = 0; |
1725 | 0 | cp->wwvh.syneng = 0; |
1726 | 0 | up->rphase = 0; |
1727 | 0 | } |
1728 | 0 | } |
1729 | | |
1730 | | |
1731 | | /* |
1732 | | * wwv_rsec - process receiver second |
1733 | | * |
1734 | | * This routine is called at the end of each receiver second to |
1735 | | * implement the per-second state machine. The machine assembles BCD |
1736 | | * digit bits, decodes miscellaneous bits and dances the leap seconds. |
1737 | | * |
1738 | | * Normally, the minute has 60 seconds numbered 0-59. If the leap |
1739 | | * warning bit is set, the last minute (1439) of 30 June (day 181 or 182 |
1740 | | * for leap years) or 31 December (day 365 or 366 for leap years) is |
1741 | | * augmented by one second numbered 60. This is accomplished by |
1742 | | * extending the minute interval by one second and teaching the state |
1743 | | * machine to ignore it. |
1744 | | */ |
1745 | | static void |
1746 | | wwv_rsec( |
1747 | | struct peer *peer, /* peer structure pointer */ |
1748 | | double bit |
1749 | | ) |
1750 | 0 | { |
1751 | 0 | static int iniflg; /* initialization flag */ |
1752 | 0 | static double bcddld[4]; /* BCD data bits */ |
1753 | 0 | static double bitvec[61]; /* bit integrator for misc bits */ |
1754 | 0 | struct refclockproc *pp; |
1755 | 0 | struct wwvunit *up; |
1756 | 0 | struct chan *cp; |
1757 | 0 | struct sync *sp, *rp; |
1758 | 0 | char tbuf[TBUF]; /* monitor buffer */ |
1759 | 0 | int sw, arg, nsec; |
1760 | |
|
1761 | 0 | pp = peer->procptr; |
1762 | 0 | up = pp->unitptr; |
1763 | 0 | if (!iniflg) { |
1764 | 0 | iniflg = 1; |
1765 | 0 | ZERO(bitvec); |
1766 | 0 | } |
1767 | | |
1768 | | /* |
1769 | | * The bit represents the probability of a hit on zero (negative |
1770 | | * values), a hit on one (positive values) or a miss (zero |
1771 | | * value). The likelihood vector is the exponential average of |
1772 | | * these probabilities. Only the bits of this vector |
1773 | | * corresponding to the miscellaneous bits of the timecode are |
1774 | | * used, but it's easier to do them all. After that, crank the |
1775 | | * seconds state machine. |
1776 | | */ |
1777 | 0 | nsec = up->rsec; |
1778 | 0 | up->rsec++; |
1779 | 0 | bitvec[nsec] += (bit - bitvec[nsec]) / TCONST; |
1780 | 0 | sw = progx[nsec].sw; |
1781 | 0 | arg = progx[nsec].arg; |
1782 | | |
1783 | | /* |
1784 | | * The minute state machine. Fly off to a particular section as |
1785 | | * directed by the transition matrix and second number. |
1786 | | */ |
1787 | 0 | switch (sw) { |
1788 | | |
1789 | | /* |
1790 | | * Ignore this second. |
1791 | | */ |
1792 | 0 | case IDLE: /* 9, 45-49 */ |
1793 | 0 | break; |
1794 | | |
1795 | | /* |
1796 | | * Probe channel stuff |
1797 | | * |
1798 | | * The WWV/H format contains data pulses in second 59 (position |
1799 | | * identifier) and second 1, but not in second 0. The minute |
1800 | | * sync pulse is contained in second 0. At the end of second 58 |
1801 | | * QSY to the probe channel, which rotates in turn over all |
1802 | | * WWV/H frequencies. At the end of second 0 measure the minute |
1803 | | * sync pulse. At the end of second 1 measure the data pulse and |
1804 | | * QSY back to the data channel. Note that the actions commented |
1805 | | * here happen at the end of the second numbered as shown. |
1806 | | * |
1807 | | * At the end of second 0 save the minute sync amplitude latched |
1808 | | * at 800 ms as the signal later used to calculate the SNR. |
1809 | | */ |
1810 | 0 | case SYNC2: /* 0 */ |
1811 | 0 | cp = &up->mitig[up->achan]; |
1812 | 0 | cp->wwv.synmax = cp->wwv.syneng; |
1813 | 0 | cp->wwvh.synmax = cp->wwvh.syneng; |
1814 | 0 | break; |
1815 | | |
1816 | | /* |
1817 | | * At the end of second 1 use the minute sync amplitude latched |
1818 | | * at 800 ms as the noise to calculate the SNR. If the minute |
1819 | | * sync pulse and SNR are above thresholds and the data pulse |
1820 | | * amplitude and SNR are above thresolds, shift a 1 into the |
1821 | | * station reachability register; otherwise, shift a 0. The |
1822 | | * number of 1 bits in the last six intervals is a component of |
1823 | | * the channel metric computed by the wwv_metric() routine. |
1824 | | * Finally, QSY back to the data channel. |
1825 | | */ |
1826 | 0 | case SYNC3: /* 1 */ |
1827 | 0 | cp = &up->mitig[up->achan]; |
1828 | | |
1829 | | /* |
1830 | | * WWV station |
1831 | | */ |
1832 | 0 | sp = &cp->wwv; |
1833 | 0 | sp->synsnr = wwv_snr(sp->synmax, sp->amp); |
1834 | 0 | sp->reach <<= 1; |
1835 | 0 | if (sp->reach & (1 << AMAX)) |
1836 | 0 | sp->count--; |
1837 | 0 | if (sp->synmax >= QTHR && sp->synsnr >= QSNR && |
1838 | 0 | !(up->status & (DGATE | BGATE))) { |
1839 | 0 | sp->reach |= 1; |
1840 | 0 | sp->count++; |
1841 | 0 | } |
1842 | 0 | sp->metric = wwv_metric(sp); |
1843 | | |
1844 | | /* |
1845 | | * WWVH station |
1846 | | */ |
1847 | 0 | rp = &cp->wwvh; |
1848 | 0 | rp->synsnr = wwv_snr(rp->synmax, rp->amp); |
1849 | 0 | rp->reach <<= 1; |
1850 | 0 | if (rp->reach & (1 << AMAX)) |
1851 | 0 | rp->count--; |
1852 | 0 | if (rp->synmax >= QTHR && rp->synsnr >= QSNR && |
1853 | 0 | !(up->status & (DGATE | BGATE))) { |
1854 | 0 | rp->reach |= 1; |
1855 | 0 | rp->count++; |
1856 | 0 | } |
1857 | 0 | rp->metric = wwv_metric(rp); |
1858 | 0 | if (pp->sloppyclockflag & CLK_FLAG4) { |
1859 | 0 | snprintf(tbuf, sizeof(tbuf), |
1860 | 0 | "wwv5 %04x %3d %4d %.0f/%.1f %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f", |
1861 | 0 | up->status, up->gain, up->yepoch, |
1862 | 0 | up->epomax, up->eposnr, up->datsig, |
1863 | 0 | up->datsnr, |
1864 | 0 | sp->refid, sp->reach & 0xffff, |
1865 | 0 | sp->metric, sp->synmax, sp->synsnr, |
1866 | 0 | rp->refid, rp->reach & 0xffff, |
1867 | 0 | rp->metric, rp->synmax, rp->synsnr); |
1868 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
1869 | 0 | #ifdef DEBUG |
1870 | 0 | if (debug) |
1871 | 0 | printf("%s\n", tbuf); |
1872 | 0 | #endif /* DEBUG */ |
1873 | 0 | } |
1874 | 0 | up->errcnt = up->digcnt = up->alarm = 0; |
1875 | | |
1876 | | /* |
1877 | | * If synchronized to a station, restart if no stations |
1878 | | * have been heard within the PANIC timeout (2 days). If |
1879 | | * not and the minute digit has been found, restart if |
1880 | | * not synchronized withing the SYNCH timeout (40 m). If |
1881 | | * not, restart if the unit digit has not been found |
1882 | | * within the DATA timeout (15 m). |
1883 | | */ |
1884 | 0 | if (up->status & INSYNC) { |
1885 | 0 | if (up->watch > PANIC) { |
1886 | 0 | wwv_newgame(peer); |
1887 | 0 | return; |
1888 | 0 | } |
1889 | 0 | } else if (up->status & DSYNC) { |
1890 | 0 | if (up->watch > SYNCH) { |
1891 | 0 | wwv_newgame(peer); |
1892 | 0 | return; |
1893 | 0 | } |
1894 | 0 | } else if (up->watch > DATA) { |
1895 | 0 | wwv_newgame(peer); |
1896 | 0 | return; |
1897 | 0 | } |
1898 | 0 | wwv_newchan(peer); |
1899 | 0 | break; |
1900 | | |
1901 | | /* |
1902 | | * Save the bit probability in the BCD data vector at the index |
1903 | | * given by the argument. Bits not used in the digit are forced |
1904 | | * to zero. |
1905 | | */ |
1906 | 0 | case COEF1: /* 4-7 */ |
1907 | 0 | bcddld[arg] = bit; |
1908 | 0 | break; |
1909 | | |
1910 | 0 | case COEF: /* 10-13, 15-17, 20-23, 25-26, |
1911 | | 30-33, 35-38, 40-41, 51-54 */ |
1912 | 0 | if (up->status & DSYNC) |
1913 | 0 | bcddld[arg] = bit; |
1914 | 0 | else |
1915 | 0 | bcddld[arg] = 0; |
1916 | 0 | break; |
1917 | | |
1918 | 0 | case COEF2: /* 18, 27-28, 42-43 */ |
1919 | 0 | bcddld[arg] = 0; |
1920 | 0 | break; |
1921 | | |
1922 | | /* |
1923 | | * Correlate coefficient vector with each valid digit vector and |
1924 | | * save in decoding matrix. We step through the decoding matrix |
1925 | | * digits correlating each with the coefficients and saving the |
1926 | | * greatest and the next lower for later SNR calculation. |
1927 | | */ |
1928 | 0 | case DECIM2: /* 29 */ |
1929 | 0 | wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2); |
1930 | 0 | break; |
1931 | | |
1932 | 0 | case DECIM3: /* 44 */ |
1933 | 0 | wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3); |
1934 | 0 | break; |
1935 | | |
1936 | 0 | case DECIM6: /* 19 */ |
1937 | 0 | wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6); |
1938 | 0 | break; |
1939 | | |
1940 | 0 | case DECIM9: /* 8, 14, 24, 34, 39 */ |
1941 | 0 | wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9); |
1942 | 0 | break; |
1943 | | |
1944 | | /* |
1945 | | * Miscellaneous bits. If above the positive threshold, declare |
1946 | | * 1; if below the negative threshold, declare 0; otherwise |
1947 | | * raise the BGATE bit. The design is intended to avoid |
1948 | | * integrating noise under low SNR conditions. |
1949 | | */ |
1950 | 0 | case MSC20: /* 55 */ |
1951 | 0 | wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9); |
1952 | | /* fall through */ |
1953 | |
|
1954 | 0 | case MSCBIT: /* 2-3, 50, 56-57 */ |
1955 | 0 | if (bitvec[nsec] > BTHR) { |
1956 | 0 | if (!(up->misc & arg)) |
1957 | 0 | up->alarm |= CMPERR; |
1958 | 0 | up->misc |= arg; |
1959 | 0 | } else if (bitvec[nsec] < -BTHR) { |
1960 | 0 | if (up->misc & arg) |
1961 | 0 | up->alarm |= CMPERR; |
1962 | 0 | up->misc &= ~arg; |
1963 | 0 | } else { |
1964 | 0 | up->status |= BGATE; |
1965 | 0 | } |
1966 | 0 | break; |
1967 | | |
1968 | | /* |
1969 | | * Save the data channel gain, then QSY to the probe channel and |
1970 | | * dim the seconds comb filters. The www_newchan() routine will |
1971 | | * light them back up. |
1972 | | */ |
1973 | 0 | case MSC21: /* 58 */ |
1974 | 0 | if (bitvec[nsec] > BTHR) { |
1975 | 0 | if (!(up->misc & arg)) |
1976 | 0 | up->alarm |= CMPERR; |
1977 | 0 | up->misc |= arg; |
1978 | 0 | } else if (bitvec[nsec] < -BTHR) { |
1979 | 0 | if (up->misc & arg) |
1980 | 0 | up->alarm |= CMPERR; |
1981 | 0 | up->misc &= ~arg; |
1982 | 0 | } else { |
1983 | 0 | up->status |= BGATE; |
1984 | 0 | } |
1985 | 0 | up->status &= ~(SELV | SELH); |
1986 | 0 | #ifdef ICOM |
1987 | 0 | if (up->fd_icom > 0) { |
1988 | 0 | up->schan = (up->schan + 1) % NCHAN; |
1989 | 0 | wwv_qsy(peer, up->schan); |
1990 | 0 | } else { |
1991 | 0 | up->mitig[up->achan].gain = up->gain; |
1992 | 0 | } |
1993 | | #else |
1994 | | up->mitig[up->achan].gain = up->gain; |
1995 | | #endif /* ICOM */ |
1996 | 0 | break; |
1997 | | |
1998 | | /* |
1999 | | * The endgames |
2000 | | * |
2001 | | * During second 59 the receiver and codec AGC are settling |
2002 | | * down, so the data pulse is unusable as quality metric. If |
2003 | | * LEPSEC is set on the last minute of 30 June or 31 December, |
2004 | | * the transmitter and receiver insert an extra second (60) in |
2005 | | * the timescale and the minute sync repeats the second. Once |
2006 | | * leaps occurred at intervals of about 18 months, but the last |
2007 | | * leap before the most recent leap in 1995 was in 1998. |
2008 | | */ |
2009 | 0 | case MIN1: /* 59 */ |
2010 | 0 | if (up->status & LEPSEC) |
2011 | 0 | break; |
2012 | | |
2013 | | /* fall through */ |
2014 | | |
2015 | 0 | case MIN2: /* 60 */ |
2016 | 0 | up->status &= ~LEPSEC; |
2017 | 0 | wwv_tsec(peer); |
2018 | 0 | up->rsec = 0; |
2019 | 0 | wwv_clock(peer); |
2020 | 0 | break; |
2021 | 0 | } |
2022 | 0 | if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & |
2023 | 0 | DSYNC)) { |
2024 | 0 | snprintf(tbuf, sizeof(tbuf), |
2025 | 0 | "wwv3 %2d %04x %3d %4d %5.0f %5.1f %5.0f %5.1f %5.0f", |
2026 | 0 | nsec, up->status, up->gain, up->yepoch, up->epomax, |
2027 | 0 | up->eposnr, up->datsig, up->datsnr, bit); |
2028 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
2029 | 0 | #ifdef DEBUG |
2030 | 0 | if (debug) |
2031 | 0 | printf("%s\n", tbuf); |
2032 | 0 | #endif /* DEBUG */ |
2033 | 0 | } |
2034 | 0 | pp->disp += AUDIO_PHI; |
2035 | 0 | } |
2036 | | |
2037 | | /* |
2038 | | * The radio clock is set if the alarm bits are all zero. After that, |
2039 | | * the time is considered valid if the second sync bit is lit. It should |
2040 | | * not be a surprise, especially if the radio is not tunable, that |
2041 | | * sometimes no stations are above the noise and the integrators |
2042 | | * discharge below the thresholds. We assume that, after a day of signal |
2043 | | * loss, the minute sync epoch will be in the same second. This requires |
2044 | | * the codec frequency be accurate within 6 PPM. Practical experience |
2045 | | * shows the frequency typically within 0.1 PPM, so after a day of |
2046 | | * signal loss, the time should be within 8.6 ms.. |
2047 | | */ |
2048 | | static void |
2049 | | wwv_clock( |
2050 | | struct peer *peer /* peer unit pointer */ |
2051 | | ) |
2052 | 0 | { |
2053 | 0 | struct refclockproc *pp; |
2054 | 0 | struct wwvunit *up; |
2055 | 0 | l_fp offset; /* offset in NTP seconds */ |
2056 | |
|
2057 | 0 | pp = peer->procptr; |
2058 | 0 | up = pp->unitptr; |
2059 | 0 | if (!(up->status & SSYNC)) |
2060 | 0 | up->alarm |= SYNERR; |
2061 | 0 | if (up->digcnt < 9) |
2062 | 0 | up->alarm |= NINERR; |
2063 | 0 | if (!(up->alarm)) |
2064 | 0 | up->status |= INSYNC; |
2065 | 0 | if (up->status & INSYNC && up->status & SSYNC) { |
2066 | 0 | if (up->misc & SECWAR) |
2067 | 0 | pp->leap = LEAP_ADDSECOND; |
2068 | 0 | else |
2069 | 0 | pp->leap = LEAP_NOWARNING; |
2070 | 0 | pp->second = up->rsec; |
2071 | 0 | pp->minute = up->decvec[MN].digit + up->decvec[MN + |
2072 | 0 | 1].digit * 10; |
2073 | 0 | pp->hour = up->decvec[HR].digit + up->decvec[HR + |
2074 | 0 | 1].digit * 10; |
2075 | 0 | pp->day = up->decvec[DA].digit + up->decvec[DA + |
2076 | 0 | 1].digit * 10 + up->decvec[DA + 2].digit * 100; |
2077 | 0 | pp->year = up->decvec[YR].digit + up->decvec[YR + |
2078 | 0 | 1].digit * 10; |
2079 | 0 | pp->year += 2000; |
2080 | 0 | L_CLR(&offset); |
2081 | 0 | if (!clocktime(pp->day, pp->hour, pp->minute, |
2082 | 0 | pp->second, GMT, up->timestamp.l_ui, |
2083 | 0 | &pp->yearstart, &offset.l_ui)) { |
2084 | 0 | up->errflg = CEVNT_BADTIME; |
2085 | 0 | } else { |
2086 | 0 | up->watch = 0; |
2087 | 0 | pp->disp = 0; |
2088 | 0 | pp->lastref = up->timestamp; |
2089 | 0 | refclock_process_offset(pp, offset, |
2090 | 0 | up->timestamp, PDELAY + up->pdelay); |
2091 | 0 | refclock_receive(peer); |
2092 | 0 | } |
2093 | 0 | } |
2094 | 0 | pp->lencode = timecode(up, pp->a_lastcode, |
2095 | 0 | sizeof(pp->a_lastcode)); |
2096 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
2097 | 0 | #ifdef DEBUG |
2098 | 0 | if (debug) |
2099 | 0 | printf("wwv: timecode %d %s\n", pp->lencode, |
2100 | 0 | pp->a_lastcode); |
2101 | 0 | #endif /* DEBUG */ |
2102 | 0 | } |
2103 | | |
2104 | | |
2105 | | /* |
2106 | | * wwv_corr4 - determine maximum-likelihood digit |
2107 | | * |
2108 | | * This routine correlates the received digit vector with the BCD |
2109 | | * coefficient vectors corresponding to all valid digits at the given |
2110 | | * position in the decoding matrix. The maximum value corresponds to the |
2111 | | * maximum-likelihood digit, while the ratio of this value to the next |
2112 | | * lower value determines the likelihood function. Note that, if the |
2113 | | * digit is invalid, the likelihood vector is averaged toward a miss. |
2114 | | */ |
2115 | | static void |
2116 | | wwv_corr4( |
2117 | | struct peer *peer, /* peer unit pointer */ |
2118 | | struct decvec *vp, /* decoding table pointer */ |
2119 | | double data[], /* received data vector */ |
2120 | | double tab[][4] /* correlation vector array */ |
2121 | | ) |
2122 | 0 | { |
2123 | 0 | struct refclockproc *pp; |
2124 | 0 | struct wwvunit *up; |
2125 | 0 | double topmax, nxtmax; /* metrics */ |
2126 | 0 | double acc; /* accumulator */ |
2127 | 0 | char tbuf[TBUF]; /* monitor buffer */ |
2128 | 0 | int mldigit; /* max likelihood digit */ |
2129 | 0 | int i, j; |
2130 | |
|
2131 | 0 | pp = peer->procptr; |
2132 | 0 | up = pp->unitptr; |
2133 | | |
2134 | | /* |
2135 | | * Correlate digit vector with each BCD coefficient vector. If |
2136 | | * any BCD digit bit is bad, consider all bits a miss. Until the |
2137 | | * minute units digit has been resolved, don't to anything else. |
2138 | | * Note the SNR is calculated as the ratio of the largest |
2139 | | * likelihood value to the next largest likelihood value. |
2140 | | */ |
2141 | 0 | mldigit = 0; |
2142 | 0 | topmax = nxtmax = -MAXAMP; |
2143 | 0 | for (i = 0; tab[i][0] != 0; i++) { |
2144 | 0 | acc = 0; |
2145 | 0 | for (j = 0; j < 4; j++) |
2146 | 0 | acc += data[j] * tab[i][j]; |
2147 | 0 | acc = (vp->like[i] += (acc - vp->like[i]) / TCONST); |
2148 | 0 | if (acc > topmax) { |
2149 | 0 | nxtmax = topmax; |
2150 | 0 | topmax = acc; |
2151 | 0 | mldigit = i; |
2152 | 0 | } else if (acc > nxtmax) { |
2153 | 0 | nxtmax = acc; |
2154 | 0 | } |
2155 | 0 | } |
2156 | 0 | vp->digprb = topmax; |
2157 | 0 | vp->digsnr = wwv_snr(topmax, nxtmax); |
2158 | | |
2159 | | /* |
2160 | | * The current maximum-likelihood digit is compared to the last |
2161 | | * maximum-likelihood digit. If different, the compare counter |
2162 | | * and maximum-likelihood digit are reset. When the compare |
2163 | | * counter reaches the BCMP threshold (3), the digit is assumed |
2164 | | * correct. When the compare counter of all nine digits have |
2165 | | * reached threshold, the clock is assumed correct. |
2166 | | * |
2167 | | * Note that the clock display digit is set before the compare |
2168 | | * counter has reached threshold; however, the clock display is |
2169 | | * not considered correct until all nine clock digits have |
2170 | | * reached threshold. This is intended as eye candy, but avoids |
2171 | | * mistakes when the signal is low and the SNR is very marginal. |
2172 | | */ |
2173 | 0 | if (vp->digprb < BTHR || vp->digsnr < BSNR) { |
2174 | 0 | up->status |= BGATE; |
2175 | 0 | } else { |
2176 | 0 | if (vp->digit != mldigit) { |
2177 | 0 | up->alarm |= CMPERR; |
2178 | 0 | if (vp->count > 0) |
2179 | 0 | vp->count--; |
2180 | 0 | if (vp->count == 0) |
2181 | 0 | vp->digit = mldigit; |
2182 | 0 | } else { |
2183 | 0 | if (vp->count < BCMP) |
2184 | 0 | vp->count++; |
2185 | 0 | if (vp->count == BCMP) { |
2186 | 0 | up->status |= DSYNC; |
2187 | 0 | up->digcnt++; |
2188 | 0 | } |
2189 | 0 | } |
2190 | 0 | } |
2191 | 0 | if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & |
2192 | 0 | INSYNC)) { |
2193 | 0 | snprintf(tbuf, sizeof(tbuf), |
2194 | 0 | "wwv4 %2d %04x %3d %4d %5.0f %2d %d %d %d %5.0f %5.1f", |
2195 | 0 | up->rsec - 1, up->status, up->gain, up->yepoch, |
2196 | 0 | up->epomax, vp->radix, vp->digit, mldigit, |
2197 | 0 | vp->count, vp->digprb, vp->digsnr); |
2198 | 0 | record_clock_stats(&peer->srcadr, tbuf); |
2199 | 0 | #ifdef DEBUG |
2200 | 0 | if (debug) |
2201 | 0 | printf("%s\n", tbuf); |
2202 | 0 | #endif /* DEBUG */ |
2203 | 0 | } |
2204 | 0 | } |
2205 | | |
2206 | | |
2207 | | /* |
2208 | | * wwv_tsec - transmitter minute processing |
2209 | | * |
2210 | | * This routine is called at the end of the transmitter minute. It |
2211 | | * implements a state machine that advances the logical clock subject to |
2212 | | * the funny rules that govern the conventional clock and calendar. |
2213 | | */ |
2214 | | static void |
2215 | | wwv_tsec( |
2216 | | struct peer *peer /* driver structure pointer */ |
2217 | | ) |
2218 | 0 | { |
2219 | 0 | struct refclockproc *pp; |
2220 | 0 | struct wwvunit *up; |
2221 | 0 | int minute, day, isleap; |
2222 | 0 | int temp; |
2223 | |
|
2224 | 0 | pp = peer->procptr; |
2225 | 0 | up = pp->unitptr; |
2226 | | |
2227 | | /* |
2228 | | * Advance minute unit of the day. Don't propagate carries until |
2229 | | * the unit minute digit has been found. |
2230 | | */ |
2231 | 0 | temp = carry(&up->decvec[MN]); /* minute units */ |
2232 | 0 | if (!(up->status & DSYNC)) |
2233 | 0 | return; |
2234 | | |
2235 | | /* |
2236 | | * Propagate carries through the day. |
2237 | | */ |
2238 | 0 | if (temp == 0) /* carry minutes */ |
2239 | 0 | temp = carry(&up->decvec[MN + 1]); |
2240 | 0 | if (temp == 0) /* carry hours */ |
2241 | 0 | temp = carry(&up->decvec[HR]); |
2242 | 0 | if (temp == 0) |
2243 | 0 | temp = carry(&up->decvec[HR + 1]); |
2244 | | // XXX: Does temp have an expected value here? |
2245 | | |
2246 | | /* |
2247 | | * Decode the current minute and day. Set leap day if the |
2248 | | * timecode leap bit is set on 30 June or 31 December. Set leap |
2249 | | * minute if the last minute on leap day, but only if the clock |
2250 | | * is syncrhronized. This code fails in 2400 AD. |
2251 | | */ |
2252 | 0 | minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * |
2253 | 0 | 10 + up->decvec[HR].digit * 60 + up->decvec[HR + |
2254 | 0 | 1].digit * 600; |
2255 | 0 | day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 + |
2256 | 0 | up->decvec[DA + 2].digit * 100; |
2257 | | |
2258 | | /* |
2259 | | * Set the leap bit on the last minute of the leap day. |
2260 | | */ |
2261 | 0 | isleap = up->decvec[YR].digit & 0x3; |
2262 | 0 | if (up->misc & SECWAR && up->status & INSYNC) { |
2263 | 0 | if ((day == (isleap ? 182 : 183) || day == (isleap ? |
2264 | 0 | 365 : 366)) && minute == 1439) |
2265 | 0 | up->status |= LEPSEC; |
2266 | 0 | } |
2267 | | |
2268 | | /* |
2269 | | * Roll the day if this the first minute and propagate carries |
2270 | | * through the year. |
2271 | | */ |
2272 | 0 | if (minute != 1440) |
2273 | 0 | return; |
2274 | | |
2275 | | // minute = 0; |
2276 | 0 | while (carry(&up->decvec[HR]) != 0); /* advance to minute 0 */ |
2277 | 0 | while (carry(&up->decvec[HR + 1]) != 0); |
2278 | 0 | day++; |
2279 | 0 | temp = carry(&up->decvec[DA]); /* carry days */ |
2280 | 0 | if (temp == 0) |
2281 | 0 | temp = carry(&up->decvec[DA + 1]); |
2282 | 0 | if (temp == 0) |
2283 | 0 | temp = carry(&up->decvec[DA + 2]); |
2284 | | // XXX: Is there an expected value of temp here? |
2285 | | |
2286 | | /* |
2287 | | * Roll the year if this the first day and propagate carries |
2288 | | * through the century. |
2289 | | */ |
2290 | 0 | if (day != (isleap ? 365 : 366)) |
2291 | 0 | return; |
2292 | | |
2293 | | // day = 1; |
2294 | 0 | while (carry(&up->decvec[DA]) != 1); /* advance to day 1 */ |
2295 | 0 | while (carry(&up->decvec[DA + 1]) != 0); |
2296 | 0 | while (carry(&up->decvec[DA + 2]) != 0); |
2297 | 0 | temp = carry(&up->decvec[YR]); /* carry years */ |
2298 | 0 | if (temp == 0) |
2299 | 0 | carry(&up->decvec[YR + 1]); |
2300 | 0 | } |
2301 | | |
2302 | | |
2303 | | /* |
2304 | | * carry - process digit |
2305 | | * |
2306 | | * This routine rotates a likelihood vector one position and increments |
2307 | | * the clock digit modulo the radix. It returns the new clock digit or |
2308 | | * zero if a carry occurred. Once synchronized, the clock digit will |
2309 | | * match the maximum-likelihood digit corresponding to that position. |
2310 | | */ |
2311 | | static int |
2312 | | carry( |
2313 | | struct decvec *dp /* decoding table pointer */ |
2314 | | ) |
2315 | 0 | { |
2316 | 0 | int temp; |
2317 | 0 | int j; |
2318 | |
|
2319 | 0 | dp->digit++; |
2320 | 0 | if (dp->digit == dp->radix) |
2321 | 0 | dp->digit = 0; |
2322 | 0 | temp = dp->like[dp->radix - 1]; |
2323 | 0 | for (j = dp->radix - 1; j > 0; j--) |
2324 | 0 | dp->like[j] = dp->like[j - 1]; |
2325 | 0 | dp->like[0] = temp; |
2326 | 0 | return (dp->digit); |
2327 | 0 | } |
2328 | | |
2329 | | |
2330 | | /* |
2331 | | * wwv_snr - compute SNR or likelihood function |
2332 | | */ |
2333 | | static double |
2334 | | wwv_snr( |
2335 | | double signal, /* signal */ |
2336 | | double noise /* noise */ |
2337 | | ) |
2338 | 0 | { |
2339 | 0 | double rval; |
2340 | | |
2341 | | /* |
2342 | | * This is a little tricky. Due to the way things are measured, |
2343 | | * either or both the signal or noise amplitude can be negative |
2344 | | * or zero. The intent is that, if the signal is negative or |
2345 | | * zero, the SNR must always be zero. This can happen with the |
2346 | | * subcarrier SNR before the phase has been aligned. On the |
2347 | | * other hand, in the likelihood function the "noise" is the |
2348 | | * next maximum down from the peak and this could be negative. |
2349 | | * However, in this case the SNR is truly stupendous, so we |
2350 | | * simply cap at MAXSNR dB (40). |
2351 | | */ |
2352 | 0 | if (signal <= 0) { |
2353 | 0 | rval = 0; |
2354 | 0 | } else if (noise <= 0) { |
2355 | 0 | rval = MAXSNR; |
2356 | 0 | } else { |
2357 | 0 | rval = 20. * log10(signal / noise); |
2358 | 0 | if (rval > MAXSNR) |
2359 | 0 | rval = MAXSNR; |
2360 | 0 | } |
2361 | 0 | return (rval); |
2362 | 0 | } |
2363 | | |
2364 | | |
2365 | | /* |
2366 | | * wwv_newchan - change to new data channel |
2367 | | * |
2368 | | * The radio actually appears to have ten channels, one channel for each |
2369 | | * of five frequencies and each of two stations (WWV and WWVH), although |
2370 | | * if not tunable only the DCHAN channel appears live. While the radio |
2371 | | * is tuned to the working data channel frequency and station for most |
2372 | | * of the minute, during seconds 59, 0 and 1 the radio is tuned to a |
2373 | | * probe frequency in order to search for minute sync pulse and data |
2374 | | * subcarrier from other transmitters. |
2375 | | * |
2376 | | * The search for WWV and WWVH operates simultaneously, with WWV minute |
2377 | | * sync pulse at 1000 Hz and WWVH at 1200 Hz. The probe frequency |
2378 | | * rotates each minute over 2.5, 5, 10, 15 and 20 MHz in order and yes, |
2379 | | * we all know WWVH is dark on 20 MHz, but few remember when WWV was lit |
2380 | | * on 25 MHz. |
2381 | | * |
2382 | | * This routine selects the best channel using a metric computed from |
2383 | | * the reachability register and minute pulse amplitude. Normally, the |
2384 | | * award goes to the the channel with the highest metric; but, in case |
2385 | | * of ties, the award goes to the channel with the highest minute sync |
2386 | | * pulse amplitude and then to the highest frequency. |
2387 | | * |
2388 | | * The routine performs an important squelch function to keep dirty data |
2389 | | * from polluting the integrators. In order to consider a station valid, |
2390 | | * the metric must be at least MTHR (13); otherwise, the station select |
2391 | | * bits are cleared so the second sync is disabled and the data bit |
2392 | | * integrators averaged to a miss. |
2393 | | */ |
2394 | | static int |
2395 | | wwv_newchan( |
2396 | | struct peer *peer /* peer structure pointer */ |
2397 | | ) |
2398 | 0 | { |
2399 | 0 | struct refclockproc *pp; |
2400 | 0 | struct wwvunit *up; |
2401 | 0 | struct sync *sp, *rp; |
2402 | 0 | double rank, dtemp; |
2403 | 0 | int i, j, rval; |
2404 | |
|
2405 | 0 | pp = peer->procptr; |
2406 | 0 | up = pp->unitptr; |
2407 | | |
2408 | | /* |
2409 | | * Search all five station pairs looking for the channel with |
2410 | | * maximum metric. |
2411 | | */ |
2412 | 0 | sp = NULL; |
2413 | 0 | j = 0; |
2414 | 0 | rank = 0; |
2415 | 0 | for (i = 0; i < NCHAN; i++) { |
2416 | 0 | rp = &up->mitig[i].wwvh; |
2417 | 0 | dtemp = rp->metric; |
2418 | 0 | if (dtemp >= rank) { |
2419 | 0 | rank = dtemp; |
2420 | 0 | sp = rp; |
2421 | 0 | j = i; |
2422 | 0 | } |
2423 | 0 | rp = &up->mitig[i].wwv; |
2424 | 0 | dtemp = rp->metric; |
2425 | 0 | if (dtemp >= rank) { |
2426 | 0 | rank = dtemp; |
2427 | 0 | sp = rp; |
2428 | 0 | j = i; |
2429 | 0 | } |
2430 | 0 | } |
2431 | | |
2432 | | /* |
2433 | | * If the strongest signal is less than the MTHR threshold (13), |
2434 | | * we are beneath the waves, so squelch the second sync and |
2435 | | * advance to the next station. This makes sure all stations are |
2436 | | * scanned when the ions grow dim. If the strongest signal is |
2437 | | * greater than the threshold, tune to that frequency and |
2438 | | * transmitter QTH. |
2439 | | */ |
2440 | 0 | up->status &= ~(SELV | SELH); |
2441 | 0 | if (rank < MTHR) { |
2442 | 0 | up->dchan = (up->dchan + 1) % NCHAN; |
2443 | 0 | if (up->status & METRIC) { |
2444 | 0 | up->status &= ~METRIC; |
2445 | 0 | refclock_report(peer, CEVNT_PROP); |
2446 | 0 | } |
2447 | 0 | rval = FALSE; |
2448 | 0 | } else { |
2449 | 0 | up->dchan = j; |
2450 | 0 | up->sptr = sp; |
2451 | 0 | memcpy(&pp->refid, sp->refid, 4); |
2452 | 0 | peer->refid = pp->refid; |
2453 | 0 | up->status |= METRIC; |
2454 | 0 | if (sp->select & SELV) { |
2455 | 0 | up->status |= SELV; |
2456 | 0 | up->pdelay = pp->fudgetime1; |
2457 | 0 | } else if (sp->select & SELH) { |
2458 | 0 | up->status |= SELH; |
2459 | 0 | up->pdelay = pp->fudgetime2; |
2460 | 0 | } else { |
2461 | 0 | up->pdelay = 0; |
2462 | 0 | } |
2463 | 0 | rval = TRUE; |
2464 | 0 | } |
2465 | 0 | #ifdef ICOM |
2466 | 0 | if (up->fd_icom > 0) |
2467 | 0 | wwv_qsy(peer, up->dchan); |
2468 | 0 | #endif /* ICOM */ |
2469 | 0 | return (rval); |
2470 | 0 | } |
2471 | | |
2472 | | |
2473 | | /* |
2474 | | * wwv_newgame - reset and start over |
2475 | | * |
2476 | | * There are three conditions resulting in a new game: |
2477 | | * |
2478 | | * 1 After finding the minute pulse (MSYNC lit), going 15 minutes |
2479 | | * (DATA) without finding the unit seconds digit. |
2480 | | * |
2481 | | * 2 After finding good data (DSYNC lit), going more than 40 minutes |
2482 | | * (SYNCH) without finding station sync (INSYNC lit). |
2483 | | * |
2484 | | * 3 After finding station sync (INSYNC lit), going more than 2 days |
2485 | | * (PANIC) without finding any station. |
2486 | | */ |
2487 | | static void |
2488 | | wwv_newgame( |
2489 | | struct peer *peer /* peer structure pointer */ |
2490 | | ) |
2491 | 0 | { |
2492 | 0 | struct refclockproc *pp; |
2493 | 0 | struct wwvunit *up; |
2494 | 0 | struct chan *cp; |
2495 | 0 | int i; |
2496 | |
|
2497 | 0 | pp = peer->procptr; |
2498 | 0 | up = pp->unitptr; |
2499 | | |
2500 | | /* |
2501 | | * Initialize strategic values. Note we set the leap bits |
2502 | | * NOTINSYNC and the refid "NONE". |
2503 | | */ |
2504 | 0 | if (up->status) |
2505 | 0 | up->errflg = CEVNT_TIMEOUT; |
2506 | 0 | peer->leap = LEAP_NOTINSYNC; |
2507 | 0 | up->watch = up->status = up->alarm = 0; |
2508 | 0 | up->avgint = MINAVG; |
2509 | 0 | up->freq = 0; |
2510 | 0 | up->gain = MAXGAIN / 2; |
2511 | | |
2512 | | /* |
2513 | | * Initialize the station processes for audio gain, select bit, |
2514 | | * station/frequency identifier and reference identifier. Start |
2515 | | * probing at the strongest channel or the default channel if |
2516 | | * nothing heard. |
2517 | | */ |
2518 | 0 | memset(up->mitig, 0, sizeof(up->mitig)); |
2519 | 0 | for (i = 0; i < NCHAN; i++) { |
2520 | 0 | cp = &up->mitig[i]; |
2521 | 0 | cp->gain = up->gain; |
2522 | 0 | cp->wwv.select = SELV; |
2523 | 0 | snprintf(cp->wwv.refid, sizeof(cp->wwv.refid), "WV%.0f", |
2524 | 0 | floor(qsy[i])); |
2525 | 0 | cp->wwvh.select = SELH; |
2526 | 0 | snprintf(cp->wwvh.refid, sizeof(cp->wwvh.refid), "WH%.0f", |
2527 | 0 | floor(qsy[i])); |
2528 | 0 | } |
2529 | 0 | up->dchan = (DCHAN + NCHAN - 1) % NCHAN; |
2530 | 0 | wwv_newchan(peer); |
2531 | 0 | up->schan = up->dchan; |
2532 | 0 | } |
2533 | | |
2534 | | /* |
2535 | | * wwv_metric - compute station metric |
2536 | | * |
2537 | | * The most significant bits represent the number of ones in the |
2538 | | * station reachability register. The least significant bits represent |
2539 | | * the minute sync pulse amplitude. The combined value is scaled 0-100. |
2540 | | */ |
2541 | | double |
2542 | | wwv_metric( |
2543 | | struct sync *sp /* station pointer */ |
2544 | | ) |
2545 | 0 | { |
2546 | 0 | double dtemp; |
2547 | |
|
2548 | 0 | dtemp = sp->count * MAXAMP; |
2549 | 0 | if (sp->synmax < MAXAMP) |
2550 | 0 | dtemp += sp->synmax; |
2551 | 0 | else |
2552 | 0 | dtemp += MAXAMP - 1; |
2553 | 0 | dtemp /= (AMAX + 1) * MAXAMP; |
2554 | 0 | return (dtemp * 100.); |
2555 | 0 | } |
2556 | | |
2557 | | |
2558 | | #ifdef ICOM |
2559 | | /* |
2560 | | * wwv_qsy - Tune ICOM receiver |
2561 | | * |
2562 | | * This routine saves the AGC for the current channel, switches to a new |
2563 | | * channel and restores the AGC for that channel. If a tunable receiver |
2564 | | * is not available, just fake it. |
2565 | | */ |
2566 | | static int |
2567 | | wwv_qsy( |
2568 | | struct peer *peer, /* peer structure pointer */ |
2569 | | int chan /* channel */ |
2570 | | ) |
2571 | 0 | { |
2572 | 0 | int rval = 0; |
2573 | 0 | struct refclockproc *pp; |
2574 | 0 | struct wwvunit *up; |
2575 | |
|
2576 | 0 | pp = peer->procptr; |
2577 | 0 | up = pp->unitptr; |
2578 | 0 | if (up->fd_icom > 0) { |
2579 | 0 | up->mitig[up->achan].gain = up->gain; |
2580 | 0 | rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, |
2581 | 0 | qsy[chan]); |
2582 | 0 | up->achan = chan; |
2583 | 0 | up->gain = up->mitig[up->achan].gain; |
2584 | 0 | } |
2585 | 0 | return (rval); |
2586 | 0 | } |
2587 | | #endif /* ICOM */ |
2588 | | |
2589 | | |
2590 | | /* |
2591 | | * timecode - assemble timecode string and length |
2592 | | * |
2593 | | * Prettytime format - similar to Spectracom |
2594 | | * |
2595 | | * sq yy ddd hh:mm:ss ld dut lset agc iden sig errs freq avgt |
2596 | | * |
2597 | | * s sync indicator ('?' or ' ') |
2598 | | * q error bits (hex 0-F) |
2599 | | * yyyy year of century |
2600 | | * ddd day of year |
2601 | | * hh hour of day |
2602 | | * mm minute of hour |
2603 | | * ss second of minute) |
2604 | | * l leap second warning (' ' or 'L') |
2605 | | * d DST state ('S', 'D', 'I', or 'O') |
2606 | | * dut DUT sign and magnitude (0.1 s) |
2607 | | * lset minutes since last clock update |
2608 | | * agc audio gain (0-255) |
2609 | | * iden reference identifier (station and frequency) |
2610 | | * sig signal quality (0-100) |
2611 | | * errs bit errors in last minute |
2612 | | * freq frequency offset (PPM) |
2613 | | * avgt averaging time (s) |
2614 | | */ |
2615 | | static int |
2616 | | timecode( |
2617 | | struct wwvunit *up, /* driver structure pointer */ |
2618 | | char * tc, /* target string */ |
2619 | | size_t tcsiz /* target max chars */ |
2620 | | ) |
2621 | 0 | { |
2622 | 0 | struct sync *sp; |
2623 | 0 | int year, day, hour, minute, second, dut; |
2624 | 0 | char synchar, leapchar, dst; |
2625 | 0 | char cptr[50]; |
2626 | | |
2627 | | |
2628 | | /* |
2629 | | * Common fixed-format fields |
2630 | | */ |
2631 | 0 | synchar = (up->status & INSYNC) ? ' ' : '?'; |
2632 | 0 | year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10 + |
2633 | 0 | 2000; |
2634 | 0 | day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 + |
2635 | 0 | up->decvec[DA + 2].digit * 100; |
2636 | 0 | hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10; |
2637 | 0 | minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10; |
2638 | 0 | second = 0; |
2639 | 0 | leapchar = (up->misc & SECWAR) ? 'L' : ' '; |
2640 | 0 | dst = dstcod[(up->misc >> 4) & 0x3]; |
2641 | 0 | dut = up->misc & 0x7; |
2642 | 0 | if (!(up->misc & DUTS)) |
2643 | 0 | dut = -dut; |
2644 | 0 | snprintf(tc, tcsiz, "%c%1X", synchar, up->alarm); |
2645 | 0 | snprintf(cptr, sizeof(cptr), |
2646 | 0 | " %4d %03d %02d:%02d:%02d %c%c %+d", |
2647 | 0 | year, day, hour, minute, second, leapchar, dst, dut); |
2648 | 0 | strlcat(tc, cptr, tcsiz); |
2649 | | |
2650 | | /* |
2651 | | * Specific variable-format fields |
2652 | | */ |
2653 | 0 | sp = up->sptr; |
2654 | 0 | snprintf(cptr, sizeof(cptr), " %d %d %s %.0f %d %.1f %d", |
2655 | 0 | up->watch, up->mitig[up->dchan].gain, sp->refid, |
2656 | 0 | sp->metric, up->errcnt, up->freq / WWV_SEC * 1e6, |
2657 | 0 | up->avgint); |
2658 | 0 | strlcat(tc, cptr, tcsiz); |
2659 | |
|
2660 | 0 | return strlen(tc); |
2661 | 0 | } |
2662 | | |
2663 | | |
2664 | | /* |
2665 | | * wwv_gain - adjust codec gain |
2666 | | * |
2667 | | * This routine is called at the end of each second. During the second |
2668 | | * the number of signal clips above the MAXAMP threshold (6000). If |
2669 | | * there are no clips, the gain is bumped up; if there are more than |
2670 | | * MAXCLP clips (100), it is bumped down. The decoder is relatively |
2671 | | * insensitive to amplitude, so this crudity works just peachy. The |
2672 | | * routine also jiggles the input port and selectively mutes the |
2673 | | * monitor. |
2674 | | */ |
2675 | | static void |
2676 | | wwv_gain( |
2677 | | struct peer *peer /* peer structure pointer */ |
2678 | | ) |
2679 | 0 | { |
2680 | 0 | struct refclockproc *pp; |
2681 | 0 | struct wwvunit *up; |
2682 | |
|
2683 | 0 | pp = peer->procptr; |
2684 | 0 | up = pp->unitptr; |
2685 | | |
2686 | | /* |
2687 | | * Apparently, the codec uses only the high order bits of the |
2688 | | * gain control field. Thus, it may take awhile for changes to |
2689 | | * wiggle the hardware bits. |
2690 | | */ |
2691 | 0 | if (up->clipcnt == 0) { |
2692 | 0 | up->gain += 4; |
2693 | 0 | if (up->gain > MAXGAIN) |
2694 | 0 | up->gain = MAXGAIN; |
2695 | 0 | } else if (up->clipcnt > MAXCLP) { |
2696 | 0 | up->gain -= 4; |
2697 | 0 | if (up->gain < 0) |
2698 | 0 | up->gain = 0; |
2699 | 0 | } |
2700 | 0 | audio_gain(up->gain, up->mongain, up->port); |
2701 | 0 | up->clipcnt = 0; |
2702 | 0 | #if DEBUG |
2703 | 0 | if (debug > 1) |
2704 | 0 | audio_show(); |
2705 | 0 | #endif |
2706 | 0 | } |
2707 | | |
2708 | | |
2709 | | #else |
2710 | | int refclock_wwv_bs; |
2711 | | #endif /* REFCLOCK */ |