154359Sroberto/* 254359Sroberto * refclock_atom - clock driver for 1-pps signals 354359Sroberto */ 454359Sroberto#ifdef HAVE_CONFIG_H 554359Sroberto#include <config.h> 654359Sroberto#endif 754359Sroberto 854359Sroberto#include <stdio.h> 954359Sroberto#include <ctype.h> 1054359Sroberto 1154359Sroberto#include "ntpd.h" 1254359Sroberto#include "ntp_io.h" 1354359Sroberto#include "ntp_unixtime.h" 1454359Sroberto#include "ntp_refclock.h" 1554359Sroberto#include "ntp_stdlib.h" 1654359Sroberto 17280849Scy/* 18280849Scy * This driver requires the PPSAPI interface (RFC 2783) 19280849Scy */ 20280849Scy#if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI) 21280849Scy#include "ppsapi_timepps.h" 22280849Scy#include "refclock_atom.h" 2382498Sroberto 2454359Sroberto/* 2554359Sroberto * This driver furnishes an interface for pulse-per-second (PPS) signals 2682498Sroberto * produced by a cesium clock, timing receiver or related equipment. It 27280849Scy * can be used to remove accumulated jitter over a congested link and 28280849Scy * retime a server before redistributing the time to clients. It can 29280849Scy *also be used as a holdover should all other synchronization sources 30280849Scy * beconme unreachable. 3154359Sroberto * 3282498Sroberto * Before this driver becomes active, the local clock must be set to 33280849Scy * within +-0.4 s by another means, such as a radio clock or NTP 3482498Sroberto * itself. There are two ways to connect the PPS signal, normally at TTL 3582498Sroberto * levels, to the computer. One is to shift to EIA levels and connect to 3682498Sroberto * pin 8 (DCD) of a serial port. This requires a level converter and 3782498Sroberto * may require a one-shot flipflop to lengthen the pulse. The other is 3882498Sroberto * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell 3982498Sroberto * port. These methods are architecture dependent. 4054359Sroberto * 41280849Scy * This driver requires the Pulse-per-Second API for Unix-like Operating 4282498Sroberto * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are 43280849Scy * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at 44280849Scy * present only the Tru64 implementation provides the full generality of 45182007Sroberto * the API with multiple PPS drivers and multiple handles per driver. If 46182007Sroberto * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h 47182007Sroberto * header file and kernel support specific to each operating system. 4854359Sroberto * 4982498Sroberto * This driver normally uses the PLL/FLL clock discipline implemented in 50182007Sroberto * the ntpd code. Ordinarily, this is the most accurate means, as the 51182007Sroberto * median filter in the driver interface is much larger than in the 52182007Sroberto * kernel. However, if the systemic clock frequency error is large (tens 53182007Sroberto * to hundreds of PPM), it's better to used the kernel support, if 54182007Sroberto * available. 5554359Sroberto * 56280849Scy * This deriver is subject to the mitigation rules described in the 57280849Scy * "mitigation rulse and the prefer peer" page. However, there is an 58280849Scy * important difference. If this driver becomes the PPS driver according 59280849Scy * to these rules, it is acrive only if (a) a prefer peer other than 60280849Scy * this driver is among the survivors or (b) there are no survivors and 61280849Scy * the minsane option of the tos command is zero. This is intended to 62280849Scy * support space missions where updates from other spacecraft are 63280849Scy * infrequent, but a reliable PPS signal, such as from an Ultra Stable 64280849Scy * Oscillator (USO) is available. 65280849Scy * 6654359Sroberto * Fudge Factors 6754359Sroberto * 68280849Scy * The PPS timestamp is captured on the rising (assert) edge if flag2 is 69280849Scy * dim (default) and on the falling (clear) edge if lit. If flag3 is dim 70280849Scy * (default), the kernel PPS support is disabled; if lit it is enabled. 71280849Scy * If flag4 is lit, each timesampt is copied to the clockstats file for 72280849Scy * later analysis. This can be useful when constructing Allan deviation 73280849Scy * plots. The time1 parameter can be used to compensate for 74280849Scy * miscellaneous device driver and OS delays. 7554359Sroberto */ 7654359Sroberto/* 7754359Sroberto * Interface definitions 7854359Sroberto */ 7982498Sroberto#define DEVICE "/dev/pps%d" /* device name and unit */ 8054359Sroberto#define PRECISION (-20) /* precision assumed (about 1 us) */ 8154359Sroberto#define REFID "PPS\0" /* reference ID */ 8254359Sroberto#define DESCRIPTION "PPS Clock Discipline" /* WRU */ 8354359Sroberto 8454359Sroberto/* 8582498Sroberto * PPS unit control structure 8654359Sroberto */ 8782498Srobertostruct ppsunit { 88280849Scy struct refclock_atom atom; /* atom structure pointer */ 89280849Scy int fddev; /* file descriptor */ 9082498Sroberto}; 9154359Sroberto 9254359Sroberto/* 9354359Sroberto * Function prototypes 9454359Sroberto */ 95280849Scystatic int atom_start (int, struct peer *); 96280849Scystatic void atom_shutdown (int, struct peer *); 97280849Scystatic void atom_poll (int, struct peer *); 98280849Scystatic void atom_timer (int, struct peer *); 9954359Sroberto 10054359Sroberto/* 10154359Sroberto * Transfer vector 10254359Sroberto */ 10354359Srobertostruct refclock refclock_atom = { 10454359Sroberto atom_start, /* start up driver */ 10554359Sroberto atom_shutdown, /* shut down driver */ 10654359Sroberto atom_poll, /* transmit poll message */ 107280849Scy noentry, /* control (not used) */ 108182007Sroberto noentry, /* initialize driver (not used) */ 109182007Sroberto noentry, /* buginfo (not used) */ 110182007Sroberto atom_timer, /* called once per second */ 111182007Sroberto}; 11254359Sroberto 11354359Sroberto 11454359Sroberto/* 11554359Sroberto * atom_start - initialize data for processing 11654359Sroberto */ 11754359Srobertostatic int 11854359Srobertoatom_start( 11982498Sroberto int unit, /* unit number (not used) */ 12082498Sroberto struct peer *peer /* peer structure pointer */ 12154359Sroberto ) 12254359Sroberto{ 12354359Sroberto struct refclockproc *pp; 124280849Scy struct ppsunit *up; 125182007Sroberto char device[80]; 12654359Sroberto 12782498Sroberto /* 12882498Sroberto * Allocate and initialize unit structure 12982498Sroberto */ 13082498Sroberto pp = peer->procptr; 13182498Sroberto peer->precision = PRECISION; 13282498Sroberto pp->clockdesc = DESCRIPTION; 133132451Sroberto pp->stratum = STRATUM_UNSPEC; 13482498Sroberto memcpy((char *)&pp->refid, REFID, 4); 13582498Sroberto up = emalloc(sizeof(struct ppsunit)); 13682498Sroberto memset(up, 0, sizeof(struct ppsunit)); 137280849Scy pp->unitptr = up; 13854359Sroberto 13954359Sroberto /* 140182007Sroberto * Open PPS device. This can be any serial or parallel port and 141182007Sroberto * not necessarily the port used for the associated radio. 14254359Sroberto */ 143280849Scy snprintf(device, sizeof(device), DEVICE, unit); 144280849Scy up->fddev = tty_open(device, O_RDWR, 0777); 14582498Sroberto if (up->fddev <= 0) { 14682498Sroberto msyslog(LOG_ERR, 147280849Scy "refclock_atom: %s: %m", device); 14882498Sroberto return (0); 14982498Sroberto } 15054359Sroberto 15154359Sroberto /* 152280849Scy * Light up the PPSAPI interface. 15354359Sroberto */ 154280849Scy return (refclock_ppsapi(up->fddev, &up->atom)); 15582498Sroberto} 15682498Sroberto 15782498Sroberto 158182007Sroberto/* 159182007Sroberto * atom_shutdown - shut down the clock 160182007Sroberto */ 161182007Srobertostatic void 162182007Srobertoatom_shutdown( 163182007Sroberto int unit, /* unit number (not used) */ 164182007Sroberto struct peer *peer /* peer structure pointer */ 165182007Sroberto ) 166182007Sroberto{ 167182007Sroberto struct refclockproc *pp; 168280849Scy struct ppsunit *up; 169182007Sroberto 170182007Sroberto pp = peer->procptr; 171280849Scy up = pp->unitptr; 172182007Sroberto if (up->fddev > 0) 173182007Sroberto close(up->fddev); 174182007Sroberto free(up); 175182007Sroberto} 176182007Sroberto 17782498Sroberto/* 178182007Sroberto * atom_timer - called once per second 17954359Sroberto */ 180280849Scyvoid 181182007Srobertoatom_timer( 182280849Scy int unit, /* unit pointer (not used) */ 18382498Sroberto struct peer *peer /* peer structure pointer */ 18454359Sroberto ) 18554359Sroberto{ 186280849Scy struct ppsunit *up; 18754359Sroberto struct refclockproc *pp; 188280849Scy char tbuf[80]; 18954359Sroberto 19054359Sroberto pp = peer->procptr; 191280849Scy up = pp->unitptr; 192280849Scy if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0) 193182007Sroberto return; 194182007Sroberto 195280849Scy peer->flags |= FLAG_PPS; 196182007Sroberto 197182007Sroberto /* 198182007Sroberto * If flag4 is lit, record each second offset to clockstats. 199182007Sroberto * That's so we can make awesome Allan deviation plots. 200182007Sroberto */ 201280849Scy if (pp->sloppyclockflag & CLK_FLAG4) { 202280849Scy snprintf(tbuf, sizeof(tbuf), "%.9f", 203280849Scy pp->filter[pp->coderecv]); 204182007Sroberto record_clock_stats(&peer->srcadr, tbuf); 205182007Sroberto } 20654359Sroberto} 20754359Sroberto 20854359Sroberto 20954359Sroberto/* 21054359Sroberto * atom_poll - called by the transmit procedure 21154359Sroberto */ 21254359Srobertostatic void 21354359Srobertoatom_poll( 21482498Sroberto int unit, /* unit number (not used) */ 21582498Sroberto struct peer *peer /* peer structure pointer */ 21654359Sroberto ) 21754359Sroberto{ 21854359Sroberto struct refclockproc *pp; 21954359Sroberto 22054359Sroberto /* 221280849Scy * Don't wiggle the clock until some other driver has numbered 222280849Scy * the seconds. 22354359Sroberto */ 224280849Scy if (sys_leap == LEAP_NOTINSYNC) 225280849Scy return; 226280849Scy 227280849Scy pp = peer->procptr; 228280849Scy pp->polls++; 22982498Sroberto if (pp->codeproc == pp->coderecv) { 230280849Scy peer->flags &= ~FLAG_PPS; 23182498Sroberto refclock_report(peer, CEVNT_TIMEOUT); 23282498Sroberto return; 23354359Sroberto } 234132451Sroberto pp->lastref = pp->lastrec; 23554359Sroberto refclock_receive(peer); 23654359Sroberto} 23754359Sroberto#else 23854359Srobertoint refclock_atom_bs; 23954359Sroberto#endif /* REFCLOCK */ 240