xref: /freebsd/contrib/ntp/ntpd/refclock_atom.c (revision 9c2daa00)
19c2daa00SOllivier Robert /*
2c0b746e5SOllivier Robert  * refclock_atom - clock driver for 1-pps signals
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert #include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #include <stdio.h>
9c0b746e5SOllivier Robert #include <ctype.h>
10c0b746e5SOllivier Robert 
11c0b746e5SOllivier Robert #include "ntpd.h"
12c0b746e5SOllivier Robert #include "ntp_io.h"
13c0b746e5SOllivier Robert #include "ntp_unixtime.h"
14c0b746e5SOllivier Robert #include "ntp_refclock.h"
15c0b746e5SOllivier Robert #include "ntp_stdlib.h"
16c0b746e5SOllivier Robert 
17c0b746e5SOllivier Robert /*
18224ba2bdSOllivier Robert  * This driver requires the PPSAPI interface (RFC 2783)
19224ba2bdSOllivier Robert  */
20c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
21a151a66cSOllivier Robert #include "ppsapi_timepps.h"
22a151a66cSOllivier Robert #include "refclock_atom.h"
23a151a66cSOllivier Robert 
24a151a66cSOllivier Robert /*
25c0b746e5SOllivier Robert  * This driver furnishes an interface for pulse-per-second (PPS) signals
26a151a66cSOllivier Robert  * produced by a cesium clock, timing receiver or related equipment. It
27a151a66cSOllivier Robert  * can be used to remove accumulated jitter over a congested link and
28c0b746e5SOllivier Robert  * retime a server before redistributing the time to clients. It can
29c0b746e5SOllivier Robert  *also be used as a holdover should all other synchronization sources
30c0b746e5SOllivier Robert  * beconme unreachable.
31c0b746e5SOllivier Robert  *
32c0b746e5SOllivier Robert  * Before this driver becomes active, the local clock must be set to
33c0b746e5SOllivier Robert  * within +-0.4 s by another means, such as a radio clock or NTP
34c0b746e5SOllivier Robert  * itself. There are two ways to connect the PPS signal, normally at TTL
35c0b746e5SOllivier Robert  * levels, to the computer. One is to shift to EIA levels and connect to
36c0b746e5SOllivier Robert  * pin 8 (DCD) of a serial port. This requires a level converter and
37224ba2bdSOllivier Robert  * may require a one-shot flipflop to lengthen the pulse. The other is
38c0b746e5SOllivier Robert  * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
39224ba2bdSOllivier Robert  * port. These methods are architecture dependent.
40224ba2bdSOllivier Robert  *
41224ba2bdSOllivier Robert  * This driver requires the Pulse-per-Second API for Unix-like Operating
42224ba2bdSOllivier Robert  * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
43224ba2bdSOllivier Robert  * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
44224ba2bdSOllivier Robert  * present only the Tru64 implementation provides the full generality of
45c0b746e5SOllivier Robert  * the API with multiple PPS drivers and multiple handles per driver. If
46224ba2bdSOllivier Robert  * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
47224ba2bdSOllivier Robert  * header file and kernel support specific to each operating system.
48224ba2bdSOllivier Robert  *
49224ba2bdSOllivier Robert  * This driver normally uses the PLL/FLL clock discipline implemented in
50224ba2bdSOllivier Robert  * the ntpd code. Ordinarily, this is the most accurate means, as the
51224ba2bdSOllivier Robert  * median filter in the driver interface is much larger than in the
52c0b746e5SOllivier Robert  * kernel. However, if the systemic clock frequency error is large (tens
53224ba2bdSOllivier Robert  * to hundreds of PPM), it's better to used the kernel support, if
54224ba2bdSOllivier Robert  * available.
55224ba2bdSOllivier Robert  *
56224ba2bdSOllivier Robert  * This deriver is subject to the mitigation rules described in the
57224ba2bdSOllivier Robert  * "mitigation rulse and the prefer peer" page. However, there is an
58224ba2bdSOllivier Robert  * important difference. If this driver becomes the PPS driver according
59224ba2bdSOllivier Robert  * to these rules, it is acrive only if (a) a prefer peer other than
60224ba2bdSOllivier Robert  * this driver is among the survivors or (b) there are no survivors and
61224ba2bdSOllivier Robert  * the minsane option of the tos command is zero. This is intended to
62c0b746e5SOllivier Robert  * support space missions where updates from other spacecraft are
63224ba2bdSOllivier Robert  * infrequent, but a reliable PPS signal, such as from an Ultra Stable
64224ba2bdSOllivier Robert  * Oscillator (USO) is available.
65224ba2bdSOllivier Robert  *
66224ba2bdSOllivier Robert  * Fudge Factors
67224ba2bdSOllivier Robert  *
68c0b746e5SOllivier Robert  * The PPS timestamp is captured on the rising (assert) edge if flag2 is
69c0b746e5SOllivier Robert  * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
70c0b746e5SOllivier Robert  * (default), the kernel PPS support is disabled; if lit it is enabled.
71c0b746e5SOllivier Robert  * If flag4 is lit, each timesampt is copied to the clockstats file for
72224ba2bdSOllivier Robert  * later analysis. This can be useful when constructing Allan deviation
73224ba2bdSOllivier Robert  * plots. The time1 parameter can be used to compensate for
74c0b746e5SOllivier Robert  * miscellaneous device driver and OS delays.
75c0b746e5SOllivier Robert  */
76c0b746e5SOllivier Robert /*
77c0b746e5SOllivier Robert  * Interface definitions
78c0b746e5SOllivier Robert  */
79c0b746e5SOllivier Robert #define DEVICE		"/dev/pps%d" /* device name and unit */
80224ba2bdSOllivier Robert #define	PRECISION	(-20)	/* precision assumed (about 1 us) */
81c0b746e5SOllivier Robert #define	REFID		"PPS\0"	/* reference ID */
82c0b746e5SOllivier Robert #define	DESCRIPTION	"PPS Clock Discipline" /* WRU */
83c0b746e5SOllivier Robert 
84c0b746e5SOllivier Robert /*
85224ba2bdSOllivier Robert  * PPS unit control structure
86224ba2bdSOllivier Robert  */
87224ba2bdSOllivier Robert struct ppsunit {
88c0b746e5SOllivier Robert 	struct refclock_atom atom; /* atom structure pointer */
89224ba2bdSOllivier Robert 	int	fddev;		/* file descriptor */
90c0b746e5SOllivier Robert };
91c0b746e5SOllivier Robert 
92224ba2bdSOllivier Robert /*
93224ba2bdSOllivier Robert  * Function prototypes
94224ba2bdSOllivier Robert  */
95224ba2bdSOllivier Robert static	int	atom_start	(int, struct peer *);
96224ba2bdSOllivier Robert static	void	atom_shutdown	(int, struct peer *);
97224ba2bdSOllivier Robert static	void	atom_poll	(int, struct peer *);
98224ba2bdSOllivier Robert static	void	atom_timer	(int, struct peer *);
99224ba2bdSOllivier Robert 
100224ba2bdSOllivier Robert /*
101c0b746e5SOllivier Robert  * Transfer vector
102224ba2bdSOllivier Robert  */
103c0b746e5SOllivier Robert struct	refclock refclock_atom = {
104c0b746e5SOllivier Robert 	atom_start,		/* start up driver */
105c0b746e5SOllivier Robert 	atom_shutdown,		/* shut down driver */
106c0b746e5SOllivier Robert 	atom_poll,		/* transmit poll message */
107c0b746e5SOllivier Robert 	noentry,		/* control (not used) */
108c0b746e5SOllivier Robert 	noentry,		/* initialize driver (not used) */
109224ba2bdSOllivier Robert 	noentry,		/* buginfo (not used) */
110224ba2bdSOllivier Robert 	atom_timer,		/* called once per second */
111224ba2bdSOllivier Robert };
112224ba2bdSOllivier Robert 
113c0b746e5SOllivier Robert 
114224ba2bdSOllivier Robert /*
115224ba2bdSOllivier Robert  * atom_start - initialize data for processing
116c0b746e5SOllivier Robert  */
117c0b746e5SOllivier Robert static int
atom_start(int unit,struct peer * peer)118c0b746e5SOllivier Robert atom_start(
119c0b746e5SOllivier Robert 	int unit,		/* unit number (not used) */
120c0b746e5SOllivier Robert 	struct peer *peer	/* peer structure pointer */
121c0b746e5SOllivier Robert 	)
122224ba2bdSOllivier Robert {
123c0b746e5SOllivier Robert 	struct refclockproc *pp;
124224ba2bdSOllivier Robert 	struct ppsunit *up;
125224ba2bdSOllivier Robert 	char	device[80];
126224ba2bdSOllivier Robert 
127c0b746e5SOllivier Robert 	/*
128224ba2bdSOllivier Robert 	 * Allocate and initialize unit structure
129224ba2bdSOllivier Robert 	 */
130224ba2bdSOllivier Robert 	pp = peer->procptr;
131224ba2bdSOllivier Robert 	peer->precision = PRECISION;
132224ba2bdSOllivier Robert 	pp->clockdesc = DESCRIPTION;
133c0b746e5SOllivier Robert 	pp->stratum = STRATUM_UNSPEC;
134c0b746e5SOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
135c0b746e5SOllivier Robert 	up = emalloc(sizeof(struct ppsunit));
136c0b746e5SOllivier Robert 	memset(up, 0, sizeof(struct ppsunit));
137c0b746e5SOllivier Robert 	pp->unitptr = up;
138c0b746e5SOllivier Robert 
139c0b746e5SOllivier Robert 	/*
140c0b746e5SOllivier Robert 	 * Open PPS device. This can be any serial or parallel port and
141c0b746e5SOllivier Robert 	 * not necessarily the port used for the associated radio.
142c0b746e5SOllivier Robert 	 */
143c0b746e5SOllivier Robert 	snprintf(device, sizeof(device), DEVICE, unit);
144224ba2bdSOllivier Robert 	up->fddev = tty_open(device, O_RDWR, 0777);
145224ba2bdSOllivier Robert 	if (up->fddev <= 0) {
146c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
147c0b746e5SOllivier Robert 			"refclock_atom: %s: %m", device);
148c0b746e5SOllivier Robert 		return (0);
149224ba2bdSOllivier Robert 	}
150224ba2bdSOllivier Robert 
151224ba2bdSOllivier Robert 	/*
152224ba2bdSOllivier Robert 	 * Light up the PPSAPI interface.
153c0b746e5SOllivier Robert 	 */
154c0b746e5SOllivier Robert 	return (refclock_ppsapi(up->fddev, &up->atom));
155c0b746e5SOllivier Robert }
156c0b746e5SOllivier Robert 
157224ba2bdSOllivier Robert 
158c0b746e5SOllivier Robert /*
159c0b746e5SOllivier Robert  * atom_shutdown - shut down the clock
160c0b746e5SOllivier Robert  */
1619c2daa00SOllivier Robert static void
atom_shutdown(int unit,struct peer * peer)162c0b746e5SOllivier Robert atom_shutdown(
163224ba2bdSOllivier Robert 	int unit,		/* unit number (not used) */
164224ba2bdSOllivier Robert 	struct peer *peer	/* peer structure pointer */
165224ba2bdSOllivier Robert 	)
166224ba2bdSOllivier Robert {
167224ba2bdSOllivier Robert 	struct refclockproc *pp;
168224ba2bdSOllivier Robert 	struct ppsunit *up;
169224ba2bdSOllivier Robert 
170224ba2bdSOllivier Robert 	pp = peer->procptr;
171224ba2bdSOllivier Robert 	up = pp->unitptr;
172224ba2bdSOllivier Robert 	if (up->fddev > 0)
173224ba2bdSOllivier Robert 		close(up->fddev);
174224ba2bdSOllivier Robert 	free(up);
175224ba2bdSOllivier Robert }
176224ba2bdSOllivier Robert 
177224ba2bdSOllivier Robert /*
178224ba2bdSOllivier Robert  * atom_timer - called once per second
179224ba2bdSOllivier Robert  */
180224ba2bdSOllivier Robert void
atom_timer(int unit,struct peer * peer)181224ba2bdSOllivier Robert atom_timer(
182224ba2bdSOllivier Robert 	int	unit,		/* unit pointer (not used) */
183224ba2bdSOllivier Robert 	struct peer *peer	/* peer structure pointer */
184224ba2bdSOllivier Robert 	)
185224ba2bdSOllivier Robert {
186224ba2bdSOllivier Robert 	struct ppsunit *up;
187224ba2bdSOllivier Robert 	struct refclockproc *pp;
188224ba2bdSOllivier Robert 	char	tbuf[80];
189224ba2bdSOllivier Robert 
190224ba2bdSOllivier Robert 	pp = peer->procptr;
191224ba2bdSOllivier Robert 	up = pp->unitptr;
192224ba2bdSOllivier Robert 	if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
193224ba2bdSOllivier Robert 		return;
194224ba2bdSOllivier Robert 
195224ba2bdSOllivier Robert 	peer->flags |= FLAG_PPS;
1969c2daa00SOllivier Robert 
197224ba2bdSOllivier Robert 	/*
198224ba2bdSOllivier Robert 	 * If flag4 is lit, record each second offset to clockstats.
199224ba2bdSOllivier Robert 	 * That's so we can make awesome Allan deviation plots.
200224ba2bdSOllivier Robert 	 */
201224ba2bdSOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG4) {
202224ba2bdSOllivier Robert 		snprintf(tbuf, sizeof(tbuf), "%.9f",
203224ba2bdSOllivier Robert 			 pp->filter[pp->coderecv]);
204224ba2bdSOllivier Robert 		record_clock_stats(&peer->srcadr, tbuf);
205224ba2bdSOllivier Robert 	}
206224ba2bdSOllivier Robert }
207224ba2bdSOllivier Robert 
208224ba2bdSOllivier Robert 
209224ba2bdSOllivier Robert /*
210224ba2bdSOllivier Robert  * atom_poll - called by the transmit procedure
211224ba2bdSOllivier Robert  */
212224ba2bdSOllivier Robert static void
atom_poll(int unit,struct peer * peer)213224ba2bdSOllivier Robert atom_poll(
214224ba2bdSOllivier Robert 	int unit,		/* unit number (not used) */
215224ba2bdSOllivier Robert 	struct peer *peer	/* peer structure pointer */
216224ba2bdSOllivier Robert 	)
217224ba2bdSOllivier Robert {
218224ba2bdSOllivier Robert 	struct refclockproc *pp;
219224ba2bdSOllivier Robert 
220224ba2bdSOllivier Robert 	/*
221224ba2bdSOllivier Robert 	 * Don't wiggle the clock until some other driver has numbered
222224ba2bdSOllivier Robert 	 * the seconds.
223224ba2bdSOllivier Robert 	 */
224224ba2bdSOllivier Robert 	if (sys_leap == LEAP_NOTINSYNC)
225224ba2bdSOllivier Robert 		return;
226224ba2bdSOllivier Robert 
227224ba2bdSOllivier Robert 	pp = peer->procptr;
228224ba2bdSOllivier Robert 	pp->polls++;
229224ba2bdSOllivier Robert 	if (pp->codeproc == pp->coderecv) {
230224ba2bdSOllivier Robert 		peer->flags &= ~FLAG_PPS;
231224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_TIMEOUT);
232224ba2bdSOllivier Robert 		return;
233224ba2bdSOllivier Robert 	}
234224ba2bdSOllivier Robert 	pp->lastref = pp->lastrec;
235224ba2bdSOllivier Robert 	refclock_receive(peer);
236224ba2bdSOllivier Robert }
237224ba2bdSOllivier Robert #else
238224ba2bdSOllivier Robert int refclock_atom_bs;
239224ba2bdSOllivier Robert #endif /* REFCLOCK */
240224ba2bdSOllivier Robert