#define LDISC_CHU 0x8 /* depredated */
#define LDISC_PPS 0x10 /* ppsclock, ppsapi */
#define LDISC_RAW 0x20 /* raw binary */
+#define LDISC_ECHO 0x40 /* enable echo */
struct refclockproc {
struct refclockio io; /* I/O handler structure */
extern void refclock_control P((struct sockaddr_storage *,
struct refclockstat *,
struct refclockstat *));
-extern int refclock_open P((char *, int, int));
+extern int refclock_open P((char *, u_int, u_int));
+extern int refclock_setup P((int, u_int, u_int));
extern void refclock_timer P((struct peer *));
extern void refclock_transmit P((struct peer *));
-extern int refclock_ioctl P((int, int));
+extern int refclock_ioctl P((int, u_int));
extern int refclock_process P((struct refclockproc *));
extern void refclock_process_offset P((struct refclockproc *, l_fp, l_fp, double));
extern void refclock_report P((struct peer *, int));
-extern int refclock_gtlin P((struct recvbuf *, char *, int,
- l_fp *));
+extern int refclock_gtlin P((struct recvbuf *, char *, int, l_fp *));
+extern int refclock_gtraw P((struct recvbuf *, char *, int, l_fp *));
#endif /* REFCLOCK */
#endif /* NTP_REFCLOCK_H */
extern u_char sys_poll; /* system poll interval (log2 s) */
extern int state; /* clock discipline state */
extern int tc_counter; /* poll-adjust counter */
-extern u_long last_time; /* time of last clock update (s) */
extern double last_offset; /* last clock offset (s) */
extern double allan_xpt; /* Allan intercept (s) */
extern double clock_jitter; /* clock jitter (s) */
peerversion = NTP_VERSION;
minpoll = NTP_MINDPOLL;
- maxpoll = NTP_MAXDPOLL;
+ if (!ISREFCLOCKADR(&peeraddr))
+ maxpoll = NTP_MAXDPOLL;
+ else
+ maxpoll = minpoll;
peerkey = 0;
peerkeystr = (u_char *)"*";
peerflags = 0;
double clock_jitter; /* offset jitter (s) */
double drift_comp; /* frequency (s/s) */
double clock_stability; /* frequency stability (s/s) */
-u_long last_time; /* last clock update time (s) */
-u_long pps_control; /* last pps sample time */
-static void rstclock P((int, double)); /* transition function */
+u_long sys_clocktime; /* last system clock update */
+u_long pps_control; /* last pps update */
+
+static void rstclock P((int, u_long, double)); /* transition function */
#ifdef KERNEL_PLL
struct timex ntv; /* kernel API parameters */
* Initialize state variables. Initially, we expect no drift
* file, so set the state to S_NSET.
*/
- rstclock(S_NSET, 0);
+ rstclock(S_NSET, 0, 0);
clock_jitter = LOGTOD(sys_precision);
}
* stepped.
*/
clock_frequency = flladj = plladj = 0;
- mu = sys_clocktime - last_time;
+ mu = peer->epoch - sys_clocktime;
rval = 1;
if (fabs(fp_offset) > clock_max && clock_max > 0) {
switch (state) {
sys_poll = NTP_MINPOLL;
rval = 2;
if (state == S_NSET) {
- rstclock(S_FREQ, 0);
+ rstclock(S_FREQ, peer->epoch, 0);
return (rval);
}
break;
}
- rstclock(S_SYNC, 0);
+ rstclock(S_SYNC, peer->epoch, 0);
} else {
switch (state) {
*/
case S_NSET:
clock_offset = fp_offset;
- rstclock(S_FREQ, fp_offset);
+ rstclock(S_FREQ, peer->epoch, fp_offset);
return (0);
/*
/*
* The FLL and PLL frequency gain constants
* depend on the poll interval and Allan
- * intercept. The PLL constant is calculated
- * throughout the poll interval range, but the
- * update interval is clamped so as not to
- * exceed the poll interval. The FLL gain is
- * zero below one-half the Allan intercept and
- * unity at MAXPOLL. It decreases as 1 /
- * (MAXPOLL + 1 - poll interval) in a feeble
- * effort to match the loop stiffness to the
- * Allan wobble. Particularly for the PLL, these
- * measures allow oversampling, but not
- * undersampling and insure stability even when
- * the rules of fair engagement are broken.
+ * intercept. The FLL is not used below one-half
+ * the Allan intercept. Above that the loop gain
+ * increases in steps to 1 / CLOCK_AVG.
*/
if (ULOGTOD(sys_poll) > allan_xpt / 2) {
dtemp = CLOCK_FLL - sys_poll;
+ if (dtemp < CLOCK_AVG)
+ dtemp = CLOCK_AVG;
flladj = (fp_offset - clock_offset) /
(max(mu, allan_xpt) * dtemp);
}
+
+ /*
+ * For the PLL the integration interval
+ * (numerator) is the minimum of the update
+ * interval and poll interval. This allows
+ * oversampling, but not undersampling.
+ */
etemp = min(mu, (u_long)ULOGTOD(sys_poll));
dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
plladj = fp_offset * etemp / (dtemp * dtemp);
break;
}
- rstclock(S_SYNC, fp_offset);
+ rstclock(S_SYNC, peer->epoch, fp_offset);
}
#ifdef KERNEL_PLL
return;
/*
- * Implement the phase and frequency adjustments. Note the
- * black art formerly practiced here has been whitewashed.
+ * Implement the phase and frequency adjustments. The gain
+ * factor (denominator) is not allowed to increase beyond the
+ * Allan intercept. It doesn't make sense to average phase noise
+ * beyond this point and it helps to damp residual offset at the
+ * longer poll intervals.
*/
- adjustment = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll));
+ adjustment = clock_offset / (CLOCK_PLL * min(ULOGTOD(sys_poll),
+ allan_xpt));
clock_offset -= adjustment;
adj_systime(adjustment + drift_comp);
#endif /* LOCKCLOCK */
*/
static void
rstclock(
- int trans, /* new state */
- double offset /* last offset */
+ int trans, /* new state */
+ u_long update, /* new update time */
+ double offset /* new offset */
)
{
state = trans;
- last_time = sys_clocktime;
+ sys_clocktime = update;
last_base = offset - clock_offset;
last_offset = clock_offset = offset;
#ifdef DEBUG
if (debug)
- printf("local_clock: time %lu clock %.6f offset %.6f freq %.3f state %d\n",
- last_time, last_base, last_offset, drift_comp *
+ printf("local_clock: time %lu base %.6f offset %.6f freq %.3f state %d\n",
+ sys_clocktime, last_base, last_offset, drift_comp *
1e6, trans);
#endif
}
*/
if (freq <= NTP_MAXFREQ && freq >= -NTP_MAXFREQ) {
drift_comp = freq;
- rstclock(S_FSET, 0);
+ rstclock(S_FSET, 0, 0);
} else {
drift_comp = 0;
}
case LOOP_FREQ: /* initial frequency */
drift_comp = freq / 1e6;
- rstclock(S_FSET, 0);
+ rstclock(S_FSET, 0, 0);
break;
}
}
sys_poll = sys_peer->minpoll;
if (sys_poll > sys_peer->maxpoll)
sys_poll = sys_peer->maxpoll;
-#ifdef REFCLOCK
- if (!(sys_peer->flags & FLAG_REFCLOCK))
- poll_update(sys_peer, sys_poll);
-#endif /* REFCLOCK */
+ poll_update(sys_peer, sys_poll);
if (sys_peer->epoch <= sys_clocktime)
return;
- sys_clocktime = sys_peer->epoch;
#ifdef DEBUG
if (debug)
printf("clock_update: at %ld assoc %d \n", current_time,
peer->estbdelay = sys_bdelay;
peer->hpoll = peer->minpoll;
peer->ppoll = peer->maxpoll;
+ peer->disp = MAXDISPERSE;
peer->jitter = SQRT(MAXDISPERSE);
peer->epoch = current_time;
#ifdef REFCLOCK
*/
#define MAXUNIT 4 /* max units */
#define FUDGEFAC .1 /* fudge correction factor */
+#define LF 0x0a /* ASCII LF */
#ifdef PPS
int fdpps; /* ppsclock legacy */
pp->lastevent = (u_char)code;
if (code == CEVNT_FAULT)
msyslog(LOG_ERR,
- "clock %s event '%s' (0x%02x)",
- refnumtoa(&peer->srcadr),
- ceventstr(code), code);
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
else {
- NLOG(NLOG_CLOCKEVENT)
- msyslog(LOG_INFO,
- "clock %s event '%s' (0x%02x)",
- refnumtoa(&peer->srcadr),
- ceventstr(code), code);
+ NLOG(NLOG_CLOCKEVENT)msyslog(LOG_INFO,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
}
}
-#ifdef DEBUG
- if (debug)
- printf("clock %s event '%s' (0x%02x)\n",
- refnumtoa(&peer->srcadr),
- ceventstr(code), code);
-#endif
}
pp = peer->procptr;
peer->received++;
peer->timereceived = current_time;
- peer->leap = pp->leap;
- if (peer->leap == LEAP_NOTINSYNC) {
+ if (peer->leap != LEAP_NOTINSYNC && pp->leap !=
+ LEAP_NOTINSYNC)
refclock_report(peer, CEVNT_FAULT);
- return;
- }
+ peer->leap = pp->leap;
if (!peer->reach)
report_event(EVNT_REACH, peer);
peer->reach |= 1;
* refclock_gtlin - groom next input line and extract timestamp
*
* This routine processes the timecode received from the clock and
- * removes the parity bit and control characters. If a timestamp is
- * present in the timecode, as produced by the tty_clk STREAMS module,
- * it returns that as the timestamp; otherwise, it returns the buffer
- * timestamp. The routine return code is the number of characters in
- * the line.
+ * strips the parity bit and control characters. It returns the number
+ * of characters in the line followed by a NULL character ('\0'), which
+ * is not included in the count. In case of an empty line, the previous
+ * line is preserved.
*/
int
refclock_gtlin(
struct recvbuf *rbufp, /* receive buffer pointer */
- char *lineptr, /* current line pointer */
- int bmax, /* remaining characters in line */
- l_fp *tsptr /* pointer to timestamp returned */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
)
{
- char *dpt, *dpend, *dp;
- int i;
- l_fp trtmp, tstmp;
- char c;
+ char s[BMAX];
+ char *dpt, *dpend, *dp;
+
+ dpt = s;
+ dpend = s + refclock_gtraw(rbufp, s, BMAX - 1, tsptr);
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++) {
+ char c;
+
+ c = *dpt & 0x7f;
+ if (c >= 0x20 && c < 0x7f)
+ *dp++ = c;
+ }
+ if (dp == lineptr)
+ return (0);
+
+ *dp = '\0';
+ return (dp - lineptr);
+}
+
+
+/*
+ * refclock_gtraw - get next line/chunk of data
+ *
+ * This routine returns the raw data received from the clock in both
+ * canonical or raw modes. The terminal interface routines map CR to LF.
+ * In canonical mode this results in two lines, one containing data
+ * followed by LF and another containing only LF. In raw mode the
+ * interface routines can deliver arbitraty chunks of data from one
+ * character to a maximum specified by the calling routine. In either
+ * mode the routine returns the number of characters in the line
+ * followed by a NULL character ('\0'), which is not included in the
+ * count.
+ *
+ * If a timestamp is present in the timecode, as produced by the tty_clk
+ * STREAMS module, it returns that as the timestamp; otherwise, it
+ * returns the buffer timestamp.
+ */
+int
+refclock_gtraw(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ char *dpt, *dpend, *dp;
+ l_fp trtmp, tstmp;
+ int i;
/*
* Check for the presence of a timestamp left by the tty_clock
dpt = (char *)rbufp->recv_buffer;
dpend = dpt + rbufp->recv_length;
trtmp = rbufp->recv_time;
-
if (dpend >= dpt + 8) {
if (buftvtots(dpend - 8, &tstmp)) {
L_SUB(&trtmp, &tstmp);
}
/*
- * Edit timecode to remove control chars. Don't monkey with the
- * line buffer if the input buffer contains no ASCII printing
- * characters.
+ * Copy the raw buffer to the user string. The string is padded
+ * with a NULL, which is not included in the character count.
*/
if (dpend - dpt > bmax - 1)
dpend = dpt + bmax - 1;
- for (dp = lineptr; dpt < dpend; dpt++) {
- c = (char) (*dpt & 0x7f);
- if (c >= ' ')
- *dp++ = c;
- }
+ for (dp = lineptr; dpt < dpend; dpt++)
+ *dp++ = *dpt;
+ *dp = '\0';
i = dp - lineptr;
- if (i > 0)
- *dp = '\0';
#ifdef DEBUG
- if (debug > 1) {
- if (i > 0)
- printf("refclock_gtlin: fd %d time %s timecode %d %s\n",
- rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
- else
- printf("refclock_gtlin: fd %d time %s\n",
- rbufp->fd, ulfptoa(&trtmp, 6));
- }
+ if (debug > 1)
+ printf("refclock_gtraw: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
#endif
*tsptr = trtmp;
return (i);
*/
int
refclock_open(
- char *dev, /* device name pointer */
- int speed, /* serial port speed (code) */
- int lflags /* line discipline flags */
+ char *dev, /* device name pointer */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
)
{
- int fd, i;
- int flags;
- TTY ttyb, *ttyp;
-#ifdef TIOCMGET
- u_long ltemp;
-#endif /* TIOCMGET */
- int omode;
+ int fd;
+ int omode;
/*
* Open serial port and set default options
*/
- flags = lflags;
-
omode = O_RDWR;
#ifdef O_NONBLOCK
omode |= O_NONBLOCK;
#endif
fd = open(dev, omode, 0777);
-
if (fd < 0) {
- msyslog(LOG_ERR, "refclock_open: %s: %m", dev);
+ msyslog(LOG_ERR, "refclock_open %s: %m", dev);
return (0);
}
+ if (!refclock_setup(fd, speed, lflags)) {
+ close(fd);
+ return (0);
+ }
+ if (!refclock_ioctl(fd, lflags)) {
+ close(fd);
+ return (0);
+ }
+ return (fd);
+}
+
+/*
+ * refclock_setup - initialize terminal interface structure
+ */
+int
+refclock_setup(
+ int fd, /* file descriptor */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int i;
+ TTY ttyb, *ttyp;
#ifdef PPS
fdpps = fd; /* ppsclock legacy */
#endif /* PPS */
/*
- * The following sections initialize the serial line port in
- * canonical (line-oriented) mode and set the specified line
- * speed, 8 bits and no parity. The modem control, break, erase
- * and kill functions are normally disabled. There is a
- * different section for each terminal interface, as selected at
- * compile time.
+ * By default, the serial line port is initialized in canonical
+ * (line-oriented) mode at specified line speed, 8 bits and no
+ * parity. LF ends the line and CR is mapped to LF. The break,
+ * erase and kill functions are disabled. There is a different
+ * section for each terminal interface, as selected at compile
+ * time. The flag bits can be used to set raw mode and echo.
*/
ttyp = &ttyb;
-
#ifdef HAVE_TERMIOS
+
/*
* POSIX serial line parameters (termios interface)
*/
if (tcgetattr(fd, ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: fd %d tcgetattr: %m", fd);
+ "refclock_setup fd %d tcgetattr: %m", fd);
return (0);
}
* Set canonical mode and local connection; set specified speed,
* 8 bits and no parity; map CR to NL; ignore break.
*/
- ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
- ttyp->c_oflag = 0;
- ttyp->c_cflag = CS8 | CLOCAL | CREAD;
- (void)cfsetispeed(&ttyb, (u_int)speed);
- (void)cfsetospeed(&ttyb, (u_int)speed);
- ttyp->c_lflag = ICANON;
- for (i = 0; i < NCCS; ++i)
- {
- ttyp->c_cc[i] = '\0';
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ cfsetispeed(&ttyb, speed);
+ cfsetospeed(&ttyb, speed);
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: 0x%x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
}
/*
- * Some special cases
+ * Set raw and echo modes. These can be changed on-fly.
*/
- if (flags & LDISC_RAW) {
- ttyp->c_iflag = 0;
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
ttyp->c_lflag = 0;
ttyp->c_cc[VMIN] = 1;
}
-#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
- /*
- * If we have modem control, check to see if modem leads are
- * active; if so, set remote connection. This is necessary for
- * the kernel pps mods to work.
- */
- ltemp = 0;
- if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
- msyslog(LOG_ERR,
- "refclock_open: fd %d TIOCMGET failed: %m", fd);
-#ifdef DEBUG
- if (debug)
- printf("refclock_open: fd %d modem status 0x%lx\n",
- fd, ltemp);
-#endif
- if (ltemp & TIOCM_DSR)
- ttyp->c_cflag &= ~CLOCAL;
-#endif /* TIOCMGET */
+ if (lflags & LDISC_ECHO)
+ ttyp->c_lflag |= ECHO;
if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: fd %d TCSANOW failed: %m", fd);
- return (0);
- }
- if (tcflush(fd, TCIOFLUSH) < 0) {
- msyslog(LOG_ERR,
- "refclock_open: fd %d TCIOFLUSH failed: %m", fd);
+ "refclock_setup fd %d TCSANOW: %m", fd);
return (0);
}
#endif /* HAVE_TERMIOS */
*/
if (ioctl(fd, TCGETA, ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: fd %d TCGETA failed: %m", fd);
+ "refclock_setup fd %d TCGETA: %m", fd);
return (0);
}
* Set canonical mode and local connection; set specified speed,
* 8 bits and no parity; map CR to NL; ignore break.
*/
- ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
- ttyp->c_oflag = 0;
- ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
- ttyp->c_lflag = ICANON;
- ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: %x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
/*
- * Some special cases
+ * Set raw and echo modes. These can be changed on-fly.
*/
- if (flags & LDISC_RAW) {
- ttyp->c_iflag = 0;
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
ttyp->c_lflag = 0;
+ ttyp->c_cc[VMIN] = 1;
}
-#ifdef TIOCMGET
- /*
- * If we have modem control, check to see if modem leads are
- * active; if so, set remote connection. This is necessary for
- * the kernel pps mods to work.
- */
- ltemp = 0;
- if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
- msyslog(LOG_ERR,
- "refclock_open: fd %d TIOCMGET failed: %m", fd);
-#ifdef DEBUG
- if (debug)
- printf("refclock_open: fd %d modem status %lx\n",
- fd, ltemp);
-#endif
- if (ltemp & TIOCM_DSR)
- ttyp->c_cflag &= ~CLOCAL;
-#endif /* TIOCMGET */
if (ioctl(fd, TCSETA, ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: fd %d TCSETA failed: %m", fd);
+ "refclock_setup fd %d TCSETA: %m", fd);
return (0);
}
#endif /* HAVE_SYSV_TTYS */
*/
if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: fd %d TIOCGETP %m", fd);
+ "refclock_setup fd %d TIOCGETP: %m", fd);
return (0);
}
- ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ if (speed)
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
ttyp->sg_flags = EVENP | ODDP | CRMOD;
if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
msyslog(LOG_ERR,
- "refclock_open: TIOCSETP failed: %m");
+ "refclock_setup TIOCSETP: %m");
return (0);
}
#endif /* HAVE_BSD_TTYS */
- if (!refclock_ioctl(fd, flags)) {
- (void)close(fd);
- msyslog(LOG_ERR,
- "refclock_open: fd %d ioctl failed: %m", fd);
- return (0);
- }
- return (fd);
+ return(1);
}
#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
#endif /* SYS_VXWORKS SYS_WINNT */
*/
int
refclock_ioctl(
- int fd, /* file descriptor */
- int flags /* line discipline flags */
+ int fd, /* file descriptor */
+ u_int lflags /* line discipline flags */
)
{
- /* simply return 1 if no UNIX line discipline is supported */
+ /*
+ * simply return 1 if no UNIX line discipline is supported
+ */
#if !defined SYS_VXWORKS && !defined SYS_WINNT
#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
-#ifdef TTYCLK
- TTY ttyb, *ttyp;
-#endif /* TTYCLK */
-
#ifdef DEBUG
if (debug)
- printf("refclock_ioctl: fd %d flags 0x%x\n", fd, flags);
+ printf("refclock_ioctl: fd %d flags 0x%x\n", fd,
+ lflags);
#endif
- if (flags == 0)
- return (1);
-#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS))
- if (flags & (LDISC_CLK | LDISC_PPS | LDISC_ACTS)) {
- msyslog(LOG_ERR,
- "refclock_ioctl: unsupported terminal interface");
- return (0);
- }
-#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */
#ifdef TTYCLK
- ttyp = &ttyb;
-#endif /* TTYCLK */
- /*
- * The following features may or may not require System V
- * STREAMS support, depending on the particular implementation.
- */
-#if defined(TTYCLK)
/*
* The TTYCLK option provides timestamping at the driver level.
* It requires the tty_clk streams module and System V STREAMS
* support. If not available, don't complain.
*/
- if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ if (lflags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
int rval = 0;
if (ioctl(fd, I_PUSH, "clk") < 0) {
msyslog(LOG_NOTICE,
- "refclock_ioctl: I_PUSH clk failed: %m");
+ "refclock_ioctl fd %d I_PUSH: %m", fd);
+ return (0);
+#ifdef CLK_SETSTR
} else {
char *str;
- if (flags & LDISC_CLKPPS)
+ if (lflags & LDISC_CLKPPS)
str = "\377";
- else if (flags & LDISC_ACTS)
+ else if (lflags & LDISC_ACTS)
str = "*";
else
str = "\n";
-#ifdef CLK_SETSTR
- if ((rval = ioctl(fd, CLK_SETSTR, str)) < 0)
+ if (ioctl(fd, CLK_SETSTR, str) < 0) {
msyslog(LOG_ERR,
- "refclock_ioctl: CLK_SETSTR failed: %m");
- if (debug)
- printf("refclock_ioctl: fd %d CLK_SETSTR %d str %s\n",
- fd, rval, str);
-#endif
+ "refclock_ioctl fd %d CLK_SETSTR: %m", fd);
+ return (0);
+ }
+#endif /*CLK_SETSTR */
}
}
#endif /* TTYCLK */
extern double last_offset;
extern double drift_comp;
extern int tc_counter;
- extern u_long last_time;
+ extern u_long sys_clocktime;
li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
sizeof(struct info_loop));
DTOLFP(drift_comp * 1e6, <mp);
HTONL_FP(<mp, &li->drift_comp);
li->compliance = htonl((u_int32)(tc_counter));
- li->watchdog_timer = htonl((u_int32)(current_time - last_time));
+ li->watchdog_timer = htonl((u_int32)(current_time - sys_clocktime));
(void) more_pkt();
flush_pkt();
* use as backup when neither a radio clock nor connectivity to Internet
* time servers is available.
*
- * For best results the propagation delay due to the individual modem
- * and telephone circuit must be known. The ACTS echo-delay measurement
- * scheme no longer works, so corrections due to propagation delay must
- * be determined by other means. Systematic corrections range from 200
- * to 400 milliseconds, depending on the particular service and call
- * routing. Variations from call to call can reach 100 miliseconds and
- * between messages during a call from a few milliseconds to 50
- * milliseconds or more.
- *
- * This driver requires a 1200-bps modem with a Hayes-compatible command
+ * This driver requires a 9600-bps 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. The
- * calling program is initiated by setting fudge flag1. This can be done
- * using ntpdc, either manually or via a cron job. In auto mode, flag1
- * is set at each poll event. In backup mode, flag1 is set at each poll
- * event, but only if the prefer peer is unreachable.
+ * require changes for nonstandard modems or special circumstances.
*
- * When flag1 is set, the calling program dials each number listed in
- * the phones command of the configuration file in turn. 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 using ntpdc.
+ * The calling program is initiated by setting fudge flag1, either
+ * manually or automatically. When flag1 is set, the calling program
+ * dials each number listed in the phones command of the configuration
+ * file in turn. 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
* 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
* ...
*
- * MJD, DST, DUT1, msADV and UTC are not used by this driver. The "*" is
- * the on-time marker.
+ * 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)
*
* PTB: +49 531 512038 (Germany)
* NPL: 0906 851 6333 (UK only)
*
- * Data format
+ * Data format (see the documentation for phone numbers and formats.)
*
* 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500
*
* 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 10 /* we need this many messages */
-#define SMAX 80 /* max string length */
+#define MSGCNT 20 /* max message count */
+#define SMAX 80 /* max clockstats line length */
/*
* Calling program modes
#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 initiate call negotiation using Bell 212A
+ * B1 US answer tone
* &C1 enable carrier detect
* &D2 hang up and return to command mode on DTR transition
* E0 modem command echo disabled
#define WAIT 2 /* DTR timeout */
#define MODEM 3 /* modem timeout */
#define ANSWER 60 /* answer timeout */
-#define CONNECT 10 /* first message timeout */
-#define TIMECODE 20 /* all messages timeout */
+#define CONNECT 10 /* first valid message timeout */
+#define TIMECODE 30 /* all valid messages timeout */
/*
* State machine codes
int retry; /* retry index */
int msgcnt; /* count of messages received */
l_fp tstamp; /* on-time timestamp */
+ char *bufptr; /* buffer pointer */
};
/*
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_poll P((int, struct peer *));
static void acts_timeout P((struct peer *));
static void acts_disc P((struct peer *));
pp->clockdesc = DESCRIPTION;
memcpy((char *)&pp->refid, REFID, 4);
peer->sstclktype = CTL_SST_TS_TELEPHONE;
+ up->bufptr = pp->a_lastcode;
return (1);
}
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
+ * interfact kicks up; so, we have to reassemble messages from
+ * arbitrary fragments. Capture the timecode at the beginning of
+ * the message and at the '*' on-time character.
+ */
+ 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
+ */
+void
+acts_message(
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ char tbuf[SMAX];
+ u_int len;
int day; /* day of the month */
int month; /* month of the year */
-
u_long mjd; /* Modified Julian Day */
double dut1; /* DUT adjustment */
double msADV; /* ACTS transmit advance (ms) */
char dstchar; /* WWVB daylight/savings indicator */
u_int dst; /* ACTS daylight/standard time */
- int len;
- char tbuf[SMAX]; /* monitor buffer */
-
- /*
- * Initialize pointers and read the timecode and timestamp.
- */
- peer = (struct peer *)rbufp->recv_srcclock;
pp = peer->procptr;
up = (struct actsunit *)pp->unitptr;
- pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
- &pp->lastrec);
- if (pp->lencode == 0) {
- up->tstamp = pp->lastrec;
- return;
- }
- pp->a_lastcode[pp->lencode] = '\0';
- sprintf(tbuf, "acts: (%d %d) %d %s", up->state, up->timer,
- pp->lencode, pp->a_lastcode);
+ len = strlen(pp->a_lastcode);
+ sprintf(tbuf, "acts: (%d %d) %d %s", up->state, up->timer, len,
+ pp->a_lastcode);
#ifdef DEBUG
if (debug)
printf("%s\n", tbuf);
* occasional errors due noise are forgivable.
*/
pp->nsec = 0;
- switch(pp->lencode) {
+ switch(len) {
/*
- * USNO on-time '*' on a line by itself.
+ * For USNO format on-time character '*', which is on a line by
+ * itself. By sure a timecode has been received.
*/
case 1:
- if (*pp->a_lastcode == '*' && up->msgcnt > 0)
+ if (*pp->a_lastcode == '*' && up->state == S_MSG)
break;
return;
refclock_report(peer, CEVNT_BADREPLY);
return;
}
+
+ /*
+ * Reset the timer and wait until NIST has calculated
+ * roundtrip delay.
+ */
+ if (up->state != S_MSG) {
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ }
+ if (flag != '#')
+ return;
+
+ pp->lastrec = up->tstamp;
pp->day = ymd2yd(pp->year, month, day);
if (leap == 1)
pp->leap = LEAP_ADDSECOND;
refclock_report(peer, CEVNT_BADREPLY);
return;
}
+
+ /*
+ * Reset the timer and wait for the on-time character.
+ */
+ if (up->state != S_MSG) {
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ }
pp->lastrec = up->tstamp;
memcpy(&pp->refid, REFUSNO, 4);
if (up->msgcnt == 0)
refclock_report(peer, CEVNT_BADREPLY);
return;
}
+
+ /*
+ * Reset the timer and wait for the on-time character.
+ */
+ if (up->state != S_MSG) {
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ }
pp->lastrec = up->tstamp;
if (leapmonth == month) {
if (leapdir == '+')
*/
case LENWWVB2:
if (sscanf(pp->a_lastcode,
- "%c%c%2d %3d %2d:%2d:%2d.%3ld %c%c",
+ "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
&synchar, &qualchar, &pp->year, &pp->day,
&pp->hour, &pp->minute, &pp->second, &pp->nsec,
- &leapchar, &dstchar) != 9) {
+ &dstchar, &leapchar, &dstchar) != 11) {
refclock_report(peer, CEVNT_BADREPLY);
return;
}
/*
* The fudge time1 value is added to each sample by the main
- * line routines. We collect a maximum of MSGCNT samples or
- * until timeout.
+ * line routines. Note that we use the median filter only when
+ * the dispersion has receeded below the threshold.
*/
if (refclock_process(pp)) {
pp->lastref = pp->lastrec;
- refclock_receive(peer);
- up->state = S_MSG;
- up->timer = TIMECODE;
+ if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+ if (up->state != S_MSG) {
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ }
} else {
refclock_report(peer, CEVNT_BADTIME);
}
}
/*
- * Open device and light up a discipline if present. We
- * use 1200 baud for modem, 9600 baud for direct
- * connect.
+ * Open device and light up a discipline if present.
*/
if (!pp->io.fd) {
sprintf(device, DEVICE, up->unit);
- if (pp->sloppyclockflag & CLK_FLAG3)
- fd = refclock_open(device, B9600,
- LDISC_ACTS);
- else
- fd = refclock_open(device, B1200,
- LDISC_ACTS);
+ fd = refclock_open(device, SPEED232,
+ LDISC_ACTS | LDISC_RAW);
if (fd < 0) {
msyslog(LOG_ERR,
"acts: device open fails");
* number, fold the tent and go home.
*/
if (up->msgcnt > 0) {
+ refclock_receive(peer);
up->retry = 0;
} else {
up->retry++;
}
}
+
+ /*
+ * If there are more numbers to dial or the modem is not in
+ * command mode, retry after a short wait.
+ */
up->msgcnt = 0;
- up->state = S_IDLE;
- if (up->retry > 0)
+ up->timer = 0;
+ up->bufptr = pp->a_lastcode;
+ if (up->retry > 0 || up->state == S_OK)
up->timer = MODEM;
- else
- up->timer = 0;
+ up->state = S_IDLE;
}
#else
* WWVB unit control structure
*/
struct wwvbunit {
- u_char tcswitch; /* timecode switch */
l_fp laststamp; /* last receive timestamp */
u_char lasthour; /* last hour (for monitor) */
u_char linect; /* count ignored lines (for monitor */
/*
* Open serial port. Use CLK line discipline, if available.
*/
- (void)sprintf(device, DEVICE, unit);
+ sprintf(device, DEVICE, unit);
if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
return (0);
*/
if (!(up = (struct wwvbunit *)
emalloc(sizeof(struct wwvbunit)))) {
- (void) close(fd);
+ close(fd);
return (0);
}
memset((char *)up, 0, sizeof(struct wwvbunit));
pp->io.datalen = 0;
pp->io.fd = fd;
if (!io_addclock(&pp->io)) {
- (void) close(fd);
+ close(fd);
free(up);
return (0);
}
* 0 provides the lowest jitter.
*/
if (temp == 0) {
- if (up->tcswitch == 0) {
- up->tcswitch = 1;
- up->laststamp = trtmp;
- } else
- up->tcswitch = 0;
+ up->laststamp = trtmp;
return;
}
pp->lencode = temp;
pp->lastrec = up->laststamp;
- up->laststamp = trtmp;
- up->tcswitch = 1;
/*
* We get down to business, check the timecode format and decode