refclock_atom.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 1996, 1999 by Sun Microsystems, Inc.
* All Rights Reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* refclock_atom - clock driver for 1-pps signals
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
#ifdef SYS_SOLARIS
# define CIOGETEV TIOCGPPSEV
#else
# ifdef PPS
# include <sys/ppsclock.h>
# endif /* PPS */
#endif /* not SYS_SOLARIS */
/*
* 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.
*
* In order for this driver to work, the local clock must be set to
* within +-500 ms by another means, such as a radio clock or NTP
* itself. The 1-pps signal is connected via a serial port and gadget
* box consisting of a one-shot and RS232 level converter. When operated
* at 38.4 kbps with a SPARCstation IPC, this arrangement has a worst-
* case jitter less than 26 us.
*
* There are three ways in which this driver can be used. The first way
* uses the LDISC_PPS line discipline and works only for the baseboard
* serial ports of the Sun SPARCstation. The PPS signal is connected via
* a gadget box to the carrier detect (CD) line of a serial port and
* flag3 of the driver configured for that port is set. This causes the
* ppsclock streams module to be configured for that port and capture a
* timestamp at the on-time transition of the PPS signal. This driver
* then reads the timestamp directly by a designated ioctl() system
* call. This provides the most accurate time and least jitter of any
* other scheme. There is no need to configure a dedicated device for
* this purpose, which ordinarily is the device used for the associated
* radio clock.
*
* The second way uses the LDISC_CLKPPS line discipline and works for
* any architecture supporting a serial port. If after a few seconds
* this driver finds no ppsclock module configured, it attempts to open
* assign the LDISC_CLKPPS line discipline to it. If the line discipline
* fails, no harm is done except the accuracy is reduced somewhat. The
* pulse generator in the gadget box is adjusted to produce a start bit
* of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line
* discipline, this produces an ASCII DEL character ('\377') followed by
* a timestamp at each seconds epoch.
*
* The third way involves an auxiliary radio clock driver which calls
* the PPS driver with a timestamp captured by that driver. This use is
* documented in the source code for the driver(s) involved. Note that
* some drivers collect the sample information themselves before calling
* our pps_sample(), and others call us knowing only that they are running
* shortly after an on-time tick and they expect us to retrieve the PPS
* offset, fudge their result, and insert it into the timestream.
*
* Fudge Factors
*
* There are no special fudge factors other than the generic and those
* explicitly defined above. The fudge time1 parameter can be used to
* compensate for miscellaneous UART and OS delays. Allow about 247 us
* for uart delays at 38400 bps and about 1 ms for SunOS streams
* nonsense.
*/
/*
* Interface definitions
*/
#ifndef PPSX
#ifdef B38400
#else
#endif
#endif /* PPSX */
#ifdef PPS
#endif /* PPS */
/*
* Imported from ntp_timer module
*/
/*
* Imported from ntpd module
*/
extern int debug; /* global debug flag */
/*
* Imported from ntp_loopfilter module
*/
#ifndef PPSX
extern int fdpps; /* pps file descriptor */
#endif /* PPSX */
extern int pps_update; /* prefer peer valid update */
/*
* Imported from ntp_proto module
*/
/*
* Unit control structure
*/
struct atomunit {
#ifdef PPS
#endif /* PPS */
int pollcnt; /* poll message counter */
};
/*
* Global variables
*/
/*
* Function prototypes
*/
static int atom_start P((int, struct peer *));
static void atom_shutdown P((int, struct peer *));
#ifdef PPS
#endif /* PPS */
static int atom_cmpl_fp P((const void *, const void *));
/*
* Transfer vector
*/
struct refclock refclock_atom = {
atom_start, /* start up driver */
atom_shutdown, /* shut down driver */
atom_poll, /* transmit poll message */
noentry, /* not used (old atom_control) */
noentry, /* initialize driver */
noentry, /* not used (old atom_buginfo) */
NOFLAGS /* not used */
};
/*
* atom_start - initialize data for processing
*/
static int
int unit;
{
struct refclockproc *pp;
#ifdef DEBUG
if (debug > 0)
#endif
/*
* Allocate and initialize unit structure
*/
return (0);
/*
* Initialize miscellaneous variables
*/
#ifdef PPS
/*
* Arm the timer for the first interrupt. Give it ten seconds to
* allow the ppsclock line to be configured, since it could be
* assigned to another driver.
*/
#endif /* PPS */
return (1);
}
/*
* atom_shutdown - shut down the clock
*/
static void
int unit;
{
struct refclockproc *pp;
#ifdef DEBUG
if (debug > 0)
#endif
if (last_atom_peer == peer)
last_atom_peer = 0;
#ifdef PPS
#endif /* PPS */
}
/*
* pps_sample - process pps sample offset -- backwards compatible interface
*/
int
{
struct refclockproc *pp;
int i;
/*
* This routine is called once per second by an auxilliary
* routine in another driver. It saves the sign-extended
* fraction supplied in the argument in a circular buffer for
* processing at the next poll event.
*/
if (!peer)
return (-1); /* no ATOM configured ? Forget it ! */
#ifdef DEBUG
if (debug > 2)
#endif
/* HACK -- use the local UN*X clock to get the time -- this is wrong */
return (0);
}
#ifdef PPS
/*
* atom_pps - receive data from the LDISC_PPS discipline
*/
static void
{
struct refclockproc *pp;
int i;
/*
* This routine is called once per second when the LDISC_PPS
* discipline 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.
*/
#ifdef DEBUG
if (debug > 3)
printf("atom_pps: pollcnt %d, fdpps = %d, serial = %ld\n",
#endif
/*
* Arm the timer for the next interrupt
*/
/*
* 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.
*/
return;
return;
}
#endif /* PPS */
#ifndef PPSX
static void atom_receive P((struct recvbuf *));
/*
* atom_receive - receive data from the serial line interface
*/
static void
{
struct refclockproc *pp;
int i;
/*
* This routine is called once per second when the serial
* interface is in use. It snatches the timestamp from the
* buffer and saves the sign-extended fraction in a circular
* buffer for processing at the next poll event.
*/
#ifdef DEBUG
if (debug > 2)
printf("atom_receive: pollcnt %d, lastrec %d\n",
#endif
/*
* Save the timestamp for billboards. Sign-extend the fraction
* and stash in the buffer. No harm is done if previous data are
* overwritten.
*/
}
#endif /* PPSX */
/*
* Compare two l_fp's - used with qsort()
*/
static int
{
return (-1);
return (0);
return (1);
}
/*
* atom_poll - called by the transmit procedure
*/
static void
int unit;
{
struct refclockproc *pp;
int i, n;
#ifdef DEBUG
if (debug > 2)
#endif
/*
* At each poll we check for timeout. At the first timeout we
* test to see if the LDISC_PPS discipline is present and, if
* so, use that. If not, we attempt to open a serial line with
* LDISC_CLKPPS discipline. If that fails, we bitch to the log
* and clam up.
*/
return;
}
#ifndef PPSX
int fd;
char device[20];
/*
* Open serial port. Use CLKPPS line discipline,
* if available. If unavailable, the code works
* anyway, but at reduced accuracy.
*/
#ifdef PPS
#else
#endif
))) {
return;
}
return;
}
}
}
#endif /* PPSX */
/*
* Valid time (leap bits zero) is returned only if the prefer
* peer has survived the intersection algorithm and within
* CLOCK_MAX 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.
*/
if (pps_update) {
} else
/*
* Copy the raw offsets and sort into ascending order
*/
for (i = 0; i < MAXSTAGE; i++)
/*
* Reject the furthest from the median of nstages samples until
* nskeep samples remain.
*/
i = 0;
while ((n - i) > NSAMPLES) {
/* reject low end */
i++;
} else {
/* reject high end */
n--;
}
}
/*
* Compute the dispersion based on the difference between the
* extremes of the remaining offsets. Add to this the time since
* the last clock update, which represents the dispersion
* increase with time. We know that NTP_MAXSKEW is 16. If the
* sum is greater than the allowed sample dispersion, bail out.
* Otherwise, return the median offset plus the configured
* fudgetime1 value.
*/
if (disp > PPSMAXDISPERSE) {
}
}
#else /* not (ATOM && REFCLOCK) */
int refclock_atom_bs;
#endif /* not (ATOM && REFCLOCK) */