/src/ntp-dev/ntpd/refclock_wwvb.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers |
3 | | */ |
4 | | |
5 | | #ifdef HAVE_CONFIG_H |
6 | | #include <config.h> |
7 | | #endif |
8 | | |
9 | | #if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM) |
10 | | |
11 | | #include "ntpd.h" |
12 | | #include "ntp_io.h" |
13 | | #include "ntp_refclock.h" |
14 | | #include "ntp_calendar.h" |
15 | | #include "ntp_stdlib.h" |
16 | | |
17 | | #include <stdio.h> |
18 | | #include <ctype.h> |
19 | | |
20 | | #ifdef HAVE_PPSAPI |
21 | | #include "ppsapi_timepps.h" |
22 | | #include "refclock_atom.h" |
23 | | #endif /* HAVE_PPSAPI */ |
24 | | |
25 | | /* |
26 | | * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB |
27 | | * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB |
28 | | * and GPS clocks have proven reliable sources of time; however, the |
29 | | * WWVB clocks have proven vulnerable to high ambient conductive RF |
30 | | * interference. The claimed accuracy of the WWVB clocks is 100 us |
31 | | * relative to the broadcast signal, while the claimed accuracy of the |
32 | | * GPS clock is 50 ns; however, in most cases the actual accuracy is |
33 | | * limited by the resolution of the timecode and the latencies of the |
34 | | * serial interface and operating system. |
35 | | * |
36 | | * The WWVB and GPS clocks should be configured for 24-hour display, |
37 | | * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and |
38 | | * baud rate 9600. If the clock is to used as the source for the IRIG |
39 | | * Audio Decoder (refclock_irig.c in this distribution), it should be |
40 | | * configured for AM IRIG output and IRIG format 1 (IRIG B with |
41 | | * signature control). The GPS clock can be configured either to respond |
42 | | * to a 'T' poll character or left running continuously. |
43 | | * |
44 | | * There are two timecode formats used by these clocks. Format 0, which |
45 | | * is available with both the Netclock/2 and 8170, and format 2, which |
46 | | * is available only with the Netclock/2, specially modified 8170 and |
47 | | * GPS. |
48 | | * |
49 | | * Format 0 (22 ASCII printing characters): |
50 | | * |
51 | | * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf> |
52 | | * |
53 | | * on-time = first <cr> |
54 | | * hh:mm:ss = hours, minutes, seconds |
55 | | * i = synchronization flag (' ' = in synch, '?' = out of synch) |
56 | | * |
57 | | * The alarm condition is indicated by other than ' ' at i, which occurs |
58 | | * during initial synchronization and when received signal is lost for |
59 | | * about ten hours. |
60 | | * |
61 | | * Format 2 (24 ASCII printing characters): |
62 | | * |
63 | | * <cr><lf>iqyy ddd hh:mm:ss.fff ld |
64 | | * |
65 | | * on-time = <cr> |
66 | | * i = synchronization flag (' ' = in synch, '?' = out of synch) |
67 | | * q = quality indicator (' ' = locked, 'A'...'D' = unlocked) |
68 | | * yy = year (as broadcast) |
69 | | * ddd = day of year |
70 | | * hh:mm:ss.fff = hours, minutes, seconds, milliseconds |
71 | | * |
72 | | * The alarm condition is indicated by other than ' ' at i, which occurs |
73 | | * during initial synchronization and when received signal is lost for |
74 | | * about ten hours. The unlock condition is indicated by other than ' ' |
75 | | * at q. |
76 | | * |
77 | | * The q is normally ' ' when the time error is less than 1 ms and a |
78 | | * character in the set 'A'...'D' when the time error is less than 10, |
79 | | * 100, 500 and greater than 500 ms respectively. The l is normally ' ', |
80 | | * but is set to 'L' early in the month of an upcoming UTC leap second |
81 | | * and reset to ' ' on the first day of the following month. The d is |
82 | | * set to 'S' for standard time 'I' on the day preceding a switch to |
83 | | * daylight time, 'D' for daylight time and 'O' on the day preceding a |
84 | | * switch to standard time. The start bit of the first <cr> is |
85 | | * synchronized to the indicated time as returned. |
86 | | * |
87 | | * This driver does not need to be told which format is in use - it |
88 | | * figures out which one from the length of the message. The driver |
89 | | * makes no attempt to correct for the intrinsic jitter of the radio |
90 | | * itself, which is a known problem with the older radios. |
91 | | * |
92 | | * PPS Signal Processing |
93 | | * |
94 | | * When PPS signal processing is enabled, and when the system clock has |
95 | | * been set by this or another driver and the PPS signal offset is |
96 | | * within 0.4 s of the system clock offset, the PPS signal replaces the |
97 | | * timecode for as long as the PPS signal is active. If for some reason |
98 | | * the PPS signal fails for one or more poll intervals, the driver |
99 | | * reverts to the timecode. If the timecode fails for one or more poll |
100 | | * intervals, the PPS signal is disconnected. |
101 | | * |
102 | | * Fudge Factors |
103 | | * |
104 | | * This driver can retrieve a table of quality data maintained |
105 | | * internally by the Netclock/2 clock. If flag4 of the fudge |
106 | | * configuration command is set to 1, the driver will retrieve this |
107 | | * table and write it to the clockstats file when the first timecode |
108 | | * message of a new day is received. |
109 | | * |
110 | | * PPS calibration fudge time 1: format 0 .003134, format 2 .004034 |
111 | | */ |
112 | | /* |
113 | | * Interface definitions |
114 | | */ |
115 | | #define DEVICE "/dev/wwvb%d" /* device name and unit */ |
116 | 0 | #define SPEED232 B9600 /* uart speed (9600 baud) */ |
117 | 0 | #define PRECISION (-13) /* precision assumed (about 100 us) */ |
118 | | #define PPS_PRECISION (-13) /* precision assumed (about 100 us) */ |
119 | 0 | #define REFID "WWVB" /* reference ID */ |
120 | 0 | #define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */ |
121 | | |
122 | 0 | #define LENWWVB0 22 /* format 0 timecode length */ |
123 | 0 | #define LENWWVB2 24 /* format 2 timecode length */ |
124 | 0 | #define LENWWVB3 29 /* format 3 timecode length */ |
125 | 0 | #define MONLIN 15 /* number of monitoring lines */ |
126 | | |
127 | | /* |
128 | | * WWVB unit control structure |
129 | | */ |
130 | | struct wwvbunit { |
131 | | #ifdef HAVE_PPSAPI |
132 | | struct refclock_atom atom; /* PPSAPI structure */ |
133 | | int ppsapi_tried; /* attempt PPSAPI once */ |
134 | | int ppsapi_lit; /* time_pps_create() worked */ |
135 | | int tcount; /* timecode sample counter */ |
136 | | int pcount; /* PPS sample counter */ |
137 | | #endif /* HAVE_PPSAPI */ |
138 | | l_fp laststamp; /* last <CR> timestamp */ |
139 | | int prev_eol_cr; /* was last EOL <CR> (not <LF>)? */ |
140 | | u_char lasthour; /* last hour (for monitor) */ |
141 | | u_char linect; /* count ignored lines (for monitor */ |
142 | | }; |
143 | | |
144 | | /* |
145 | | * Function prototypes |
146 | | */ |
147 | | static int wwvb_start (int, struct peer *); |
148 | | static void wwvb_shutdown (int, struct peer *); |
149 | | static void wwvb_receive (struct recvbuf *); |
150 | | static void wwvb_poll (int, struct peer *); |
151 | | static void wwvb_timer (int, struct peer *); |
152 | | #ifdef HAVE_PPSAPI |
153 | | static void wwvb_control (int, const struct refclockstat *, |
154 | | struct refclockstat *, struct peer *); |
155 | | #define WWVB_CONTROL wwvb_control |
156 | | #else |
157 | | #define WWVB_CONTROL noentry |
158 | | #endif /* HAVE_PPSAPI */ |
159 | | |
160 | | /* |
161 | | * Transfer vector |
162 | | */ |
163 | | struct refclock refclock_wwvb = { |
164 | | wwvb_start, /* start up driver */ |
165 | | wwvb_shutdown, /* shut down driver */ |
166 | | wwvb_poll, /* transmit poll message */ |
167 | | WWVB_CONTROL, /* fudge set/change notification */ |
168 | | noentry, /* initialize driver (not used) */ |
169 | | noentry, /* not used (old wwvb_buginfo) */ |
170 | | wwvb_timer /* called once per second */ |
171 | | }; |
172 | | |
173 | | |
174 | | /* |
175 | | * wwvb_start - open the devices and initialize data for processing |
176 | | */ |
177 | | static int |
178 | | wwvb_start( |
179 | | int unit, |
180 | | struct peer *peer |
181 | | ) |
182 | 0 | { |
183 | 0 | register struct wwvbunit *up; |
184 | 0 | struct refclockproc *pp; |
185 | 0 | int fd; |
186 | 0 | char device[20]; |
187 | | |
188 | | /* |
189 | | * Open serial port. Use CLK line discipline, if available. |
190 | | */ |
191 | 0 | snprintf(device, sizeof(device), DEVICE, unit); |
192 | 0 | fd = refclock_open(device, SPEED232, LDISC_CLK); |
193 | 0 | if (fd <= 0) |
194 | 0 | return (0); |
195 | | |
196 | | /* |
197 | | * Allocate and initialize unit structure |
198 | | */ |
199 | 0 | up = emalloc_zero(sizeof(*up)); |
200 | 0 | pp = peer->procptr; |
201 | 0 | pp->io.clock_recv = wwvb_receive; |
202 | 0 | pp->io.srcclock = peer; |
203 | 0 | pp->io.datalen = 0; |
204 | 0 | pp->io.fd = fd; |
205 | 0 | if (!io_addclock(&pp->io)) { |
206 | 0 | close(fd); |
207 | 0 | pp->io.fd = -1; |
208 | 0 | free(up); |
209 | 0 | return (0); |
210 | 0 | } |
211 | 0 | pp->unitptr = up; |
212 | | |
213 | | /* |
214 | | * Initialize miscellaneous variables |
215 | | */ |
216 | 0 | peer->precision = PRECISION; |
217 | 0 | pp->clockdesc = DESCRIPTION; |
218 | 0 | memcpy(&pp->refid, REFID, 4); |
219 | 0 | return (1); |
220 | 0 | } |
221 | | |
222 | | |
223 | | /* |
224 | | * wwvb_shutdown - shut down the clock |
225 | | */ |
226 | | static void |
227 | | wwvb_shutdown( |
228 | | int unit, |
229 | | struct peer *peer |
230 | | ) |
231 | 0 | { |
232 | 0 | struct refclockproc * pp; |
233 | 0 | struct wwvbunit * up; |
234 | |
|
235 | 0 | pp = peer->procptr; |
236 | 0 | up = pp->unitptr; |
237 | 0 | if (-1 != pp->io.fd) |
238 | 0 | io_closeclock(&pp->io); |
239 | 0 | if (NULL != up) |
240 | 0 | free(up); |
241 | 0 | } |
242 | | |
243 | | |
244 | | /* |
245 | | * wwvb_receive - receive data from the serial interface |
246 | | */ |
247 | | static void |
248 | | wwvb_receive( |
249 | | struct recvbuf *rbufp |
250 | | ) |
251 | 0 | { |
252 | 0 | struct wwvbunit *up; |
253 | 0 | struct refclockproc *pp; |
254 | 0 | struct peer *peer; |
255 | |
|
256 | 0 | l_fp trtmp; /* arrival timestamp */ |
257 | 0 | int tz; /* time zone */ |
258 | 0 | int day, month; /* ddd conversion */ |
259 | 0 | int temp; /* int temp */ |
260 | 0 | char syncchar; /* synchronization indicator */ |
261 | 0 | char qualchar; /* quality indicator */ |
262 | 0 | char leapchar; /* leap indicator */ |
263 | 0 | char dstchar; /* daylight/standard indicator */ |
264 | 0 | char tmpchar; /* trashbin */ |
265 | | |
266 | | /* |
267 | | * Initialize pointers and read the timecode and timestamp |
268 | | */ |
269 | 0 | peer = rbufp->recv_peer; |
270 | 0 | pp = peer->procptr; |
271 | 0 | up = pp->unitptr; |
272 | 0 | temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); |
273 | | |
274 | | /* |
275 | | * Note we get a buffer and timestamp for both a <cr> and <lf>, |
276 | | * but only the <cr> timestamp is retained. Note: in format 0 on |
277 | | * a Netclock/2 or upgraded 8170 the start bit is delayed 100 |
278 | | * +-50 us relative to the pps; however, on an unmodified 8170 |
279 | | * the start bit can be delayed up to 10 ms. In format 2 the |
280 | | * reading precision is only to the millisecond. Thus, unless |
281 | | * you have a PPS gadget and don't have to have the year, format |
282 | | * 0 provides the lowest jitter. |
283 | | * Save the timestamp of each <CR> in up->laststamp. Lines with |
284 | | * no characters occur for every <LF>, and for some <CR>s when |
285 | | * format 0 is used. Format 0 starts and ends each cycle with a |
286 | | * <CR><LF> pair, format 2 starts each cycle with its only pair. |
287 | | * The preceding <CR> is the on-time character for both formats. |
288 | | * The timestamp provided with non-empty lines corresponds to |
289 | | * the <CR> following the timecode, which is ultimately not used |
290 | | * with format 0 and is used for the following timecode for |
291 | | * format 2. |
292 | | */ |
293 | 0 | if (temp == 0) { |
294 | 0 | if (up->prev_eol_cr) { |
295 | 0 | DPRINTF(2, ("wwvb: <LF> @ %s\n", |
296 | 0 | prettydate(&trtmp))); |
297 | 0 | } else { |
298 | 0 | up->laststamp = trtmp; |
299 | 0 | DPRINTF(2, ("wwvb: <CR> @ %s\n", |
300 | 0 | prettydate(&trtmp))); |
301 | 0 | } |
302 | 0 | up->prev_eol_cr = !up->prev_eol_cr; |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | pp->lencode = temp; |
306 | 0 | pp->lastrec = up->laststamp; |
307 | 0 | up->laststamp = trtmp; |
308 | 0 | up->prev_eol_cr = TRUE; |
309 | 0 | DPRINTF(2, ("wwvb: code @ %s\n" |
310 | 0 | " using %s minus one char\n", |
311 | 0 | prettydate(&trtmp), prettydate(&pp->lastrec))); |
312 | 0 | if (L_ISZERO(&pp->lastrec)) |
313 | 0 | return; |
314 | | |
315 | | /* |
316 | | * We get down to business, check the timecode format and decode |
317 | | * its contents. This code uses the timecode length to determine |
318 | | * format 0, 2 or 3. If the timecode has invalid length or is |
319 | | * not in proper format, we declare bad format and exit. |
320 | | */ |
321 | 0 | syncchar = qualchar = leapchar = dstchar = ' '; |
322 | 0 | tz = 0; |
323 | 0 | switch (pp->lencode) { |
324 | | |
325 | 0 | case LENWWVB0: |
326 | | |
327 | | /* |
328 | | * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" |
329 | | */ |
330 | 0 | if (sscanf(pp->a_lastcode, |
331 | 0 | "%c %3d %2d:%2d:%2d%c%cTZ=%2d", |
332 | 0 | &syncchar, &pp->day, &pp->hour, &pp->minute, |
333 | 0 | &pp->second, &tmpchar, &dstchar, &tz) == 8) { |
334 | 0 | pp->nsec = 0; |
335 | 0 | break; |
336 | 0 | } |
337 | 0 | goto bad_format; |
338 | | |
339 | 0 | case LENWWVB2: |
340 | | |
341 | | /* |
342 | | * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ |
343 | 0 | if (sscanf(pp->a_lastcode, |
344 | 0 | "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", |
345 | 0 | &syncchar, &qualchar, &pp->year, &pp->day, |
346 | 0 | &pp->hour, &pp->minute, &pp->second, &pp->nsec, |
347 | 0 | &leapchar) == 9) { |
348 | 0 | pp->nsec *= 1000000; |
349 | 0 | break; |
350 | 0 | } |
351 | 0 | goto bad_format; |
352 | | |
353 | 0 | case LENWWVB3: |
354 | | |
355 | | /* |
356 | | * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" |
357 | | * WARNING: Undocumented, and the on-time character # is |
358 | | * not yet handled correctly by this driver. It may be |
359 | | * as simple as compensating for an additional 1/960 s. |
360 | | */ |
361 | 0 | if (sscanf(pp->a_lastcode, |
362 | 0 | "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", |
363 | 0 | &syncchar, &pp->year, &month, &day, &pp->hour, |
364 | 0 | &pp->minute, &pp->second, &dstchar, &leapchar) == 8) |
365 | 0 | { |
366 | 0 | pp->day = ymd2yd(pp->year, month, day); |
367 | 0 | pp->nsec = 0; |
368 | 0 | break; |
369 | 0 | } |
370 | 0 | goto bad_format; |
371 | | |
372 | 0 | default: |
373 | 0 | bad_format: |
374 | | |
375 | | /* |
376 | | * Unknown format: If dumping internal table, record |
377 | | * stats; otherwise, declare bad format. |
378 | | */ |
379 | 0 | if (up->linect > 0) { |
380 | 0 | up->linect--; |
381 | 0 | record_clock_stats(&peer->srcadr, |
382 | 0 | pp->a_lastcode); |
383 | 0 | } else { |
384 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
385 | 0 | } |
386 | 0 | return; |
387 | 0 | } |
388 | | |
389 | | /* |
390 | | * Decode synchronization, quality and leap characters. If |
391 | | * unsynchronized, set the leap bits accordingly and exit. |
392 | | * Otherwise, set the leap bits according to the leap character. |
393 | | * Once synchronized, the dispersion depends only on the |
394 | | * quality character. |
395 | | */ |
396 | 0 | switch (qualchar) { |
397 | | |
398 | 0 | case ' ': |
399 | 0 | pp->disp = .001; |
400 | 0 | pp->lastref = pp->lastrec; |
401 | 0 | break; |
402 | | |
403 | 0 | case 'A': |
404 | 0 | pp->disp = .01; |
405 | 0 | break; |
406 | | |
407 | 0 | case 'B': |
408 | 0 | pp->disp = .1; |
409 | 0 | break; |
410 | | |
411 | 0 | case 'C': |
412 | 0 | pp->disp = .5; |
413 | 0 | break; |
414 | | |
415 | 0 | case 'D': |
416 | 0 | pp->disp = MAXDISPERSE; |
417 | 0 | break; |
418 | | |
419 | 0 | default: |
420 | 0 | pp->disp = MAXDISPERSE; |
421 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
422 | 0 | break; |
423 | 0 | } |
424 | 0 | if (syncchar != ' ') |
425 | 0 | pp->leap = LEAP_NOTINSYNC; |
426 | 0 | else if (leapchar == 'L') |
427 | 0 | pp->leap = LEAP_ADDSECOND; |
428 | 0 | else |
429 | 0 | pp->leap = LEAP_NOWARNING; |
430 | | |
431 | | /* |
432 | | * Process the new sample in the median filter and determine the |
433 | | * timecode timestamp, but only if the PPS is not in control. |
434 | | */ |
435 | | #ifdef HAVE_PPSAPI |
436 | | up->tcount++; |
437 | | if (peer->flags & FLAG_PPS) |
438 | | return; |
439 | | |
440 | | #endif /* HAVE_PPSAPI */ |
441 | 0 | if (!refclock_process_f(pp, pp->fudgetime2)) |
442 | 0 | refclock_report(peer, CEVNT_BADTIME); |
443 | 0 | } |
444 | | |
445 | | |
446 | | /* |
447 | | * wwvb_timer - called once per second by the transmit procedure |
448 | | */ |
449 | | static void |
450 | | wwvb_timer( |
451 | | int unit, |
452 | | struct peer *peer |
453 | | ) |
454 | 0 | { |
455 | 0 | register struct wwvbunit *up; |
456 | 0 | struct refclockproc *pp; |
457 | 0 | char pollchar; /* character sent to clock */ |
458 | 0 | #ifdef DEBUG |
459 | 0 | l_fp now; |
460 | 0 | #endif |
461 | | |
462 | | /* |
463 | | * Time to poll the clock. The Spectracom clock responds to a |
464 | | * 'T' by returning a timecode in the format(s) specified above. |
465 | | * Note there is no checking on state, since this may not be the |
466 | | * only customer reading the clock. Only one customer need poll |
467 | | * the clock; all others just listen in. |
468 | | */ |
469 | 0 | pp = peer->procptr; |
470 | 0 | up = pp->unitptr; |
471 | 0 | if (up->linect > 0) |
472 | 0 | pollchar = 'R'; |
473 | 0 | else |
474 | 0 | pollchar = 'T'; |
475 | 0 | if (write(pp->io.fd, &pollchar, 1) != 1) |
476 | 0 | refclock_report(peer, CEVNT_FAULT); |
477 | 0 | #ifdef DEBUG |
478 | 0 | get_systime(&now); |
479 | 0 | if (debug) |
480 | 0 | printf("%c poll at %s\n", pollchar, prettydate(&now)); |
481 | 0 | #endif |
482 | | #ifdef HAVE_PPSAPI |
483 | | if (up->ppsapi_lit && |
484 | | refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) { |
485 | | up->pcount++, |
486 | | peer->flags |= FLAG_PPS; |
487 | | peer->precision = PPS_PRECISION; |
488 | | } |
489 | | #endif /* HAVE_PPSAPI */ |
490 | 0 | } |
491 | | |
492 | | |
493 | | /* |
494 | | * wwvb_poll - called by the transmit procedure |
495 | | */ |
496 | | static void |
497 | | wwvb_poll( |
498 | | int unit, |
499 | | struct peer *peer |
500 | | ) |
501 | 0 | { |
502 | 0 | register struct wwvbunit *up; |
503 | 0 | struct refclockproc *pp; |
504 | | |
505 | | /* |
506 | | * Sweep up the samples received since the last poll. If none |
507 | | * are received, declare a timeout and keep going. |
508 | | */ |
509 | 0 | pp = peer->procptr; |
510 | 0 | up = pp->unitptr; |
511 | 0 | pp->polls++; |
512 | | |
513 | | /* |
514 | | * If the monitor flag is set (flag4), we dump the internal |
515 | | * quality table at the first timecode beginning the day. |
516 | | */ |
517 | 0 | if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < |
518 | 0 | (int)up->lasthour) |
519 | 0 | up->linect = MONLIN; |
520 | 0 | up->lasthour = (u_char)pp->hour; |
521 | | |
522 | | /* |
523 | | * Process median filter samples. If none received, declare a |
524 | | * timeout and keep going. |
525 | | */ |
526 | | #ifdef HAVE_PPSAPI |
527 | | if (up->pcount == 0) { |
528 | | peer->flags &= ~FLAG_PPS; |
529 | | peer->precision = PRECISION; |
530 | | } |
531 | | if (up->tcount == 0) { |
532 | | pp->coderecv = pp->codeproc; |
533 | | refclock_report(peer, CEVNT_TIMEOUT); |
534 | | return; |
535 | | } |
536 | | up->pcount = up->tcount = 0; |
537 | | #else /* HAVE_PPSAPI */ |
538 | 0 | if (pp->coderecv == pp->codeproc) { |
539 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
540 | 0 | return; |
541 | 0 | } |
542 | 0 | #endif /* HAVE_PPSAPI */ |
543 | 0 | refclock_receive(peer); |
544 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
545 | 0 | #ifdef DEBUG |
546 | 0 | if (debug) |
547 | 0 | printf("wwvb: timecode %d %s\n", pp->lencode, |
548 | 0 | pp->a_lastcode); |
549 | 0 | #endif |
550 | 0 | } |
551 | | |
552 | | |
553 | | /* |
554 | | * wwvb_control - fudge parameters have been set or changed |
555 | | */ |
556 | | #ifdef HAVE_PPSAPI |
557 | | static void |
558 | | wwvb_control( |
559 | | int unit, |
560 | | const struct refclockstat *in_st, |
561 | | struct refclockstat *out_st, |
562 | | struct peer *peer |
563 | | ) |
564 | | { |
565 | | register struct wwvbunit *up; |
566 | | struct refclockproc *pp; |
567 | | |
568 | | pp = peer->procptr; |
569 | | up = pp->unitptr; |
570 | | |
571 | | if (!(pp->sloppyclockflag & CLK_FLAG1)) { |
572 | | if (!up->ppsapi_tried) |
573 | | return; |
574 | | up->ppsapi_tried = 0; |
575 | | if (!up->ppsapi_lit) |
576 | | return; |
577 | | peer->flags &= ~FLAG_PPS; |
578 | | peer->precision = PRECISION; |
579 | | time_pps_destroy(up->atom.handle); |
580 | | up->atom.handle = 0; |
581 | | up->ppsapi_lit = 0; |
582 | | return; |
583 | | } |
584 | | |
585 | | if (up->ppsapi_tried) |
586 | | return; |
587 | | /* |
588 | | * Light up the PPSAPI interface. |
589 | | */ |
590 | | up->ppsapi_tried = 1; |
591 | | if (refclock_ppsapi(pp->io.fd, &up->atom)) { |
592 | | up->ppsapi_lit = 1; |
593 | | return; |
594 | | } |
595 | | |
596 | | msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails", |
597 | | refnumtoa(&peer->srcadr)); |
598 | | } |
599 | | #endif /* HAVE_PPSAPI */ |
600 | | |
601 | | #else |
602 | | int refclock_wwvb_bs; |
603 | | #endif /* REFCLOCK */ |