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