summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/refclock_atom.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/refclock_atom.c')
-rw-r--r--contrib/ntp/ntpd/refclock_atom.c313
1 files changed, 163 insertions, 150 deletions
diff --git a/contrib/ntp/ntpd/refclock_atom.c b/contrib/ntp/ntpd/refclock_atom.c
index 51153ae..2e469ec 100644
--- a/contrib/ntp/ntpd/refclock_atom.c
+++ b/contrib/ntp/ntpd/refclock_atom.c
@@ -1,4 +1,3 @@
-
/*
* refclock_atom - clock driver for 1-pps signals
*/
@@ -18,13 +17,7 @@
#if defined(REFCLOCK) && defined(CLOCK_ATOM)
#ifdef HAVE_PPSAPI
-# ifdef HAVE_TIMEPPS_H
-# include <timepps.h>
-# else
-# ifdef HAVE_SYS_TIMEPPS_H
-# include <sys/timepps.h>
-# endif
-# endif
+# include "ppsapi_timepps.h"
#endif /* HAVE_PPSAPI */
/*
@@ -48,7 +41,12 @@
* 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
- * the API with multiple PPS drivers and multiple handles per driver.
+ * 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
@@ -61,16 +59,20 @@
* configuration file.
*
* This driver normally uses the PLL/FLL clock discipline implemented in
- * the ntpd code. If kernel support is available, the kernel PLL/FLL
- * clock discipline is used instead. The default configuration is not to
- * use the kernel PPS discipline, if present. The kernel PPS discipline
- * can be enabled using the pps command.
+ * the ntpd code. Ordinarily, this is the most accurate means, as the
+ * median filter in the driver interface is much larger than in the
+ * kernel. However, if the systemic clock frequency error is large (tens
+ * to hundreds of PPM), it's better to used the kernel support, if
+ * available.
*
* Fudge Factors
*
- * There are no special fudge factors other than the generic. The fudge
- * time1 parameter can be used to compensate for miscellaneous device
- * driver and OS delays.
+ * 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.
*/
/*
* Interface definitions
@@ -84,7 +86,6 @@
#define DESCRIPTION "PPS Clock Discipline" /* WRU */
#define NANOSECOND 1000000000 /* one second (ns) */
#define RANGEGATE 500000 /* range gate (ns) */
-#define ASTAGE 8 /* filter stages */
static struct peer *pps_peer; /* atom driver for PPS sources */
@@ -106,34 +107,38 @@ struct ppsunit {
*/
static int atom_start P((int, struct peer *));
static void atom_poll P((int, struct peer *));
-#ifdef HAVE_PPSAPI
static void atom_shutdown P((int, struct peer *));
+#ifdef HAVE_PPSAPI
static void atom_control P((int, struct refclockstat *, struct
refclockstat *, struct peer *));
-static int atom_pps P((struct peer *));
-static int atom_ppsapi P((struct peer *, int, int));
+static void atom_timer P((int, struct peer *));
+static int atom_ppsapi P((struct peer *, int));
#endif /* HAVE_PPSAPI */
/*
* Transfer vector
*/
+#ifdef HAVE_PPSAPI
struct refclock refclock_atom = {
atom_start, /* start up driver */
-#ifdef HAVE_PPSAPI
atom_shutdown, /* shut down driver */
-#else
- noentry, /* shut down driver */
-#endif /* HAVE_PPSAPI */
atom_poll, /* transmit poll message */
-#ifdef HAVE_PPSAPI
atom_control, /* fudge control */
-#else
- noentry, /* fudge control */
-#endif /* HAVE_PPSAPI */
- noentry, /* initialize driver */
- noentry, /* not used (old atom_buginfo) */
+ 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 */
/*
@@ -148,7 +153,8 @@ atom_start(
struct refclockproc *pp;
#ifdef HAVE_PPSAPI
register struct ppsunit *up;
- char device[80];
+ char device[80];
+ int mode;
#endif /* HAVE_PPSAPI */
/*
@@ -160,23 +166,17 @@ atom_start(
pp->clockdesc = DESCRIPTION;
pp->stratum = STRATUM_UNSPEC;
memcpy((char *)&pp->refid, REFID, 4);
- peer->burst = ASTAGE;
#ifdef HAVE_PPSAPI
up = emalloc(sizeof(struct ppsunit));
memset(up, 0, sizeof(struct ppsunit));
pp->unitptr = (caddr_t)up;
/*
- * Open PPS device. If this fails and some driver has already
- * opened the associated radio device, fdpps has the file
- * descriptor for it.
+ * 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);
- if (up->fddev <= 0 && fdpps > 0) {
- strcpy(device, pps_device);
- up->fddev = fdpps;
- }
if (up->fddev <= 0) {
msyslog(LOG_ERR,
"refclock_atom: %s: %m", device);
@@ -184,22 +184,55 @@ atom_start(
}
/*
- * Light off the PPSAPI interface. If this PPS device is shared
- * with the radio device, take the default options from the pps
- * command. This is for legacy purposes.
+ * Light off the PPSAPI interface.
*/
if (time_pps_create(up->fddev, &up->handle) < 0) {
msyslog(LOG_ERR,
"refclock_atom: time_pps_create failed: %m");
return (0);
}
- return (atom_ppsapi(peer, 0, 0));
+
+ /*
+ * If the mode is nonzero, use that for the time_pps_setparams()
+ * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
+ * flag3 is lit.
+ */
+ mode = peer->ttl;
+ if (mode == 0)
+ mode = PPS_CAPTUREASSERT;
+ return (atom_ppsapi(peer, mode));
#else /* HAVE_PPSAPI */
return (1);
#endif /* HAVE_PPSAPI */
}
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ register 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
@@ -213,10 +246,17 @@ atom_control(
)
{
struct refclockproc *pp;
+ int mode;
pp = peer->procptr;
- atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
- pp->sloppyclockflag & CLK_FLAG3);
+ 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);
}
@@ -226,8 +266,7 @@ atom_control(
int
atom_ppsapi(
struct peer *peer, /* peer structure pointer */
- int enb_clear, /* clear enable */
- int enb_hardpps /* hardpps enable */
+ int mode /* mode */
)
{
struct refclockproc *pp;
@@ -236,29 +275,23 @@ atom_ppsapi(
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));
- if (enb_clear)
- up->pps_params.mode = capability & PPS_CAPTURECLEAR;
- else
- up->pps_params.mode = capability & PPS_CAPTUREASSERT;
- if (!up->pps_params.mode) {
- msyslog(LOG_ERR,
- "refclock_atom: invalid capture edge %d",
- enb_clear);
- return (0);
- }
- up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ 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 (enb_hardpps) {
+ 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) {
@@ -272,9 +305,9 @@ atom_ppsapi(
if (debug) {
time_pps_getparams(up->handle, &up->pps_params);
printf(
- "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
+ "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, enb_hardpps);
+ up->pps_params.mode);
}
#endif
return (1);
@@ -282,39 +315,16 @@ atom_ppsapi(
/*
- * atom_shutdown - shut down the clock
- */
-static void
-atom_shutdown(
- int unit, /* unit number (not used) */
- struct peer *peer /* peer structure pointer */
- )
-{
- struct refclockproc *pp;
- register struct ppsunit *up;
-
- pp = peer->procptr;
- up = (struct ppsunit *)pp->unitptr;
- if (up->fddev > 0)
- close(up->fddev);
- if (up->handle != 0)
- time_pps_destroy(up->handle);
- if (pps_peer == peer)
- pps_peer = 0;
- free(up);
-}
-
-
-/*
- * atom_pps - receive data from the PPSAPI interface
+ * 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 int
-atom_pps(
+static void
+atom_timer(
+ int unit, /* unit number (not used) */
struct peer *peer /* peer structure pointer */
)
{
@@ -322,7 +332,9 @@ atom_pps(
struct refclockproc *pp;
pps_info_t pps_info;
struct timespec timeout, ts;
- double dtemp;
+ long sec, nsec;
+ double dtemp;
+ char tbuf[80]; /* monitor buffer */
/*
* Convert the timespec nanoseconds field to signed double and
@@ -334,34 +346,58 @@ atom_pps(
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (up->handle == 0)
- return (-1);
+ 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)
- return (-1);
+ &timeout) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
if (up->pps_params.mode & PPS_CAPTUREASSERT) {
- if (pps_info.assert_sequence ==
- up->pps_info.assert_sequence)
- return (1);
ts = up->pps_info.assert_timestamp;
} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
- if (pps_info.clear_sequence ==
- up->pps_info.clear_sequence)
- return (1);
ts = up->pps_info.clear_timestamp;
} else {
- return (-1);
- }
- if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
- up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
- (ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
- up->ts.tv_nsec < RANGEGATE))) {
- up->ts = ts;
- return (1);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
}
+
+ /*
+ * 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)
@@ -371,11 +407,16 @@ atom_pps(
ts.tv_nsec -= NANOSECOND;
dtemp = -(double)ts.tv_nsec / NANOSECOND;
SAMPLE(dtemp + pp->fudgetime1);
+ if (pp->sloppyclockflag & CLK_FLAG4){
+ sprintf(tbuf, "%.9f", dtemp);
+ record_clock_stats(&peer->srcadr, tbuf);
+ }
#ifdef DEBUG
if (debug > 1)
- printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
+ printf("atom_timer: %lu %f %f\n", current_time,
+ dtemp, pp->fudgetime1);
#endif
- return (0);
+ return;
}
#endif /* HAVE_PPSAPI */
@@ -387,6 +428,9 @@ atom_pps(
* 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(
@@ -399,8 +443,9 @@ pps_sample(
double doffset;
peer = pps_peer;
- if (peer == 0) /* nobody home */
+ if (peer == NULL)
return (1);
+
pp = peer->procptr;
/*
@@ -417,12 +462,9 @@ pps_sample(
return (0);
}
+
/*
* atom_poll - called by the transmit procedure
- *
- * This routine is called once per second when in burst mode to save PPS
- * sample offsets in the median filter. At the end of the burst period
- * the samples are processed as a heap and the clock filter updated.
*/
static void
atom_poll(
@@ -431,66 +473,37 @@ atom_poll(
)
{
struct refclockproc *pp;
-#ifdef HAVE_PPSAPI
- int err;
-#endif /* HAVE_PPSAPI */
-
- /*
- * Accumulate samples in the median filter. If a noise sample,
- * return with no prejudice; if a protocol error, get mean;
- * otherwise, cool. At the end of each poll interval, do a
- * little bookeeping and process the surviving samples.
- */
pp = peer->procptr;
pp->polls++;
-#ifdef HAVE_PPSAPI
- err = atom_pps(peer);
- if (err < 0) {
- refclock_report(peer, CEVNT_FAULT);
- return;
- }
-#endif /* HAVE_PPSAPI */
/*
* Valid time is returned only if the prefer peer has survived
- * the intersection algorithm and within clock_max of local time
+ * 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 are set no-warning on
- * the first valid update and the stratum is set at the prefer
- * peer, unless overriden by a fudge command.
+ * 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.
*/
- if (peer->burst > 0)
- return;
- peer->leap = LEAP_NOTINSYNC;
if (pp->codeproc == pp->coderecv) {
refclock_report(peer, CEVNT_TIMEOUT);
- peer->burst = ASTAGE;
return;
} else if (sys_prefer == NULL) {
pp->codeproc = pp->coderecv;
- peer->burst = ASTAGE;
return;
- } else if (fabs(sys_prefer->offset) > clock_max) {
+ } else if (fabs(sys_prefer->offset) >= 0.4) {
pp->codeproc = pp->coderecv;
- peer->burst = ASTAGE;
return;
}
- pp->leap = LEAP_NOWARNING;
+ pp->leap = sys_prefer->leap;
if (pp->stratum >= STRATUM_UNSPEC)
peer->stratum = sys_prefer->stratum;
else
peer->stratum = pp->stratum;
- if (peer->stratum == STRATUM_REFCLOCK || peer->stratum ==
- STRATUM_UNSPEC)
- peer->refid = pp->refid;
- else
- peer->refid = addr2refid(&sys_prefer->srcadr);
pp->lastref = pp->lastrec;
refclock_receive(peer);
- peer->burst = ASTAGE;
}
#else
int refclock_atom_bs;
@@ -499,6 +512,6 @@ pps_sample(
l_fp *offset /* PPS offset */
)
{
- return 1;
+ return (1);
}
#endif /* REFCLOCK */
OpenPOWER on IntegriCloud