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