2 * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
9 #if defined(REFCLOCK) && defined(CLOCK_ACTS)
13 #include "ntp_unixtime.h"
14 #include "ntp_refclock.h"
15 #include "ntp_stdlib.h"
16 #include "ntp_control.h"
20 #ifdef HAVE_SYS_IOCTL_H
21 # include <sys/ioctl.h>
22 #endif /* HAVE_SYS_IOCTL_H */
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)
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.
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.
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.
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
65 * flag1 force a call in manual mode
66 * flag2 enable port locking (not verified)
70 * time1 offset adjustment (s)
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.
80 * National Institute of Science and Technology (NIST)
82 * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
86 * National Institute of Standards and Technology
87 * Telephone Time Service, Generator 3B
88 * Enter question mark "?" for HELP
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>
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.
100 * US Naval Observatory (USNO)
102 * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
104 * Data Format (two lines, repeating at one-second intervals)
106 * jjjjj nnn hhmmss UTC<CR><LF>
109 * jjjjj modified Julian day number (not used)
111 * hhmmss second of day
112 * * on-time marker for previous timecode
115 * USNO does not correct for the propagation delay. A fudge time1 of
116 * about .06 s is advisable.
118 * European Services (PTB, NPL, etc.)
120 * PTB: +49 531 512038 (Germany)
121 * NPL: 0906 851 6333 (UK only)
123 * Data format (see the documentation for phone numbers and formats.)
125 * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
127 * Spectracom GPS and WWVB Receivers
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.
135 * Interface definitions
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 */
147 * Calling program modes (mode)
149 #define MODE_BACKUP 0 /* backup mode */
150 #define MODE_AUTO 1 /* automatic mode */
151 #define MODE_MANUAL 2 /* manual mode */
154 * Service identifiers (message length)
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 */
168 * Modem setup strings. These may have to be changed for
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
182 const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
183 const char *modem_setup = def_modem_setup;
186 * Timeouts (all in seconds)
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 */
195 * State machine codes
198 S_IDLE, /* wait for poll */
199 S_SETUP, /* send modem setup */
200 S_CONNECT, /* wait for answer */
201 S_MSG /* wait for timecode */
205 * Unit control structure
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[] */
219 * Function prototypes
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 *);
232 * Transfer vector (conditional structure name)
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 */
245 * Initialize data for processing
254 struct refclockproc *pp;
258 * Allocate and initialize unit structure
260 up = emalloc_zero(sizeof(struct actsunit));
264 pp->io.clock_recv = acts_receive;
265 pp->io.srcclock = peer;
270 * Initialize miscellaneous variables
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");
280 modem_setup = estrdup(setup);
288 * acts_shutdown - shut down the clock
297 struct refclockproc *pp;
300 * Warning: do this only when a call is not in progress.
310 * acts_receive - receive data from the serial interface
314 struct recvbuf *rbufp
318 struct refclockproc *pp;
320 char tbuf[sizeof(up->buf)];
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.
331 peer = rbufp->recv_peer;
334 octets = sizeof(up->buf) - (up->bufptr - up->buf);
335 refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
336 for (tptr = tbuf; *tptr != '\0'; tptr++) {
338 if (up->bufptr == up->buf) {
339 up->tstamp = pp->lastrec;
343 up->bufptr = up->buf;
344 acts_message(peer, up->buf);
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");
359 * acts_message - process message
368 struct refclockproc *pp;
372 DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
375 * What to do depends on the state and the first token in the
382 * Extract the first token in the line.
384 strlcpy(tbuf, msg, sizeof(tbuf));
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.
395 if (strcmp(tbuf, "OK") != 0) {
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.
402 if (!strcmp(tbuf, modem_setup))
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);
416 up->state = S_CONNECT;
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.
427 if (strcmp(tbuf, "CONNECT") != 0)
430 report_event(PEVNT_CLOCK, peer, msg);
432 up->timer = TIMECODE;
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.
441 if (strcmp(tbuf, "NO") == 0)
442 report_event(PEVNT_CLOCK, peer, msg);
443 if (up->msgcnt < MAXCODE)
444 acts_timecode(peer, msg);
446 acts_timeout(peer, S_MSG);
451 * Other response. Tell us about it.
453 report_event(PEVNT_CLOCK, peer, msg);
459 * acts_timeout - called on timeout
468 struct refclockproc *pp;
472 char lockfile[128], pidbuf[8];
475 * The state machine is driven by messages from the modem,
476 * when first started and at timeout.
483 * System poll event. Lock the modem port, open the device
484 * and send the setup command.
488 return; /* port is already open */
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.
495 if (pp->sloppyclockflag & CLK_FLAG2) {
496 snprintf(lockfile, sizeof(lockfile), LOCKFILE,
498 fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
501 report_event(PEVNT_CLOCK, peer, "acts: port busy");
504 snprintf(pidbuf, sizeof(pidbuf), "%d\n",
506 if (write(fd, pidbuf, strlen(pidbuf)) < 0)
507 msyslog(LOG_ERR, "acts: write lock fails %m");
512 * Open the device in raw mode and link the I/O.
514 snprintf(device, sizeof(device), DEVICE,
516 fd = refclock_open(device, SPEED232, LDISC_ACTS |
517 LDISC_RAW | LDISC_REMOTE);
519 msyslog(LOG_ERR, "acts: open fails %m");
523 if (!io_addclock(&pp->io)) {
524 msyslog(LOG_ERR, "acts: addclock fails");
530 up->bufptr = up->buf;
533 * If the port is directly connected to the device, skip
534 * the modem business and send 'T' for Spectrabum.
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");
540 up->timer = TIMECODE;
545 * Initialize the modem. This works with Hayes-
548 mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
550 rc = write(pp->io.fd, modem_setup, strlen(modem_setup));
552 msyslog(LOG_ERR, "acts: write SETUP fails %m");
553 write(pp->io.fd, "\r", 1);
559 * In SETUP state the modem did not respond OK to setup string.
562 report_event(PEVNT_CLOCK, peer, "no modem");
566 * In CONNECT state the call did not complete. Abort the call.
569 report_event(PEVNT_CLOCK, peer, "no answer");
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.
578 if (up->msgcnt == 0) {
579 report_event(PEVNT_CLOCK, peer, "no timecodes");
581 pp->lastref = pp->lastrec;
582 record_clock_stats(&peer->srcadr, pp->a_lastcode);
583 refclock_receive(peer);
592 * acts_close - close and prepare for next call.
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.
604 struct refclockproc *pp;
610 if (pp->io.fd != -1) {
611 report_event(PEVNT_CLOCK, peer, "close");
613 if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
614 msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
615 io_closeclock(&pp->io);
618 if (pp->sloppyclockflag & CLK_FLAG2) {
619 snprintf(lockfile, sizeof(lockfile),
623 if (up->msgcnt == 0 && up->retry > 0) {
624 if (sys_phone[up->retry] != NULL) {
636 * acts_poll - called by the transmit routine
645 struct refclockproc *pp;
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.
657 * In manual mode the calling program is activated by the ntpdc
658 * program using the enable flag (fudge flag1), either manually
665 * In automatic mode the calling program runs continuously at
666 * intervals determined by the poll event or specified timeout.
672 * In backup mode the calling program runs continuously as long
673 * as either no peers are available or this peer is selected.
676 if (!(sys_peer == NULL || sys_peer == peer))
682 if (S_IDLE == up->state) {
684 acts_timeout(peer, S_IDLE);
690 * acts_timer - called at one-second intervals
699 struct refclockproc *pp;
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.
709 if (up->timer == 0) {
710 if (pp->sloppyclockflag & CLK_FLAG1) {
711 pp->sloppyclockflag &= ~CLK_FLAG1;
712 acts_timeout(peer, S_IDLE);
717 acts_timeout(peer, up->state);
722 * acts_timecode - identify the service and parse the timecode message
726 struct peer * peer, /* peer structure pointer */
727 const char * str /* timecode string */
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 */
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 #) */
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 */
749 int leapmonth; /* PTB/NPL month of leap */
750 char leapdir; /* PTB/NPL leap direction */
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.
760 switch (strlen(str)) {
763 * For USNO format on-time character '*', which is on a line by
764 * itself. Be sure a timecode has been received.
767 if (*str == '*' && up->msgcnt > 0)
773 * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
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);
785 pp->day = ymd2yd(pp->year, month, day);
786 pp->leap = LEAP_NOWARNING;
788 pp->leap = LEAP_ADDSECOND;
790 pp->leap = LEAP_DELSECOND;
791 memcpy(&pp->refid, REFACTS, 4);
793 if (flag != '#' && up->msgcnt < 10)
799 * USNO format: "jjjjj nnn hhmmss UTC"
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);
810 * Wait for the on-time character, which follows in a
811 * separate message. There is no provision for leap
814 pp->leap = LEAP_NOWARNING;
815 memcpy(&pp->refid, REFUSNO, 4);
820 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
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);
831 pp->leap = LEAP_NOWARNING;
832 if (leapmonth == month) {
834 pp->leap = LEAP_ADDSECOND;
835 else if (leapdir == '-')
836 pp->leap = LEAP_DELSECOND;
838 pp->day = ymd2yd(pp->year, month, day);
839 memcpy(&pp->refid, REFPTB, 4);
845 * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
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);
854 pp->leap = LEAP_NOWARNING;
856 pp->leap = LEAP_NOTINSYNC;
857 memcpy(&pp->refid, REFWWVB, 4);
862 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
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);
873 pp->leap = LEAP_NOWARNING;
875 pp->leap = LEAP_NOTINSYNC;
876 else if (leapchar == 'L')
877 pp->leap = LEAP_ADDSECOND;
878 memcpy(&pp->refid, REFWWVB, 4);
883 * None of the above. Just forget about it and wait for the next
884 * message or timeout.
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.
896 peer->refid = pp->refid;
897 pp->lastrec = up->tstamp;
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);
907 pp->lastref = pp->lastrec;
910 int refclock_acts_bs;
911 #endif /* REFCLOCK */