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