1 /* $NetBSD: refclock_atom.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
4 * refclock_atom - clock driver for 1-pps signals
15 #include "ntp_unixtime.h"
16 #include "ntp_refclock.h"
17 #include "ntp_stdlib.h"
19 #if defined(REFCLOCK) && defined(CLOCK_ATOM)
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
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
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
80 * Interface definitions
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 */
96 * PPS unit control structure
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
*));
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 */
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
151 int unit
, /* unit number (not used) */
152 struct peer
*peer
/* peer structure pointer */
155 struct refclockproc
*pp
;
157 register struct ppsunit
*up
;
160 #endif /* HAVE_PPSAPI */
163 * Allocate and initialize unit structure
167 peer
->precision
= PRECISION
;
168 pp
->clockdesc
= DESCRIPTION
;
169 pp
->stratum
= STRATUM_UNSPEC
;
170 memcpy((char *)&pp
->refid
, REFID
, 4);
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) {
184 "refclock_atom: %s: %m", device
);
189 * Light off the PPSAPI interface.
191 if (time_pps_create(up
->fddev
, &up
->handle
) < 0) {
193 "refclock_atom: time_pps_create failed: %m");
198 * If the mode is nonzero, use that for the time_pps_setparams()
199 * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
204 mode
= PPS_CAPTUREASSERT
;
205 return (atom_ppsapi(peer
, mode
));
206 #else /* HAVE_PPSAPI */
208 #endif /* HAVE_PPSAPI */
213 * atom_shutdown - shut down the clock
217 int unit
, /* unit number (not used) */
218 struct peer
*peer
/* peer structure pointer */
221 struct refclockproc
*pp
;
222 register struct ppsunit
*up
;
225 up
= (struct ppsunit
*)pp
->unitptr
;
230 time_pps_destroy(up
->handle
);
231 #endif /* HAVE_PPSAPI */
232 if (pps_peer
== peer
)
240 * atom_control - fudge 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
;
254 if (peer
->ttl
!= 0) /* all legal modes must be nonzero */
257 if (pp
->sloppyclockflag
& CLK_FLAG2
)
258 mode
= PPS_CAPTURECLEAR
;
260 mode
= PPS_CAPTUREASSERT
;
261 atom_ppsapi(peer
, mode
);
270 struct peer
*peer
, /* peer structure pointer */
274 struct refclockproc
*pp
;
275 register struct ppsunit
*up
;
279 up
= (struct ppsunit
*)pp
->unitptr
;
283 if (time_pps_getcap(up
->handle
, &capability
) < 0) {
285 "refclock_atom: time_pps_getcap failed: %m");
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) {
293 "refclock_atom: time_pps_setparams failed: %m");
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) {
301 "refclock_atom: time_pps_kcbind failed: %m");
308 time_pps_getparams(up
->handle
, &up
->pps_params
);
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
);
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
329 int unit
, /* unit number (not used) */
330 struct peer
*peer
/* peer structure pointer */
333 register struct ppsunit
*up
;
334 struct refclockproc
*pp
;
336 struct timespec timeout
, ts
;
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.
349 up
= (struct ppsunit
*)pp
->unitptr
;
355 memcpy(&pps_info
, &up
->pps_info
, sizeof(pps_info_t
));
356 if (time_pps_fetch(up
->handle
, PPS_TSFMT_TSPEC
, &up
->pps_info
,
358 refclock_report(peer
, CEVNT_FAULT
);
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
;
366 refclock_report(peer
, CEVNT_FAULT
);
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
;
393 } else if (nsec
>= NANOSECOND
) {
397 if (sec
* NANOSECOND
+ nsec
> NANOSECOND
+ RANGEGATE
)
400 else if (sec
* NANOSECOND
+ nsec
< NANOSECOND
- RANGEGATE
)
403 pp
->lastrec
.l_ui
= ts
.tv_sec
+ JAN_1970
;
404 dtemp
= ts
.tv_nsec
* FRAC
/ 1e9
;
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
);
418 printf("atom_timer: %lu %f %f\n", current_time
,
419 dtemp
, pp
->fudgetime1
);
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.
439 l_fp
*offset
/* PPS offset */
442 register struct peer
*peer
;
443 struct refclockproc
*pp
;
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
;
461 L_ADDF(&lftmp
, pp
->lastrec
.l_f
);
462 LFPTOD(&lftmp
, doffset
);
463 SAMPLE(-doffset
+ pp
->fudgetime1
);
469 * atom_poll - called by the transmit procedure
473 int unit
, /* unit number (not used) */
474 struct peer
*peer
/* peer structure pointer */
477 struct refclockproc
*pp
;
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
490 if (pp
->codeproc
== pp
->coderecv
) {
491 refclock_report(peer
, CEVNT_TIMEOUT
);
494 } else if (sys_prefer
== NULL
) {
495 pp
->codeproc
= pp
->coderecv
;
498 } else if (fabs(sys_prefer
->offset
) >= 0.4) {
499 pp
->codeproc
= pp
->coderecv
;
502 pp
->leap
= sys_prefer
->leap
;
503 if (pp
->stratum
>= STRATUM_UNSPEC
)
504 peer
->stratum
= sys_prefer
->stratum
;
506 peer
->stratum
= pp
->stratum
;
507 pp
->lastref
= pp
->lastrec
;
508 refclock_receive(peer
);
511 int refclock_atom_bs
;
514 l_fp
*offset
/* PPS offset */
519 #endif /* REFCLOCK */