xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_atom.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: refclock_atom.c,v 1.6 2024/08/18 20:47:18 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  * refclock_atom - clock driver for 1-pps signals
5abb0f93cSkardel  */
6abb0f93cSkardel #ifdef HAVE_CONFIG_H
7abb0f93cSkardel #include <config.h>
8abb0f93cSkardel #endif
9abb0f93cSkardel 
10abb0f93cSkardel #include <stdio.h>
11abb0f93cSkardel #include <ctype.h>
12abb0f93cSkardel 
13abb0f93cSkardel #include "ntpd.h"
14abb0f93cSkardel #include "ntp_io.h"
15abb0f93cSkardel #include "ntp_unixtime.h"
16abb0f93cSkardel #include "ntp_refclock.h"
17abb0f93cSkardel #include "ntp_stdlib.h"
18abb0f93cSkardel 
19abb0f93cSkardel /*
20abb0f93cSkardel  * This driver requires the PPSAPI interface (RFC 2783)
21abb0f93cSkardel  */
22abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
23abb0f93cSkardel #include "ppsapi_timepps.h"
24abb0f93cSkardel #include "refclock_atom.h"
25abb0f93cSkardel 
26abb0f93cSkardel /*
27abb0f93cSkardel  * This driver furnishes an interface for pulse-per-second (PPS) signals
28abb0f93cSkardel  * produced by a cesium clock, timing receiver or related equipment. It
29abb0f93cSkardel  * can be used to remove accumulated jitter over a congested link and
30abb0f93cSkardel  * retime a server before redistributing the time to clients. It can
31abb0f93cSkardel  *also be used as a holdover should all other synchronization sources
32abb0f93cSkardel  * beconme unreachable.
33abb0f93cSkardel  *
34abb0f93cSkardel  * Before this driver becomes active, the local clock must be set to
35abb0f93cSkardel  * within +-0.4 s by another means, such as a radio clock or NTP
36abb0f93cSkardel  * itself. There are two ways to connect the PPS signal, normally at TTL
37abb0f93cSkardel  * levels, to the computer. One is to shift to EIA levels and connect to
38abb0f93cSkardel  * pin 8 (DCD) of a serial port. This requires a level converter and
39abb0f93cSkardel  * may require a one-shot flipflop to lengthen the pulse. The other is
40abb0f93cSkardel  * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
41abb0f93cSkardel  * port. These methods are architecture dependent.
42abb0f93cSkardel  *
43abb0f93cSkardel  * This driver requires the Pulse-per-Second API for Unix-like Operating
44abb0f93cSkardel  * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
45abb0f93cSkardel  * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
46abb0f93cSkardel  * present only the Tru64 implementation provides the full generality of
47abb0f93cSkardel  * the API with multiple PPS drivers and multiple handles per driver. If
48abb0f93cSkardel  * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
49abb0f93cSkardel  * header file and kernel support specific to each operating system.
50abb0f93cSkardel  *
51abb0f93cSkardel  * This driver normally uses the PLL/FLL clock discipline implemented in
52abb0f93cSkardel  * the ntpd code. Ordinarily, this is the most accurate means, as the
53abb0f93cSkardel  * median filter in the driver interface is much larger than in the
54abb0f93cSkardel  * kernel. However, if the systemic clock frequency error is large (tens
55abb0f93cSkardel  * to hundreds of PPM), it's better to used the kernel support, if
56abb0f93cSkardel  * available.
57abb0f93cSkardel  *
58abb0f93cSkardel  * This deriver is subject to the mitigation rules described in the
59abb0f93cSkardel  * "mitigation rulse and the prefer peer" page. However, there is an
60abb0f93cSkardel  * important difference. If this driver becomes the PPS driver according
61abb0f93cSkardel  * to these rules, it is acrive only if (a) a prefer peer other than
62abb0f93cSkardel  * this driver is among the survivors or (b) there are no survivors and
63abb0f93cSkardel  * the minsane option of the tos command is zero. This is intended to
64abb0f93cSkardel  * support space missions where updates from other spacecraft are
65abb0f93cSkardel  * infrequent, but a reliable PPS signal, such as from an Ultra Stable
66abb0f93cSkardel  * Oscillator (USO) is available.
67abb0f93cSkardel  *
68abb0f93cSkardel  * Fudge Factors
69abb0f93cSkardel  *
70abb0f93cSkardel  * The PPS timestamp is captured on the rising (assert) edge if flag2 is
71abb0f93cSkardel  * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
72abb0f93cSkardel  * (default), the kernel PPS support is disabled; if lit it is enabled.
73abb0f93cSkardel  * If flag4 is lit, each timesampt is copied to the clockstats file for
74abb0f93cSkardel  * later analysis. This can be useful when constructing Allan deviation
75abb0f93cSkardel  * plots. The time1 parameter can be used to compensate for
76abb0f93cSkardel  * miscellaneous device driver and OS delays.
77abb0f93cSkardel  */
78abb0f93cSkardel /*
79abb0f93cSkardel  * Interface definitions
80abb0f93cSkardel  */
81abb0f93cSkardel #define DEVICE		"/dev/pps%d" /* device name and unit */
82abb0f93cSkardel #define	PRECISION	(-20)	/* precision assumed (about 1 us) */
83abb0f93cSkardel #define	REFID		"PPS\0"	/* reference ID */
84abb0f93cSkardel #define	DESCRIPTION	"PPS Clock Discipline" /* WRU */
85abb0f93cSkardel 
86abb0f93cSkardel /*
87abb0f93cSkardel  * PPS unit control structure
88abb0f93cSkardel  */
89abb0f93cSkardel struct ppsunit {
90abb0f93cSkardel 	struct refclock_atom atom; /* atom structure pointer */
91abb0f93cSkardel 	int	fddev;		/* file descriptor */
92abb0f93cSkardel };
93abb0f93cSkardel 
94abb0f93cSkardel /*
95abb0f93cSkardel  * Function prototypes
96abb0f93cSkardel  */
97abb0f93cSkardel static	int	atom_start	(int, struct peer *);
98abb0f93cSkardel static	void	atom_shutdown	(int, struct peer *);
99abb0f93cSkardel static	void	atom_poll	(int, struct peer *);
100abb0f93cSkardel static	void	atom_timer	(int, struct peer *);
101abb0f93cSkardel 
102abb0f93cSkardel /*
103abb0f93cSkardel  * Transfer vector
104abb0f93cSkardel  */
105abb0f93cSkardel struct	refclock refclock_atom = {
106abb0f93cSkardel 	atom_start,		/* start up driver */
107abb0f93cSkardel 	atom_shutdown,		/* shut down driver */
108abb0f93cSkardel 	atom_poll,		/* transmit poll message */
109abb0f93cSkardel 	noentry,		/* control (not used) */
110abb0f93cSkardel 	noentry,		/* initialize driver (not used) */
111abb0f93cSkardel 	noentry,		/* buginfo (not used) */
112abb0f93cSkardel 	atom_timer,		/* called once per second */
113abb0f93cSkardel };
114abb0f93cSkardel 
115abb0f93cSkardel 
116abb0f93cSkardel /*
117abb0f93cSkardel  * atom_start - initialize data for processing
118abb0f93cSkardel  */
119abb0f93cSkardel static int
120abb0f93cSkardel atom_start(
121abb0f93cSkardel 	int unit,		/* unit number (not used) */
122abb0f93cSkardel 	struct peer *peer	/* peer structure pointer */
123abb0f93cSkardel 	)
124abb0f93cSkardel {
125abb0f93cSkardel 	struct refclockproc *pp;
126abb0f93cSkardel 	struct ppsunit *up;
127abb0f93cSkardel 	char	device[80];
128abb0f93cSkardel 
129abb0f93cSkardel 	/*
130abb0f93cSkardel 	 * Allocate and initialize unit structure
131abb0f93cSkardel 	 */
132abb0f93cSkardel 	pp = peer->procptr;
133abb0f93cSkardel 	peer->precision = PRECISION;
134abb0f93cSkardel 	pp->clockdesc = DESCRIPTION;
135abb0f93cSkardel 	pp->stratum = STRATUM_UNSPEC;
136abb0f93cSkardel 	memcpy((char *)&pp->refid, REFID, 4);
137abb0f93cSkardel 	up = emalloc(sizeof(struct ppsunit));
138abb0f93cSkardel 	memset(up, 0, sizeof(struct ppsunit));
1398585484eSchristos 	pp->unitptr = up;
140abb0f93cSkardel 
141abb0f93cSkardel 	/*
142abb0f93cSkardel 	 * Open PPS device. This can be any serial or parallel port and
143abb0f93cSkardel 	 * not necessarily the port used for the associated radio.
144abb0f93cSkardel 	 */
145f003fb54Skardel 	snprintf(device, sizeof(device), DEVICE, unit);
146abb0f93cSkardel 	up->fddev = tty_open(device, O_RDWR, 0777);
147abb0f93cSkardel 	if (up->fddev <= 0) {
148abb0f93cSkardel 		msyslog(LOG_ERR,
149abb0f93cSkardel 			"refclock_atom: %s: %m", device);
150abb0f93cSkardel 		return (0);
151abb0f93cSkardel 	}
152abb0f93cSkardel 
153abb0f93cSkardel 	/*
154abb0f93cSkardel 	 * Light up the PPSAPI interface.
155abb0f93cSkardel 	 */
156abb0f93cSkardel 	return (refclock_ppsapi(up->fddev, &up->atom));
157abb0f93cSkardel }
158abb0f93cSkardel 
159abb0f93cSkardel 
160abb0f93cSkardel /*
161abb0f93cSkardel  * atom_shutdown - shut down the clock
162abb0f93cSkardel  */
163abb0f93cSkardel static void
164abb0f93cSkardel atom_shutdown(
165abb0f93cSkardel 	int unit,		/* unit number (not used) */
166abb0f93cSkardel 	struct peer *peer	/* peer structure pointer */
167abb0f93cSkardel 	)
168abb0f93cSkardel {
169abb0f93cSkardel 	struct refclockproc *pp;
170abb0f93cSkardel 	struct ppsunit *up;
171abb0f93cSkardel 
172abb0f93cSkardel 	pp = peer->procptr;
1738585484eSchristos 	up = pp->unitptr;
174abb0f93cSkardel 	if (up->fddev > 0)
175abb0f93cSkardel 		close(up->fddev);
176abb0f93cSkardel 	free(up);
177abb0f93cSkardel }
178abb0f93cSkardel 
179abb0f93cSkardel /*
180abb0f93cSkardel  * atom_timer - called once per second
181abb0f93cSkardel  */
182abb0f93cSkardel void
183abb0f93cSkardel atom_timer(
184abb0f93cSkardel 	int	unit,		/* unit pointer (not used) */
185abb0f93cSkardel 	struct peer *peer	/* peer structure pointer */
186abb0f93cSkardel 	)
187abb0f93cSkardel {
188abb0f93cSkardel 	struct ppsunit *up;
189abb0f93cSkardel 	struct refclockproc *pp;
190abb0f93cSkardel 	char	tbuf[80];
191abb0f93cSkardel 
192abb0f93cSkardel 	pp = peer->procptr;
1938585484eSchristos 	up = pp->unitptr;
194abb0f93cSkardel 	if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
195abb0f93cSkardel 		return;
196abb0f93cSkardel 
197abb0f93cSkardel 	peer->flags |= FLAG_PPS;
198abb0f93cSkardel 
199abb0f93cSkardel 	/*
200abb0f93cSkardel 	 * If flag4 is lit, record each second offset to clockstats.
201abb0f93cSkardel 	 * That's so we can make awesome Allan deviation plots.
202abb0f93cSkardel 	 */
203abb0f93cSkardel 	if (pp->sloppyclockflag & CLK_FLAG4) {
204f003fb54Skardel 		snprintf(tbuf, sizeof(tbuf), "%.9f",
205f003fb54Skardel 			 pp->filter[pp->coderecv]);
206abb0f93cSkardel 		record_clock_stats(&peer->srcadr, tbuf);
207abb0f93cSkardel 	}
208abb0f93cSkardel }
209abb0f93cSkardel 
210abb0f93cSkardel 
211abb0f93cSkardel /*
212abb0f93cSkardel  * atom_poll - called by the transmit procedure
213abb0f93cSkardel  */
214abb0f93cSkardel static void
215abb0f93cSkardel atom_poll(
216abb0f93cSkardel 	int unit,		/* unit number (not used) */
217abb0f93cSkardel 	struct peer *peer	/* peer structure pointer */
218abb0f93cSkardel 	)
219abb0f93cSkardel {
220abb0f93cSkardel 	struct refclockproc *pp;
221abb0f93cSkardel 
222abb0f93cSkardel 	/*
223abb0f93cSkardel 	 * Don't wiggle the clock until some other driver has numbered
224abb0f93cSkardel 	 * the seconds.
225abb0f93cSkardel 	 */
226abb0f93cSkardel 	if (sys_leap == LEAP_NOTINSYNC)
227abb0f93cSkardel 		return;
228abb0f93cSkardel 
229abb0f93cSkardel 	pp = peer->procptr;
230abb0f93cSkardel 	pp->polls++;
231abb0f93cSkardel 	if (pp->codeproc == pp->coderecv) {
232abb0f93cSkardel 		peer->flags &= ~FLAG_PPS;
233abb0f93cSkardel 		refclock_report(peer, CEVNT_TIMEOUT);
234abb0f93cSkardel 		return;
235abb0f93cSkardel 	}
236abb0f93cSkardel 	pp->lastref = pp->lastrec;
237abb0f93cSkardel 	refclock_receive(peer);
238abb0f93cSkardel }
239abb0f93cSkardel #else
240*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
241abb0f93cSkardel #endif /* REFCLOCK */
242