1 /* $NetBSD: refclock_acts.c,v 1.4 2006/06/18 21:25:13 kardel Exp $ */
4 * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
11 #if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
15 #include "ntp_unixtime.h"
16 #include "ntp_refclock.h"
17 #include "ntp_stdlib.h"
18 #include "ntp_control.h"
22 #ifdef HAVE_SYS_IOCTL_H
23 # include <sys/ioctl.h>
24 #endif /* HAVE_SYS_IOCTL_H */
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.
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. For reasons
39 * unrelated to this driver, the data set ready (DSR) control line
40 * should not be set when this driver is first started.
42 * The calling program is initiated by setting fudge flag1, either
43 * manually or automatically. When flag1 is set, the calling program
44 * dials the first number in the phone command of the configuration
45 * file. If that call fails, the calling program dials the second number
46 * and so on. The number is specified by the Hayes ATDT prefix followed
47 * by the number itself, including the prefix and long-distance digits
48 * and delay code, if necessary. The flag1 is reset and the calling
49 * program terminated if (a) a valid clock update has been determined,
50 * (b) no more numbers remain in the list, (c) a device fault or timeout
51 * occurs or (d) fudge flag1 is reset manually.
53 * The driver is transparent to each of the modem time services and
54 * Spectracom radios. It selects the parsing algorithm depending on the
55 * message length. There is some hazard should the message be corrupted.
56 * However, the data format is checked carefully and only if all checks
57 * succeed is the message accepted. Corrupted lines are discarded
62 * flag1 force a call in manual mode
63 * flag2 enable port locking (not verified)
64 * flag3 no modem; port is directly connected to device
67 * time1 offset adjustment (s)
69 * Ordinarily, the serial port is connected to a modem; however, it can
70 * be connected directly to a device or another computer for testing and
71 * calibration. In this case set fudge flag3 and the driver will send a
72 * single character 'T' at each poll event. In principle, fudge flag2
73 * enables port locking, allowing the modem to be shared when not in use
74 * by this driver. At least on Solaris with the current NTP I/O
75 * routines, this results only in lots of ugly error messages.
78 * National Institute of Science and Technology (NIST)
80 * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
84 * National Institute of Standards and Technology
85 * Telephone Time Service, Generator 3B
86 * Enter question mark "?" for HELP
88 * MJD YR MO DA H M S ST S UT1 msADV <OTM>
89 * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
92 * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
93 * the on-time markers echoed by the driver and used by NIST to measure
94 * and correct for the propagation delay.
96 * US Naval Observatory (USNO)
98 * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
100 * Data Format (two lines, repeating at one-second intervals)
102 * jjjjj nnn hhmmss UTC<CR><LF>
105 * jjjjj modified Julian day number (not used)
107 * hhmmss second of day
108 * * on-time marker for previous timecode
111 * USNO does not correct for the propagation delay. A fudge time1 of
112 * about .06 s is advisable.
114 * European Services (PTB, NPL, etc.)
116 * PTB: +49 531 512038 (Germany)
117 * NPL: 0906 851 6333 (UK only)
119 * Data format (see the documentation for phone numbers and formats.)
121 * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
123 * Spectracom GPS and WWVB Receivers
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.
131 * Interface definitions
133 #define DEVICE "/dev/acts%d" /* device name and unit */
134 #define SPEED232 B9600 /* uart speed (9600 baud) */
135 #define PRECISION (-10) /* precision assumed (about 1 ms) */
136 #define LOCKFILE "/var/spool/locks/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 SMAX 256 /* max clockstats line length */
143 * Calling program modes
145 #define MODE_AUTO 0 /* automatic mode */
146 #define MODE_BACKUP 1 /* backup mode */
147 #define MODE_MANUAL 2 /* manual mode */
150 * Service identifiers.
152 #define REFACTS "NIST" /* NIST reference ID */
153 #define LENACTS 50 /* NIST format */
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 */
164 * Modem setup strings. These may have to be changed for some modems.
168 * &C0 disable carrier detect
169 * &D2 hang up and return to command mode on DTR transition
170 * E0 modem command echo disabled
171 * l1 set modem speaker volume to low level
172 * M1 speaker enabled until carrier detect
173 * Q0 return result codes
174 * V1 return result codes as English words
176 #define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
177 #define MODEM_HANGUP "ATH\r" /* modem disconnect */
180 * Timeouts (all in seconds)
182 #define SETUP 3 /* setup timeout */
183 #define DTR 1 /* DTR timeout */
184 #define ANSWER 60 /* answer timeout */
185 #define CONNECT 20 /* first valid message timeout */
186 #define TIMECODE 30 /* all valid messages timeout */
189 * State machine codes
191 #define S_IDLE 0 /* wait for poll */
192 #define S_OK 1 /* wait for modem setup */
193 #define S_DTR 2 /* wait for modem DTR */
194 #define S_CONNECT 3 /* wait for answer*/
195 #define S_FIRST 4 /* wait for first valid message */
196 #define S_MSG 5 /* wait for all messages */
197 #define S_CLOSE 6 /* wait after sending disconnect */
200 * Unit control structure
203 int unit
; /* unit number */
204 int state
; /* the first one was Delaware */
205 int timer
; /* timeout counter */
206 int retry
; /* retry index */
207 int msgcnt
; /* count of messages received */
208 l_fp tstamp
; /* on-time timestamp */
209 char *bufptr
; /* buffer pointer */
213 * Function prototypes
215 static int acts_start
P((int, struct peer
*));
216 static void acts_shutdown
P((int, struct peer
*));
217 static void acts_receive
P((struct recvbuf
*));
218 static void acts_message
P((struct peer
*));
219 static void acts_timecode
P((struct peer
*, char *));
220 static void acts_poll
P((int, struct peer
*));
221 static void acts_timeout
P((struct peer
*));
222 static void acts_disc
P((struct peer
*));
223 static void acts_timer
P((int, struct peer
*));
226 * Transfer vector (conditional structure name)
228 struct refclock refclock_acts
= {
229 acts_start
, /* start up driver */
230 acts_shutdown
, /* shut down driver */
231 acts_poll
, /* transmit poll message */
232 noentry
, /* not used */
233 noentry
, /* not used */
234 noentry
, /* not used */
235 acts_timer
/* housekeeping timer */
238 struct refclock refclock_ptb
;
241 * Initialize data for processing
250 struct refclockproc
*pp
;
253 * Allocate and initialize unit structure
255 up
= emalloc(sizeof(struct actsunit
));
259 memset(up
, 0, sizeof(struct actsunit
));
262 pp
->unitptr
= (caddr_t
)up
;
263 pp
->io
.clock_recv
= acts_receive
;
264 pp
->io
.srcclock
= (caddr_t
)peer
;
268 * Initialize miscellaneous variables
270 peer
->precision
= PRECISION
;
271 pp
->clockdesc
= DESCRIPTION
;
272 memcpy((char *)&pp
->refid
, REFID
, 4);
273 peer
->sstclktype
= CTL_SST_TS_TELEPHONE
;
274 peer
->flags
&= ~FLAG_FIXPOLL
;
275 up
->bufptr
= pp
->a_lastcode
;
281 * acts_shutdown - shut down the clock
290 struct refclockproc
*pp
;
293 * Warning: do this only when a call is not in progress.
296 up
= (struct actsunit
*)pp
->unitptr
;
302 * acts_receive - receive data from the serial interface
306 struct recvbuf
*rbufp
310 struct refclockproc
*pp
;
316 * Initialize pointers and read the timecode and timestamp. Note
317 * we are in raw mode and victim of whatever the terminal
318 * interface kicks up; so, we have to reassemble messages from
319 * arbitrary fragments. Capture the timecode at the beginning of
320 * the message and at the '*' and '#' on-time characters.
322 peer
= (struct peer
*)rbufp
->recv_srcclock
;
324 up
= (struct actsunit
*)pp
->unitptr
;
325 pp
->lencode
= refclock_gtraw(rbufp
, tbuf
, BMAX
- (up
->bufptr
-
326 pp
->a_lastcode
), &pp
->lastrec
);
327 for (tptr
= tbuf
; *tptr
!= '\0'; tptr
++) {
329 if (up
->bufptr
== pp
->a_lastcode
) {
330 up
->tstamp
= pp
->lastrec
;
336 up
->bufptr
= pp
->a_lastcode
;
338 } else if (!iscntrl((int)*tptr
)) {
339 *up
->bufptr
++ = *tptr
;
340 if (*tptr
== '*' || *tptr
== '#') {
341 up
->tstamp
= pp
->lastrec
;
342 write(pp
->io
.fd
, tptr
, 1);
350 * acts_message - process message
358 struct refclockproc
*pp
;
366 * What to do depends on the state and the first token in the
367 * message. A NO token sends the message to the clockstats.
370 up
= (struct actsunit
*)pp
->unitptr
;
372 ioctl(pp
->io
.fd
, TIOCMGET
, (char *)&modem
);
373 sprintf(tbuf
, "acts: %04x (%d %d) %lu %s", modem
, up
->state
,
374 up
->timer
, (unsigned long)strlen(pp
->a_lastcode
), pp
->a_lastcode
);
376 printf("%s\n", tbuf
);
378 strncpy(tbuf
, pp
->a_lastcode
, SMAX
);
380 if (strcmp(tbuf
, "NO") == 0)
381 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
385 * We are waiting for the OK response to the modem setup
386 * command. When this happens, raise DTR and dial the number
390 if (strcmp(tbuf
, "OK") != 0) {
391 msyslog(LOG_ERR
, "acts: setup error %s",
396 ioctl(pp
->io
.fd
, TIOCMBIS
, (char *)&dtr
);
402 * We are waiting for the call to be answered. All we care about
403 * here is token CONNECT. Send the message to the clockstats.
406 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
407 if (strcmp(tbuf
, "CONNECT") != 0) {
416 * We are waiting for a timecode. Pass it to the parser.
420 acts_timecode(peer
, pp
->a_lastcode
);
426 * acts_timecode - identify the service and parse the timecode message
430 struct peer
*peer
, /* peer structure pointer */
431 char *str
/* timecode string */
435 struct refclockproc
*pp
;
436 int day
; /* day of the month */
437 int month
; /* month of the year */
438 u_long mjd
; /* Modified Julian Day */
439 double dut1
; /* DUT adjustment */
441 u_int dst
; /* ACTS daylight/standard time */
442 u_int leap
; /* ACTS leap indicator */
443 double msADV
; /* ACTS transmit advance (ms) */
444 char utc
[10]; /* ACTS timescale */
445 char flag
; /* ACTS on-time character (* or #) */
447 char synchar
; /* WWVB synchronized indicator */
448 char qualchar
; /* WWVB quality indicator */
449 char leapchar
; /* WWVB leap indicator */
450 char dstchar
; /* WWVB daylight/savings indicator */
451 int tz
; /* WWVB timezone */
453 u_int leapmonth
; /* PTB/NPL month of leap */
454 char leapdir
; /* PTB/NPL leap direction */
457 * The parser selects the modem format based on the message
458 * length. Since the data are checked carefully, occasional
459 * errors due noise are forgivable.
462 up
= (struct actsunit
*)pp
->unitptr
;
464 switch(strlen(str
)) {
467 * For USNO format on-time character '*', which is on a line by
468 * itself. Be sure a timecode has been received.
471 if (*str
== '*' && up
->msgcnt
> 0)
477 * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
482 "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
483 &mjd
, &pp
->year
, &month
, &day
, &pp
->hour
,
484 &pp
->minute
, &pp
->second
, &dst
, &leap
, &dut1
,
485 &msADV
, utc
, &flag
) != 13) {
486 refclock_report(peer
, CEVNT_BADREPLY
);
491 * Wait until ACTS has calculated the roundtrip delay.
492 * We don't need to do anything, as ACTS adjusts the
498 pp
->day
= ymd2yd(pp
->year
, month
, day
);
499 pp
->leap
= LEAP_NOWARNING
;
501 pp
->leap
= LEAP_ADDSECOND
;
502 else if (pp
->leap
== 2)
503 pp
->leap
= LEAP_DELSECOND
;
504 memcpy(&pp
->refid
, REFACTS
, 4);
506 record_clock_stats(&peer
->srcadr
, str
);
511 * USNO format: "jjjjj nnn hhmmss UTC"
514 if (sscanf(str
, "%5ld %3d %2d%2d%2d %3s",
515 &mjd
, &pp
->day
, &pp
->hour
, &pp
->minute
,
516 &pp
->second
, utc
) != 6) {
517 refclock_report(peer
, CEVNT_BADREPLY
);
522 * Wait for the on-time character, which follows in a
523 * separate message. There is no provision for leap
526 pp
->leap
= LEAP_NOWARNING
;
527 memcpy(&pp
->refid
, REFUSNO
, 4);
529 record_clock_stats(&peer
->srcadr
, str
);
534 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
538 "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
539 &pp
->second
, &pp
->year
, &month
, &day
, &pp
->hour
,
540 &pp
->minute
, &mjd
, &dut1
, &leapdir
, &leapmonth
,
541 &msADV
, &flag
) != 12) {
542 refclock_report(peer
, CEVNT_BADREPLY
);
545 pp
->leap
= LEAP_NOWARNING
;
546 if (leapmonth
== month
) {
548 pp
->leap
= LEAP_ADDSECOND
;
549 else if (leapdir
== '-')
550 pp
->leap
= LEAP_DELSECOND
;
552 pp
->day
= ymd2yd(pp
->year
, month
, day
);
553 memcpy(&pp
->refid
, REFPTB
, 4);
555 record_clock_stats(&peer
->srcadr
, str
);
561 * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
564 if (sscanf(str
, "%c %3d %2d:%2d:%2d %cTZ=%2d",
565 &synchar
, &pp
->day
, &pp
->hour
, &pp
->minute
,
566 &pp
->second
, &dstchar
, &tz
) != 7) {
567 refclock_report(peer
, CEVNT_BADREPLY
);
570 pp
->leap
= LEAP_NOWARNING
;
572 pp
->leap
= LEAP_NOTINSYNC
;
573 memcpy(&pp
->refid
, REFWWVB
, 4);
575 record_clock_stats(&peer
->srcadr
, str
);
580 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
583 if (sscanf(str
, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
584 &synchar
, &qualchar
, &pp
->year
, &pp
->day
,
585 &pp
->hour
, &pp
->minute
, &pp
->second
, &pp
->nsec
,
586 &dstchar
, &leapchar
, &dstchar
) != 11) {
587 refclock_report(peer
, CEVNT_BADREPLY
);
591 pp
->leap
= LEAP_NOWARNING
;
593 pp
->leap
= LEAP_NOTINSYNC
;
594 else if (leapchar
== 'L')
595 pp
->leap
= LEAP_ADDSECOND
;
596 memcpy(&pp
->refid
, REFWWVB
, 4);
598 record_clock_stats(&peer
->srcadr
, str
);
603 * None of the above. Just forget about it and wait for the next
604 * message or timeout.
611 * We have a valid timecode. The fudge time1 value is added to
612 * each sample by the main line routines. Note that in current
613 * telephone networks the propatation time can be different for
614 * each call and can reach 200 ms for some calls.
616 peer
->refid
= pp
->refid
;
617 pp
->lastrec
= up
->tstamp
;
618 if (!refclock_process(pp
)) {
619 refclock_report(peer
, CEVNT_BADTIME
);
622 pp
->lastref
= pp
->lastrec
;
623 if (peer
->disp
> MAXDISTANCE
)
624 refclock_receive(peer
);
625 if (up
->state
!= S_MSG
) {
627 up
->timer
= TIMECODE
;
633 * acts_poll - called by the transmit routine
642 struct refclockproc
*pp
;
645 * This routine is called at every system poll. All it does is
646 * set flag1 under certain conditions. The real work is done by
647 * the timeout routine and state machine.
650 up
= (struct actsunit
*)pp
->unitptr
;
654 * In manual mode the calling program is activated by the ntpdc
655 * program using the enable flag (fudge flag1), either manually
663 * In automatic mode the calling program runs continuously at
664 * intervals determined by the poll event or specified timeout.
667 pp
->sloppyclockflag
|= CLK_FLAG1
;
671 * In backup mode the calling program runs continuously as long
672 * as either no peers are available or this peer is selected.
675 if (sys_peer
== NULL
|| sys_peer
== peer
)
676 pp
->sloppyclockflag
|= CLK_FLAG1
;
683 * acts_timer - called at one-second intervals
692 struct refclockproc
*pp
;
695 * This routine implments a timeout which runs for a programmed
696 * interval. The counter is initialized by the state machine and
697 * counts down to zero. Upon reaching zero, the state machine is
698 * called. If flag1 is set while in S_IDLE state, force a
702 up
= (struct actsunit
*)pp
->unitptr
;
703 if (pp
->sloppyclockflag
& CLK_FLAG1
&& up
->state
== S_IDLE
) {
717 * acts_timeout - called on timeout
725 struct refclockproc
*pp
;
728 char lockfile
[128], pidbuf
[8];
732 * The state machine is driven by messages from the modem, when
733 * first stated and at timeout.
736 up
= (struct actsunit
*)pp
->unitptr
;
737 pp
->sloppyclockflag
&= ~CLK_FLAG1
;
738 if (sys_phone
[up
->retry
] == NULL
&& !(pp
->sloppyclockflag
&
740 msyslog(LOG_ERR
, "acts: no phones");
746 * System poll event. Lock the modem port and open the device.
751 * Lock the modem port. If busy, retry later. Note: if
752 * something fails between here and the close, the lock
753 * file may not be removed.
755 if (pp
->sloppyclockflag
& CLK_FLAG2
) {
756 sprintf(lockfile
, LOCKFILE
, up
->unit
);
757 fd
= open(lockfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
760 msyslog(LOG_ERR
, "acts: port busy");
763 sprintf(pidbuf
, "%d\n", (u_int
)getpid());
764 write(fd
, pidbuf
, strlen(pidbuf
));
769 * Open the device in raw mode and link the I/O.
772 sprintf(device
, DEVICE
, up
->unit
);
773 fd
= refclock_open(device
, SPEED232
,
774 LDISC_ACTS
| LDISC_RAW
| LDISC_REMOTE
);
779 if (!io_addclock(&pp
->io
)) {
781 "acts: addclock fails");
789 * If the port is directly connected to the device, skip
790 * the modem business and send 'T' for Spectrabum.
792 if (pp
->sloppyclockflag
& CLK_FLAG3
) {
793 if (write(pp
->io
.fd
, "T", 1) < 0) {
794 msyslog(LOG_ERR
, "acts: write %m");
803 * Initialize the modem. This works with Hayes commands.
807 printf("acts: setup %s\n", MODEM_SETUP
);
809 if (write(pp
->io
.fd
, MODEM_SETUP
, strlen(MODEM_SETUP
)) <
811 msyslog(LOG_ERR
, "acts: write %m");
819 * In OK state the modem did not respond to setup.
822 msyslog(LOG_ERR
, "acts: no modem");
826 * In DTR state we are waiting for the modem to settle down
827 * before hammering it with a dial command.
830 sprintf(tbuf
, "DIAL #%d %s", up
->retry
,
831 sys_phone
[up
->retry
]);
832 record_clock_stats(&peer
->srcadr
, tbuf
);
835 printf("%s\n", tbuf
);
837 write(pp
->io
.fd
, sys_phone
[up
->retry
],
838 strlen(sys_phone
[up
->retry
]));
839 write(pp
->io
.fd
, "\r", 1);
840 up
->state
= S_CONNECT
;
845 * In CONNECT state the call did not complete.
848 msyslog(LOG_ERR
, "acts: no answer");
852 * In FIRST state no messages were received.
855 msyslog(LOG_ERR
, "acts: no messages");
859 * In CLOSE state hangup is complete. Close the doors and
860 * windows and get some air.
865 * Close the device and unlock a shared modem.
868 io_closeclock(&pp
->io
);
870 if (pp
->sloppyclockflag
& CLK_FLAG2
) {
871 sprintf(lockfile
, LOCKFILE
, up
->unit
);
878 * If messages were received, fold the tent and wait for
879 * the next poll. If no messages and there are more
880 * numbers to dial, retry after a short wait.
882 up
->bufptr
= pp
->a_lastcode
;
885 if ( up
->msgcnt
== 0) {
887 if (sys_phone
[up
->retry
] == NULL
)
902 * acts_disc - disconnect the call and clean the place up.
910 struct refclockproc
*pp
;
914 * We get here if the call terminated successfully or if an
915 * error occured. If the median filter has something in it,feed
916 * the data to the clock filter. If a modem port, drop DTR to
917 * force command mode and send modem hangup.
920 up
= (struct actsunit
*)pp
->unitptr
;
922 refclock_receive(peer
);
923 if (!(pp
->sloppyclockflag
& CLK_FLAG3
)) {
924 ioctl(pp
->io
.fd
, TIOCMBIC
, (char *)&dtr
);
925 write(pp
->io
.fd
, MODEM_HANGUP
, strlen(MODEM_HANGUP
));
932 int refclock_acts_bs
;
933 #endif /* REFCLOCK */