1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
/*
* 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"
/*
* 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 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 +-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
* may require a one-shot flipflop to lengthen the pulse. The other is
* to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
* port. These methods are architecture dependent.
*
* 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 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.
*
* This driver normally uses the PLL/FLL clock discipline implemented in
* 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.
*
* 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
*
* 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
*/
#define DEVICE "/dev/pps%d" /* device name and unit */
#define PRECISION (-20) /* precision assumed (about 1 us) */
#define REFID "PPS\0" /* reference ID */
#define DESCRIPTION "PPS Clock Discipline" /* WRU */
/*
* PPS unit control structure
*/
struct ppsunit {
struct refclock_atom atom; /* atom structure pointer */
int fddev; /* file descriptor */
};
/*
* Function prototypes
*/
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
*/
struct refclock refclock_atom = {
atom_start, /* start up driver */
atom_shutdown, /* shut down driver */
atom_poll, /* transmit poll message */
noentry, /* control (not used) */
noentry, /* initialize driver (not used) */
noentry, /* buginfo (not used) */
atom_timer, /* called once per second */
};
/*
* atom_start - initialize data for processing
*/
static int
atom_start(
int unit, /* unit number (not used) */
struct peer *peer /* peer structure pointer */
)
{
struct refclockproc *pp;
struct ppsunit *up;
char device[80];
/*
* Allocate and initialize unit structure
*/
pp = peer->procptr;
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
pp->stratum = STRATUM_UNSPEC;
memcpy((char *)&pp->refid, REFID, 4);
up = emalloc(sizeof(struct ppsunit));
memset(up, 0, sizeof(struct ppsunit));
pp->unitptr = (caddr_t)up;
/*
* Open PPS device. This can be any serial or parallel port and
* not necessarily the port used for the associated radio.
*/
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 up the PPSAPI interface.
*/
return (refclock_ppsapi(up->fddev, &up->atom));
}
/*
* 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;
struct ppsunit *up;
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (up->fddev > 0)
close(up->fddev);
free(up);
}
/*
* atom_timer - called once per second
*/
void
atom_timer(
int unit, /* unit pointer (not used) */
struct peer *peer /* peer structure pointer */
)
{
struct ppsunit *up;
struct refclockproc *pp;
char tbuf[80];
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
return;
peer->flags |= FLAG_PPS;
/*
* If flag4 is lit, record each second offset to clockstats.
* That's so we can make awesome Allan deviation plots.
*/
if (pp->sloppyclockflag & CLK_FLAG4) {
snprintf(tbuf, sizeof(tbuf), "%.9f",
pp->filter[pp->coderecv]);
record_clock_stats(&peer->srcadr, tbuf);
}
}
/*
* atom_poll - called by the transmit procedure
*/
static void
atom_poll(
int unit, /* unit number (not used) */
struct peer *peer /* peer structure pointer */
)
{
struct refclockproc *pp;
/*
* Don't wiggle the clock until some other driver has numbered
* the seconds.
*/
if (sys_leap == LEAP_NOTINSYNC)
return;
pp = peer->procptr;
pp->polls++;
if (pp->codeproc == pp->coderecv) {
peer->flags &= ~FLAG_PPS;
refclock_report(peer, CEVNT_TIMEOUT);
return;
}
pp->lastref = pp->lastrec;
refclock_receive(peer);
}
#else
int refclock_atom_bs;
#endif /* REFCLOCK */
|