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