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.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_atom.c b/contrib/ntp/ntpd/refclock_atom.c
new file mode 100644
index 0000000..63e17c1
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_atom.c
@@ -0,0 +1,488 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ATOM)
+
+#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 HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+#ifdef HAVE_PPSAPI
+#include <sys/timepps.h>
+#endif /* HAVE_PPSAPI */
+
+/*
+ * 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 flopflop and RS232 level converter.
+ * Conntection is either via the carrier detect (DCD) lead or via the
+ * receive data (RD) lead. The incidental jitter using the DCD lead is
+ * essentially the interrupt latency. The incidental jitter using the RD
+ * lead has an additional component due to the line sampling clock. When
+ * operated at 38.4 kbps, this arrangement has a worst-case jitter less
+ * than 26 us.
+ *
+ * There are four ways in which this driver can be used. They are
+ * described in decreasing order of merit below. The first way uses the
+ * ppsapi STREAMS module and the LDISC_PPS line discipline, while the
+ * second way uses the ppsclock STREAMS module and the LDISC_PPS line
+ * discipline. Either of these works only for the baseboard serial ports
+ * of the Sun SPARC IPC and clones. However, the ppsapi uses the
+ * proposed IETF interface expected to become standard for PPS signals.
+ * The serial port to be used is specified by the pps command in the
+ * configuration file. This driver reads the timestamp directly by a
+ * designated ioctl() system call.
+ *
+ * The third 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
+ * a serial port device /dev/pps%d, where %d is the unit number, and
+ * 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 fourth 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
+ * pps_sample(), and others call knowing only that they are running
+ * shortly after an on-time tick and they expect 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. 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 STREAMS nonsense with older workstations. Velocities may
+ * vary with modern workstations.
+ */
+/*
+ * Interface definitions
+ */
+#ifdef HAVE_PPSAPI
+extern int pps_assert;
+#endif /* HAVE_PPSAPI */
+#ifdef TTYCLK
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#ifdef B38400
+#define SPEED232 B38400 /* uart speed (38400 baud) */
+#else
+#define SPEED232 EXTB /* as above */
+#endif
+#endif /* TTYCLK */
+
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+
+#define FLAG_TTY 0x01 /* tty_clk heard from */
+#define FLAG_PPS 0x02 /* ppsclock heard from */
+#define FLAG_AUX 0x04 /* auxiliary PPS source */
+
+static struct peer *pps_peer; /* atom driver for auxiliary PPS sources */
+
+#ifdef TTYCLK
+static void atom_receive P((struct recvbuf *));
+#endif /* TTYCLK */
+
+/*
+ * Unit control structure
+ */
+struct atomunit {
+#ifdef HAVE_PPSAPI
+ pps_info_t pps_info; /* pps_info control */
+#endif /* HAVE_PPSAPI */
+#ifdef PPS
+ struct ppsclockev ev; /* ppsclock control */
+#endif /* PPS */
+ int flags; /* flags that wave */
+};
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_shutdown P((int, struct peer *));
+static void atom_poll P((int, struct peer *));
+#if defined(PPS) || defined(HAVE_PPSAPI)
+static int atom_pps P((struct peer *));
+#endif /* PPS || HAVE_PPSAPI */
+
+/*
+ * 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
+atom_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ int flags;
+#ifdef TTYCLK
+ int fd = 0;
+ char device[20];
+ int ldisc = LDISC_CLKPPS;
+#endif /* TTYCLK */
+
+ pps_peer = peer;
+ flags = 0;
+
+#ifdef TTYCLK
+# if defined(SCO5_CLOCK)
+ ldisc = LDISC_RAW; /* DCD timestamps without any line discipline */
+# endif
+ /*
+ * Open serial port. Use LDISC_CLKPPS line discipline only
+ * if the LDISC_PPS line discipline is not availble,
+ */
+# if defined(PPS) || defined(HAVE_PPSAPI)
+ if (fdpps <= 0)
+# endif
+ {
+ (void)sprintf(device, DEVICE, unit);
+ if ((fd = refclock_open(device, SPEED232, ldisc)) != 0)
+ flags |= FLAG_TTY;
+ }
+#endif /* TTYCLK */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct atomunit *)emalloc(sizeof(struct atomunit)))) {
+#ifdef TTYCLK
+ if (flags & FLAG_TTY)
+ (void) close(fd);
+#endif /* TTYCLK */
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct atomunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+#ifdef TTYCLK
+ if (flags & FLAG_TTY) {
+ pp->io.clock_recv = atom_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ }
+#endif /* TTYCLK */
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->flags = flags;
+ return (1);
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+#ifdef TTYCLK
+ if (up->flags & FLAG_TTY)
+ io_closeclock(&pp->io);
+#endif /* TTYCLK */
+ if (pps_peer == peer)
+ pps_peer = 0;
+ free(up);
+}
+
+
+#if defined(PPS) || defined(HAVE_PPSAPI)
+/*
+ * atom_pps - receive data from the LDISC_PPS discipline
+ */
+static int
+atom_pps(
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+#ifdef HAVE_PPSAPI
+ struct timespec timeout;
+# ifdef HAVE_TIMESPEC
+ struct timespec ts;
+# else
+ struct timeval ts;
+# endif /* HAVE_TIMESPEC */
+#endif /* HAVE_PPSAPI */
+ l_fp lftmp;
+ double doffset;
+ int i;
+#if !defined(HAVE_PPSAPI)
+ int request =
+# ifdef HAVE_CIOGETEV
+ CIOGETEV
+# endif
+# ifdef HAVE_TIOCGPPSEV
+ TIOCGPPSEV
+# endif
+ ;
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * 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.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * 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. Round the nanoseconds
+ * to microseconds with great care.
+ */
+ if (fdpps <= 0)
+ return (1);
+#ifdef HAVE_PPSAPI
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ i = up->pps_info.assert_sequence;
+ if (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &up->pps_info, &timeout)
+ < 0)
+ return (2);
+ if (i == up->pps_info.assert_sequence)
+ return (3);
+ if (pps_assert)
+ ts = up->pps_info.assert_timestamp;
+ else
+ ts = up->pps_info.clear_timestamp;
+ pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
+ ts.tv_nsec = (ts.tv_nsec + 500) / 1000;
+ if (ts.tv_nsec > 1000000) {
+ ts.tv_nsec -= 1000000;
+ ts.tv_sec++;
+ }
+ TVUTOTSF(ts.tv_nsec, pp->lastrec.l_uf);
+#else
+ i = up->ev.serial;
+ if (ioctl(fdpps, request, (caddr_t)&up->ev) < 0)
+ return (2);
+ if (i == up->ev.serial)
+ return (3);
+ pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970;
+ TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf);
+#endif /* HAVE_PPSAPI */
+ up->flags |= FLAG_PPS;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+ return (0);
+}
+#endif /* PPS || HAVE_PPSAPI */
+
+#ifdef TTYCLK
+/*
+ * atom_receive - receive data from the LDISC_CLK discipline
+ */
+static void
+atom_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp lftmp;
+ double doffset;
+
+ /*
+ * 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.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * Save the timestamp for billboards. Sign-extend the fraction
+ * and stash in the buffer. No harm is done if previous data are
+ * overwritten. Do this only if the ppsclock gizmo is not
+ * working.
+ */
+ if (up->flags & FLAG_PPS)
+ return;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+}
+#endif /* TTYCLK */
+
+/*
+ * pps_sample - receive PPS data from some other clock driver
+ */
+int
+pps_sample(
+ l_fp *offset
+ )
+{
+ register struct peer *peer;
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ double doffset;
+
+ /*
+ * 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.
+ */
+ peer = pps_peer;
+ if (peer == 0) /* nobody home */
+ return 1;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * 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.
+ */
+ up->flags |= FLAG_AUX;
+ pp->lastrec = *offset;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+ return (0);
+}
+
+/*
+ * atom_poll - called by the transmit procedure
+ */
+static void
+atom_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+#if defined(PPS) || defined(HAVE_PPSAPI)
+ register struct atomunit *up;
+#endif /* PPS || HAVE_PPSAPI */
+ struct refclockproc *pp;
+
+ /*
+ * Accumulate samples in the median filter. At the end of each
+ * poll interval, do a little bookeeping and process the
+ * samples.
+ */
+ pp = peer->procptr;
+#if defined(PPS) || defined(HAVE_PPSAPI)
+ up = (struct atomunit *)pp->unitptr;
+ if (!(up->flags & !(FLAG_AUX | FLAG_TTY))) {
+ int err;
+
+ err = atom_pps(peer);
+ if (err > 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+#endif /* PPS || HAVE_PPSAPI */
+ pp->polls++;
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+
+ /*
+ * 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) {
+ pp->leap = LEAP_NOWARNING;
+ } else {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ pp->variance = 0;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = MAXSTAGE;
+}
+
+#else
+int refclock_atom_bs;
+#endif /* REFCLOCK */
OpenPOWER on IntegriCloud