154359Sroberto/*
254359Sroberto * refclock_atom - clock driver for 1-pps signals
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
554359Sroberto#include <config.h>
654359Sroberto#endif
754359Sroberto
854359Sroberto#include <stdio.h>
954359Sroberto#include <ctype.h>
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_unixtime.h"
1454359Sroberto#include "ntp_refclock.h"
1554359Sroberto#include "ntp_stdlib.h"
1654359Sroberto
17280849Scy/*
18280849Scy * This driver requires the PPSAPI interface (RFC 2783)
19280849Scy */
20280849Scy#if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
21280849Scy#include "ppsapi_timepps.h"
22280849Scy#include "refclock_atom.h"
2382498Sroberto
2454359Sroberto/*
2554359Sroberto * This driver furnishes an interface for pulse-per-second (PPS) signals
2682498Sroberto * produced by a cesium clock, timing receiver or related equipment. It
27280849Scy * can be used to remove accumulated jitter over a congested link and
28280849Scy * retime a server before redistributing the time to clients. It can
29280849Scy *also be used as a holdover should all other synchronization sources
30280849Scy * beconme unreachable.
3154359Sroberto *
3282498Sroberto * Before this driver becomes active, the local clock must be set to
33280849Scy * within +-0.4 s by another means, such as a radio clock or NTP
3482498Sroberto * itself. There are two ways to connect the PPS signal, normally at TTL
3582498Sroberto * levels, to the computer. One is to shift to EIA levels and connect to
3682498Sroberto * pin 8 (DCD) of a serial port. This requires a level converter and
3782498Sroberto * may require a one-shot flipflop to lengthen the pulse. The other is
3882498Sroberto * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
3982498Sroberto * port. These methods are architecture dependent.
4054359Sroberto *
41280849Scy * This driver requires the Pulse-per-Second API for Unix-like Operating
4282498Sroberto * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
43280849Scy * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
44280849Scy * present only the Tru64 implementation provides the full generality of
45182007Sroberto * the API with multiple PPS drivers and multiple handles per driver. If
46182007Sroberto * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
47182007Sroberto * header file and kernel support specific to each operating system.
4854359Sroberto *
4982498Sroberto * This driver normally uses the PLL/FLL clock discipline implemented in
50182007Sroberto * the ntpd code. Ordinarily, this is the most accurate means, as the
51182007Sroberto * median filter in the driver interface is much larger than in the
52182007Sroberto * kernel. However, if the systemic clock frequency error is large (tens
53182007Sroberto * to hundreds of PPM), it's better to used the kernel support, if
54182007Sroberto * available.
5554359Sroberto *
56280849Scy * This deriver is subject to the mitigation rules described in the
57280849Scy * "mitigation rulse and the prefer peer" page. However, there is an
58280849Scy * important difference. If this driver becomes the PPS driver according
59280849Scy * to these rules, it is acrive only if (a) a prefer peer other than
60280849Scy * this driver is among the survivors or (b) there are no survivors and
61280849Scy * the minsane option of the tos command is zero. This is intended to
62280849Scy * support space missions where updates from other spacecraft are
63280849Scy * infrequent, but a reliable PPS signal, such as from an Ultra Stable
64280849Scy * Oscillator (USO) is available.
65280849Scy *
6654359Sroberto * Fudge Factors
6754359Sroberto *
68280849Scy * The PPS timestamp is captured on the rising (assert) edge if flag2 is
69280849Scy * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
70280849Scy * (default), the kernel PPS support is disabled; if lit it is enabled.
71280849Scy * If flag4 is lit, each timesampt is copied to the clockstats file for
72280849Scy * later analysis. This can be useful when constructing Allan deviation
73280849Scy * plots. The time1 parameter can be used to compensate for
74280849Scy * miscellaneous device driver and OS delays.
7554359Sroberto */
7654359Sroberto/*
7754359Sroberto * Interface definitions
7854359Sroberto */
7982498Sroberto#define DEVICE		"/dev/pps%d" /* device name and unit */
8054359Sroberto#define	PRECISION	(-20)	/* precision assumed (about 1 us) */
8154359Sroberto#define	REFID		"PPS\0"	/* reference ID */
8254359Sroberto#define	DESCRIPTION	"PPS Clock Discipline" /* WRU */
8354359Sroberto
8454359Sroberto/*
8582498Sroberto * PPS unit control structure
8654359Sroberto */
8782498Srobertostruct ppsunit {
88280849Scy	struct refclock_atom atom; /* atom structure pointer */
89280849Scy	int	fddev;		/* file descriptor */
9082498Sroberto};
9154359Sroberto
9254359Sroberto/*
9354359Sroberto * Function prototypes
9454359Sroberto */
95280849Scystatic	int	atom_start	(int, struct peer *);
96280849Scystatic	void	atom_shutdown	(int, struct peer *);
97280849Scystatic	void	atom_poll	(int, struct peer *);
98280849Scystatic	void	atom_timer	(int, struct peer *);
9954359Sroberto
10054359Sroberto/*
10154359Sroberto * Transfer vector
10254359Sroberto */
10354359Srobertostruct	refclock refclock_atom = {
10454359Sroberto	atom_start,		/* start up driver */
10554359Sroberto	atom_shutdown,		/* shut down driver */
10654359Sroberto	atom_poll,		/* transmit poll message */
107280849Scy	noentry,		/* control (not used) */
108182007Sroberto	noentry,		/* initialize driver (not used) */
109182007Sroberto	noentry,		/* buginfo (not used) */
110182007Sroberto	atom_timer,		/* called once per second */
111182007Sroberto};
11254359Sroberto
11354359Sroberto
11454359Sroberto/*
11554359Sroberto * atom_start - initialize data for processing
11654359Sroberto */
11754359Srobertostatic int
11854359Srobertoatom_start(
11982498Sroberto	int unit,		/* unit number (not used) */
12082498Sroberto	struct peer *peer	/* peer structure pointer */
12154359Sroberto	)
12254359Sroberto{
12354359Sroberto	struct refclockproc *pp;
124280849Scy	struct ppsunit *up;
125182007Sroberto	char	device[80];
12654359Sroberto
12782498Sroberto	/*
12882498Sroberto	 * Allocate and initialize unit structure
12982498Sroberto	 */
13082498Sroberto	pp = peer->procptr;
13182498Sroberto	peer->precision = PRECISION;
13282498Sroberto	pp->clockdesc = DESCRIPTION;
133132451Sroberto	pp->stratum = STRATUM_UNSPEC;
13482498Sroberto	memcpy((char *)&pp->refid, REFID, 4);
13582498Sroberto	up = emalloc(sizeof(struct ppsunit));
13682498Sroberto	memset(up, 0, sizeof(struct ppsunit));
137280849Scy	pp->unitptr = up;
13854359Sroberto
13954359Sroberto	/*
140182007Sroberto	 * Open PPS device. This can be any serial or parallel port and
141182007Sroberto	 * not necessarily the port used for the associated radio.
14254359Sroberto	 */
143280849Scy	snprintf(device, sizeof(device), DEVICE, unit);
144280849Scy	up->fddev = tty_open(device, O_RDWR, 0777);
14582498Sroberto	if (up->fddev <= 0) {
14682498Sroberto		msyslog(LOG_ERR,
147280849Scy			"refclock_atom: %s: %m", device);
14882498Sroberto		return (0);
14982498Sroberto	}
15054359Sroberto
15154359Sroberto	/*
152280849Scy	 * Light up the PPSAPI interface.
15354359Sroberto	 */
154280849Scy	return (refclock_ppsapi(up->fddev, &up->atom));
15582498Sroberto}
15682498Sroberto
15782498Sroberto
158182007Sroberto/*
159182007Sroberto * atom_shutdown - shut down the clock
160182007Sroberto */
161182007Srobertostatic void
162182007Srobertoatom_shutdown(
163182007Sroberto	int unit,		/* unit number (not used) */
164182007Sroberto	struct peer *peer	/* peer structure pointer */
165182007Sroberto	)
166182007Sroberto{
167182007Sroberto	struct refclockproc *pp;
168280849Scy	struct ppsunit *up;
169182007Sroberto
170182007Sroberto	pp = peer->procptr;
171280849Scy	up = pp->unitptr;
172182007Sroberto	if (up->fddev > 0)
173182007Sroberto		close(up->fddev);
174182007Sroberto	free(up);
175182007Sroberto}
176182007Sroberto
17782498Sroberto/*
178182007Sroberto * atom_timer - called once per second
17954359Sroberto */
180280849Scyvoid
181182007Srobertoatom_timer(
182280849Scy	int	unit,		/* unit pointer (not used) */
18382498Sroberto	struct peer *peer	/* peer structure pointer */
18454359Sroberto	)
18554359Sroberto{
186280849Scy	struct ppsunit *up;
18754359Sroberto	struct refclockproc *pp;
188280849Scy	char	tbuf[80];
18954359Sroberto
19054359Sroberto	pp = peer->procptr;
191280849Scy	up = pp->unitptr;
192280849Scy	if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
193182007Sroberto		return;
194182007Sroberto
195280849Scy	peer->flags |= FLAG_PPS;
196182007Sroberto
197182007Sroberto	/*
198182007Sroberto	 * If flag4 is lit, record each second offset to clockstats.
199182007Sroberto	 * That's so we can make awesome Allan deviation plots.
200182007Sroberto	 */
201280849Scy	if (pp->sloppyclockflag & CLK_FLAG4) {
202280849Scy		snprintf(tbuf, sizeof(tbuf), "%.9f",
203280849Scy			 pp->filter[pp->coderecv]);
204182007Sroberto		record_clock_stats(&peer->srcadr, tbuf);
205182007Sroberto	}
20654359Sroberto}
20754359Sroberto
20854359Sroberto
20954359Sroberto/*
21054359Sroberto * atom_poll - called by the transmit procedure
21154359Sroberto */
21254359Srobertostatic void
21354359Srobertoatom_poll(
21482498Sroberto	int unit,		/* unit number (not used) */
21582498Sroberto	struct peer *peer	/* peer structure pointer */
21654359Sroberto	)
21754359Sroberto{
21854359Sroberto	struct refclockproc *pp;
21954359Sroberto
22054359Sroberto	/*
221280849Scy	 * Don't wiggle the clock until some other driver has numbered
222280849Scy	 * the seconds.
22354359Sroberto	 */
224280849Scy	if (sys_leap == LEAP_NOTINSYNC)
225280849Scy		return;
226280849Scy
227280849Scy	pp = peer->procptr;
228280849Scy	pp->polls++;
22982498Sroberto	if (pp->codeproc == pp->coderecv) {
230280849Scy		peer->flags &= ~FLAG_PPS;
23182498Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
23282498Sroberto		return;
23354359Sroberto	}
234132451Sroberto	pp->lastref = pp->lastrec;
23554359Sroberto	refclock_receive(peer);
23654359Sroberto}
23754359Sroberto#else
23854359Srobertoint refclock_atom_bs;
23954359Sroberto#endif /* REFCLOCK */
240