📄 refclock_acts.c
字号:
/* * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time * Services */#ifdef HAVE_CONFIG_H#include <config.h>#endif#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))#include "ntpd.h"#include "ntp_io.h"#include "ntp_unixtime.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#include "ntp_control.h"#include <stdio.h>#include <ctype.h>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif /* HAVE_SYS_IOCTL_H *//* * This driver supports the US (NIST, USNO) and European (PTB, NPL, * etc.) modem time services, as well as Spectracom GPS and WWVB * receivers connected via a modem. The driver periodically dials a * number from a telephone list, receives the timecode data and * calculates the local clock correction. It is designed primarily for * use as backup when neither a radio clock nor connectivity to Internet * time servers is available. * * This driver requires a modem with a Hayes-compatible command set and * control over the modem data terminal ready (DTR) control line. The * modem setup string is hard-coded in the driver and may require * changes for nonstandard modems or special circumstances. For reasons * unrelated to this driver, the data set ready (DSR) control line * should not be set when this driver is first started. * * The calling program is initiated by setting fudge flag1, either * manually or automatically. When flag1 is set, the calling program * dials the first number in the phone command of the configuration * file. If that call fails, the calling program dials the second number * and so on. The number is specified by the Hayes ATDT prefix followed * by the number itself, including the prefix and long-distance digits * and delay code, if necessary. The flag1 is reset and the calling * program terminated if (a) a valid clock update has been determined, * (b) no more numbers remain in the list, (c) a device fault or timeout * occurs or (d) fudge flag1 is reset manually. * * The driver is transparent to each of the modem time services and * Spectracom radios. It selects the parsing algorithm depending on the * message length. There is some hazard should the message be corrupted. * However, the data format is checked carefully and only if all checks * succeed is the message accepted. Corrupted lines are discarded * without complaint. * * Fudge controls * * flag1 force a call in manual mode * flag2 enable port locking (not verified) * flag3 no modem; port is directly connected to device * flag4 not used * * time1 offset adjustment (s) * * Ordinarily, the serial port is connected to a modem; however, it can * be connected directly to a device or another computer for testing and * calibration. In this case set fudge flag3 and the driver will send a * single character 'T' at each poll event. In principle, fudge flag2 * enables port locking, allowing the modem to be shared when not in use * by this driver. At least on Solaris with the current NTP I/O * routines, this results only in lots of ugly error messages. *//* * National Institute of Science and Technology (NIST) * * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) * * Data Format * * National Institute of Standards and Technology * Telephone Time Service, Generator 3B * Enter question mark "?" for HELP * D L D * MJD YR MO DA H M S ST S UT1 msADV <OTM> * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> * ... * * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is * the on-time markers echoed by the driver and used by NIST to measure * and correct for the propagation delay. * * US Naval Observatory (USNO) * * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) * * Data Format (two lines, repeating at one-second intervals) * * jjjjj nnn hhmmss UTC<CR><LF> * *<CR><LF> * * jjjjj modified Julian day number (not used) * nnn day of year * hhmmss second of day * * on-time marker for previous timecode * ... * * USNO does not correct for the propagation delay. A fudge time1 of * about .06 s is advisable. * * European Services (PTB, NPL, etc.) * * PTB: +49 531 512038 (Germany) * NPL: 0906 851 6333 (UK only) * * Data format (see the documentation for phone numbers and formats.) * * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> * * Spectracom GPS and WWVB Receivers * * If a modem is connected to a Spectracom receiver, this driver will * call it up and retrieve the time in one of two formats. As this * driver does not send anything, the radio will have to either be * configured in continuous mode or be polled by another local driver. *//* * Interface definitions */#define DEVICE "/dev/acts%d" /* device name and unit */#define SPEED232 B9600 /* uart speed (9600 baud) */#define PRECISION (-10) /* precision assumed (about 1 ms) */#define LOCKFILE "/var/spool/locks/LCK..cua%d"#define DESCRIPTION "Automated Computer Time Service" /* WRU */#define REFID "NONE" /* default reference ID */#define MSGCNT 20 /* max message count */#define SMAX 256 /* max clockstats line length *//* * Calling program modes */#define MODE_AUTO 0 /* automatic mode */#define MODE_BACKUP 1 /* backup mode */#define MODE_MANUAL 2 /* manual mode *//* * Service identifiers. */#define REFACTS "NIST" /* NIST reference ID */#define LENACTS 50 /* NIST format */#define REFUSNO "USNO" /* USNO reference ID */#define LENUSNO 20 /* USNO */#define REFPTB "PTB\0" /* PTB/NPL reference ID */#define LENPTB 78 /* PTB/NPL format */#define REFWWVB "WWVB" /* WWVB reference ID */#define LENWWVB0 22 /* WWVB format 0 */#define LENWWVB2 24 /* WWVB format 2 */#define LF 0x0a /* ASCII LF *//* * Modem setup strings. These may have to be changed for some modems. * * AT command prefix * B1 US answer tone * &C0 disable carrier detect * &D2 hang up and return to command mode on DTR transition * E0 modem command echo disabled * l1 set modem speaker volume to low level * M1 speaker enabled until carrier detect * Q0 return result codes * V1 return result codes as English words */#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */#define MODEM_HANGUP "ATH\r" /* modem disconnect *//* * Timeouts (all in seconds) */#define SETUP 3 /* setup timeout */#define DTR 1 /* DTR timeout */#define ANSWER 60 /* answer timeout */#define CONNECT 20 /* first valid message timeout */#define TIMECODE 30 /* all valid messages timeout *//* * State machine codes */#define S_IDLE 0 /* wait for poll */#define S_OK 1 /* wait for modem setup */#define S_DTR 2 /* wait for modem DTR */#define S_CONNECT 3 /* wait for answer*/#define S_FIRST 4 /* wait for first valid message */#define S_MSG 5 /* wait for all messages */#define S_CLOSE 6 /* wait after sending disconnect *//* * Unit control structure */struct actsunit { int unit; /* unit number */ int state; /* the first one was Delaware */ int timer; /* timeout counter */ int retry; /* retry index */ int msgcnt; /* count of messages received */ l_fp tstamp; /* on-time timestamp */ char *bufptr; /* buffer pointer */};/* * Function prototypes */static int acts_start P((int, struct peer *));static void acts_shutdown P((int, struct peer *));static void acts_receive P((struct recvbuf *));static void acts_message P((struct peer *));static void acts_timecode P((struct peer *, char *));static void acts_poll P((int, struct peer *));static void acts_timeout P((struct peer *));static void acts_disc P((struct peer *));static void acts_timer P((int, struct peer *));/* * Transfer vector (conditional structure name) */struct refclock refclock_acts = { acts_start, /* start up driver */ acts_shutdown, /* shut down driver */ acts_poll, /* transmit poll message */ noentry, /* not used */ noentry, /* not used */ noentry, /* not used */ acts_timer /* housekeeping timer */};struct refclock refclock_ptb;/* * Initialize data for processing */static intacts_start ( int unit, struct peer *peer ){ struct actsunit *up; struct refclockproc *pp; /* * Allocate and initialize unit structure */ up = emalloc(sizeof(struct actsunit)); if (up == NULL) return (0); memset(up, 0, sizeof(struct actsunit)); up->unit = unit; pp = peer->procptr; pp->unitptr = (caddr_t)up; pp->io.clock_recv = acts_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 4); peer->sstclktype = CTL_SST_TS_TELEPHONE; peer->flags &= ~FLAG_FIXPOLL; up->bufptr = pp->a_lastcode; return (1);}/* * acts_shutdown - shut down the clock */static voidacts_shutdown ( int unit, struct peer *peer ){ struct actsunit *up; struct refclockproc *pp; /* * Warning: do this only when a call is not in progress. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; free(up);}/* * acts_receive - receive data from the serial interface */static voidacts_receive ( struct recvbuf *rbufp ){ struct actsunit *up; struct refclockproc *pp; struct peer *peer; char tbuf[BMAX]; char *tptr; /* * Initialize pointers and read the timecode and timestamp. Note * we are in raw mode and victim of whatever the terminal * interface kicks up; so, we have to reassemble messages from * arbitrary fragments. Capture the timecode at the beginning of * the message and at the '*' and '#' on-time characters. */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct actsunit *)pp->unitptr; pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr - pp->a_lastcode), &pp->lastrec); for (tptr = tbuf; *tptr != '\0'; tptr++) { if (*tptr == LF) { if (up->bufptr == pp->a_lastcode) { up->tstamp = pp->lastrec; continue; } else { *up->bufptr = '\0'; acts_message(peer); up->bufptr = pp->a_lastcode; } } else { *up->bufptr++ = *tptr; if (*tptr == '*' || *tptr == '#') { up->tstamp = pp->lastrec; write(pp->io.fd, tptr, 1); } } }}/* * acts_message - process message */voidacts_message( struct peer *peer ){ struct actsunit *up; struct refclockproc *pp; int dtr = TIOCM_DTR; char tbuf[SMAX];#ifdef DEBUG u_int modem;#endif /* * What to do depends on the state and the first token in the * message. A NO token sends the message to the clockstats. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr;#ifdef DEBUG ioctl(pp->io.fd, TIOCMGET, (char *)&modem); sprintf(tbuf, "acts: %04x (%d %d) %d %s", modem, up->state, up->timer, strlen(pp->a_lastcode), pp->a_lastcode); if (debug) printf("%s\n", tbuf);#endif strncpy(tbuf, pp->a_lastcode, SMAX); strtok(tbuf, " "); if (strcmp(tbuf, "NO") == 0) record_clock_stats(&peer->srcadr, pp->a_lastcode); switch(up->state) { /* * We are waiting for the OK response to the modem setup * command. When this happens, raise DTR and dial the number * followed by \r. */ case S_OK: if (strcmp(tbuf, "OK") != 0) { msyslog(LOG_ERR, "acts: setup error %s", pp->a_lastcode); acts_disc(peer); return; } ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); up->state = S_DTR; up->timer = DTR; return; /* * We are waiting for the call to be answered. All we care about * here is token CONNECT. Send the message to the clockstats. */ case S_CONNECT: record_clock_stats(&peer->srcadr, pp->a_lastcode); if (strcmp(tbuf, "CONNECT") != 0) { acts_disc(peer); return; } up->state = S_FIRST; up->timer = CONNECT; return; /* * We are waiting for a timecode. Pass it to the parser. */ case S_FIRST: case S_MSG: acts_timecode(peer, pp->a_lastcode); break; }}/* * acts_timecode - identify the service and parse the timecode message */voidacts_timecode( struct peer *peer, /* peer structure pointer */ char *str /* timecode string */ ){ struct actsunit *up; struct refclockproc *pp; int day; /* day of the month */ int month; /* month of the year */ u_long mjd; /* Modified Julian Day */ double dut1; /* DUT adjustment */ u_int dst; /* ACTS daylight/standard time */ u_int leap; /* ACTS leap indicator */ double msADV; /* ACTS transmit advance (ms) */ char utc[10]; /* ACTS timescale */ char flag; /* ACTS on-time character (* or #) */ char synchar; /* WWVB synchronized indicator */ char qualchar; /* WWVB quality indicator */ char leapchar; /* WWVB leap indicator */ char dstchar; /* WWVB daylight/savings indicator */ int tz; /* WWVB timezone */ u_int leapmonth; /* PTB/NPL month of leap */ char leapdir; /* PTB/NPL leap direction */ /* * The parser selects the modem format based on the message * length. Since the data are checked carefully, occasional * errors due noise are forgivable. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; pp->nsec = 0; switch(strlen(str)) { /* * For USNO format on-time character '*', which is on a line by * itself. Be sure a timecode has been received.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -