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