Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_atom.c
blobb036b2c6c0eb514bb9d388041613e0ef8893ebed
1 /* $NetBSD: refclock_atom.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
3 /*
4 * refclock_atom - clock driver for 1-pps signals
5 */
6 #ifdef HAVE_CONFIG_H
7 #include <config.h>
8 #endif
10 #include <stdio.h>
11 #include <ctype.h>
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_unixtime.h"
16 #include "ntp_refclock.h"
17 #include "ntp_stdlib.h"
19 #if defined(REFCLOCK) && defined(CLOCK_ATOM)
21 #ifdef HAVE_PPSAPI
22 # include "ppsapi_timepps.h"
23 #endif /* HAVE_PPSAPI */
26 * This driver furnishes an interface for pulse-per-second (PPS) signals
27 * produced by a cesium clock, timing receiver or related equipment. It
28 * can be used to remove accumulated jitter and retime a secondary
29 * server when synchronized to a primary server over a congested, wide-
30 * area network and before redistributing the time to local clients.
32 * Before this driver becomes active, the local clock must be set to
33 * within +-500 ms 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.
41 * Both methods require a modified device driver and kernel interface
42 * compatible with the Pulse-per-Second API for Unix-like Operating
43 * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
44 * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
45 * present only the Alpha implementation provides the full generality of
46 * the API with multiple PPS drivers and multiple handles per driver. If
47 * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
48 * header file and kernel support specific to each operating system.
49 * However, this driver can operate without this interface if means are
50 * proviced to call the pps_sample() routine from another driver. Please
51 * note; if the PPSAPI interface is present, it must be used.
53 * In many configurations a single port is used for the radio timecode
54 * and PPS signal. In order to provide for this configuration and others
55 * involving dedicated multiple serial/parallel ports, the driver first
56 * attempts to open the device /dev/pps%d, where %d is the unit number.
57 * If this fails, the driver attempts to open the device specified by
58 * the pps configuration command. If a port is to be shared, the pps
59 * command must be placed before the radio device(s) and the radio
60 * device(s) must be placed before the PPS driver(s) in the
61 * configuration file.
63 * This driver normally uses the PLL/FLL clock discipline implemented in
64 * the ntpd code. Ordinarily, this is the most accurate means, as the
65 * median filter in the driver interface is much larger than in the
66 * kernel. However, if the systemic clock frequency error is large (tens
67 * to hundreds of PPM), it's better to used the kernel support, if
68 * available.
70 * Fudge Factors
72 * If flag2 is dim (default), the on-time epoch is the assert edge of
73 * the PPS signal; if lit, the on-time epoch is the clear edge. If flag2
74 * is lit, the assert edge is used; if flag3 is dim (default), the
75 * kernel PPS support is disabled; if lit it is enabled. The time1
76 * parameter can be used to compensate for miscellaneous device driver
77 * and OS delays.
80 * Interface definitions
82 #ifdef HAVE_PPSAPI
83 #define DEVICE "/dev/pps%d" /* device name and unit */
84 #endif /* HAVE_PPSAPI */
86 #define PRECISION (-20) /* precision assumed (about 1 us) */
87 #define REFID "PPS\0" /* reference ID */
88 #define DESCRIPTION "PPS Clock Discipline" /* WRU */
89 #define NANOSECOND 1000000000 /* one second (ns) */
90 #define RANGEGATE 500000 /* range gate (ns) */
92 static struct peer *pps_peer; /* atom driver for PPS sources */
94 #ifdef HAVE_PPSAPI
96 * PPS unit control structure
98 struct ppsunit {
99 struct timespec ts; /* last timestamp */
100 int fddev; /* pps device descriptor */
101 pps_params_t pps_params; /* pps parameters */
102 pps_info_t pps_info; /* last pps data */
103 pps_handle_t handle; /* pps handlebars */
105 #endif /* HAVE_PPSAPI */
108 * Function prototypes
110 static int atom_start P((int, struct peer *));
111 static void atom_poll P((int, struct peer *));
112 static void atom_shutdown P((int, struct peer *));
113 #ifdef HAVE_PPSAPI
114 static void atom_control P((int, struct refclockstat *, struct
115 refclockstat *, struct peer *));
116 static void atom_timer P((int, struct peer *));
117 static int atom_ppsapi P((struct peer *, int));
118 #endif /* HAVE_PPSAPI */
121 * Transfer vector
123 #ifdef HAVE_PPSAPI
124 struct refclock refclock_atom = {
125 atom_start, /* start up driver */
126 atom_shutdown, /* shut down driver */
127 atom_poll, /* transmit poll message */
128 atom_control, /* fudge control */
129 noentry, /* initialize driver (not used) */
130 noentry, /* buginfo (not used) */
131 atom_timer, /* called once per second */
133 #else /* HAVE_PPSAPI */
134 struct refclock refclock_atom = {
135 atom_start, /* start up driver */
136 atom_shutdown, /* shut down driver */
137 atom_poll, /* transmit poll message */
138 noentry, /* fudge control (not used) */
139 noentry, /* initialize driver (not used) */
140 noentry, /* buginfo (not used) */
141 NOFLAGS /* not used */
143 #endif /* HAVE_PPPSAPI */
147 * atom_start - initialize data for processing
149 static int
150 atom_start(
151 int unit, /* unit number (not used) */
152 struct peer *peer /* peer structure pointer */
155 struct refclockproc *pp;
156 #ifdef HAVE_PPSAPI
157 register struct ppsunit *up;
158 char device[80];
159 int mode;
160 #endif /* HAVE_PPSAPI */
163 * Allocate and initialize unit structure
165 pps_peer = peer;
166 pp = peer->procptr;
167 peer->precision = PRECISION;
168 pp->clockdesc = DESCRIPTION;
169 pp->stratum = STRATUM_UNSPEC;
170 memcpy((char *)&pp->refid, REFID, 4);
171 #ifdef HAVE_PPSAPI
172 up = emalloc(sizeof(struct ppsunit));
173 memset(up, 0, sizeof(struct ppsunit));
174 pp->unitptr = (caddr_t)up;
177 * Open PPS device. This can be any serial or parallel port and
178 * not necessarily the port used for the associated radio.
180 sprintf(device, DEVICE, unit);
181 up->fddev = open(device, O_RDWR, 0777);
182 if (up->fddev <= 0) {
183 msyslog(LOG_ERR,
184 "refclock_atom: %s: %m", device);
185 return (0);
189 * Light off the PPSAPI interface.
191 if (time_pps_create(up->fddev, &up->handle) < 0) {
192 msyslog(LOG_ERR,
193 "refclock_atom: time_pps_create failed: %m");
194 return (0);
198 * If the mode is nonzero, use that for the time_pps_setparams()
199 * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
200 * flag3 is lit.
202 mode = peer->ttl;
203 if (mode == 0)
204 mode = PPS_CAPTUREASSERT;
205 return (atom_ppsapi(peer, mode));
206 #else /* HAVE_PPSAPI */
207 return (1);
208 #endif /* HAVE_PPSAPI */
213 * atom_shutdown - shut down the clock
215 static void
216 atom_shutdown(
217 int unit, /* unit number (not used) */
218 struct peer *peer /* peer structure pointer */
221 struct refclockproc *pp;
222 register struct ppsunit *up;
224 pp = peer->procptr;
225 up = (struct ppsunit *)pp->unitptr;
226 #ifdef HAVE_PPSAPI
227 if (up->fddev > 0)
228 close(up->fddev);
229 if (up->handle != 0)
230 time_pps_destroy(up->handle);
231 #endif /* HAVE_PPSAPI */
232 if (pps_peer == peer)
233 pps_peer = NULL;
234 free(up);
238 #ifdef HAVE_PPSAPI
240 * atom_control - fudge control
242 static void
243 atom_control(
244 int unit, /* unit (not used */
245 struct refclockstat *in, /* input parameters (not uded) */
246 struct refclockstat *out, /* output parameters (not used) */
247 struct peer *peer /* peer structure pointer */
250 struct refclockproc *pp;
251 int mode;
253 pp = peer->procptr;
254 if (peer->ttl != 0) /* all legal modes must be nonzero */
255 return;
257 if (pp->sloppyclockflag & CLK_FLAG2)
258 mode = PPS_CAPTURECLEAR;
259 else
260 mode = PPS_CAPTUREASSERT;
261 atom_ppsapi(peer, mode);
266 * Initialize PPSAPI
269 atom_ppsapi(
270 struct peer *peer, /* peer structure pointer */
271 int mode /* mode */
274 struct refclockproc *pp;
275 register struct ppsunit *up;
276 int capability;
278 pp = peer->procptr;
279 up = (struct ppsunit *)pp->unitptr;
280 if (up->handle == 0)
281 return (0);
283 if (time_pps_getcap(up->handle, &capability) < 0) {
284 msyslog(LOG_ERR,
285 "refclock_atom: time_pps_getcap failed: %m");
286 return (0);
288 memset(&up->pps_params, 0, sizeof(pps_params_t));
289 up->pps_params.api_version = PPS_API_VERS_1;
290 up->pps_params.mode = mode | PPS_TSFMT_TSPEC;
291 if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
292 msyslog(LOG_ERR,
293 "refclock_atom: time_pps_setparams failed: %m");
294 return (0);
296 if (pp->sloppyclockflag & CLK_FLAG3) {
297 if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
298 up->pps_params.mode & ~PPS_TSFMT_TSPEC,
299 PPS_TSFMT_TSPEC) < 0) {
300 msyslog(LOG_ERR,
301 "refclock_atom: time_pps_kcbind failed: %m");
302 return (0);
304 pps_enable = 1;
306 #if DEBUG
307 if (debug) {
308 time_pps_getparams(up->handle, &up->pps_params);
309 printf(
310 "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",
311 up->fddev, capability, up->pps_params.api_version,
312 up->pps_params.mode);
314 #endif
315 return (1);
320 * atom_timer - called once per second
322 * This routine is called once per second when the PPSAPI interface is
323 * present. It snatches the PPS timestamp from the kernel and saves the
324 * sign-extended fraction in a circular buffer for processing at the
325 * next poll event.
327 static void
328 atom_timer(
329 int unit, /* unit number (not used) */
330 struct peer *peer /* peer structure pointer */
333 register struct ppsunit *up;
334 struct refclockproc *pp;
335 pps_info_t pps_info;
336 struct timespec timeout, ts;
337 long sec, nsec;
338 double dtemp;
339 char tbuf[80]; /* monitor buffer */
342 * Convert the timespec nanoseconds field to signed double and
343 * save in the median filter. for billboards. No harm is done if
344 * previous data are overwritten. If the discipline comes bum or
345 * the data grow stale, just forget it. A range gate rejects new
346 * samples if less than a jiggle time from the next second.
348 pp = peer->procptr;
349 up = (struct ppsunit *)pp->unitptr;
350 if (up->handle == 0)
351 return;
353 timeout.tv_sec = 0;
354 timeout.tv_nsec = 0;
355 memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
356 if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
357 &timeout) < 0) {
358 refclock_report(peer, CEVNT_FAULT);
359 return;
361 if (up->pps_params.mode & PPS_CAPTUREASSERT) {
362 ts = up->pps_info.assert_timestamp;
363 } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
364 ts = up->pps_info.clear_timestamp;
365 } else {
366 refclock_report(peer, CEVNT_FAULT);
367 return;
371 * There can be zero, one or two PPS seconds between polls. If
372 * zero, either the poll clock is slightly faster than the PPS
373 * clock or the PPS clock has died. If the PPS clock advanced
374 * once between polls, we make sure the fraction time difference
375 * since the last sample is within the range gate of 5 ms (500
376 * PPM). If the PPS clock advanced twice since the last poll,
377 * the poll bracketed more than one second and the first second
378 * was lost to a slip. Since the interval since the last sample
379 * found is now two seconds, just widen the range gate. If the
380 * PPS clock advanced three or more times, either the signal has
381 * failed for a number of seconds or we have runts, in which
382 * case just ignore them.
384 * If flag4 is lit, record each second offset to clockstats.
385 * That's so we can make awesome Allan deviation plots.
387 sec = ts.tv_sec - up->ts.tv_sec;
388 nsec = ts.tv_nsec - up->ts.tv_nsec;
389 up->ts = ts;
390 if (nsec < 0) {
391 sec --;
392 nsec += NANOSECOND;
393 } else if (nsec >= NANOSECOND) {
394 sec++;
395 nsec -= NANOSECOND;
397 if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)
398 return;
400 else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)
401 return;
403 pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
404 dtemp = ts.tv_nsec * FRAC / 1e9;
405 if (dtemp >= FRAC)
406 pp->lastrec.l_ui++;
407 pp->lastrec.l_uf = (u_int32)dtemp;
408 if (ts.tv_nsec > NANOSECOND / 2)
409 ts.tv_nsec -= NANOSECOND;
410 dtemp = -(double)ts.tv_nsec / NANOSECOND;
411 SAMPLE(dtemp + pp->fudgetime1);
412 if (pp->sloppyclockflag & CLK_FLAG4){
413 sprintf(tbuf, "%.9f", dtemp);
414 record_clock_stats(&peer->srcadr, tbuf);
416 #ifdef DEBUG
417 if (debug > 1)
418 printf("atom_timer: %lu %f %f\n", current_time,
419 dtemp, pp->fudgetime1);
420 #endif
421 return;
423 #endif /* HAVE_PPSAPI */
427 * pps_sample - receive PPS data from some other clock driver
429 * This routine is called once per second when the external clock driver
430 * processes PPS information. It processes the PPS timestamp and saves
431 * the sign-extended fraction in a circular buffer for processing at the
432 * next poll event. This works only for a single PPS device.
434 * The routine should be used by another configured driver ONLY when
435 * this driver is configured as well and the PPSAPI is NOT in use.
438 pps_sample(
439 l_fp *offset /* PPS offset */
442 register struct peer *peer;
443 struct refclockproc *pp;
444 l_fp lftmp;
445 double doffset;
447 peer = pps_peer;
448 if (peer == NULL)
449 return (1);
451 pp = peer->procptr;
454 * Convert the timeval to l_fp and save for billboards. Sign-
455 * extend the fraction and stash in the buffer. No harm is done
456 * if previous data are overwritten. If the discipline comes bum
457 * or the data grow stale, just forget it.
459 pp->lastrec = *offset;
460 L_CLR(&lftmp);
461 L_ADDF(&lftmp, pp->lastrec.l_f);
462 LFPTOD(&lftmp, doffset);
463 SAMPLE(-doffset + pp->fudgetime1);
464 return (0);
469 * atom_poll - called by the transmit procedure
471 static void
472 atom_poll(
473 int unit, /* unit number (not used) */
474 struct peer *peer /* peer structure pointer */
477 struct refclockproc *pp;
478 pp = peer->procptr;
479 pp->polls++;
482 * Valid time is returned only if the prefer peer has survived
483 * the intersection algorithm and within 0.4 s of local time
484 * and not too long ago. This ensures the PPS time is within
485 * 0.5 s of the local time and the seconds numbering is
486 * unambiguous. Note that the leap bits, stratum and refid are
487 * set from the prefer peer, unless overriden by a fudge
488 * command.
490 if (pp->codeproc == pp->coderecv) {
491 refclock_report(peer, CEVNT_TIMEOUT);
492 return;
494 } else if (sys_prefer == NULL) {
495 pp->codeproc = pp->coderecv;
496 return;
498 } else if (fabs(sys_prefer->offset) >= 0.4) {
499 pp->codeproc = pp->coderecv;
500 return;
502 pp->leap = sys_prefer->leap;
503 if (pp->stratum >= STRATUM_UNSPEC)
504 peer->stratum = sys_prefer->stratum;
505 else
506 peer->stratum = pp->stratum;
507 pp->lastref = pp->lastrec;
508 refclock_receive(peer);
510 #else
511 int refclock_atom_bs;
513 pps_sample(
514 l_fp *offset /* PPS offset */
517 return (1);
519 #endif /* REFCLOCK */