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