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