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