1 /* $NetBSD: refclock_acts.c,v 1.13 2024/08/18 20:47:18 christos Exp $ */
2
3 /*
4 * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
5 * Services
6 */
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #if defined(REFCLOCK) && defined(CLOCK_ACTS)
12
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_unixtime.h"
16 #include "ntp_refclock.h"
17 #include "ntp_stdlib.h"
18 #include "ntp_control.h"
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #ifdef HAVE_SYS_IOCTL_H
23 # include <sys/ioctl.h>
24 #endif /* HAVE_SYS_IOCTL_H */
25
26 /*
27 * This driver supports the US (NIST, USNO) and European (PTB, NPL,
28 * etc.) modem time services, as well as Spectracom GPS and WWVB
29 * receivers connected via a modem. The driver periodically dials a
30 * number from a telephone list, receives the timecode data and
31 * calculates the local clock correction. It is designed primarily for
32 * use as backup when neither a radio clock nor connectivity to Internet
33 * time servers is available.
34 *
35 * This driver requires a modem with a Hayes-compatible command set and
36 * control over the modem data terminal ready (DTR) control line. The
37 * modem setup string is hard-coded in the driver and may require
38 * changes for nonstandard modems or special circumstances.
39 *
40 * When enabled, the calling program dials the first number in the
41 * phones file. If that call fails, it dials the second number and
42 * so on. The phone number is specified by the Hayes ATDT prefix
43 * followed by the number itself, including the long-distance prefix
44 * and delay code, if necessary. The calling program is enabled
45 * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
46 * when no other synchronization sources are present, and (c) at each
47 * poll interval whether or not other synchronization sources are
48 * present. The calling program disconnects if (a) the called party
49 * is busy or does not answer, (b) the called party disconnects
50 * before a sufficient nuimber of timecodes have been received.
51 *
52 * The driver is transparent to each of the modem time services and
53 * Spectracom radios. It selects the parsing algorithm depending on the
54 * message length. There is some hazard should the message be corrupted.
55 * However, the data format is checked carefully and only if all checks
56 * succeed is the message accepted. Corrupted lines are discarded
57 * without complaint.
58 *
59 * Fudge controls
60 *
61 * flag1 force a call in manual mode
62 * flag2 enable port locking (not verified)
63 * flag3 not used
64 * flag4 not used
65 *
66 * time1 offset adjustment (s)
67 *
68 * Ordinarily, the serial port is connected to a modem and the phones
69 * list is defined. If no phones list is defined, the port can be
70 * connected directly to a device or another computer. In this case the
71 * driver will send a single character 'T' at each poll event. If
72 * fudge flag2 is enabled, port locking allows the modem to be shared
73 * when not in use by this driver.
74 */
75 /*
76 * National Institute of Science and Technology (NIST)
77 *
78 * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
79 *
80 * Data Format
81 *
82 * National Institute of Standards and Technology
83 * Telephone Time Service, Generator 3B
84 * Enter question mark "?" for HELP
85 * D L D
86 * MJD YR MO DA H M S ST S UT1 msADV <OTM>
87 * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
88 * ...
89 *
90 * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
91 * the on-time markers echoed by the driver and used by NIST to measure
92 * and correct for the propagation delay. Note: the ACTS timecode has
93 * recently been changed to eliminate the * on-time indicator. The
94 * reason for this and the long term implications are not clear.
95 *
96 * US Naval Observatory (USNO)
97 *
98 * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
99 *
100 * Data Format (two lines, repeating at one-second intervals)
101 *
102 * jjjjj nnn hhmmss UTC<CR><LF>
103 * *<CR><LF>
104 *
105 * jjjjj modified Julian day number (not used)
106 * nnn day of year
107 * hhmmss second of day
108 * * on-time marker for previous timecode
109 * ...
110 *
111 * USNO does not correct for the propagation delay. A fudge time1 of
112 * about .06 s is advisable.
113 *
114 * European Services (PTB, NPL, etc.)
115 *
116 * PTB: +49 531 512038 (Germany)
117 * NPL: 0906 851 6333 (UK only)
118 *
119 * Data format (see the documentation for phone numbers and formats.)
120 *
121 * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
122 *
123 * Spectracom GPS and WWVB Receivers
124 *
125 * If a modem is connected to a Spectracom receiver, this driver will
126 * call it up and retrieve the time in one of two formats. As this
127 * driver does not send anything, the radio will have to either be
128 * configured in continuous mode or be polled by another local driver.
129 */
130 /*
131 * Interface definitions
132 */
133 #define DEVICE "/dev/acts%d" /* device name and unit */
134 #define SPEED232 B19200 /* uart speed (19200 bps) */
135 #define PRECISION (-10) /* precision assumed (about 1 ms) */
136 #define LOCKFILE "/var/spool/lock/LCK..cua%d"
137 #define DESCRIPTION "Automated Computer Time Service" /* WRU */
138 #define REFID "NONE" /* default reference ID */
139 #define MSGCNT 20 /* max message count */
140 #define MAXPHONE 10 /* max number of phone numbers */
141
142 /*
143 * Calling program modes (mode)
144 */
145 #define MODE_BACKUP 0 /* backup mode */
146 #define MODE_AUTO 1 /* automatic mode */
147 #define MODE_MANUAL 2 /* manual mode */
148
149 /*
150 * Service identifiers (message length)
151 */
152 #define REFACTS "NIST" /* NIST reference ID */
153 #define LENACTS 50 /* NIST format A */
154 #define REFUSNO "USNO" /* USNO reference ID */
155 #define LENUSNO 20 /* USNO */
156 #define REFPTB "PTB\0" /* PTB/NPL reference ID */
157 #define LENPTB 78 /* PTB/NPL format */
158 #define REFWWVB "WWVB" /* WWVB reference ID */
159 #define LENWWVB0 22 /* WWVB format 0 */
160 #define LENWWVB2 24 /* WWVB format 2 */
161 #define LF 0x0a /* ASCII LF */
162
163 /*
164 * Modem setup strings. These may have to be changed for
165 * some modems.
166 *
167 * AT command prefix
168 * B1 US answer tone
169 * &C0 disable carrier detect
170 * &D2 hang up and return to command mode on DTR transition
171 * E0 modem command echo disabled
172 * L1 set modem speaker volume to low level
173 * M1 speaker enabled until carrier detect
174 * Q0 return result codes
175 * V1 return result codes as English words
176 * Y1 enable long-space disconnect
177 */
178 const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
179 const char *modem_setup = def_modem_setup;
180
181 /*
182 * Timeouts (all in seconds)
183 */
184 #define SETUP 3 /* setup timeout */
185 #define REDIAL 30 /* redial timeout */
186 #define ANSWER 60 /* answer timeout */
187 #define TIMECODE 60 /* message timeout */
188 #define MAXCODE 20 /* max timecodes */
189
190 /*
191 * State machine codes
192 */
193 typedef enum {
194 S_IDLE, /* wait for poll */
195 S_SETUP, /* send modem setup */
196 S_CONNECT, /* wait for answer */
197 S_MSG /* wait for timecode */
198 } teModemState;
199
200 /*
201 * Unit control structure
202 */
203 struct actsunit {
204 int unit; /* unit number */
205 int state; /* the first one was Delaware */
206 int timer; /* timeout counter */
207 int retry; /* retry index */
208 int msgcnt; /* count of messages received */
209 l_fp tstamp; /* on-time timestamp */
210 char *bufptr; /* next incoming char stored here */
211 char buf[BMAX]; /* bufptr roams within buf[] */
212 };
213
214 /*
215 * Function prototypes
216 */
217 static int acts_start (int, struct peer *);
218 static void acts_shutdown (int, struct peer *);
219 static void acts_receive (struct recvbuf *);
220 static void acts_message (struct peer *, const char *);
221 static void acts_timecode (struct peer *, const char *);
222 static void acts_poll (int, struct peer *);
223 static void acts_timeout (struct peer *, teModemState);
224 static void acts_timer (int, struct peer *);
225 static void acts_close (struct peer *);
226
227 /*
228 * Transfer vector (conditional structure name)
229 */
230 struct refclock refclock_acts = {
231 acts_start, /* start up driver */
232 acts_shutdown, /* shut down driver */
233 acts_poll, /* transmit poll message */
234 noentry, /* not used */
235 noentry, /* not used */
236 noentry, /* not used */
237 acts_timer /* housekeeping timer */
238 };
239
240 /*
241 * Initialize data for processing
242 */
243 static int
acts_start(int unit,struct peer * peer)244 acts_start(
245 int unit,
246 struct peer *peer
247 )
248 {
249 struct actsunit *up;
250 struct refclockproc *pp;
251 const char *setup;
252
253 /*
254 * Allocate and initialize unit structure
255 */
256 up = emalloc_zero(sizeof(struct actsunit));
257 up->unit = unit;
258 pp = peer->procptr;
259 pp->unitptr = up;
260 pp->io.clock_recv = acts_receive;
261 pp->io.srcclock = peer;
262 pp->io.datalen = 0;
263 pp->io.fd = -1;
264
265 /*
266 * Initialize miscellaneous variables
267 */
268 peer->precision = PRECISION;
269 pp->clockdesc = DESCRIPTION;
270 memcpy(&pp->refid, REFID, 4);
271 peer->sstclktype = CTL_SST_TS_TELEPHONE;
272 up->bufptr = up->buf;
273 if (def_modem_setup == modem_setup) {
274 setup = get_ext_sys_var("modemsetup");
275 if (setup != NULL)
276 modem_setup = estrdup(setup);
277 }
278
279 return (1);
280 }
281
282
283 /*
284 * acts_shutdown - shut down the clock
285 */
286 static void
acts_shutdown(int unit,struct peer * peer)287 acts_shutdown(
288 int unit,
289 struct peer *peer
290 )
291 {
292 struct actsunit *up;
293 struct refclockproc *pp;
294
295 /*
296 * Warning: do this only when a call is not in progress.
297 */
298 pp = peer->procptr;
299 up = pp->unitptr;
300 acts_close(peer);
301 free(up);
302 }
303
304
305 /*
306 * acts_receive - receive data from the serial interface
307 */
308 static void
acts_receive(struct recvbuf * rbufp)309 acts_receive(
310 struct recvbuf *rbufp
311 )
312 {
313 struct actsunit *up;
314 struct refclockproc *pp;
315 struct peer *peer;
316 char tbuf[sizeof(up->buf)];
317 char * tptr;
318 int octets;
319
320 /*
321 * Initialize pointers and read the timecode and timestamp. Note
322 * we are in raw mode and victim of whatever the terminal
323 * interface kicks up; so, we have to reassemble messages from
324 * arbitrary fragments. Capture the timecode at the beginning of
325 * the message and at the '*' and '#' on-time characters.
326 */
327 peer = rbufp->recv_peer;
328 pp = peer->procptr;
329 up = pp->unitptr;
330 octets = sizeof(up->buf) - (up->bufptr - up->buf);
331 refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
332 for (tptr = tbuf; *tptr != '\0'; tptr++) {
333 if (*tptr == LF) {
334 if (up->bufptr == up->buf) {
335 up->tstamp = pp->lastrec;
336 continue;
337 } else {
338 *up->bufptr = '\0';
339 up->bufptr = up->buf;
340 acts_message(peer, up->buf);
341 }
342 } else if (!iscntrl((unsigned char)*tptr)) {
343 *up->bufptr++ = *tptr;
344 if (*tptr == '*' || *tptr == '#') {
345 up->tstamp = pp->lastrec;
346 refclock_write(peer, tptr, 1, "data");
347 }
348 }
349 }
350 }
351
352
353 /*
354 * acts_message - process message
355 */
356 void
acts_message(struct peer * peer,const char * msg)357 acts_message(
358 struct peer *peer,
359 const char *msg
360 )
361 {
362 struct actsunit *up;
363 struct refclockproc *pp;
364 char tbuf[BMAX];
365 int dtr = TIOCM_DTR;
366
367 DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
368
369 /*
370 * What to do depends on the state and the first token in the
371 * message.
372 */
373 pp = peer->procptr;
374 up = pp->unitptr;
375
376 /*
377 * Extract the first token in the line.
378 */
379 strlcpy(tbuf, msg, sizeof(tbuf));
380 strtok(tbuf, " ");
381 switch (up->state) {
382
383 /*
384 * We are waiting for the OK response to the modem setup
385 * command. When this happens, dial the number followed.
386 * If anything other than OK is received, just ignore it
387 * and wait for timeoue.
388 */
389 case S_SETUP:
390 if (strcmp(tbuf, "OK") != 0) {
391 /*
392 * We disable echo with MODEM_SETUP's E0 but
393 * if the modem was previously E1, we will
394 * see MODEM_SETUP echoed before the OK/ERROR.
395 * Ignore it.
396 */
397 if (!strcmp(tbuf, modem_setup))
398 return;
399 break;
400 }
401
402 mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
403 up->retry, sys_phone[up->retry]);
404 if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
405 msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
406 refclock_write(peer, sys_phone[up->retry],
407 strlen(sys_phone[up->retry]),
408 "DIAL");
409 refclock_write(peer, "\r", 1, "CR");
410 up->retry++;
411 up->state = S_CONNECT;
412 up->timer = ANSWER;
413 return;
414
415 /*
416 * We are waiting for the CONNECT response to the dial
417 * command. When this happens, listen for timecodes. If
418 * somthing other than CONNECT is received, like BUSY
419 * or NO CARRIER, abort the call.
420 */
421 case S_CONNECT:
422 if (strcmp(tbuf, "CONNECT") != 0)
423 break;
424
425 report_event(PEVNT_CLOCK, peer, msg);
426 up->state = S_MSG;
427 up->timer = TIMECODE;
428 return;
429
430 /*
431 * We are waiting for a timecode response. Pass it to
432 * the parser. If NO CARRIER is received, save the
433 * messages and abort the call.
434 */
435 case S_MSG:
436 if (strcmp(tbuf, "NO") == 0)
437 report_event(PEVNT_CLOCK, peer, msg);
438 if (up->msgcnt < MAXCODE)
439 acts_timecode(peer, msg);
440 else
441 acts_timeout(peer, S_MSG);
442 return;
443 }
444
445 /*
446 * Other response. Tell us about it.
447 */
448 report_event(PEVNT_CLOCK, peer, msg);
449 acts_close(peer);
450 }
451
452
453 /*
454 * acts_timeout - called on timeout
455 */
456 static void
acts_timeout(struct peer * peer,teModemState dstate)457 acts_timeout(
458 struct peer *peer,
459 teModemState dstate
460 )
461 {
462 struct actsunit *up;
463 struct refclockproc *pp;
464 int fd;
465 char device[20];
466 char lockfile[128], pidbuf[8];
467
468 /*
469 * The state machine is driven by messages from the modem,
470 * when first started and at timeout.
471 */
472 pp = peer->procptr;
473 up = pp->unitptr;
474 switch (dstate) {
475
476 /*
477 * System poll event. Lock the modem port, open the device
478 * and send the setup command.
479 */
480 case S_IDLE:
481 if (-1 != pp->io.fd)
482 return; /* port is already open */
483
484 /*
485 * Lock the modem port. If busy, retry later. Note: if
486 * something fails between here and the close, the lock
487 * file may not be removed.
488 */
489 if (pp->sloppyclockflag & CLK_FLAG2) {
490 snprintf(lockfile, sizeof(lockfile), LOCKFILE,
491 up->unit);
492 fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
493 0644);
494 if (fd < 0) {
495 report_event(PEVNT_CLOCK, peer, "acts: port busy");
496 return;
497 }
498 snprintf(pidbuf, sizeof(pidbuf), "%d\n",
499 (u_int)getpid());
500 if (write(fd, pidbuf, strlen(pidbuf)) < 0)
501 msyslog(LOG_ERR, "acts: write lock fails %m");
502 close(fd);
503 }
504
505 /*
506 * Open the device in raw mode and link the I/O.
507 */
508 snprintf(device, sizeof(device), DEVICE,
509 up->unit);
510 fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_ACTS |
511 LDISC_RAW | LDISC_REMOTE);
512 if (fd < 0) {
513 msyslog(LOG_ERR, "acts: open fails %m");
514 return;
515 }
516 pp->io.fd = fd;
517 if (!io_addclock(&pp->io)) {
518 msyslog(LOG_ERR, "acts: addclock fails");
519 close(fd);
520 pp->io.fd = -1;
521 return;
522 }
523 up->msgcnt = 0;
524 up->bufptr = up->buf;
525
526 /*
527 * If the port is directly connected to the device, skip
528 * the modem business and send 'T' for Spectrabum.
529 */
530 if (sys_phone[up->retry] == NULL) {
531 refclock_write(peer, "T", 1, "T");
532 up->state = S_MSG;
533 up->timer = TIMECODE;
534 return;
535 }
536
537 /*
538 * Initialize the modem. This works with Hayes-
539 * compatible modems.
540 */
541 mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
542 modem_setup);
543 refclock_write(peer, modem_setup, strlen(modem_setup),
544 "SETUP");
545 refclock_write(peer, "\r", 1, "CR");
546 up->state = S_SETUP;
547 up->timer = SETUP;
548 return;
549
550 /*
551 * In SETUP state the modem did not respond OK to setup string.
552 */
553 case S_SETUP:
554 report_event(PEVNT_CLOCK, peer, "no modem");
555 break;
556
557 /*
558 * In CONNECT state the call did not complete. Abort the call.
559 */
560 case S_CONNECT:
561 report_event(PEVNT_CLOCK, peer, "no answer");
562 break;
563
564 /*
565 * In MSG states no further timecodes are expected. If any
566 * timecodes have arrived, update the clock. In any case,
567 * terminate the call.
568 */
569 case S_MSG:
570 if (up->msgcnt == 0) {
571 report_event(PEVNT_CLOCK, peer, "no timecodes");
572 } else {
573 pp->lastref = pp->lastrec;
574 record_clock_stats(&peer->srcadr, pp->a_lastcode);
575 refclock_receive(peer);
576 }
577 break;
578 }
579 acts_close(peer);
580 }
581
582
583 /*
584 * acts_close - close and prepare for next call.
585 *
586 * In ClOSE state no further protocol actions are required
587 * other than to close and release the device and prepare to
588 * dial the next number if necessary.
589 */
590 void
acts_close(struct peer * peer)591 acts_close(
592 struct peer *peer
593 )
594 {
595 struct actsunit *up;
596 struct refclockproc *pp;
597 char lockfile[128];
598 int dtr;
599
600 pp = peer->procptr;
601 up = pp->unitptr;
602 if (pp->io.fd != -1) {
603 report_event(PEVNT_CLOCK, peer, "close");
604 dtr = TIOCM_DTR;
605 if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
606 msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
607 io_closeclock(&pp->io);
608 pp->io.fd = -1;
609 }
610 if (pp->sloppyclockflag & CLK_FLAG2) {
611 snprintf(lockfile, sizeof(lockfile),
612 LOCKFILE, up->unit);
613 unlink(lockfile);
614 }
615 if (up->msgcnt == 0 && up->retry > 0) {
616 if (sys_phone[up->retry] != NULL) {
617 up->state = S_IDLE;
618 up->timer = REDIAL;
619 return;
620 }
621 }
622 up->state = S_IDLE;
623 up->timer = 0;
624 }
625
626
627 /*
628 * acts_poll - called by the transmit routine
629 */
630 static void
acts_poll(int unit,struct peer * peer)631 acts_poll(
632 int unit,
633 struct peer *peer
634 )
635 {
636 struct actsunit *up;
637 struct refclockproc *pp;
638
639 /*
640 * This routine is called at every system poll. All it does is
641 * set flag1 under certain conditions. The real work is done by
642 * the timeout routine and state machine.
643 */
644 pp = peer->procptr;
645 up = pp->unitptr;
646 switch (peer->ttl) {
647
648 /*
649 * In manual mode the calling program is activated by the ntpdc
650 * program using the enable flag (fudge flag1), either manually
651 * or by a cron job.
652 */
653 case MODE_MANUAL:
654 return;
655
656 /*
657 * In automatic mode the calling program runs continuously at
658 * intervals determined by the poll event or specified timeout.
659 */
660 case MODE_AUTO:
661 break;
662
663 /*
664 * In backup mode the calling program runs continuously as long
665 * as either no peers are available or this peer is selected.
666 */
667 case MODE_BACKUP:
668 if (!(sys_peer == NULL || sys_peer == peer))
669 return;
670
671 break;
672 }
673 pp->polls++;
674 if (S_IDLE == up->state) {
675 up->retry = 0;
676 acts_timeout(peer, S_IDLE);
677 }
678 }
679
680
681 /*
682 * acts_timer - called at one-second intervals
683 */
684 static void
acts_timer(int unit,struct peer * peer)685 acts_timer(
686 int unit,
687 struct peer *peer
688 )
689 {
690 struct actsunit *up;
691 struct refclockproc *pp;
692
693 /*
694 * This routine implments a timeout which runs for a programmed
695 * interval. The counter is initialized by the state machine and
696 * counts down to zero. Upon reaching zero, the state machine is
697 * called. If flag1 is set while timer is zero, force a call.
698 */
699 pp = peer->procptr;
700 up = pp->unitptr;
701 if (up->timer == 0) {
702 if (pp->sloppyclockflag & CLK_FLAG1) {
703 pp->sloppyclockflag &= ~CLK_FLAG1;
704 acts_timeout(peer, S_IDLE);
705 }
706 } else {
707 up->timer--;
708 if (up->timer == 0)
709 acts_timeout(peer, up->state);
710 }
711 }
712
713 /*
714 * acts_timecode - identify the service and parse the timecode message
715 */
716 void
acts_timecode(struct peer * peer,const char * str)717 acts_timecode(
718 struct peer * peer, /* peer structure pointer */
719 const char * str /* timecode string */
720 )
721 {
722 struct actsunit *up;
723 struct refclockproc *pp;
724 int day; /* day of the month */
725 int month; /* month of the year */
726 u_long mjd; /* Modified Julian Day */
727 double dut1; /* DUT adjustment */
728
729 u_int dst; /* ACTS daylight/standard time */
730 u_int leap; /* ACTS leap indicator */
731 double msADV; /* ACTS transmit advance (ms) */
732 char utc[10]; /* ACTS timescale */
733 char flag; /* ACTS on-time character (* or #) */
734
735 char synchar; /* WWVB synchronized indicator */
736 char qualchar; /* WWVB quality indicator */
737 char leapchar; /* WWVB leap indicator */
738 char dstchar; /* WWVB daylight/savings indicator */
739 int tz; /* WWVB timezone */
740
741 int leapmonth; /* PTB/NPL month of leap */
742 char leapdir; /* PTB/NPL leap direction */
743
744 /*
745 * The parser selects the modem format based on the message
746 * length. Since the data are checked carefully, occasional
747 * errors due noise are forgivable.
748 */
749 pp = peer->procptr;
750 up = pp->unitptr;
751 pp->nsec = 0;
752 switch (strlen(str)) {
753
754 /*
755 * For USNO format on-time character '*', which is on a line by
756 * itself. Be sure a timecode has been received.
757 */
758 case 1:
759 if (*str == '*' && up->msgcnt > 0)
760 break;
761
762 return;
763
764 /*
765 * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
766 * UTC(NIST) *".
767 */
768 case LENACTS:
769 if (sscanf(str,
770 "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
771 &mjd, &pp->year, &month, &day, &pp->hour,
772 &pp->minute, &pp->second, &dst, &leap, &dut1,
773 &msADV, utc, &flag) != 13) {
774 refclock_report(peer, CEVNT_BADREPLY);
775 return;
776 }
777 pp->day = ymd2yd(pp->year, month, day);
778 pp->leap = LEAP_NOWARNING;
779 if (leap == 1)
780 pp->leap = LEAP_ADDSECOND;
781 else if (leap == 2)
782 pp->leap = LEAP_DELSECOND;
783 memcpy(&pp->refid, REFACTS, 4);
784 up->msgcnt++;
785 if (flag != '#' && up->msgcnt < 10)
786 return;
787
788 break;
789
790 /*
791 * USNO format: "jjjjj nnn hhmmss UTC"
792 */
793 case LENUSNO:
794 if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
795 &mjd, &pp->day, &pp->hour, &pp->minute,
796 &pp->second, utc) != 6) {
797 refclock_report(peer, CEVNT_BADREPLY);
798 return;
799 }
800
801 /*
802 * Wait for the on-time character, which follows in a
803 * separate message. There is no provision for leap
804 * warning.
805 */
806 pp->leap = LEAP_NOWARNING;
807 memcpy(&pp->refid, REFUSNO, 4);
808 up->msgcnt++;
809 break;
810
811 /*
812 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
813 */
814 case LENPTB:
815 if (sscanf(str,
816 "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
817 &pp->second, &pp->year, &month, &day, &pp->hour,
818 &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
819 &msADV, &flag) != 12) {
820 refclock_report(peer, CEVNT_BADREPLY);
821 return;
822 }
823 pp->leap = LEAP_NOWARNING;
824 if (leapmonth == month) {
825 if (leapdir == '+')
826 pp->leap = LEAP_ADDSECOND;
827 else if (leapdir == '-')
828 pp->leap = LEAP_DELSECOND;
829 }
830 pp->day = ymd2yd(pp->year, month, day);
831 memcpy(&pp->refid, REFPTB, 4);
832 up->msgcnt++;
833 break;
834
835
836 /*
837 * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
838 */
839 case LENWWVB0:
840 if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
841 &synchar, &pp->day, &pp->hour, &pp->minute,
842 &pp->second, &dstchar, &tz) != 7) {
843 refclock_report(peer, CEVNT_BADREPLY);
844 return;
845 }
846 pp->leap = LEAP_NOWARNING;
847 if (synchar != ' ')
848 pp->leap = LEAP_NOTINSYNC;
849 memcpy(&pp->refid, REFWWVB, 4);
850 up->msgcnt++;
851 break;
852
853 /*
854 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
855 */
856 case LENWWVB2:
857 if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
858 &synchar, &qualchar, &pp->year, &pp->day,
859 &pp->hour, &pp->minute, &pp->second, &pp->nsec,
860 &dstchar, &leapchar, &dstchar) != 11) {
861 refclock_report(peer, CEVNT_BADREPLY);
862 return;
863 }
864 pp->nsec *= 1000000;
865 pp->leap = LEAP_NOWARNING;
866 if (synchar != ' ')
867 pp->leap = LEAP_NOTINSYNC;
868 else if (leapchar == 'L')
869 pp->leap = LEAP_ADDSECOND;
870 memcpy(&pp->refid, REFWWVB, 4);
871 up->msgcnt++;
872 break;
873
874 /*
875 * None of the above. Just forget about it and wait for the next
876 * message or timeout.
877 */
878 default:
879 return;
880 }
881
882 /*
883 * We have a valid timecode. The fudge time1 value is added to
884 * each sample by the main line routines. Note that in current
885 * telephone networks the propatation time can be different for
886 * each call and can reach 200 ms for some calls.
887 */
888 peer->refid = pp->refid;
889 pp->lastrec = up->tstamp;
890 if (up->msgcnt == 0)
891 return;
892
893 strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
894 pp->lencode = strlen(pp->a_lastcode);
895 if (!refclock_process(pp)) {
896 refclock_report(peer, CEVNT_BADTIME);
897 return;
898 }
899 pp->lastref = pp->lastrec;
900 }
901 #else
902 NONEMPTY_TRANSLATION_UNIT
903 #endif /* REFCLOCK */
904