summaryrefslogtreecommitdiff
path: root/ntpd/refclock_atom.c
blob: b3c0d6b46ab0cb61e2a34bba76d673673fbe50d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * refclock_atom - clock driver for 1-pps signals
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <ctype.h>

#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"

/*
 * This driver requires the PPSAPI interface (RFC 2783)
 */
#if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
#include "ppsapi_timepps.h"
#include "refclock_atom.h"

/*
 * This driver furnishes an interface for pulse-per-second (PPS) signals
 * produced by a cesium clock, timing receiver or related equipment. It
 * can be used to remove accumulated jitter over a congested link and
 * retime a server before redistributing the time to clients. It can 
 *also be used as a holdover should all other synchronization sources
 * beconme unreachable.
 *
 * Before this driver becomes active, the local clock must be set to
 * within +-0.4 s by another means, such as a radio clock or NTP
 * itself. There are two ways to connect the PPS signal, normally at TTL
 * levels, to the computer. One is to shift to EIA levels and connect to
 * pin 8 (DCD) of a serial port. This requires a level converter and
 * may require a one-shot flipflop to lengthen the pulse. The other is
 * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
 * port. These methods are architecture dependent.
 *
 * This driver requires the Pulse-per-Second API for Unix-like Operating
 * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
 * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
 * present only the Tru64 implementation provides the full generality of
 * the API with multiple PPS drivers and multiple handles per driver. If
 * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
 * header file and kernel support specific to each operating system.
 *
 * This driver normally uses the PLL/FLL clock discipline implemented in
 * the ntpd code. Ordinarily, this is the most accurate means, as the
 * median filter in the driver interface is much larger than in the
 * kernel. However, if the systemic clock frequency error is large (tens
 * to hundreds of PPM), it's better to used the kernel support, if
 * available.
 *
 * This deriver is subject to the mitigation rules described in the
 * "mitigation rulse and the prefer peer" page. However, there is an
 * important difference. If this driver becomes the PPS driver according
 * to these rules, it is acrive only if (a) a prefer peer other than
 * this driver is among the survivors or (b) there are no survivors and
 * the minsane option of the tos command is zero. This is intended to
 * support space missions where updates from other spacecraft are
 * infrequent, but a reliable PPS signal, such as from an Ultra Stable
 * Oscillator (USO) is available.
 *
 * Fudge Factors
 *
 * The PPS timestamp is captured on the rising (assert) edge if flag2 is
 * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
 * (default), the kernel PPS support is disabled; if lit it is enabled.
 * If flag4 is lit, each timesampt is copied to the clockstats file for
 * later analysis. This can be useful when constructing Allan deviation
 * plots. The time1 parameter can be used to compensate for
 * miscellaneous device driver and OS delays.
 */
/*
 * Interface definitions
 */
#define DEVICE		"/dev/pps%d" /* device name and unit */
#define	PRECISION	(-20)	/* precision assumed (about 1 us) */
#define	REFID		"PPS\0"	/* reference ID */
#define	DESCRIPTION	"PPS Clock Discipline" /* WRU */

/*
 * PPS unit control structure
 */
struct ppsunit {
	struct refclock_atom atom; /* atom structure pointer */
	int	fddev;		/* file descriptor */
};

/*
 * Function prototypes
 */
static	int	atom_start	(int, struct peer *);
static	void	atom_shutdown	(int, struct peer *);
static	void	atom_poll	(int, struct peer *);
static	void	atom_timer	(int, struct peer *);

/*
 * Transfer vector
 */
struct	refclock refclock_atom = {
	atom_start,		/* start up driver */
	atom_shutdown,		/* shut down driver */
	atom_poll,		/* transmit poll message */
	noentry,		/* control (not used) */
	noentry,		/* initialize driver (not used) */
	noentry,		/* buginfo (not used) */
	atom_timer,		/* called once per second */
};


/*
 * atom_start - initialize data for processing
 */
static int
atom_start(
	int unit,		/* unit number (not used) */
	struct peer *peer	/* peer structure pointer */
	)
{
	struct refclockproc *pp;
	struct ppsunit *up;
	char	device[80];

	/*
	 * Allocate and initialize unit structure
	 */
	pp = peer->procptr;
	peer->precision = PRECISION;
	pp->clockdesc = DESCRIPTION;
	pp->stratum = STRATUM_UNSPEC;
	memcpy((char *)&pp->refid, REFID, 4);
	up = emalloc(sizeof(struct ppsunit));
	memset(up, 0, sizeof(struct ppsunit));
	pp->unitptr = up;

	/*
	 * Open PPS device. This can be any serial or parallel port and
	 * not necessarily the port used for the associated radio.
	 */
	snprintf(device, sizeof(device), DEVICE, unit);
	up->fddev = tty_open(device, O_RDWR, 0777);
	if (up->fddev <= 0) {
		msyslog(LOG_ERR,
			"refclock_atom: %s: %m", device);
		return (0);
	}

	/*
	 * Light up the PPSAPI interface.
	 */
	return (refclock_ppsapi(up->fddev, &up->atom));
}


/*
 * atom_shutdown - shut down the clock
 */
static void
atom_shutdown(
	int unit,		/* unit number (not used) */
	struct peer *peer	/* peer structure pointer */
	)
{
	struct refclockproc *pp;
	struct ppsunit *up;

	pp = peer->procptr;
	up = pp->unitptr;
	if (up->fddev > 0)
		close(up->fddev);
	free(up);
}

/*
 * atom_timer - called once per second
 */
void
atom_timer(
	int	unit,		/* unit pointer (not used) */
	struct peer *peer	/* peer structure pointer */
	)
{
	struct ppsunit *up;
	struct refclockproc *pp;
	char	tbuf[80];

	pp = peer->procptr;
	up = pp->unitptr;
	if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
		return;

	peer->flags |= FLAG_PPS;

	/*
	 * If flag4 is lit, record each second offset to clockstats.
	 * That's so we can make awesome Allan deviation plots.
	 */
	if (pp->sloppyclockflag & CLK_FLAG4) {
		snprintf(tbuf, sizeof(tbuf), "%.9f",
			 pp->filter[pp->coderecv]);
		record_clock_stats(&peer->srcadr, tbuf);
	}
}


/*
 * atom_poll - called by the transmit procedure
 */
static void
atom_poll(
	int unit,		/* unit number (not used) */
	struct peer *peer	/* peer structure pointer */
	)
{
	struct refclockproc *pp;

	/*
	 * Don't wiggle the clock until some other driver has numbered
	 * the seconds.
	 */
	if (sys_leap == LEAP_NOTINSYNC)
		return;

	pp = peer->procptr;
	pp->polls++;
	if (pp->codeproc == pp->coderecv) {
		peer->flags &= ~FLAG_PPS;
		refclock_report(peer, CEVNT_TIMEOUT);
		return;
	}
	pp->lastref = pp->lastrec;
	refclock_receive(peer);
}
#else
int refclock_atom_bs;
#endif /* REFCLOCK */