diff options
Diffstat (limited to 'ntpd/refclock_atom.c')
-rw-r--r-- | ntpd/refclock_atom.c | 402 |
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 */ |