2 * refclock_atom - clock driver for 1-pps signals
13 #include "ntp_unixtime.h"
14 #include "ntp_refclock.h"
15 #include "ntp_stdlib.h"
17 #if defined(REFCLOCK) && defined(CLOCK_ATOM)
20 # ifdef HAVE_TIMEPPS_H
23 # ifdef HAVE_SYS_TIMEPPS_H
24 # include <sys/timepps.h>
27 #endif /* HAVE_PPSAPI */
30 * This driver furnishes an interface for pulse-per-second (PPS) signals
31 * produced by a cesium clock, timing receiver or related equipment. It
32 * can be used to remove accumulated jitter and retime a secondary
33 * server when synchronized to a primary server over a congested, wide-
34 * area network and before redistributing the time to local clients.
36 * Before this driver becomes active, the local clock must be set to
37 * within +-500 ms by another means, such as a radio clock or NTP
38 * itself. There are two ways to connect the PPS signal, normally at TTL
39 * levels, to the computer. One is to shift to EIA levels and connect to
40 * pin 8 (DCD) of a serial port. This requires a level converter and
41 * may require a one-shot flipflop to lengthen the pulse. The other is
42 * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
43 * port. These methods are architecture dependent.
45 * Both methods require a modified device driver and kernel interface
46 * compatible with the Pulse-per-Second API for Unix-like Operating
47 * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
48 * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
49 * present only the Alpha implementation provides the full generality of
50 * the API with multiple PPS drivers and multiple handles per driver.
52 * In many configurations a single port is used for the radio timecode
53 * and PPS signal. In order to provide for this configuration and others
54 * involving dedicated multiple serial/parallel ports, the driver first
55 * attempts to open the device /dev/pps%d, where %d is the unit number.
56 * If this fails, the driver attempts to open the device specified by
57 * the pps configuration command. If a port is to be shared, the pps
58 * command must be placed before the radio device(s) and the radio
59 * device(s) must be placed before the PPS driver(s) in the
62 * This driver normally uses the PLL/FLL clock discipline implemented in
63 * the ntpd code. If kernel support is available, the kernel PLL/FLL
64 * clock discipline is used instead. The default configuration is not to
65 * use the kernel PPS discipline, if present. The kernel PPS discipline
66 * can be enabled using the pps command.
70 * There are no special fudge factors other than the generic. The fudge
71 * time1 parameter can be used to compensate for miscellaneous device
72 * driver and OS delays.
75 * Interface definitions
78 extern int pps_assert; /* selects rising or falling edge */
79 extern int pps_hardpps; /* enables the kernel PPS interface */
80 #define DEVICE "/dev/pps%d" /* device name and unit */
81 #endif /* HAVE_PPSAPI */
83 #define PRECISION (-20) /* precision assumed (about 1 us) */
84 #define REFID "PPS\0" /* reference ID */
85 #define DESCRIPTION "PPS Clock Discipline" /* WRU */
86 #define NANOSECOND 1000000000 /* one second (ns) */
87 #define RANGEGATE 500000 /* range gate (ns) */
88 #define ASTAGE 8 /* filter stages */
90 static struct peer *pps_peer; /* atom driver for PPS sources */
94 * PPS unit control structure
97 struct timespec ts; /* last timestamp */
98 int fddev; /* pps device descriptor */
99 pps_params_t pps_params; /* pps parameters */
100 pps_info_t pps_info; /* last pps data */
101 pps_handle_t handle; /* pps handlebars */
103 #endif /* HAVE_PPSAPI */
106 * Function prototypes
108 static int atom_start P((int, struct peer *));
109 static void atom_poll P((int, struct peer *));
111 static void atom_shutdown P((int, struct peer *));
112 static void atom_control P((int, struct refclockstat *, struct
113 refclockstat *, struct peer *));
114 static int atom_pps P((struct peer *));
115 static int atom_ppsapi P((struct peer *, int, int));
116 #endif /* HAVE_PPSAPI */
121 struct refclock refclock_atom = {
122 atom_start, /* start up driver */
124 atom_shutdown, /* shut down driver */
126 noentry, /* shut down driver */
127 #endif /* HAVE_PPSAPI */
128 atom_poll, /* transmit poll message */
130 atom_control, /* fudge control */
132 noentry, /* fudge control */
133 #endif /* HAVE_PPSAPI */
134 noentry, /* initialize driver */
135 noentry, /* not used (old atom_buginfo) */
136 NOFLAGS /* not used */
141 * atom_start - initialize data for processing
145 int unit, /* unit number (not used) */
146 struct peer *peer /* peer structure pointer */
149 struct refclockproc *pp;
151 register struct ppsunit *up;
153 #endif /* HAVE_PPSAPI */
156 * Allocate and initialize unit structure
160 peer->precision = PRECISION;
161 pp->clockdesc = DESCRIPTION;
162 memcpy((char *)&pp->refid, REFID, 4);
163 peer->burst = ASTAGE;
164 peer->stratum = STRATUM_UNSPEC;
166 up = emalloc(sizeof(struct ppsunit));
167 memset(up, 0, sizeof(struct ppsunit));
168 pp->unitptr = (caddr_t)up;
171 * Open PPS device. If this fails and some driver has already
172 * opened the associated radio device, fdpps has the file
175 sprintf(device, DEVICE, unit);
176 up->fddev = open(device, O_RDWR, 0777);
177 if (up->fddev <= 0 && fdpps > 0) {
178 strcpy(device, pps_device);
181 if (up->fddev <= 0) {
183 "refclock_atom: %s: %m", device);
188 * Light off the PPSAPI interface. If this PPS device is shared
189 * with the radio device, take the default options from the pps
190 * command. This is for legacy purposes.
192 if (time_pps_create(up->fddev, &up->handle) < 0) {
194 "refclock_atom: time_pps_create failed: %m");
197 return (atom_ppsapi(peer, pps_assert, pps_hardpps));
198 #else /* HAVE_PPSAPI */
200 #endif /* HAVE_PPSAPI */
206 * atom_control - fudge control
210 int unit, /* unit (not used */
211 struct refclockstat *in, /* input parameters (not uded) */
212 struct refclockstat *out, /* output parameters (not used) */
213 struct peer *peer /* peer structure pointer */
216 struct refclockproc *pp;
219 atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
220 pp->sloppyclockflag & CLK_FLAG3);
229 struct peer *peer, /* peer structure pointer */
230 int enb_clear, /* clear enable */
231 int enb_hardpps /* hardpps enable */
234 struct refclockproc *pp;
235 register struct ppsunit *up;
239 up = (struct ppsunit *)pp->unitptr;
240 if (time_pps_getcap(up->handle, &capability) < 0) {
242 "refclock_atom: time_pps_getcap failed: %m");
245 memset(&up->pps_params, 0, sizeof(pps_params_t));
247 up->pps_params.mode = capability & PPS_CAPTURECLEAR;
249 up->pps_params.mode = capability & PPS_CAPTUREASSERT;
250 if (!up->pps_params.mode) {
252 "refclock_atom: invalid capture edge %d",
256 up->pps_params.mode |= PPS_TSFMT_TSPEC;
257 if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
259 "refclock_atom: time_pps_setparams failed: %m");
263 if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
264 up->pps_params.mode & ~PPS_TSFMT_TSPEC,
265 PPS_TSFMT_TSPEC) < 0) {
267 "refclock_atom: time_pps_kcbind failed: %m");
274 time_pps_getparams(up->handle, &up->pps_params);
276 "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
277 up->fddev, capability, up->pps_params.api_version,
278 up->pps_params.mode, enb_hardpps);
286 * atom_shutdown - shut down the clock
290 int unit, /* unit number (not used) */
291 struct peer *peer /* peer structure pointer */
294 struct refclockproc *pp;
295 register struct ppsunit *up;
298 up = (struct ppsunit *)pp->unitptr;
302 time_pps_destroy(up->handle);
303 if (pps_peer == peer)
310 * atom_pps - receive data from the PPSAPI interface
312 * This routine is called once per second when the PPSAPI interface is
313 * present. It snatches the PPS timestamp from the kernel and saves the
314 * sign-extended fraction in a circular buffer for processing at the
319 struct peer *peer /* peer structure pointer */
322 register struct ppsunit *up;
323 struct refclockproc *pp;
325 struct timespec timeout, ts;
329 * Convert the timespec nanoseconds field to signed double and
330 * save in the median filter. for billboards. No harm is done if
331 * previous data are overwritten. If the discipline comes bum or
332 * the data grow stale, just forget it. A range gate rejects new
333 * samples if less than a jiggle time from the next second.
336 up = (struct ppsunit *)pp->unitptr;
341 memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
342 if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
345 if (up->pps_params.mode & PPS_CAPTUREASSERT) {
346 if (pps_info.assert_sequence ==
347 up->pps_info.assert_sequence)
349 ts = up->pps_info.assert_timestamp;
350 } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
351 if (pps_info.clear_sequence ==
352 up->pps_info.clear_sequence)
354 ts = up->pps_info.clear_timestamp;
358 if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
359 up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
360 (ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
361 up->ts.tv_nsec < RANGEGATE))) {
366 pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
367 dtemp = ts.tv_nsec * FRAC / 1e9;
370 pp->lastrec.l_uf = (u_int32)dtemp;
371 if (ts.tv_nsec > NANOSECOND / 2)
372 ts.tv_nsec -= NANOSECOND;
373 dtemp = -(double)ts.tv_nsec / NANOSECOND;
374 SAMPLE(dtemp + pp->fudgetime1);
377 printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
381 #endif /* HAVE_PPSAPI */
385 * pps_sample - receive PPS data from some other clock driver
387 * This routine is called once per second when the external clock driver
388 * processes PPS information. It processes the PPS timestamp and saves
389 * the sign-extended fraction in a circular buffer for processing at the
390 * next poll event. This works only for a single PPS device.
394 l_fp *offset /* PPS offset */
397 register struct peer *peer;
398 struct refclockproc *pp;
403 if (peer == 0) /* nobody home */
408 * Convert the timeval to l_fp and save for billboards. Sign-
409 * extend the fraction and stash in the buffer. No harm is done
410 * if previous data are overwritten. If the discipline comes bum
411 * or the data grow stale, just forget it.
413 pp->lastrec = *offset;
415 L_ADDF(&lftmp, pp->lastrec.l_f);
416 LFPTOD(&lftmp, doffset);
417 SAMPLE(-doffset + pp->fudgetime1);
422 * atom_poll - called by the transmit procedure
424 * This routine is called once per second when in burst mode to save PPS
425 * sample offsets in the median filter. At the end of the burst period
426 * the samples are processed as a heap and the clock filter updated.
430 int unit, /* unit number (not used) */
431 struct peer *peer /* peer structure pointer */
434 struct refclockproc *pp;
437 #endif /* HAVE_PPSAPI */
440 * Accumulate samples in the median filter. If a noise sample,
441 * return with no prejudice; if a protocol error, get mean;
442 * otherwise, cool. At the end of each poll interval, do a
443 * little bookeeping and process the surviving samples.
448 err = atom_pps(peer);
450 refclock_report(peer, CEVNT_FAULT);
453 #endif /* HAVE_PPSAPI */
456 * Valid time is returned only if the prefer peer has survived
457 * the intersection algorithm and within clock_max of local time
458 * and not too long ago. This ensures the PPS time is within
459 * +-0.5 s of the local time and the seconds numbering is
460 * unambiguous. Note that the leap bits are set no-warning on
461 * the first valid update and the stratum is set at the prefer
466 peer->stratum = STRATUM_UNSPEC;
467 if (pp->codeproc == pp->coderecv) {
468 refclock_report(peer, CEVNT_TIMEOUT);
469 peer->burst = ASTAGE;
472 } else if (!sys_prefer) {
473 pp->codeproc = pp->coderecv;
474 peer->burst = ASTAGE;
477 } else if (fabs(sys_prefer->offset) > clock_max) {
478 pp->codeproc = pp->coderecv;
479 peer->burst = ASTAGE;
482 peer->stratum = sys_prefer->stratum;
483 if (peer->stratum <= 1)
484 peer->refid = pp->refid;
486 peer->refid = peer->srcadr.sin_addr.s_addr;
487 pp->leap = LEAP_NOWARNING;
488 refclock_receive(peer);
489 peer->burst = ASTAGE;
492 int refclock_atom_bs;
495 l_fp *offset /* PPS offset */
500 #endif /* REFCLOCK */