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