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