aboutsummaryrefslogtreecommitdiff
path: root/ntpd/refclock_atom.c
diff options
context:
space:
mode:
Diffstat (limited to 'ntpd/refclock_atom.c')
-rw-r--r--ntpd/refclock_atom.c402
1 files changed, 62 insertions, 340 deletions
diff --git a/ntpd/refclock_atom.c b/ntpd/refclock_atom.c
index 2e469ecd4054..d8a9d3e27add 100644
--- a/ntpd/refclock_atom.c
+++ b/ntpd/refclock_atom.c
@@ -14,21 +14,23 @@
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
-#if defined(REFCLOCK) && defined(CLOCK_ATOM)
-
-#ifdef HAVE_PPSAPI
-# include "ppsapi_timepps.h"
-#endif /* HAVE_PPSAPI */
+/*
+ * This driver requires the PPSAPI interface (RFC 2783)
+ */
+#if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
+#include "ppsapi_timepps.h"
+#include "refclock_atom.h"
/*
* This driver furnishes an interface for pulse-per-second (PPS) signals
* produced by a cesium clock, timing receiver or related equipment. It
- * can be used to remove accumulated jitter and retime a secondary
- * server when synchronized to a primary server over a congested, wide-
- * area network and before redistributing the time to local clients.
+ * can be used to remove accumulated jitter over a congested link and
+ * retime a server before redistributing the time to clients. It can
+ *also be used as a holdover should all other synchronization sources
+ * beconme unreachable.
*
* Before this driver becomes active, the local clock must be set to
- * within +-500 ms by another means, such as a radio clock or NTP
+ * within +-0.4 s by another means, such as a radio clock or NTP
* itself. There are two ways to connect the PPS signal, normally at TTL
* levels, to the computer. One is to shift to EIA levels and connect to
* pin 8 (DCD) of a serial port. This requires a level converter and
@@ -36,27 +38,13 @@
* to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
* port. These methods are architecture dependent.
*
- * Both methods require a modified device driver and kernel interface
- * compatible with the Pulse-per-Second API for Unix-like Operating
+ * This driver requires the Pulse-per-Second API for Unix-like Operating
* Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
- * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
- * present only the Alpha implementation provides the full generality of
+ * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
+ * present only the Tru64 implementation provides the full generality of
* the API with multiple PPS drivers and multiple handles per driver. If
* the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
* header file and kernel support specific to each operating system.
- * However, this driver can operate without this interface if means are
- * proviced to call the pps_sample() routine from another driver. Please
- * note; if the PPSAPI interface is present, it must be used.
- *
- * In many configurations a single port is used for the radio timecode
- * and PPS signal. In order to provide for this configuration and others
- * involving dedicated multiple serial/parallel ports, the driver first
- * attempts to open the device /dev/pps%d, where %d is the unit number.
- * If this fails, the driver attempts to open the device specified by
- * the pps configuration command. If a port is to be shared, the pps
- * command must be placed before the radio device(s) and the radio
- * device(s) must be placed before the PPS driver(s) in the
- * configuration file.
*
* This driver normally uses the PLL/FLL clock discipline implemented in
* the ntpd code. Ordinarily, this is the most accurate means, as the
@@ -65,80 +53,62 @@
* to hundreds of PPM), it's better to used the kernel support, if
* available.
*
+ * This deriver is subject to the mitigation rules described in the
+ * "mitigation rulse and the prefer peer" page. However, there is an
+ * important difference. If this driver becomes the PPS driver according
+ * to these rules, it is acrive only if (a) a prefer peer other than
+ * this driver is among the survivors or (b) there are no survivors and
+ * the minsane option of the tos command is zero. This is intended to
+ * support space missions where updates from other spacecraft are
+ * infrequent, but a reliable PPS signal, such as from an Ultra Stable
+ * Oscillator (USO) is available.
+ *
* Fudge Factors
*
- * If flag2 is dim (default), the on-time epoch is the assert edge of
- * the PPS signal; if lit, the on-time epoch is the clear edge. If flag2
- * is lit, the assert edge is used; if flag3 is dim (default), the
- * kernel PPS support is disabled; if lit it is enabled. The time1
- * parameter can be used to compensate for miscellaneous device driver
- * and OS delays.
+ * The PPS timestamp is captured on the rising (assert) edge if flag2 is
+ * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
+ * (default), the kernel PPS support is disabled; if lit it is enabled.
+ * If flag4 is lit, each timesampt is copied to the clockstats file for
+ * later analysis. This can be useful when constructing Allan deviation
+ * plots. The time1 parameter can be used to compensate for
+ * miscellaneous device driver and OS delays.
*/
/*
* Interface definitions
*/
-#ifdef HAVE_PPSAPI
#define DEVICE "/dev/pps%d" /* device name and unit */
-#endif /* HAVE_PPSAPI */
-
#define PRECISION (-20) /* precision assumed (about 1 us) */
#define REFID "PPS\0" /* reference ID */
#define DESCRIPTION "PPS Clock Discipline" /* WRU */
-#define NANOSECOND 1000000000 /* one second (ns) */
-#define RANGEGATE 500000 /* range gate (ns) */
-static struct peer *pps_peer; /* atom driver for PPS sources */
-
-#ifdef HAVE_PPSAPI
/*
* PPS unit control structure
*/
struct ppsunit {
- struct timespec ts; /* last timestamp */
- int fddev; /* pps device descriptor */
- pps_params_t pps_params; /* pps parameters */
- pps_info_t pps_info; /* last pps data */
- pps_handle_t handle; /* pps handlebars */
+ struct refclock_atom atom; /* atom structure pointer */
+ int fddev; /* file descriptor */
};
-#endif /* HAVE_PPSAPI */
/*
* Function prototypes
*/
-static int atom_start P((int, struct peer *));
-static void atom_poll P((int, struct peer *));
-static void atom_shutdown P((int, struct peer *));
-#ifdef HAVE_PPSAPI
-static void atom_control P((int, struct refclockstat *, struct
- refclockstat *, struct peer *));
-static void atom_timer P((int, struct peer *));
-static int atom_ppsapi P((struct peer *, int));
-#endif /* HAVE_PPSAPI */
+static int atom_start (int, struct peer *);
+static void atom_shutdown (int, struct peer *);
+static void atom_poll (int, struct peer *);
+static void atom_timer (int, struct peer *);
/*
* Transfer vector
*/
-#ifdef HAVE_PPSAPI
struct refclock refclock_atom = {
atom_start, /* start up driver */
atom_shutdown, /* shut down driver */
atom_poll, /* transmit poll message */
- atom_control, /* fudge control */
+ noentry, /* control (not used) */
noentry, /* initialize driver (not used) */
noentry, /* buginfo (not used) */
atom_timer, /* called once per second */
};
-#else /* HAVE_PPSAPI */
-struct refclock refclock_atom = {
- atom_start, /* start up driver */
- atom_shutdown, /* shut down driver */
- atom_poll, /* transmit poll message */
- noentry, /* fudge control (not used) */
- noentry, /* initialize driver (not used) */
- noentry, /* buginfo (not used) */
- NOFLAGS /* not used */
-};
-#endif /* HAVE_PPPSAPI */
/*
@@ -151,22 +121,17 @@ atom_start(
)
{
struct refclockproc *pp;
-#ifdef HAVE_PPSAPI
- register struct ppsunit *up;
+ struct ppsunit *up;
char device[80];
- int mode;
-#endif /* HAVE_PPSAPI */
/*
* Allocate and initialize unit structure
*/
- pps_peer = peer;
pp = peer->procptr;
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
pp->stratum = STRATUM_UNSPEC;
memcpy((char *)&pp->refid, REFID, 4);
-#ifdef HAVE_PPSAPI
up = emalloc(sizeof(struct ppsunit));
memset(up, 0, sizeof(struct ppsunit));
pp->unitptr = (caddr_t)up;
@@ -175,35 +140,18 @@ atom_start(
* Open PPS device. This can be any serial or parallel port and
* not necessarily the port used for the associated radio.
*/
- sprintf(device, DEVICE, unit);
- up->fddev = open(device, O_RDWR, 0777);
+ snprintf(device, sizeof(device), DEVICE, unit);
+ up->fddev = tty_open(device, O_RDWR, 0777);
if (up->fddev <= 0) {
msyslog(LOG_ERR,
- "refclock_atom: %s: %m", device);
- return (0);
- }
-
- /*
- * Light off the PPSAPI interface.
- */
- if (time_pps_create(up->fddev, &up->handle) < 0) {
- msyslog(LOG_ERR,
- "refclock_atom: time_pps_create failed: %m");
+ "refclock_atom: %s: %m", device);
return (0);
}
/*
- * If the mode is nonzero, use that for the time_pps_setparams()
- * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
- * flag3 is lit.
+ * Light up the PPSAPI interface.
*/
- mode = peer->ttl;
- if (mode == 0)
- mode = PPS_CAPTUREASSERT;
- return (atom_ppsapi(peer, mode));
-#else /* HAVE_PPSAPI */
- return (1);
-#endif /* HAVE_PPSAPI */
+ return (refclock_ppsapi(up->fddev, &up->atom));
}
@@ -217,249 +165,44 @@ atom_shutdown(
)
{
struct refclockproc *pp;
- register struct ppsunit *up;
+ struct ppsunit *up;
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
-#ifdef HAVE_PPSAPI
if (up->fddev > 0)
close(up->fddev);
- if (up->handle != 0)
- time_pps_destroy(up->handle);
-#endif /* HAVE_PPSAPI */
- if (pps_peer == peer)
- pps_peer = NULL;
free(up);
}
-
-#ifdef HAVE_PPSAPI
-/*
- * atom_control - fudge control
- */
-static void
-atom_control(
- int unit, /* unit (not used */
- struct refclockstat *in, /* input parameters (not uded) */
- struct refclockstat *out, /* output parameters (not used) */
- struct peer *peer /* peer structure pointer */
- )
-{
- struct refclockproc *pp;
- int mode;
-
- pp = peer->procptr;
- if (peer->ttl != 0) /* all legal modes must be nonzero */
- return;
-
- if (pp->sloppyclockflag & CLK_FLAG2)
- mode = PPS_CAPTURECLEAR;
- else
- mode = PPS_CAPTUREASSERT;
- atom_ppsapi(peer, mode);
-}
-
-
-/*
- * Initialize PPSAPI
- */
-int
-atom_ppsapi(
- struct peer *peer, /* peer structure pointer */
- int mode /* mode */
- )
-{
- struct refclockproc *pp;
- register struct ppsunit *up;
- int capability;
-
- pp = peer->procptr;
- up = (struct ppsunit *)pp->unitptr;
- if (up->handle == 0)
- return (0);
-
- if (time_pps_getcap(up->handle, &capability) < 0) {
- msyslog(LOG_ERR,
- "refclock_atom: time_pps_getcap failed: %m");
- return (0);
- }
- memset(&up->pps_params, 0, sizeof(pps_params_t));
- up->pps_params.api_version = PPS_API_VERS_1;
- up->pps_params.mode = mode | PPS_TSFMT_TSPEC;
- if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
- msyslog(LOG_ERR,
- "refclock_atom: time_pps_setparams failed: %m");
- return (0);
- }
- if (pp->sloppyclockflag & CLK_FLAG3) {
- if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
- up->pps_params.mode & ~PPS_TSFMT_TSPEC,
- PPS_TSFMT_TSPEC) < 0) {
- msyslog(LOG_ERR,
- "refclock_atom: time_pps_kcbind failed: %m");
- return (0);
- }
- pps_enable = 1;
- }
-#if DEBUG
- if (debug) {
- time_pps_getparams(up->handle, &up->pps_params);
- printf(
- "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",
- up->fddev, capability, up->pps_params.api_version,
- up->pps_params.mode);
- }
-#endif
- return (1);
-}
-
-
/*
* atom_timer - called once per second
- *
- * This routine is called once per second when the PPSAPI interface is
- * present. It snatches the PPS timestamp from the kernel and saves the
- * sign-extended fraction in a circular buffer for processing at the
- * next poll event.
*/
-static void
+void
atom_timer(
- int unit, /* unit number (not used) */
+ int unit, /* unit pointer (not used) */
struct peer *peer /* peer structure pointer */
)
{
- register struct ppsunit *up;
+ struct ppsunit *up;
struct refclockproc *pp;
- pps_info_t pps_info;
- struct timespec timeout, ts;
- long sec, nsec;
- double dtemp;
- char tbuf[80]; /* monitor buffer */
+ char tbuf[80];
- /*
- * Convert the timespec nanoseconds field to signed double and
- * save in the median filter. for billboards. No harm is done if
- * previous data are overwritten. If the discipline comes bum or
- * the data grow stale, just forget it. A range gate rejects new
- * samples if less than a jiggle time from the next second.
- */
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
- if (up->handle == 0)
+ if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
return;
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
- memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
- if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
- &timeout) < 0) {
- refclock_report(peer, CEVNT_FAULT);
- return;
- }
- if (up->pps_params.mode & PPS_CAPTUREASSERT) {
- ts = up->pps_info.assert_timestamp;
- } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
- ts = up->pps_info.clear_timestamp;
- } else {
- refclock_report(peer, CEVNT_FAULT);
- return;
- }
+ peer->flags |= FLAG_PPS;
/*
- * There can be zero, one or two PPS seconds between polls. If
- * zero, either the poll clock is slightly faster than the PPS
- * clock or the PPS clock has died. If the PPS clock advanced
- * once between polls, we make sure the fraction time difference
- * since the last sample is within the range gate of 5 ms (500
- * PPM). If the PPS clock advanced twice since the last poll,
- * the poll bracketed more than one second and the first second
- * was lost to a slip. Since the interval since the last sample
- * found is now two seconds, just widen the range gate. If the
- * PPS clock advanced three or more times, either the signal has
- * failed for a number of seconds or we have runts, in which
- * case just ignore them.
- *
* If flag4 is lit, record each second offset to clockstats.
* That's so we can make awesome Allan deviation plots.
*/
- sec = ts.tv_sec - up->ts.tv_sec;
- nsec = ts.tv_nsec - up->ts.tv_nsec;
- up->ts = ts;
- if (nsec < 0) {
- sec --;
- nsec += NANOSECOND;
- } else if (nsec >= NANOSECOND) {
- sec++;
- nsec -= NANOSECOND;
- }
- if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)
- return;
-
- else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)
- return;
-
- pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
- dtemp = ts.tv_nsec * FRAC / 1e9;
- if (dtemp >= FRAC)
- pp->lastrec.l_ui++;
- pp->lastrec.l_uf = (u_int32)dtemp;
- if (ts.tv_nsec > NANOSECOND / 2)
- ts.tv_nsec -= NANOSECOND;
- dtemp = -(double)ts.tv_nsec / NANOSECOND;
- SAMPLE(dtemp + pp->fudgetime1);
- if (pp->sloppyclockflag & CLK_FLAG4){
- sprintf(tbuf, "%.9f", dtemp);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ snprintf(tbuf, sizeof(tbuf), "%.9f",
+ pp->filter[pp->coderecv]);
record_clock_stats(&peer->srcadr, tbuf);
}
-#ifdef DEBUG
- if (debug > 1)
- printf("atom_timer: %lu %f %f\n", current_time,
- dtemp, pp->fudgetime1);
-#endif
- return;
-}
-#endif /* HAVE_PPSAPI */
-
-
-/*
- * pps_sample - receive PPS data from some other clock driver
- *
- * This routine is called once per second when the external clock driver
- * processes PPS information. It processes the PPS timestamp and saves
- * the sign-extended fraction in a circular buffer for processing at the
- * next poll event. This works only for a single PPS device.
- *
- * The routine should be used by another configured driver ONLY when
- * this driver is configured as well and the PPSAPI is NOT in use.
- */
-int
-pps_sample(
- l_fp *offset /* PPS offset */
- )
-{
- register struct peer *peer;
- struct refclockproc *pp;
- l_fp lftmp;
- double doffset;
-
- peer = pps_peer;
- if (peer == NULL)
- return (1);
-
- pp = peer->procptr;
-
- /*
- * Convert the timeval to l_fp and save for billboards. Sign-
- * extend the fraction and stash in the buffer. No harm is done
- * if previous data are overwritten. If the discipline comes bum
- * or the data grow stale, just forget it.
- */
- pp->lastrec = *offset;
- L_CLR(&lftmp);
- L_ADDF(&lftmp, pp->lastrec.l_f);
- LFPTOD(&lftmp, doffset);
- SAMPLE(-doffset + pp->fudgetime1);
- return (0);
}
@@ -473,45 +216,24 @@ atom_poll(
)
{
struct refclockproc *pp;
- pp = peer->procptr;
- pp->polls++;
/*
- * Valid time is returned only if the prefer peer has survived
- * the intersection algorithm and within 0.4 s of local time
- * and not too long ago. This ensures the PPS time is within
- * 0.5 s of the local time and the seconds numbering is
- * unambiguous. Note that the leap bits, stratum and refid are
- * set from the prefer peer, unless overriden by a fudge
- * command.
+ * Don't wiggle the clock until some other driver has numbered
+ * the seconds.
*/
- if (pp->codeproc == pp->coderecv) {
- refclock_report(peer, CEVNT_TIMEOUT);
- return;
-
- } else if (sys_prefer == NULL) {
- pp->codeproc = pp->coderecv;
+ if (sys_leap == LEAP_NOTINSYNC)
return;
- } else if (fabs(sys_prefer->offset) >= 0.4) {
- pp->codeproc = pp->coderecv;
+ pp = peer->procptr;
+ pp->polls++;
+ if (pp->codeproc == pp->coderecv) {
+ peer->flags &= ~FLAG_PPS;
+ refclock_report(peer, CEVNT_TIMEOUT);
return;
}
- pp->leap = sys_prefer->leap;
- if (pp->stratum >= STRATUM_UNSPEC)
- peer->stratum = sys_prefer->stratum;
- else
- peer->stratum = pp->stratum;
pp->lastref = pp->lastrec;
refclock_receive(peer);
}
#else
int refclock_atom_bs;
-int
-pps_sample(
- l_fp *offset /* PPS offset */
- )
-{
- return (1);
-}
#endif /* REFCLOCK */