refclock_wwvb.c revision 1.2 1 1.2 kardel /* $NetBSD: refclock_wwvb.c,v 1.2 2012/02/01 07:46:22 kardel 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.1 kardel static void wwvb_control (int, 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.1 kardel #define WWVB_CONTROL noentry
160 1.1 kardel #endif /* HAVE_PPSAPI */
161 1.1 kardel
162 1.1 kardel /*
163 1.1 kardel * Transfer vector
164 1.1 kardel */
165 1.1 kardel struct refclock refclock_wwvb = {
166 1.1 kardel wwvb_start, /* start up driver */
167 1.1 kardel wwvb_shutdown, /* shut down driver */
168 1.1 kardel wwvb_poll, /* transmit poll message */
169 1.1 kardel WWVB_CONTROL, /* fudge set/change notification */
170 1.1 kardel noentry, /* initialize driver (not used) */
171 1.1 kardel noentry, /* not used (old wwvb_buginfo) */
172 1.1 kardel wwvb_timer /* called once per second */
173 1.1 kardel };
174 1.1 kardel
175 1.1 kardel
176 1.1 kardel /*
177 1.1 kardel * wwvb_start - open the devices and initialize data for processing
178 1.1 kardel */
179 1.1 kardel static int
180 1.1 kardel wwvb_start(
181 1.1 kardel int unit,
182 1.1 kardel struct peer *peer
183 1.1 kardel )
184 1.1 kardel {
185 1.1 kardel register struct wwvbunit *up;
186 1.1 kardel struct refclockproc *pp;
187 1.1 kardel int fd;
188 1.1 kardel char device[20];
189 1.1 kardel
190 1.1 kardel /*
191 1.1 kardel * Open serial port. Use CLK line discipline, if available.
192 1.1 kardel */
193 1.2 kardel snprintf(device, sizeof(device), DEVICE, unit);
194 1.2 kardel fd = refclock_open(device, SPEED232, LDISC_CLK);
195 1.2 kardel if (fd <= 0)
196 1.1 kardel return (0);
197 1.1 kardel
198 1.1 kardel /*
199 1.1 kardel * Allocate and initialize unit structure
200 1.1 kardel */
201 1.2 kardel up = emalloc_zero(sizeof(*up));
202 1.1 kardel pp = peer->procptr;
203 1.1 kardel pp->io.clock_recv = wwvb_receive;
204 1.2 kardel pp->io.srcclock = (void *)peer;
205 1.1 kardel pp->io.datalen = 0;
206 1.1 kardel pp->io.fd = fd;
207 1.1 kardel if (!io_addclock(&pp->io)) {
208 1.1 kardel close(fd);
209 1.2 kardel pp->io.fd = -1;
210 1.1 kardel free(up);
211 1.1 kardel return (0);
212 1.1 kardel }
213 1.2 kardel pp->unitptr = up;
214 1.1 kardel
215 1.1 kardel /*
216 1.1 kardel * Initialize miscellaneous variables
217 1.1 kardel */
218 1.1 kardel peer->precision = PRECISION;
219 1.1 kardel pp->clockdesc = DESCRIPTION;
220 1.2 kardel memcpy(&pp->refid, REFID, 4);
221 1.1 kardel return (1);
222 1.1 kardel }
223 1.1 kardel
224 1.1 kardel
225 1.1 kardel /*
226 1.1 kardel * wwvb_shutdown - shut down the clock
227 1.1 kardel */
228 1.1 kardel static void
229 1.1 kardel wwvb_shutdown(
230 1.1 kardel int unit,
231 1.1 kardel struct peer *peer
232 1.1 kardel )
233 1.1 kardel {
234 1.1 kardel register struct wwvbunit *up;
235 1.1 kardel struct refclockproc *pp;
236 1.1 kardel
237 1.1 kardel pp = peer->procptr;
238 1.2 kardel up = pp->unitptr;
239 1.2 kardel if (-1 != pp->io.fd)
240 1.2 kardel io_closeclock(&pp->io);
241 1.2 kardel if (NULL != up)
242 1.2 kardel free(up);
243 1.1 kardel }
244 1.1 kardel
245 1.1 kardel
246 1.1 kardel /*
247 1.1 kardel * wwvb_receive - receive data from the serial interface
248 1.1 kardel */
249 1.1 kardel static void
250 1.1 kardel wwvb_receive(
251 1.1 kardel struct recvbuf *rbufp
252 1.1 kardel )
253 1.1 kardel {
254 1.1 kardel struct wwvbunit *up;
255 1.1 kardel struct refclockproc *pp;
256 1.1 kardel struct peer *peer;
257 1.1 kardel
258 1.1 kardel l_fp trtmp; /* arrival timestamp */
259 1.1 kardel int tz; /* time zone */
260 1.1 kardel int day, month; /* ddd conversion */
261 1.1 kardel int temp; /* int temp */
262 1.1 kardel char syncchar; /* synchronization indicator */
263 1.1 kardel char qualchar; /* quality indicator */
264 1.1 kardel char leapchar; /* leap indicator */
265 1.1 kardel char dstchar; /* daylight/standard indicator */
266 1.1 kardel char tmpchar; /* trashbin */
267 1.1 kardel
268 1.1 kardel /*
269 1.1 kardel * Initialize pointers and read the timecode and timestamp
270 1.1 kardel */
271 1.2 kardel peer = rbufp->recv_peer;
272 1.1 kardel pp = peer->procptr;
273 1.2 kardel up = pp->unitptr;
274 1.1 kardel temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
275 1.1 kardel
276 1.1 kardel /*
277 1.1 kardel * Note we get a buffer and timestamp for both a <cr> and <lf>,
278 1.1 kardel * but only the <cr> timestamp is retained. Note: in format 0 on
279 1.1 kardel * a Netclock/2 or upgraded 8170 the start bit is delayed 100
280 1.1 kardel * +-50 us relative to the pps; however, on an unmodified 8170
281 1.1 kardel * the start bit can be delayed up to 10 ms. In format 2 the
282 1.1 kardel * reading precision is only to the millisecond. Thus, unless
283 1.1 kardel * you have a PPS gadget and don't have to have the year, format
284 1.1 kardel * 0 provides the lowest jitter.
285 1.2 kardel * Save the timestamp of each <CR> in up->laststamp. Lines with
286 1.2 kardel * no characters occur for every <LF>, and for some <CR>s when
287 1.2 kardel * format 0 is used. Format 0 starts and ends each cycle with a
288 1.2 kardel * <CR><LF> pair, format 2 starts each cycle with its only pair.
289 1.2 kardel * The preceding <CR> is the on-time character for both formats.
290 1.2 kardel * The timestamp provided with non-empty lines corresponds to
291 1.2 kardel * the <CR> following the timecode, which is ultimately not used
292 1.2 kardel * with format 0 and is used for the following timecode for
293 1.2 kardel * format 2.
294 1.1 kardel */
295 1.1 kardel if (temp == 0) {
296 1.2 kardel if (up->prev_eol_cr) {
297 1.2 kardel DPRINTF(2, ("wwvb: <LF> @ %s\n",
298 1.2 kardel prettydate(&trtmp)));
299 1.2 kardel } else {
300 1.2 kardel up->laststamp = trtmp;
301 1.2 kardel DPRINTF(2, ("wwvb: <CR> @ %s\n",
302 1.2 kardel prettydate(&trtmp)));
303 1.2 kardel }
304 1.2 kardel up->prev_eol_cr = !up->prev_eol_cr;
305 1.1 kardel return;
306 1.1 kardel }
307 1.1 kardel pp->lencode = temp;
308 1.1 kardel pp->lastrec = up->laststamp;
309 1.2 kardel up->laststamp = trtmp;
310 1.2 kardel up->prev_eol_cr = TRUE;
311 1.2 kardel DPRINTF(2, ("wwvb: code @ %s\n"
312 1.2 kardel " using %s minus one char\n",
313 1.2 kardel prettydate(&trtmp), prettydate(&pp->lastrec)));
314 1.2 kardel if (L_ISZERO(&pp->lastrec))
315 1.2 kardel return;
316 1.1 kardel
317 1.1 kardel /*
318 1.1 kardel * We get down to business, check the timecode format and decode
319 1.1 kardel * its contents. This code uses the timecode length to determine
320 1.1 kardel * format 0, 2 or 3. If the timecode has invalid length or is
321 1.1 kardel * not in proper format, we declare bad format and exit.
322 1.1 kardel */
323 1.1 kardel syncchar = qualchar = leapchar = dstchar = ' ';
324 1.1 kardel tz = 0;
325 1.1 kardel switch (pp->lencode) {
326 1.1 kardel
327 1.1 kardel case LENWWVB0:
328 1.1 kardel
329 1.1 kardel /*
330 1.1 kardel * Timecode format 0: "I ddd hh:mm:ss DTZ=nn"
331 1.1 kardel */
332 1.1 kardel if (sscanf(pp->a_lastcode,
333 1.1 kardel "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
334 1.1 kardel &syncchar, &pp->day, &pp->hour, &pp->minute,
335 1.2 kardel &pp->second, &tmpchar, &dstchar, &tz) == 8) {
336 1.1 kardel pp->nsec = 0;
337 1.1 kardel break;
338 1.2 kardel }
339 1.2 kardel goto bad_format;
340 1.1 kardel
341 1.1 kardel case LENWWVB2:
342 1.1 kardel
343 1.1 kardel /*
344 1.1 kardel * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
345 1.1 kardel if (sscanf(pp->a_lastcode,
346 1.1 kardel "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
347 1.1 kardel &syncchar, &qualchar, &pp->year, &pp->day,
348 1.1 kardel &pp->hour, &pp->minute, &pp->second, &pp->nsec,
349 1.2 kardel &leapchar) == 9) {
350 1.1 kardel pp->nsec *= 1000000;
351 1.1 kardel break;
352 1.2 kardel }
353 1.2 kardel goto bad_format;
354 1.1 kardel
355 1.1 kardel case LENWWVB3:
356 1.1 kardel
357 1.2 kardel /*
358 1.1 kardel * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
359 1.2 kardel * WARNING: Undocumented, and the on-time character # is
360 1.2 kardel * not yet handled correctly by this driver. It may be
361 1.2 kardel * as simple as compensating for an additional 1/960 s.
362 1.1 kardel */
363 1.1 kardel if (sscanf(pp->a_lastcode,
364 1.1 kardel "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
365 1.1 kardel &syncchar, &pp->year, &month, &day, &pp->hour,
366 1.1 kardel &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
367 1.1 kardel {
368 1.1 kardel pp->day = ymd2yd(pp->year, month, day);
369 1.1 kardel pp->nsec = 0;
370 1.1 kardel break;
371 1.1 kardel }
372 1.2 kardel goto bad_format;
373 1.1 kardel
374 1.1 kardel default:
375 1.2 kardel bad_format:
376 1.1 kardel
377 1.1 kardel /*
378 1.1 kardel * Unknown format: If dumping internal table, record
379 1.1 kardel * stats; otherwise, declare bad format.
380 1.1 kardel */
381 1.1 kardel if (up->linect > 0) {
382 1.1 kardel up->linect--;
383 1.1 kardel record_clock_stats(&peer->srcadr,
384 1.1 kardel pp->a_lastcode);
385 1.1 kardel } else {
386 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
387 1.1 kardel }
388 1.1 kardel return;
389 1.1 kardel }
390 1.1 kardel
391 1.1 kardel /*
392 1.1 kardel * Decode synchronization, quality and leap characters. If
393 1.1 kardel * unsynchronized, set the leap bits accordingly and exit.
394 1.1 kardel * Otherwise, set the leap bits according to the leap character.
395 1.1 kardel * Once synchronized, the dispersion depends only on the
396 1.1 kardel * quality character.
397 1.1 kardel */
398 1.1 kardel switch (qualchar) {
399 1.1 kardel
400 1.1 kardel case ' ':
401 1.1 kardel pp->disp = .001;
402 1.1 kardel pp->lastref = pp->lastrec;
403 1.1 kardel break;
404 1.1 kardel
405 1.1 kardel case 'A':
406 1.1 kardel pp->disp = .01;
407 1.1 kardel break;
408 1.1 kardel
409 1.1 kardel case 'B':
410 1.1 kardel pp->disp = .1;
411 1.1 kardel break;
412 1.1 kardel
413 1.1 kardel case 'C':
414 1.1 kardel pp->disp = .5;
415 1.1 kardel break;
416 1.1 kardel
417 1.1 kardel case 'D':
418 1.1 kardel pp->disp = MAXDISPERSE;
419 1.1 kardel break;
420 1.1 kardel
421 1.1 kardel default:
422 1.1 kardel pp->disp = MAXDISPERSE;
423 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
424 1.1 kardel break;
425 1.1 kardel }
426 1.1 kardel if (syncchar != ' ')
427 1.1 kardel pp->leap = LEAP_NOTINSYNC;
428 1.1 kardel else if (leapchar == 'L')
429 1.1 kardel pp->leap = LEAP_ADDSECOND;
430 1.1 kardel else
431 1.1 kardel pp->leap = LEAP_NOWARNING;
432 1.1 kardel
433 1.1 kardel /*
434 1.1 kardel * Process the new sample in the median filter and determine the
435 1.1 kardel * timecode timestamp, but only if the PPS is not in control.
436 1.1 kardel */
437 1.1 kardel #ifdef HAVE_PPSAPI
438 1.1 kardel up->tcount++;
439 1.1 kardel if (peer->flags & FLAG_PPS)
440 1.1 kardel return;
441 1.1 kardel
442 1.1 kardel #endif /* HAVE_PPSAPI */
443 1.1 kardel if (!refclock_process_f(pp, pp->fudgetime2))
444 1.1 kardel refclock_report(peer, CEVNT_BADTIME);
445 1.1 kardel }
446 1.1 kardel
447 1.1 kardel
448 1.1 kardel /*
449 1.1 kardel * wwvb_timer - called once per second by the transmit procedure
450 1.1 kardel */
451 1.1 kardel static void
452 1.1 kardel wwvb_timer(
453 1.1 kardel int unit,
454 1.1 kardel struct peer *peer
455 1.1 kardel )
456 1.1 kardel {
457 1.1 kardel register struct wwvbunit *up;
458 1.1 kardel struct refclockproc *pp;
459 1.1 kardel char pollchar; /* character sent to clock */
460 1.2 kardel l_fp now;
461 1.1 kardel
462 1.1 kardel /*
463 1.1 kardel * Time to poll the clock. The Spectracom clock responds to a
464 1.1 kardel * 'T' by returning a timecode in the format(s) specified above.
465 1.1 kardel * Note there is no checking on state, since this may not be the
466 1.1 kardel * only customer reading the clock. Only one customer need poll
467 1.1 kardel * the clock; all others just listen in.
468 1.1 kardel */
469 1.1 kardel pp = peer->procptr;
470 1.2 kardel up = pp->unitptr;
471 1.1 kardel if (up->linect > 0)
472 1.1 kardel pollchar = 'R';
473 1.1 kardel else
474 1.1 kardel pollchar = 'T';
475 1.1 kardel if (write(pp->io.fd, &pollchar, 1) != 1)
476 1.1 kardel refclock_report(peer, CEVNT_FAULT);
477 1.2 kardel #ifdef DEBUG
478 1.2 kardel get_systime(&now);
479 1.2 kardel if (debug)
480 1.2 kardel printf("%c poll at %s\n", pollchar, prettydate(&now));
481 1.2 kardel #endif
482 1.1 kardel #ifdef HAVE_PPSAPI
483 1.1 kardel if (up->ppsapi_lit &&
484 1.1 kardel refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
485 1.1 kardel up->pcount++,
486 1.1 kardel peer->flags |= FLAG_PPS;
487 1.1 kardel peer->precision = PPS_PRECISION;
488 1.1 kardel }
489 1.1 kardel #endif /* HAVE_PPSAPI */
490 1.1 kardel }
491 1.1 kardel
492 1.1 kardel
493 1.1 kardel /*
494 1.1 kardel * wwvb_poll - called by the transmit procedure
495 1.1 kardel */
496 1.1 kardel static void
497 1.1 kardel wwvb_poll(
498 1.1 kardel int unit,
499 1.1 kardel struct peer *peer
500 1.1 kardel )
501 1.1 kardel {
502 1.1 kardel register struct wwvbunit *up;
503 1.1 kardel struct refclockproc *pp;
504 1.1 kardel
505 1.1 kardel /*
506 1.1 kardel * Sweep up the samples received since the last poll. If none
507 1.1 kardel * are received, declare a timeout and keep going.
508 1.1 kardel */
509 1.1 kardel pp = peer->procptr;
510 1.2 kardel up = pp->unitptr;
511 1.1 kardel pp->polls++;
512 1.1 kardel
513 1.1 kardel /*
514 1.1 kardel * If the monitor flag is set (flag4), we dump the internal
515 1.1 kardel * quality table at the first timecode beginning the day.
516 1.1 kardel */
517 1.1 kardel if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
518 1.1 kardel (int)up->lasthour)
519 1.1 kardel up->linect = MONLIN;
520 1.1 kardel up->lasthour = (u_char)pp->hour;
521 1.1 kardel
522 1.1 kardel /*
523 1.1 kardel * Process median filter samples. If none received, declare a
524 1.1 kardel * timeout and keep going.
525 1.1 kardel */
526 1.1 kardel #ifdef HAVE_PPSAPI
527 1.1 kardel if (up->pcount == 0) {
528 1.1 kardel peer->flags &= ~FLAG_PPS;
529 1.1 kardel peer->precision = PRECISION;
530 1.1 kardel }
531 1.1 kardel if (up->tcount == 0) {
532 1.1 kardel pp->coderecv = pp->codeproc;
533 1.1 kardel refclock_report(peer, CEVNT_TIMEOUT);
534 1.1 kardel return;
535 1.1 kardel }
536 1.1 kardel up->pcount = up->tcount = 0;
537 1.1 kardel #else /* HAVE_PPSAPI */
538 1.1 kardel if (pp->coderecv == pp->codeproc) {
539 1.1 kardel refclock_report(peer, CEVNT_TIMEOUT);
540 1.1 kardel return;
541 1.1 kardel }
542 1.1 kardel #endif /* HAVE_PPSAPI */
543 1.1 kardel refclock_receive(peer);
544 1.1 kardel record_clock_stats(&peer->srcadr, pp->a_lastcode);
545 1.1 kardel #ifdef DEBUG
546 1.1 kardel if (debug)
547 1.1 kardel printf("wwvb: timecode %d %s\n", pp->lencode,
548 1.1 kardel pp->a_lastcode);
549 1.1 kardel #endif
550 1.1 kardel }
551 1.1 kardel
552 1.1 kardel
553 1.1 kardel /*
554 1.1 kardel * wwvb_control - fudge parameters have been set or changed
555 1.1 kardel */
556 1.1 kardel #ifdef HAVE_PPSAPI
557 1.1 kardel static void
558 1.1 kardel wwvb_control(
559 1.1 kardel int unit,
560 1.1 kardel struct refclockstat *in_st,
561 1.1 kardel struct refclockstat *out_st,
562 1.1 kardel struct peer *peer
563 1.1 kardel )
564 1.1 kardel {
565 1.1 kardel register struct wwvbunit *up;
566 1.1 kardel struct refclockproc *pp;
567 1.1 kardel
568 1.1 kardel pp = peer->procptr;
569 1.2 kardel up = pp->unitptr;
570 1.1 kardel
571 1.1 kardel if (!(pp->sloppyclockflag & CLK_FLAG1)) {
572 1.1 kardel if (!up->ppsapi_tried)
573 1.1 kardel return;
574 1.1 kardel up->ppsapi_tried = 0;
575 1.1 kardel if (!up->ppsapi_lit)
576 1.1 kardel return;
577 1.1 kardel peer->flags &= ~FLAG_PPS;
578 1.1 kardel peer->precision = PRECISION;
579 1.1 kardel time_pps_destroy(up->atom.handle);
580 1.1 kardel up->atom.handle = 0;
581 1.1 kardel up->ppsapi_lit = 0;
582 1.1 kardel return;
583 1.1 kardel }
584 1.1 kardel
585 1.1 kardel if (up->ppsapi_tried)
586 1.1 kardel return;
587 1.1 kardel /*
588 1.1 kardel * Light up the PPSAPI interface.
589 1.1 kardel */
590 1.1 kardel up->ppsapi_tried = 1;
591 1.1 kardel if (refclock_ppsapi(pp->io.fd, &up->atom)) {
592 1.1 kardel up->ppsapi_lit = 1;
593 1.1 kardel return;
594 1.1 kardel }
595 1.1 kardel
596 1.1 kardel NLOG(NLOG_CLOCKINFO)
597 1.1 kardel msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails",
598 1.1 kardel refnumtoa(&peer->srcadr));
599 1.1 kardel }
600 1.1 kardel #endif /* HAVE_PPSAPI */
601 1.1 kardel
602 1.1 kardel #else
603 1.1 kardel int refclock_wwvb_bs;
604 1.1 kardel #endif /* REFCLOCK */
605