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