1 /*	$NetBSD: refclock_atom.c,v 1.1.1.1 2009/12/13 16:55:45 kardel 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
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 = (caddr_t)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 	sprintf(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
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 = (struct ppsunit *)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
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 = (struct ppsunit *)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 		sprintf(tbuf, "%.9f", pp->filter[pp->coderecv]);
205 		record_clock_stats(&peer->srcadr, tbuf);
206 	}
207 }
208 
209 
210 /*
211  * atom_poll - called by the transmit procedure
212  */
213 static void
214 atom_poll(
215 	int unit,		/* unit number (not used) */
216 	struct peer *peer	/* peer structure pointer */
217 	)
218 {
219 	struct refclockproc *pp;
220 
221 	/*
222 	 * Don't wiggle the clock until some other driver has numbered
223 	 * the seconds.
224 	 */
225 	if (sys_leap == LEAP_NOTINSYNC)
226 		return;
227 
228 	pp = peer->procptr;
229 	pp->polls++;
230 	if (pp->codeproc == pp->coderecv) {
231 		peer->flags &= ~FLAG_PPS;
232 		refclock_report(peer, CEVNT_TIMEOUT);
233 		return;
234 	}
235 	pp->lastref = pp->lastrec;
236 	refclock_receive(peer);
237 }
238 #else
239 int refclock_atom_bs;
240 #endif /* REFCLOCK */
241