1*eabc0478Schristos /* $NetBSD: refclock_atom.c,v 1.6 2024/08/18 20:47:18 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * refclock_atom - clock driver for 1-pps signals 5abb0f93cSkardel */ 6abb0f93cSkardel #ifdef HAVE_CONFIG_H 7abb0f93cSkardel #include <config.h> 8abb0f93cSkardel #endif 9abb0f93cSkardel 10abb0f93cSkardel #include <stdio.h> 11abb0f93cSkardel #include <ctype.h> 12abb0f93cSkardel 13abb0f93cSkardel #include "ntpd.h" 14abb0f93cSkardel #include "ntp_io.h" 15abb0f93cSkardel #include "ntp_unixtime.h" 16abb0f93cSkardel #include "ntp_refclock.h" 17abb0f93cSkardel #include "ntp_stdlib.h" 18abb0f93cSkardel 19abb0f93cSkardel /* 20abb0f93cSkardel * This driver requires the PPSAPI interface (RFC 2783) 21abb0f93cSkardel */ 22abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI) 23abb0f93cSkardel #include "ppsapi_timepps.h" 24abb0f93cSkardel #include "refclock_atom.h" 25abb0f93cSkardel 26abb0f93cSkardel /* 27abb0f93cSkardel * This driver furnishes an interface for pulse-per-second (PPS) signals 28abb0f93cSkardel * produced by a cesium clock, timing receiver or related equipment. It 29abb0f93cSkardel * can be used to remove accumulated jitter over a congested link and 30abb0f93cSkardel * retime a server before redistributing the time to clients. It can 31abb0f93cSkardel *also be used as a holdover should all other synchronization sources 32abb0f93cSkardel * beconme unreachable. 33abb0f93cSkardel * 34abb0f93cSkardel * Before this driver becomes active, the local clock must be set to 35abb0f93cSkardel * within +-0.4 s by another means, such as a radio clock or NTP 36abb0f93cSkardel * itself. There are two ways to connect the PPS signal, normally at TTL 37abb0f93cSkardel * levels, to the computer. One is to shift to EIA levels and connect to 38abb0f93cSkardel * pin 8 (DCD) of a serial port. This requires a level converter and 39abb0f93cSkardel * may require a one-shot flipflop to lengthen the pulse. The other is 40abb0f93cSkardel * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell 41abb0f93cSkardel * port. These methods are architecture dependent. 42abb0f93cSkardel * 43abb0f93cSkardel * This driver requires the Pulse-per-Second API for Unix-like Operating 44abb0f93cSkardel * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are 45abb0f93cSkardel * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at 46abb0f93cSkardel * present only the Tru64 implementation provides the full generality of 47abb0f93cSkardel * the API with multiple PPS drivers and multiple handles per driver. If 48abb0f93cSkardel * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h 49abb0f93cSkardel * header file and kernel support specific to each operating system. 50abb0f93cSkardel * 51abb0f93cSkardel * This driver normally uses the PLL/FLL clock discipline implemented in 52abb0f93cSkardel * the ntpd code. Ordinarily, this is the most accurate means, as the 53abb0f93cSkardel * median filter in the driver interface is much larger than in the 54abb0f93cSkardel * kernel. However, if the systemic clock frequency error is large (tens 55abb0f93cSkardel * to hundreds of PPM), it's better to used the kernel support, if 56abb0f93cSkardel * available. 57abb0f93cSkardel * 58abb0f93cSkardel * This deriver is subject to the mitigation rules described in the 59abb0f93cSkardel * "mitigation rulse and the prefer peer" page. However, there is an 60abb0f93cSkardel * important difference. If this driver becomes the PPS driver according 61abb0f93cSkardel * to these rules, it is acrive only if (a) a prefer peer other than 62abb0f93cSkardel * this driver is among the survivors or (b) there are no survivors and 63abb0f93cSkardel * the minsane option of the tos command is zero. This is intended to 64abb0f93cSkardel * support space missions where updates from other spacecraft are 65abb0f93cSkardel * infrequent, but a reliable PPS signal, such as from an Ultra Stable 66abb0f93cSkardel * Oscillator (USO) is available. 67abb0f93cSkardel * 68abb0f93cSkardel * Fudge Factors 69abb0f93cSkardel * 70abb0f93cSkardel * The PPS timestamp is captured on the rising (assert) edge if flag2 is 71abb0f93cSkardel * dim (default) and on the falling (clear) edge if lit. If flag3 is dim 72abb0f93cSkardel * (default), the kernel PPS support is disabled; if lit it is enabled. 73abb0f93cSkardel * If flag4 is lit, each timesampt is copied to the clockstats file for 74abb0f93cSkardel * later analysis. This can be useful when constructing Allan deviation 75abb0f93cSkardel * plots. The time1 parameter can be used to compensate for 76abb0f93cSkardel * miscellaneous device driver and OS delays. 77abb0f93cSkardel */ 78abb0f93cSkardel /* 79abb0f93cSkardel * Interface definitions 80abb0f93cSkardel */ 81abb0f93cSkardel #define DEVICE "/dev/pps%d" /* device name and unit */ 82abb0f93cSkardel #define PRECISION (-20) /* precision assumed (about 1 us) */ 83abb0f93cSkardel #define REFID "PPS\0" /* reference ID */ 84abb0f93cSkardel #define DESCRIPTION "PPS Clock Discipline" /* WRU */ 85abb0f93cSkardel 86abb0f93cSkardel /* 87abb0f93cSkardel * PPS unit control structure 88abb0f93cSkardel */ 89abb0f93cSkardel struct ppsunit { 90abb0f93cSkardel struct refclock_atom atom; /* atom structure pointer */ 91abb0f93cSkardel int fddev; /* file descriptor */ 92abb0f93cSkardel }; 93abb0f93cSkardel 94abb0f93cSkardel /* 95abb0f93cSkardel * Function prototypes 96abb0f93cSkardel */ 97abb0f93cSkardel static int atom_start (int, struct peer *); 98abb0f93cSkardel static void atom_shutdown (int, struct peer *); 99abb0f93cSkardel static void atom_poll (int, struct peer *); 100abb0f93cSkardel static void atom_timer (int, struct peer *); 101abb0f93cSkardel 102abb0f93cSkardel /* 103abb0f93cSkardel * Transfer vector 104abb0f93cSkardel */ 105abb0f93cSkardel struct refclock refclock_atom = { 106abb0f93cSkardel atom_start, /* start up driver */ 107abb0f93cSkardel atom_shutdown, /* shut down driver */ 108abb0f93cSkardel atom_poll, /* transmit poll message */ 109abb0f93cSkardel noentry, /* control (not used) */ 110abb0f93cSkardel noentry, /* initialize driver (not used) */ 111abb0f93cSkardel noentry, /* buginfo (not used) */ 112abb0f93cSkardel atom_timer, /* called once per second */ 113abb0f93cSkardel }; 114abb0f93cSkardel 115abb0f93cSkardel 116abb0f93cSkardel /* 117abb0f93cSkardel * atom_start - initialize data for processing 118abb0f93cSkardel */ 119abb0f93cSkardel static int 120abb0f93cSkardel atom_start( 121abb0f93cSkardel int unit, /* unit number (not used) */ 122abb0f93cSkardel struct peer *peer /* peer structure pointer */ 123abb0f93cSkardel ) 124abb0f93cSkardel { 125abb0f93cSkardel struct refclockproc *pp; 126abb0f93cSkardel struct ppsunit *up; 127abb0f93cSkardel char device[80]; 128abb0f93cSkardel 129abb0f93cSkardel /* 130abb0f93cSkardel * Allocate and initialize unit structure 131abb0f93cSkardel */ 132abb0f93cSkardel pp = peer->procptr; 133abb0f93cSkardel peer->precision = PRECISION; 134abb0f93cSkardel pp->clockdesc = DESCRIPTION; 135abb0f93cSkardel pp->stratum = STRATUM_UNSPEC; 136abb0f93cSkardel memcpy((char *)&pp->refid, REFID, 4); 137abb0f93cSkardel up = emalloc(sizeof(struct ppsunit)); 138abb0f93cSkardel memset(up, 0, sizeof(struct ppsunit)); 1398585484eSchristos pp->unitptr = up; 140abb0f93cSkardel 141abb0f93cSkardel /* 142abb0f93cSkardel * Open PPS device. This can be any serial or parallel port and 143abb0f93cSkardel * not necessarily the port used for the associated radio. 144abb0f93cSkardel */ 145f003fb54Skardel snprintf(device, sizeof(device), DEVICE, unit); 146abb0f93cSkardel up->fddev = tty_open(device, O_RDWR, 0777); 147abb0f93cSkardel if (up->fddev <= 0) { 148abb0f93cSkardel msyslog(LOG_ERR, 149abb0f93cSkardel "refclock_atom: %s: %m", device); 150abb0f93cSkardel return (0); 151abb0f93cSkardel } 152abb0f93cSkardel 153abb0f93cSkardel /* 154abb0f93cSkardel * Light up the PPSAPI interface. 155abb0f93cSkardel */ 156abb0f93cSkardel return (refclock_ppsapi(up->fddev, &up->atom)); 157abb0f93cSkardel } 158abb0f93cSkardel 159abb0f93cSkardel 160abb0f93cSkardel /* 161abb0f93cSkardel * atom_shutdown - shut down the clock 162abb0f93cSkardel */ 163abb0f93cSkardel static void 164abb0f93cSkardel atom_shutdown( 165abb0f93cSkardel int unit, /* unit number (not used) */ 166abb0f93cSkardel struct peer *peer /* peer structure pointer */ 167abb0f93cSkardel ) 168abb0f93cSkardel { 169abb0f93cSkardel struct refclockproc *pp; 170abb0f93cSkardel struct ppsunit *up; 171abb0f93cSkardel 172abb0f93cSkardel pp = peer->procptr; 1738585484eSchristos up = pp->unitptr; 174abb0f93cSkardel if (up->fddev > 0) 175abb0f93cSkardel close(up->fddev); 176abb0f93cSkardel free(up); 177abb0f93cSkardel } 178abb0f93cSkardel 179abb0f93cSkardel /* 180abb0f93cSkardel * atom_timer - called once per second 181abb0f93cSkardel */ 182abb0f93cSkardel void 183abb0f93cSkardel atom_timer( 184abb0f93cSkardel int unit, /* unit pointer (not used) */ 185abb0f93cSkardel struct peer *peer /* peer structure pointer */ 186abb0f93cSkardel ) 187abb0f93cSkardel { 188abb0f93cSkardel struct ppsunit *up; 189abb0f93cSkardel struct refclockproc *pp; 190abb0f93cSkardel char tbuf[80]; 191abb0f93cSkardel 192abb0f93cSkardel pp = peer->procptr; 1938585484eSchristos up = pp->unitptr; 194abb0f93cSkardel if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0) 195abb0f93cSkardel return; 196abb0f93cSkardel 197abb0f93cSkardel peer->flags |= FLAG_PPS; 198abb0f93cSkardel 199abb0f93cSkardel /* 200abb0f93cSkardel * If flag4 is lit, record each second offset to clockstats. 201abb0f93cSkardel * That's so we can make awesome Allan deviation plots. 202abb0f93cSkardel */ 203abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG4) { 204f003fb54Skardel snprintf(tbuf, sizeof(tbuf), "%.9f", 205f003fb54Skardel pp->filter[pp->coderecv]); 206abb0f93cSkardel record_clock_stats(&peer->srcadr, tbuf); 207abb0f93cSkardel } 208abb0f93cSkardel } 209abb0f93cSkardel 210abb0f93cSkardel 211abb0f93cSkardel /* 212abb0f93cSkardel * atom_poll - called by the transmit procedure 213abb0f93cSkardel */ 214abb0f93cSkardel static void 215abb0f93cSkardel atom_poll( 216abb0f93cSkardel int unit, /* unit number (not used) */ 217abb0f93cSkardel struct peer *peer /* peer structure pointer */ 218abb0f93cSkardel ) 219abb0f93cSkardel { 220abb0f93cSkardel struct refclockproc *pp; 221abb0f93cSkardel 222abb0f93cSkardel /* 223abb0f93cSkardel * Don't wiggle the clock until some other driver has numbered 224abb0f93cSkardel * the seconds. 225abb0f93cSkardel */ 226abb0f93cSkardel if (sys_leap == LEAP_NOTINSYNC) 227abb0f93cSkardel return; 228abb0f93cSkardel 229abb0f93cSkardel pp = peer->procptr; 230abb0f93cSkardel pp->polls++; 231abb0f93cSkardel if (pp->codeproc == pp->coderecv) { 232abb0f93cSkardel peer->flags &= ~FLAG_PPS; 233abb0f93cSkardel refclock_report(peer, CEVNT_TIMEOUT); 234abb0f93cSkardel return; 235abb0f93cSkardel } 236abb0f93cSkardel pp->lastref = pp->lastrec; 237abb0f93cSkardel refclock_receive(peer); 238abb0f93cSkardel } 239abb0f93cSkardel #else 240*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 241abb0f93cSkardel #endif /* REFCLOCK */ 242