xref: /freebsd/contrib/ntp/ntpd/refclock_jupiter.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
29c2daa00SOllivier Robert  * Copyright (c) 1997, 1998, 2003
3c0b746e5SOllivier Robert  *	The Regents of the University of California.  All rights reserved.
4c0b746e5SOllivier Robert  *
5c0b746e5SOllivier Robert  * Redistribution and use in source and binary forms, with or without
6c0b746e5SOllivier Robert  * modification, are permitted provided that the following conditions
7c0b746e5SOllivier Robert  * are met:
8c0b746e5SOllivier Robert  * 1. Redistributions of source code must retain the above copyright
9c0b746e5SOllivier Robert  *    notice, this list of conditions and the following disclaimer.
10c0b746e5SOllivier Robert  * 2. Redistributions in binary form must reproduce the above copyright
11c0b746e5SOllivier Robert  *    notice, this list of conditions and the following disclaimer in the
12c0b746e5SOllivier Robert  *    documentation and/or other materials provided with the distribution.
13c0b746e5SOllivier Robert  * 3. All advertising materials mentioning features or use of this software
14c0b746e5SOllivier Robert  *    must display the following acknowledgement:
15c0b746e5SOllivier Robert  *	This product includes software developed by the University of
16c0b746e5SOllivier Robert  *	California, Lawrence Berkeley Laboratory.
17c0b746e5SOllivier Robert  * 4. The name of the University may not be used to endorse or promote
18c0b746e5SOllivier Robert  *    products derived from this software without specific prior
19c0b746e5SOllivier Robert  *    written permission.
20c0b746e5SOllivier Robert  *
21c0b746e5SOllivier Robert  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22c0b746e5SOllivier Robert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23c0b746e5SOllivier Robert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24c0b746e5SOllivier Robert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25c0b746e5SOllivier Robert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26c0b746e5SOllivier Robert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27c0b746e5SOllivier Robert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28c0b746e5SOllivier Robert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29c0b746e5SOllivier Robert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30c0b746e5SOllivier Robert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31c0b746e5SOllivier Robert  * SUCH DAMAGE.
32c0b746e5SOllivier Robert  */
33c0b746e5SOllivier Robert 
34c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
35c0b746e5SOllivier Robert # include <config.h>
36c0b746e5SOllivier Robert #endif
37c0b746e5SOllivier Robert 
382d4e511cSCy Schubert /* This clock *REQUIRES* the PPS API to be available */
399c2daa00SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
40c0b746e5SOllivier Robert 
41c0b746e5SOllivier Robert #include "ntpd.h"
42c0b746e5SOllivier Robert #include "ntp_io.h"
43c0b746e5SOllivier Robert #include "ntp_refclock.h"
44c0b746e5SOllivier Robert #include "ntp_unixtime.h"
45c0b746e5SOllivier Robert #include "ntp_stdlib.h"
462d4e511cSCy Schubert #include "ntp_calendar.h"
472d4e511cSCy Schubert #include "ntp_calgps.h"
482d4e511cSCy Schubert #include "timespecops.h"
49c0b746e5SOllivier Robert 
50224ba2bdSOllivier Robert #include <stdio.h>
51224ba2bdSOllivier Robert #include <ctype.h>
52224ba2bdSOllivier Robert 
53c0b746e5SOllivier Robert #include "jupiter.h"
54ea906c41SOllivier Robert #include "ppsapi_timepps.h"
55c0b746e5SOllivier Robert 
562b15cb3dSCy Schubert #ifdef WORDS_BIGENDIAN
57c0b746e5SOllivier Robert #define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
58c0b746e5SOllivier Robert #define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
59c0b746e5SOllivier Robert #else
602b15cb3dSCy Schubert #define getshort(s) ((u_short)(s))
612b15cb3dSCy Schubert #define putshort(s) ((u_short)(s))
62c0b746e5SOllivier Robert #endif
63c0b746e5SOllivier Robert 
64c0b746e5SOllivier Robert /*
65c0b746e5SOllivier Robert  * This driver supports the Rockwell Jupiter GPS Receiver board
66c0b746e5SOllivier Robert  * adapted to precision timing applications.  It requires the
67c0b746e5SOllivier Robert  * ppsclock line discipline or streams module described in the
68c0b746e5SOllivier Robert  * Line Disciplines and Streams Drivers page. It also requires a
69c0b746e5SOllivier Robert  * gadget box and 1-PPS level converter, such as described in the
70c0b746e5SOllivier Robert  * Pulse-per-second (PPS) Signal Interfacing page.
71c0b746e5SOllivier Robert  *
72c0b746e5SOllivier Robert  * It may work (with minor modifications) with other Rockwell GPS
73c0b746e5SOllivier Robert  * receivers such as the CityTracker.
74c0b746e5SOllivier Robert  */
75c0b746e5SOllivier Robert 
76c0b746e5SOllivier Robert /*
77c0b746e5SOllivier Robert  * GPS Definitions
78c0b746e5SOllivier Robert  */
79c0b746e5SOllivier Robert #define	DEVICE		"/dev/gps%d"	/* device name and unit */
80c0b746e5SOllivier Robert #define	SPEED232	B9600		/* baud */
81c0b746e5SOllivier Robert 
82c0b746e5SOllivier Robert /*
83c0b746e5SOllivier Robert  * Radio interface parameters
84c0b746e5SOllivier Robert  */
85c0b746e5SOllivier Robert #define	PRECISION	(-18)	/* precision assumed (about 4 us) */
86c0b746e5SOllivier Robert #define	REFID	"GPS\0"		/* reference id */
87c0b746e5SOllivier Robert #define	DESCRIPTION	"Rockwell Jupiter GPS Receiver" /* who we are */
88c0b746e5SOllivier Robert #define	DEFFUDGETIME	0	/* default fudge time (ms) */
89c0b746e5SOllivier Robert 
90c0b746e5SOllivier Robert /* Unix timestamp for the GPS epoch: January 6, 1980 */
91c0b746e5SOllivier Robert #define GPS_EPOCH 315964800
92c0b746e5SOllivier Robert 
93f391d6bcSXin LI /* Rata Die Number of first day of GPS epoch. This is the number of days
94f391d6bcSXin LI  * since 0000-12-31 to 1980-01-06 in the proleptic Gregorian Calendar.
95f391d6bcSXin LI  */
96f391d6bcSXin LI #define RDN_GPS_EPOCH (4*146097 + 138431 + 1)
97f391d6bcSXin LI 
98c0b746e5SOllivier Robert /* Double short to unsigned int */
99c0b746e5SOllivier Robert #define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
100c0b746e5SOllivier Robert 
101c0b746e5SOllivier Robert /* Double short to signed int */
102c0b746e5SOllivier Robert #define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
103c0b746e5SOllivier Robert 
104c0b746e5SOllivier Robert /* One week's worth of seconds */
105c0b746e5SOllivier Robert #define WEEKSECS (7 * 24 * 60 * 60)
106c0b746e5SOllivier Robert 
107c0b746e5SOllivier Robert /*
108c0b746e5SOllivier Robert  * Jupiter unit control structure.
109c0b746e5SOllivier Robert  */
1109c2daa00SOllivier Robert struct instance {
1119c2daa00SOllivier Robert 	struct peer *peer;		/* peer */
1122d4e511cSCy Schubert 
1139c2daa00SOllivier Robert 	pps_params_t pps_params;	/* pps parameters */
1149c2daa00SOllivier Robert 	pps_info_t pps_info;		/* last pps data */
1159c2daa00SOllivier Robert 	pps_handle_t pps_handle;	/* pps handle */
1169c2daa00SOllivier Robert 	u_int	assert;			/* pps edge to use */
117ea906c41SOllivier Robert 	u_int	hardpps;		/* enable kernel mode */
1182d4e511cSCy Schubert 	l_fp 		rcv_pps;	/* last pps timestamp */
1192d4e511cSCy Schubert 	l_fp	        rcv_next;	/* rcv time of next reftime */
1202d4e511cSCy Schubert 	TGpsDatum	ref_next;	/* next GPS time stamp to use with PPS */
1212d4e511cSCy Schubert 	TGpsDatum	piv_next;	/* pivot for week date unfolding */
1222d4e511cSCy Schubert 	uint16_t	piv_hold;	/* TTL for pivot value */
1232d4e511cSCy Schubert 	uint16_t	rcvtout;	/* receive timeout ticker */
124c0b746e5SOllivier Robert 	int wantid;			/* don't reconfig on channel id msg */
125c0b746e5SOllivier Robert 	u_int  moving;			/* mobile platform? */
1269c2daa00SOllivier Robert 	u_char sloppyclockflag;		/* fudge flags */
127c0b746e5SOllivier Robert 	u_short sbuf[512];		/* local input buffer */
128c0b746e5SOllivier Robert 	int ssize;			/* space used in sbuf */
129c0b746e5SOllivier Robert };
130c0b746e5SOllivier Robert 
131c0b746e5SOllivier Robert /*
132c0b746e5SOllivier Robert  * Function prototypes
133c0b746e5SOllivier Robert  */
1342d4e511cSCy Schubert static	void	jupiter_canmsg	(struct instance * const, u_int);
1352b15cb3dSCy Schubert static	u_short	jupiter_cksum	(u_short *, u_int);
1362d4e511cSCy Schubert static	int	jupiter_config	(struct instance * const);
1372b15cb3dSCy Schubert static	void	jupiter_debug	(struct peer *, const char *,
1384e1ef62aSXin LI 				 const char *, ...) NTP_PRINTF(3, 4);
1392d4e511cSCy Schubert static	const char *	jupiter_parse_t	(struct instance * const, u_short *, l_fp);
1402d4e511cSCy Schubert static	const char *	jupiter_parse_gpos(struct instance * const, u_short *);
1412d4e511cSCy Schubert static	void	jupiter_platform(struct instance * const, u_int);
1422b15cb3dSCy Schubert static	void	jupiter_poll	(int, struct peer *);
1432b15cb3dSCy Schubert static	void	jupiter_control	(int, const struct refclockstat *,
1442b15cb3dSCy Schubert 				 struct refclockstat *, struct peer *);
1452d4e511cSCy Schubert static	int	jupiter_ppsapi	(struct instance * const);
1462d4e511cSCy Schubert static	int	jupiter_pps	(struct instance * const);
1472d4e511cSCy Schubert static	int	jupiter_recv	(struct instance * const);
1482d4e511cSCy Schubert static	void	jupiter_receive (struct recvbuf * const rbufp);
1492d4e511cSCy Schubert static	void	jupiter_reqmsg	(struct instance * const, u_int, u_int);
1502d4e511cSCy Schubert static	void	jupiter_reqonemsg(struct instance * const, u_int);
1512d4e511cSCy Schubert static	char *	jupiter_send	(struct instance * const, struct jheader *);
1522b15cb3dSCy Schubert static	void	jupiter_shutdown(int, struct peer *);
1532b15cb3dSCy Schubert static	int	jupiter_start	(int, struct peer *);
1542d4e511cSCy Schubert static	void	jupiter_ticker	(int, struct peer *);
155c0b746e5SOllivier Robert 
156c0b746e5SOllivier Robert /*
157c0b746e5SOllivier Robert  * Transfer vector
158c0b746e5SOllivier Robert  */
159c0b746e5SOllivier Robert struct	refclock refclock_jupiter = {
160c0b746e5SOllivier Robert 	jupiter_start,		/* start up driver */
161c0b746e5SOllivier Robert 	jupiter_shutdown,	/* shut down driver */
162c0b746e5SOllivier Robert 	jupiter_poll,		/* transmit poll message */
1639c2daa00SOllivier Robert 	jupiter_control,	/* (clock control) */
164c0b746e5SOllivier Robert 	noentry,		/* (clock init) */
165c0b746e5SOllivier Robert 	noentry,		/* (clock buginfo) */
1662d4e511cSCy Schubert 	jupiter_ticker		/* 1HZ ticker */
167c0b746e5SOllivier Robert };
168c0b746e5SOllivier Robert 
169c0b746e5SOllivier Robert /*
170c0b746e5SOllivier Robert  * jupiter_start - open the devices and initialize data for processing
171c0b746e5SOllivier Robert  */
172c0b746e5SOllivier Robert static int
173c0b746e5SOllivier Robert jupiter_start(
1749c2daa00SOllivier Robert 	int unit,
1759c2daa00SOllivier Robert 	struct peer *peer
176c0b746e5SOllivier Robert 	)
177c0b746e5SOllivier Robert {
1782d4e511cSCy Schubert 	struct refclockproc * const pp = peer->procptr;
1792d4e511cSCy Schubert 	struct instance * up;
1802b15cb3dSCy Schubert 	int fd;
181c0b746e5SOllivier Robert 	char gpsdev[20];
182c0b746e5SOllivier Robert 
183c0b746e5SOllivier Robert 	/*
184c0b746e5SOllivier Robert 	 * Open serial port
185c0b746e5SOllivier Robert 	 */
1862b15cb3dSCy Schubert 	snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
187a466cc55SCy Schubert 	fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_RAW);
1882b15cb3dSCy Schubert 	if (fd <= 0) {
1892b15cb3dSCy Schubert 		jupiter_debug(peer, "jupiter_start", "open %s: %m",
1902b15cb3dSCy Schubert 			      gpsdev);
191c0b746e5SOllivier Robert 		return (0);
192c0b746e5SOllivier Robert 	}
193c0b746e5SOllivier Robert 
194c0b746e5SOllivier Robert 	/* Allocate unit structure */
1952d4e511cSCy Schubert 	up = emalloc_zero(sizeof(*up));
1962d4e511cSCy Schubert 	up->peer = peer;
197c0b746e5SOllivier Robert 	pp->io.clock_recv = jupiter_receive;
1982b15cb3dSCy Schubert 	pp->io.srcclock = peer;
199c0b746e5SOllivier Robert 	pp->io.datalen = 0;
200c0b746e5SOllivier Robert 	pp->io.fd = fd;
201c0b746e5SOllivier Robert 	if (!io_addclock(&pp->io)) {
2022b15cb3dSCy Schubert 		close(fd);
2032b15cb3dSCy Schubert 		pp->io.fd = -1;
2042d4e511cSCy Schubert 		free(up);
205c0b746e5SOllivier Robert 		return (0);
206c0b746e5SOllivier Robert 	}
2072d4e511cSCy Schubert 	pp->unitptr = up;
208c0b746e5SOllivier Robert 
209c0b746e5SOllivier Robert 	/*
210c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
211c0b746e5SOllivier Robert 	 */
212c0b746e5SOllivier Robert 	peer->precision = PRECISION;
213c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
214c0b746e5SOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
215c0b746e5SOllivier Robert 
2162d4e511cSCy Schubert 	up->assert = 1;
2172d4e511cSCy Schubert 	up->hardpps = 0;
2189c2daa00SOllivier Robert 	/*
2199c2daa00SOllivier Robert 	 * Start the PPSAPI interface if it is there. Default to use
2209c2daa00SOllivier Robert 	 * the assert edge and do not enable the kernel hardpps.
2219c2daa00SOllivier Robert 	 */
2222d4e511cSCy Schubert 	if (time_pps_create(fd, &up->pps_handle) < 0) {
2232d4e511cSCy Schubert 		up->pps_handle = 0;
2249c2daa00SOllivier Robert 		msyslog(LOG_ERR,
2259c2daa00SOllivier Robert 			"refclock_jupiter: time_pps_create failed: %m");
2269c2daa00SOllivier Robert 	}
2272d4e511cSCy Schubert 	else if (!jupiter_ppsapi(up))
2289c2daa00SOllivier Robert 		goto clean_up;
229c0b746e5SOllivier Robert 
230c0b746e5SOllivier Robert 	/* Ensure the receiver is properly configured */
2312d4e511cSCy Schubert 	if (!jupiter_config(up))
2329c2daa00SOllivier Robert 		goto clean_up;
233c0b746e5SOllivier Robert 
2342d4e511cSCy Schubert 	jupiter_pps(up);	/* get current PPS state */
235c0b746e5SOllivier Robert 	return (1);
2369c2daa00SOllivier Robert 
2379c2daa00SOllivier Robert clean_up:
2389c2daa00SOllivier Robert 	jupiter_shutdown(unit, peer);
2399c2daa00SOllivier Robert 	pp->unitptr = 0;
2409c2daa00SOllivier Robert 	return (0);
241c0b746e5SOllivier Robert }
242c0b746e5SOllivier Robert 
243c0b746e5SOllivier Robert /*
244c0b746e5SOllivier Robert  * jupiter_shutdown - shut down the clock
245c0b746e5SOllivier Robert  */
246c0b746e5SOllivier Robert static void
2479c2daa00SOllivier Robert jupiter_shutdown(int unit, struct peer *peer)
248c0b746e5SOllivier Robert {
2492d4e511cSCy Schubert 	struct refclockproc * const pp = peer->procptr;
2502d4e511cSCy Schubert 	struct instance *     const up = pp->unitptr;
251c0b746e5SOllivier Robert 
2522d4e511cSCy Schubert 	if (!up)
2539c2daa00SOllivier Robert 		return;
2549c2daa00SOllivier Robert 
2552d4e511cSCy Schubert 	if (up->pps_handle) {
2562d4e511cSCy Schubert 		time_pps_destroy(up->pps_handle);
2572d4e511cSCy Schubert 		up->pps_handle = 0;
2589c2daa00SOllivier Robert 	}
2599c2daa00SOllivier Robert 
2602b15cb3dSCy Schubert 	if (pp->io.fd != -1)
261c0b746e5SOllivier Robert 		io_closeclock(&pp->io);
2622d4e511cSCy Schubert 	free(up);
263c0b746e5SOllivier Robert }
264c0b746e5SOllivier Robert 
265c0b746e5SOllivier Robert /*
266c0b746e5SOllivier Robert  * jupiter_config - Configure the receiver
267c0b746e5SOllivier Robert  */
2689c2daa00SOllivier Robert static int
2692d4e511cSCy Schubert jupiter_config(struct instance * const up)
270c0b746e5SOllivier Robert {
2712d4e511cSCy Schubert 	jupiter_debug(up->peer, __func__, "init receiver");
272c0b746e5SOllivier Robert 
273c0b746e5SOllivier Robert 	/*
274c0b746e5SOllivier Robert 	 * Initialize the unit variables
275c0b746e5SOllivier Robert 	 */
2762d4e511cSCy Schubert 	up->sloppyclockflag = up->peer->procptr->sloppyclockflag;
2772d4e511cSCy Schubert 	up->moving = !!(up->sloppyclockflag & CLK_FLAG2);
2782d4e511cSCy Schubert 	if (up->moving)
2792d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "mobile platform");
280c0b746e5SOllivier Robert 
2812d4e511cSCy Schubert 	ZERO(up->rcv_next);
2822d4e511cSCy Schubert 	ZERO(up->ref_next);
2832d4e511cSCy Schubert 	ZERO(up->piv_next);
2842d4e511cSCy Schubert 	up->ssize = 0;
285c0b746e5SOllivier Robert 
286c0b746e5SOllivier Robert 	/* Stop outputting all messages */
2872d4e511cSCy Schubert 	jupiter_canmsg(up, JUPITER_ALL);
288c0b746e5SOllivier Robert 
289c0b746e5SOllivier Robert 	/* Request the receiver id so we can syslog the firmware version */
2902d4e511cSCy Schubert 	jupiter_reqonemsg(up, JUPITER_O_ID);
291c0b746e5SOllivier Robert 
292c0b746e5SOllivier Robert 	/* Flag that this the id was requested (so we don't get called again) */
2932d4e511cSCy Schubert 	up->wantid = 1;
294c0b746e5SOllivier Robert 
295c0b746e5SOllivier Robert 	/* Request perodic time mark pulse messages */
2962d4e511cSCy Schubert 	jupiter_reqmsg(up, JUPITER_O_PULSE, 1);
2979c2daa00SOllivier Robert 
2989c2daa00SOllivier Robert 	/* Request perodic geodetic position status */
2992d4e511cSCy Schubert 	jupiter_reqmsg(up, JUPITER_O_GPOS, 1);
300c0b746e5SOllivier Robert 
301c0b746e5SOllivier Robert 	/* Set application platform type */
3022d4e511cSCy Schubert 	if (up->moving)
3032d4e511cSCy Schubert 		jupiter_platform(up, JUPITER_I_PLAT_MED);
304c0b746e5SOllivier Robert 	else
3052d4e511cSCy Schubert 		jupiter_platform(up, JUPITER_I_PLAT_LOW);
3069c2daa00SOllivier Robert 
3079c2daa00SOllivier Robert 	return (1);
308c0b746e5SOllivier Robert }
309c0b746e5SOllivier Robert 
3102d4e511cSCy Schubert static void
3112d4e511cSCy Schubert jupiter_checkpps(
3122d4e511cSCy Schubert 	struct refclockproc * const pp,
3132d4e511cSCy Schubert 	struct instance *     const up
3142d4e511cSCy Schubert 	)
3152d4e511cSCy Schubert {
3162d4e511cSCy Schubert 	l_fp		tstamp, delta;
3172d4e511cSCy Schubert 	struct calendar	cd;
3182d4e511cSCy Schubert 
3192d4e511cSCy Schubert 	if (jupiter_pps(up) || !up->piv_next.weeks)
3202d4e511cSCy Schubert 		return;
3212d4e511cSCy Schubert 
3222d4e511cSCy Schubert 	/* check delay between pulse message and pulse. */
3232d4e511cSCy Schubert 	delta = up->rcv_pps;		/* set by jupiter_pps() */
3242d4e511cSCy Schubert 	L_SUB(&delta, &up->rcv_next);	/* recv time pulse message */
3252d4e511cSCy Schubert 	if (delta.l_ui != 0 || delta.l_uf >= 0xC0000000) {
3262d4e511cSCy Schubert 		up->ref_next.weeks = 0;	/* consider as consumed... */
3272d4e511cSCy Schubert 		return;
3282d4e511cSCy Schubert 	}
3292d4e511cSCy Schubert 
3302d4e511cSCy Schubert 	pp->lastrec = up->rcv_pps;
3312d4e511cSCy Schubert 	tstamp = ntpfp_from_gpsdatum(&up->ref_next);
3322d4e511cSCy Schubert 	refclock_process_offset(pp, tstamp, up->rcv_pps, pp->fudgetime1);
3332d4e511cSCy Schubert 	up->rcvtout = 2;
3342d4e511cSCy Schubert 
3352d4e511cSCy Schubert 	gpscal_to_calendar(&cd, &up->ref_next);
3362d4e511cSCy Schubert 	refclock_save_lcode(pp, ntpcal_iso8601std(NULL, 0, &cd),
3372d4e511cSCy Schubert 			    (size_t)-1);
3382d4e511cSCy Schubert 	up->ref_next.weeks = 0;	/* consumed... */
3392d4e511cSCy Schubert }
3402d4e511cSCy Schubert 
3412d4e511cSCy Schubert /*
3422d4e511cSCy Schubert  * jupiter_ticker - process periodic checks
3432d4e511cSCy Schubert  */
3442d4e511cSCy Schubert static void
3452d4e511cSCy Schubert jupiter_ticker(int unit, struct peer *peer)
3462d4e511cSCy Schubert {
3472d4e511cSCy Schubert 	struct refclockproc * const pp = peer->procptr;
3482d4e511cSCy Schubert 	struct instance *     const up = pp->unitptr;
3492d4e511cSCy Schubert 
3502d4e511cSCy Schubert 	if (!up)
3512d4e511cSCy Schubert 		return;
3522d4e511cSCy Schubert 
3532d4e511cSCy Schubert 	/* check if we can add another sample now */
3542d4e511cSCy Schubert 	jupiter_checkpps(pp, up);
3552d4e511cSCy Schubert 
3562d4e511cSCy Schubert 	/* check the pivot update cycle */
3572d4e511cSCy Schubert 	if (up->piv_hold && !--up->piv_hold)
3582d4e511cSCy Schubert 		ZERO(up->piv_next);
3592d4e511cSCy Schubert 
3602d4e511cSCy Schubert 	if (up->rcvtout)
3612d4e511cSCy Schubert 		--up->rcvtout;
3622d4e511cSCy Schubert 	else if (pp->coderecv != pp->codeproc)
3632d4e511cSCy Schubert 		refclock_samples_expire(pp, 1);
3642d4e511cSCy Schubert }
3652d4e511cSCy Schubert 
3669c2daa00SOllivier Robert /*
3679c2daa00SOllivier Robert  * Initialize PPSAPI
3689c2daa00SOllivier Robert  */
3699c2daa00SOllivier Robert int
3709c2daa00SOllivier Robert jupiter_ppsapi(
3712d4e511cSCy Schubert 	struct instance * const up	/* unit structure pointer */
3729c2daa00SOllivier Robert 	)
3739c2daa00SOllivier Robert {
3749c2daa00SOllivier Robert 	int capability;
3759c2daa00SOllivier Robert 
3762d4e511cSCy Schubert 	if (time_pps_getcap(up->pps_handle, &capability) < 0) {
3779c2daa00SOllivier Robert 		msyslog(LOG_ERR,
3789c2daa00SOllivier Robert 		    "refclock_jupiter: time_pps_getcap failed: %m");
3799c2daa00SOllivier Robert 		return (0);
3809c2daa00SOllivier Robert 	}
3812d4e511cSCy Schubert 	memset(&up->pps_params, 0, sizeof(pps_params_t));
3822d4e511cSCy Schubert 	if (!up->assert)
3832d4e511cSCy Schubert 		up->pps_params.mode = capability & PPS_CAPTURECLEAR;
3849c2daa00SOllivier Robert 	else
3852d4e511cSCy Schubert 		up->pps_params.mode = capability & PPS_CAPTUREASSERT;
3862d4e511cSCy Schubert 	if (!(up->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
3879c2daa00SOllivier Robert 		msyslog(LOG_ERR,
3889c2daa00SOllivier Robert 		    "refclock_jupiter: invalid capture edge %d",
3892d4e511cSCy Schubert 		    up->assert);
3909c2daa00SOllivier Robert 		return (0);
3919c2daa00SOllivier Robert 	}
3922d4e511cSCy Schubert 	up->pps_params.mode |= PPS_TSFMT_TSPEC;
3932d4e511cSCy Schubert 	if (time_pps_setparams(up->pps_handle, &up->pps_params) < 0) {
3949c2daa00SOllivier Robert 		msyslog(LOG_ERR,
3959c2daa00SOllivier Robert 		    "refclock_jupiter: time_pps_setparams failed: %m");
3969c2daa00SOllivier Robert 		return (0);
3979c2daa00SOllivier Robert 	}
3982d4e511cSCy Schubert 	if (up->hardpps) {
3992d4e511cSCy Schubert 		if (time_pps_kcbind(up->pps_handle, PPS_KC_HARDPPS,
4002d4e511cSCy Schubert 				    up->pps_params.mode & ~PPS_TSFMT_TSPEC,
4019c2daa00SOllivier Robert 				    PPS_TSFMT_TSPEC) < 0) {
4029c2daa00SOllivier Robert 			msyslog(LOG_ERR,
4039c2daa00SOllivier Robert 			    "refclock_jupiter: time_pps_kcbind failed: %m");
4049c2daa00SOllivier Robert 			return (0);
4059c2daa00SOllivier Robert 		}
4062b15cb3dSCy Schubert 		hardpps_enable = 1;
4079c2daa00SOllivier Robert 	}
4082d4e511cSCy Schubert /*	up->peer->precision = PPS_PRECISION; */
4099c2daa00SOllivier Robert 
4109c2daa00SOllivier Robert #if DEBUG
4119c2daa00SOllivier Robert 	if (debug) {
4122d4e511cSCy Schubert 		time_pps_getparams(up->pps_handle, &up->pps_params);
4132d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__,
4149c2daa00SOllivier Robert 			"pps capability 0x%x version %d mode 0x%x kern %d",
4152d4e511cSCy Schubert 			capability, up->pps_params.api_version,
4162d4e511cSCy Schubert 			up->pps_params.mode, up->hardpps);
4179c2daa00SOllivier Robert 	}
4189c2daa00SOllivier Robert #endif
4199c2daa00SOllivier Robert 
4209c2daa00SOllivier Robert 	return (1);
4219c2daa00SOllivier Robert }
4229c2daa00SOllivier Robert 
4239c2daa00SOllivier Robert /*
4249c2daa00SOllivier Robert  * Get PPSAPI timestamps.
4259c2daa00SOllivier Robert  *
4269c2daa00SOllivier Robert  * Return 0 on failure and 1 on success.
4279c2daa00SOllivier Robert  */
4289c2daa00SOllivier Robert static int
4292d4e511cSCy Schubert jupiter_pps(struct instance * const up)
4309c2daa00SOllivier Robert {
4319c2daa00SOllivier Robert 	pps_info_t pps_info;
4329c2daa00SOllivier Robert 	struct timespec timeout, ts;
4339c2daa00SOllivier Robert 	l_fp tstmp;
4349c2daa00SOllivier Robert 
4359c2daa00SOllivier Robert 	/*
4369c2daa00SOllivier Robert 	 * Convert the timespec nanoseconds field to ntp l_fp units.
4379c2daa00SOllivier Robert 	 */
4382d4e511cSCy Schubert 	if (up->pps_handle == 0)
4399c2daa00SOllivier Robert 		return 1;
4409c2daa00SOllivier Robert 	timeout.tv_sec = 0;
4419c2daa00SOllivier Robert 	timeout.tv_nsec = 0;
4422d4e511cSCy Schubert 	memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
4432d4e511cSCy Schubert 	if (time_pps_fetch(up->pps_handle, PPS_TSFMT_TSPEC, &up->pps_info,
4449c2daa00SOllivier Robert 	    &timeout) < 0)
4459c2daa00SOllivier Robert 		return 1;
4462d4e511cSCy Schubert 	if (up->pps_params.mode & PPS_CAPTUREASSERT) {
4479c2daa00SOllivier Robert 		if (pps_info.assert_sequence ==
4482d4e511cSCy Schubert 		    up->pps_info.assert_sequence)
4499c2daa00SOllivier Robert 			return 1;
4502d4e511cSCy Schubert 		ts = up->pps_info.assert_timestamp;
4512d4e511cSCy Schubert 	} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
4529c2daa00SOllivier Robert 		if (pps_info.clear_sequence ==
4532d4e511cSCy Schubert 		    up->pps_info.clear_sequence)
4549c2daa00SOllivier Robert 			return 1;
4552d4e511cSCy Schubert 		ts = up->pps_info.clear_timestamp;
4569c2daa00SOllivier Robert 	} else {
4579c2daa00SOllivier Robert 		return 1;
4589c2daa00SOllivier Robert 	}
4599c2daa00SOllivier Robert 
4602d4e511cSCy Schubert 	tstmp = tspec_stamp_to_lfp(ts);
4612d4e511cSCy Schubert 	if (L_ISEQU(&tstmp, &up->rcv_pps))
4622d4e511cSCy Schubert 		return 1;
4632d4e511cSCy Schubert 
4642d4e511cSCy Schubert 	up->rcv_pps = tstmp;
4659c2daa00SOllivier Robert 	return 0;
4669c2daa00SOllivier Robert }
4679c2daa00SOllivier Robert 
468c0b746e5SOllivier Robert /*
469c0b746e5SOllivier Robert  * jupiter_poll - jupiter watchdog routine
470c0b746e5SOllivier Robert  */
471c0b746e5SOllivier Robert static void
4729c2daa00SOllivier Robert jupiter_poll(int unit, struct peer *peer)
473c0b746e5SOllivier Robert {
4742d4e511cSCy Schubert 	struct refclockproc * const pp = peer->procptr;
4752d4e511cSCy Schubert 	struct instance *     const up = pp->unitptr;
476c0b746e5SOllivier Robert 
4772d4e511cSCy Schubert 	pp->polls++;
478c0b746e5SOllivier Robert 
479c0b746e5SOllivier Robert 	/*
4802d4e511cSCy Schubert 	 * If we have new samples since last poll, everything is fine.
4812d4e511cSCy Schubert 	 * if not, blarb loudly.
482c0b746e5SOllivier Robert 	 */
4832d4e511cSCy Schubert 	if (pp->coderecv != pp->codeproc) {
4842d4e511cSCy Schubert 		refclock_receive(peer);
4852d4e511cSCy Schubert 		refclock_report(peer, CEVNT_NOMINAL);
486c0b746e5SOllivier Robert 	} else {
487c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_TIMEOUT);
488c0b746e5SOllivier Robert 
489c0b746e5SOllivier Robert 		/* Request the receiver id to trigger a reconfig */
4902d4e511cSCy Schubert 		jupiter_reqonemsg(up, JUPITER_O_ID);
4912d4e511cSCy Schubert 		up->wantid = 0;
492c0b746e5SOllivier Robert 	}
493c0b746e5SOllivier Robert }
494c0b746e5SOllivier Robert 
495c0b746e5SOllivier Robert /*
4969c2daa00SOllivier Robert  * jupiter_control - fudge control
4979c2daa00SOllivier Robert  */
4989c2daa00SOllivier Robert static void
4999c2daa00SOllivier Robert jupiter_control(
5009c2daa00SOllivier Robert 	int unit,		/* unit (not used) */
5012b15cb3dSCy Schubert 	const struct refclockstat *in, /* input parameters (not used) */
5029c2daa00SOllivier Robert 	struct refclockstat *out, /* output parameters (not used) */
5039c2daa00SOllivier Robert 	struct peer *peer	/* peer structure pointer */
5049c2daa00SOllivier Robert 	)
5059c2daa00SOllivier Robert {
5062d4e511cSCy Schubert 	struct refclockproc * const pp = peer->procptr;
5072d4e511cSCy Schubert 	struct instance *     const up = pp->unitptr;
5082d4e511cSCy Schubert 
5099c2daa00SOllivier Robert 	u_char sloppyclockflag;
5109c2daa00SOllivier Robert 
5112d4e511cSCy Schubert 	up->assert = !(pp->sloppyclockflag & CLK_FLAG3);
5122d4e511cSCy Schubert 	jupiter_ppsapi(up);
5139c2daa00SOllivier Robert 
5142d4e511cSCy Schubert 	sloppyclockflag = up->sloppyclockflag;
5152d4e511cSCy Schubert 	up->sloppyclockflag = pp->sloppyclockflag;
5162d4e511cSCy Schubert 	if ((up->sloppyclockflag & CLK_FLAG2) !=
5179c2daa00SOllivier Robert 	    (sloppyclockflag & CLK_FLAG2)) {
5182b15cb3dSCy Schubert 		jupiter_debug(peer, __func__,
5199c2daa00SOllivier Robert 		    "mode switch: reset receiver");
5202d4e511cSCy Schubert 		jupiter_config(up);
5219c2daa00SOllivier Robert 		return;
5229c2daa00SOllivier Robert 	}
5239c2daa00SOllivier Robert }
5249c2daa00SOllivier Robert 
5259c2daa00SOllivier Robert /*
526c0b746e5SOllivier Robert  * jupiter_receive - receive gps data
527c0b746e5SOllivier Robert  * Gag me!
528c0b746e5SOllivier Robert  */
529c0b746e5SOllivier Robert static void
5302d4e511cSCy Schubert jupiter_receive(struct recvbuf * const rbufp)
531c0b746e5SOllivier Robert {
5322d4e511cSCy Schubert 	struct peer *         const peer = rbufp->recv_peer;
5332d4e511cSCy Schubert 	struct refclockproc * const pp   = peer->procptr;
5342d4e511cSCy Schubert 	struct instance *     const up   = pp->unitptr;
5352d4e511cSCy Schubert 
5362b15cb3dSCy Schubert 	size_t bpcnt;
5372d4e511cSCy Schubert 	int cc, size;
5382b15cb3dSCy Schubert 	const char *cp;
5399c2daa00SOllivier Robert 	u_char *bp;
5409c2daa00SOllivier Robert 	u_short *sp;
5419c2daa00SOllivier Robert 	struct jid *ip;
5429c2daa00SOllivier Robert 	struct jheader *hp;
543c0b746e5SOllivier Robert 
544c0b746e5SOllivier Robert 	/* Initialize pointers and read the timecode and timestamp */
545c0b746e5SOllivier Robert 	bp = (u_char *)rbufp->recv_buffer;
546c0b746e5SOllivier Robert 	bpcnt = rbufp->recv_length;
547c0b746e5SOllivier Robert 
548c0b746e5SOllivier Robert 	/* This shouldn't happen */
5492d4e511cSCy Schubert 	if (bpcnt > sizeof(up->sbuf) - up->ssize)
5502d4e511cSCy Schubert 		bpcnt = sizeof(up->sbuf) - up->ssize;
551c0b746e5SOllivier Robert 
552c0b746e5SOllivier Robert 	/* Append to input buffer */
5532d4e511cSCy Schubert 	memcpy((u_char *)up->sbuf + up->ssize, bp, bpcnt);
5542d4e511cSCy Schubert 	up->ssize += bpcnt;
555c0b746e5SOllivier Robert 
5569c2daa00SOllivier Robert 	/* While there's at least a header and we parse an intact message */
5572d4e511cSCy Schubert 	while (up->ssize > (int)sizeof(*hp) && (cc = jupiter_recv(up)) > 0) {
5582d4e511cSCy Schubert 		hp = (struct jheader *)up->sbuf;
559c0b746e5SOllivier Robert 		sp = (u_short *)(hp + 1);
560c0b746e5SOllivier Robert 		size = cc - sizeof(*hp);
561c0b746e5SOllivier Robert 		switch (getshort(hp->id)) {
562c0b746e5SOllivier Robert 
563c0b746e5SOllivier Robert 		case JUPITER_O_PULSE:
5642d4e511cSCy Schubert 			/* first see if we can push another sample: */
5652d4e511cSCy Schubert 			jupiter_checkpps(pp, up);
5662d4e511cSCy Schubert 
567c0b746e5SOllivier Robert 			if (size != sizeof(struct jpulse)) {
5682b15cb3dSCy Schubert 				jupiter_debug(peer, __func__,
5692b15cb3dSCy Schubert 				    "pulse: len %d != %u",
570c0b746e5SOllivier Robert 				    size, (int)sizeof(struct jpulse));
571c0b746e5SOllivier Robert 				refclock_report(peer, CEVNT_BADREPLY);
572c0b746e5SOllivier Robert 				break;
573c0b746e5SOllivier Robert 			}
574c0b746e5SOllivier Robert 
5752d4e511cSCy Schubert 			/* Parse timecode (even when there's no pps)
5762d4e511cSCy Schubert 			 *
5772d4e511cSCy Schubert 			 * There appears to be a firmware bug related to
5782d4e511cSCy Schubert 			 * the pulse message; in addition to the one per
5792d4e511cSCy Schubert 			 * second messages, we get an extra pulse
580c0b746e5SOllivier Robert 			 * message once an hour (on the anniversary of
581c0b746e5SOllivier Robert 			 * the cold start). It seems to come 200 ms
5822d4e511cSCy Schubert 			 * after the one requested.
5832d4e511cSCy Schubert 			 *
5842d4e511cSCy Schubert 			 * But since we feed samples only when a new PPS
5852d4e511cSCy Schubert 			 * pulse is found we can simply ignore that and
5862d4e511cSCy Schubert 			 * aggregate/update any existing timing message.
587c0b746e5SOllivier Robert 			 */
5882d4e511cSCy Schubert 			if ((cp = jupiter_parse_t(up, sp, rbufp->recv_time)) != NULL) {
5892b15cb3dSCy Schubert 				jupiter_debug(peer, __func__,
5902b15cb3dSCy Schubert 				    "pulse: %s", cp);
591c0b746e5SOllivier Robert 			}
5929c2daa00SOllivier Robert 			break;
5939c2daa00SOllivier Robert 
5949c2daa00SOllivier Robert 		case JUPITER_O_GPOS:
5959c2daa00SOllivier Robert 			if (size != sizeof(struct jgpos)) {
5962b15cb3dSCy Schubert 				jupiter_debug(peer, __func__,
5972b15cb3dSCy Schubert 				    "gpos: len %d != %u",
5989c2daa00SOllivier Robert 				    size, (int)sizeof(struct jgpos));
5999c2daa00SOllivier Robert 				refclock_report(peer, CEVNT_BADREPLY);
6009c2daa00SOllivier Robert 				break;
6019c2daa00SOllivier Robert 			}
6029c2daa00SOllivier Robert 
6032d4e511cSCy Schubert 			if ((cp = jupiter_parse_gpos(up, sp)) != NULL) {
6042b15cb3dSCy Schubert 				jupiter_debug(peer, __func__,
6052b15cb3dSCy Schubert 				    "gpos: %s", cp);
6069c2daa00SOllivier Robert 				break;
6079c2daa00SOllivier Robert 			}
608c0b746e5SOllivier Robert 			break;
609c0b746e5SOllivier Robert 
610c0b746e5SOllivier Robert 		case JUPITER_O_ID:
611c0b746e5SOllivier Robert 			if (size != sizeof(struct jid)) {
6122b15cb3dSCy Schubert 				jupiter_debug(peer, __func__,
6132b15cb3dSCy Schubert 				    "id: len %d != %u",
614c0b746e5SOllivier Robert 				    size, (int)sizeof(struct jid));
615c0b746e5SOllivier Robert 				refclock_report(peer, CEVNT_BADREPLY);
616c0b746e5SOllivier Robert 				break;
617c0b746e5SOllivier Robert 			}
618c0b746e5SOllivier Robert 			/*
619c0b746e5SOllivier Robert 			 * If we got this message because the Jupiter
6209c2daa00SOllivier Robert 			 * just powered instance, it needs to be reconfigured.
621c0b746e5SOllivier Robert 			 */
622c0b746e5SOllivier Robert 			ip = (struct jid *)sp;
6232b15cb3dSCy Schubert 			jupiter_debug(peer, __func__,
6242b15cb3dSCy Schubert 			    "%s chan ver %s, %s (%s)",
625c0b746e5SOllivier Robert 			    ip->chans, ip->vers, ip->date, ip->opts);
626c0b746e5SOllivier Robert 			msyslog(LOG_DEBUG,
627ea906c41SOllivier Robert 			    "jupiter_receive: %s chan ver %s, %s (%s)",
628c0b746e5SOllivier Robert 			    ip->chans, ip->vers, ip->date, ip->opts);
6292d4e511cSCy Schubert 			if (up->wantid)
6302d4e511cSCy Schubert 				up->wantid = 0;
631c0b746e5SOllivier Robert 			else {
6322b15cb3dSCy Schubert 				jupiter_debug(peer, __func__, "reset receiver");
6332d4e511cSCy Schubert 				jupiter_config(up);
6349c2daa00SOllivier Robert 				/*
6359c2daa00SOllivier Robert 				 * Restore since jupiter_config() just
6369c2daa00SOllivier Robert 				 * zeroed it
6379c2daa00SOllivier Robert 				 */
6382d4e511cSCy Schubert 				up->ssize = cc;
639c0b746e5SOllivier Robert 			}
640c0b746e5SOllivier Robert 			break;
641c0b746e5SOllivier Robert 
642c0b746e5SOllivier Robert 		default:
6432b15cb3dSCy Schubert 			jupiter_debug(peer, __func__, "unknown message id %d",
644c0b746e5SOllivier Robert 			    getshort(hp->id));
645c0b746e5SOllivier Robert 			break;
646c0b746e5SOllivier Robert 		}
6472d4e511cSCy Schubert 		up->ssize -= cc;
6482d4e511cSCy Schubert 		if (up->ssize < 0) {
649c0b746e5SOllivier Robert 			fprintf(stderr, "jupiter_recv: negative ssize!\n");
650c0b746e5SOllivier Robert 			abort();
6512d4e511cSCy Schubert 		} else if (up->ssize > 0)
6522d4e511cSCy Schubert 			memcpy(up->sbuf, (u_char *)up->sbuf + cc, up->ssize);
653c0b746e5SOllivier Robert 	}
654c0b746e5SOllivier Robert }
655c0b746e5SOllivier Robert 
6562b15cb3dSCy Schubert static const char *
6572d4e511cSCy Schubert jupiter_parse_t(
6582d4e511cSCy Schubert 	struct instance * const up,
6592d4e511cSCy Schubert 	u_short *               sp,
6602d4e511cSCy Schubert 	l_fp               rcvtime
6612d4e511cSCy Schubert 	)
662c0b746e5SOllivier Robert {
6639c2daa00SOllivier Robert 	struct jpulse *jp;
6649c2daa00SOllivier Robert 	u_int32 sweek;
6659c2daa00SOllivier Robert 	u_short flags;
6662d4e511cSCy Schubert 	l_fp fofs;
667c0b746e5SOllivier Robert 
668c0b746e5SOllivier Robert 	jp = (struct jpulse *)sp;
6692d4e511cSCy Schubert 	flags = getshort(jp->flags);
6702d4e511cSCy Schubert 
6712d4e511cSCy Schubert 	/* Toss if not designated "valid" by the gps.
6722d4e511cSCy Schubert 	 * !!NOTE!! do *not* kill data received so far!
6732d4e511cSCy Schubert 	 */
6742d4e511cSCy Schubert 	if ((flags & JUPITER_O_PULSE_VALID) == 0) {
6752d4e511cSCy Schubert 		refclock_report(up->peer, CEVNT_BADTIME);
6762d4e511cSCy Schubert 		return ("time mark not valid");
6772d4e511cSCy Schubert 	}
6782d4e511cSCy Schubert 
6792d4e511cSCy Schubert 	up->rcv_next = rcvtime; /* remember when this happened */
680c0b746e5SOllivier Robert 
681c0b746e5SOllivier Robert 	/* The timecode is presented as seconds into the current GPS week */
6829c2daa00SOllivier Robert 	sweek = DS2UI(jp->sweek) % WEEKSECS;
6832d4e511cSCy Schubert 	/* check if we have to apply the UTC offset ourselves */
6842d4e511cSCy Schubert 	if ((flags & JUPITER_O_PULSE_UTC) == 0) {
6852d4e511cSCy Schubert 		struct timespec tofs;
6862d4e511cSCy Schubert 		tofs.tv_sec  = getshort(jp->offs);
6872d4e511cSCy Schubert 		tofs.tv_nsec = DS2I(jp->offns);
6882d4e511cSCy Schubert 		fofs = tspec_intv_to_lfp(tofs);
6892d4e511cSCy Schubert 		L_NEG(&fofs);
6902d4e511cSCy Schubert 	} else {
6912d4e511cSCy Schubert 		ZERO(fofs);
6922d4e511cSCy Schubert 	}
693c0b746e5SOllivier Robert 
694c0b746e5SOllivier Robert 	/*
695c0b746e5SOllivier Robert 	 * If we don't know the current GPS week, calculate it from the
696c0b746e5SOllivier Robert 	 * current time. (It's too bad they didn't include this
6972d4e511cSCy Schubert 	 * important value in the pulse message).
698c0b746e5SOllivier Robert 	 *
6992d4e511cSCy Schubert 	 * So we pick the pivot value from the other messages like gpos
7002d4e511cSCy Schubert 	 * or chan if we can. Of course, the PULSE message can be in UTC
7012d4e511cSCy Schubert 	 * or GPS time scale, and the other messages are simply always
7022d4e511cSCy Schubert 	 * GPS time.
7032d4e511cSCy Schubert 	 *
7042d4e511cSCy Schubert 	 * But as long as the difference between the time stamps is less
7052d4e511cSCy Schubert 	 * than a half week, the unfolding of a week time is unambigeous
7062d4e511cSCy Schubert 	 * and well suited for the problem we have here. And we won't
7072d4e511cSCy Schubert 	 * see *that* many leap seconds, ever.
708c0b746e5SOllivier Robert 	 */
7092d4e511cSCy Schubert 	if (up->piv_next.weeks) {
7102d4e511cSCy Schubert 		up->ref_next = gpscal_from_weektime2(
7112d4e511cSCy Schubert 			sweek, fofs, &up->piv_next);
7122d4e511cSCy Schubert 		up->piv_next = up->ref_next;
7132d4e511cSCy Schubert 	} else {
7142d4e511cSCy Schubert 		up->ref_next = gpscal_from_weektime1(
7152d4e511cSCy Schubert 			sweek, fofs, rcvtime);
7169c2daa00SOllivier Robert 	}
7179c2daa00SOllivier Robert 
7189c2daa00SOllivier Robert 
719c0b746e5SOllivier Robert 
720c0b746e5SOllivier Robert 	return (NULL);
721c0b746e5SOllivier Robert }
722c0b746e5SOllivier Robert 
7232b15cb3dSCy Schubert static const char *
7242d4e511cSCy Schubert jupiter_parse_gpos(
7252d4e511cSCy Schubert 	struct instance * const up,
7262d4e511cSCy Schubert 	u_short *               sp
7272d4e511cSCy Schubert 	)
728c0b746e5SOllivier Robert {
7299c2daa00SOllivier Robert 	struct jgpos *jg;
7302d4e511cSCy Schubert 	struct calendar	tref;
7319c2daa00SOllivier Robert 	char *cp;
7322d4e511cSCy Schubert 	struct timespec tofs;
7332d4e511cSCy Schubert 	uint16_t	raw_week;
7342d4e511cSCy Schubert 	uint32_t	raw_secs;
735c0b746e5SOllivier Robert 
7369c2daa00SOllivier Robert 	jg = (struct jgpos *)sp;
737c0b746e5SOllivier Robert 
7389c2daa00SOllivier Robert 	if (jg->navval != 0) {
739c0b746e5SOllivier Robert 		/*
7409c2daa00SOllivier Robert 		 * Solution not valid. Use caution and refuse
7419c2daa00SOllivier Robert 		 * to determine GPS week from this message.
742c0b746e5SOllivier Robert 		 */
7439c2daa00SOllivier Robert 		return ("Navigation solution not valid");
744c0b746e5SOllivier Robert 	}
745c0b746e5SOllivier Robert 
7462d4e511cSCy Schubert 	raw_week = getshort(jg->gweek);
7472d4e511cSCy Schubert 	raw_secs = DS2UI(jg->sweek);
7482d4e511cSCy Schubert 	tofs.tv_sec  = 0;
7492d4e511cSCy Schubert 	tofs.tv_nsec = DS2UI(jg->nsweek);
7502d4e511cSCy Schubert 	up->piv_next = gpscal_from_gpsweek(raw_week, raw_secs,
7512d4e511cSCy Schubert 					   tspec_intv_to_lfp(tofs));
7522d4e511cSCy Schubert 	up->piv_hold = 60;
753f391d6bcSXin LI 
7542d4e511cSCy Schubert 	gpscal_to_calendar(&tref, &up->piv_next);
7552d4e511cSCy Schubert 	cp = ntpcal_iso8601std(NULL, 0, &tref);
7562d4e511cSCy Schubert 	jupiter_debug(up->peer, __func__,
7572d4e511cSCy Schubert 		"GPS %s (gweek/sweek %hu/%u)",
7582d4e511cSCy Schubert 		      cp, (unsigned short)raw_week, (unsigned int)raw_secs);
7599c2daa00SOllivier Robert 	return (NULL);
760c0b746e5SOllivier Robert }
761c0b746e5SOllivier Robert 
762c0b746e5SOllivier Robert /*
763c0b746e5SOllivier Robert  * jupiter_debug - print debug messages
764c0b746e5SOllivier Robert  */
765c0b746e5SOllivier Robert static void
7662b15cb3dSCy Schubert jupiter_debug(
7672b15cb3dSCy Schubert 	struct peer *	peer,
7682b15cb3dSCy Schubert 	const char *	function,
7692b15cb3dSCy Schubert 	const char *	fmt,
7702b15cb3dSCy Schubert 	...
7712b15cb3dSCy Schubert 	)
772c0b746e5SOllivier Robert {
7739c2daa00SOllivier Robert 	char	buffer[200];
774c0b746e5SOllivier Robert 	va_list	ap;
775c0b746e5SOllivier Robert 
776c0b746e5SOllivier Robert 	va_start(ap, fmt);
777c0b746e5SOllivier Robert 	/*
778c0b746e5SOllivier Robert 	 * Print debug message to stdout
779c0b746e5SOllivier Robert 	 * In the future, we may want to get get more creative...
780c0b746e5SOllivier Robert 	 */
7812b15cb3dSCy Schubert 	mvsnprintf(buffer, sizeof(buffer), fmt, ap);
7822b15cb3dSCy Schubert 	record_clock_stats(&peer->srcadr, buffer);
783ea906c41SOllivier Robert #ifdef DEBUG
7849c2daa00SOllivier Robert 	if (debug) {
7852b15cb3dSCy Schubert 		printf("%s: %s\n", function, buffer);
7869c2daa00SOllivier Robert 		fflush(stdout);
7879c2daa00SOllivier Robert 	}
788ea906c41SOllivier Robert #endif
789c0b746e5SOllivier Robert 
790c0b746e5SOllivier Robert 	va_end(ap);
791c0b746e5SOllivier Robert }
792c0b746e5SOllivier Robert 
793c0b746e5SOllivier Robert /* Checksum and transmit a message to the Jupiter */
794c0b746e5SOllivier Robert static char *
7952d4e511cSCy Schubert jupiter_send(
7962d4e511cSCy Schubert 	struct instance * const up,
7972d4e511cSCy Schubert 	struct jheader *        hp
7982d4e511cSCy Schubert 	)
799c0b746e5SOllivier Robert {
8009c2daa00SOllivier Robert 	u_int len, size;
8012b15cb3dSCy Schubert 	ssize_t cc;
8029c2daa00SOllivier Robert 	u_short *sp;
803c0b746e5SOllivier Robert 	static char errstr[132];
804c0b746e5SOllivier Robert 
805c0b746e5SOllivier Robert 	size = sizeof(*hp);
806c0b746e5SOllivier Robert 	hp->hsum = putshort(jupiter_cksum((u_short *)hp,
807c0b746e5SOllivier Robert 	    (size / sizeof(u_short)) - 1));
808c0b746e5SOllivier Robert 	len = getshort(hp->len);
809c0b746e5SOllivier Robert 	if (len > 0) {
810c0b746e5SOllivier Robert 		sp = (u_short *)(hp + 1);
811c0b746e5SOllivier Robert 		sp[len] = putshort(jupiter_cksum(sp, len));
812c0b746e5SOllivier Robert 		size += (len + 1) * sizeof(u_short);
813c0b746e5SOllivier Robert 	}
814c0b746e5SOllivier Robert 
8152d4e511cSCy Schubert 	if ((cc = write(up->peer->procptr->io.fd, (char *)hp, size)) < 0) {
8162b15cb3dSCy Schubert 		msnprintf(errstr, sizeof(errstr), "write: %m");
817c0b746e5SOllivier Robert 		return (errstr);
8182b15cb3dSCy Schubert 	} else if (cc != (int)size) {
8192b15cb3dSCy Schubert 		snprintf(errstr, sizeof(errstr), "short write (%zd != %u)", cc, size);
820c0b746e5SOllivier Robert 		return (errstr);
821c0b746e5SOllivier Robert 	}
822c0b746e5SOllivier Robert 	return (NULL);
823c0b746e5SOllivier Robert }
824c0b746e5SOllivier Robert 
825c0b746e5SOllivier Robert /* Request periodic message output */
826c0b746e5SOllivier Robert static struct {
827c0b746e5SOllivier Robert 	struct jheader jheader;
828c0b746e5SOllivier Robert 	struct jrequest jrequest;
829c0b746e5SOllivier Robert } reqmsg = {
830c0b746e5SOllivier Robert 	{ putshort(JUPITER_SYNC), 0,
831c0b746e5SOllivier Robert 	    putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1),
832ea906c41SOllivier Robert 	    0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK |
833ea906c41SOllivier Robert 	    JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 },
834c0b746e5SOllivier Robert 	{ 0, 0, 0, 0 }
835c0b746e5SOllivier Robert };
836c0b746e5SOllivier Robert 
837c0b746e5SOllivier Robert /* An interval of zero means to output on trigger */
838c0b746e5SOllivier Robert static void
8392d4e511cSCy Schubert jupiter_reqmsg(
8402d4e511cSCy Schubert 	struct instance * const up,
8412d4e511cSCy Schubert 	u_int                   id,
8422d4e511cSCy Schubert 	u_int             interval
8432d4e511cSCy Schubert 	)
844c0b746e5SOllivier Robert {
8459c2daa00SOllivier Robert 	struct jheader *hp;
8469c2daa00SOllivier Robert 	struct jrequest *rp;
8479c2daa00SOllivier Robert 	char *cp;
848c0b746e5SOllivier Robert 
849c0b746e5SOllivier Robert 	hp = &reqmsg.jheader;
850c0b746e5SOllivier Robert 	hp->id = putshort(id);
851c0b746e5SOllivier Robert 	rp = &reqmsg.jrequest;
852c0b746e5SOllivier Robert 	rp->trigger = putshort(interval == 0);
853c0b746e5SOllivier Robert 	rp->interval = putshort(interval);
8542d4e511cSCy Schubert 	if ((cp = jupiter_send(up, hp)) != NULL)
8552d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "%u: %s", id, cp);
856c0b746e5SOllivier Robert }
857c0b746e5SOllivier Robert 
858c0b746e5SOllivier Robert /* Cancel periodic message output */
859c0b746e5SOllivier Robert static struct jheader canmsg = {
860c0b746e5SOllivier Robert 	putshort(JUPITER_SYNC), 0, 0, 0,
861ea906c41SOllivier Robert 	JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC,
862c0b746e5SOllivier Robert 	0
863c0b746e5SOllivier Robert };
864c0b746e5SOllivier Robert 
865c0b746e5SOllivier Robert static void
8662d4e511cSCy Schubert jupiter_canmsg(
8672d4e511cSCy Schubert 	struct instance * const up,
8682d4e511cSCy Schubert 	u_int                   id
8692d4e511cSCy Schubert 	)
870c0b746e5SOllivier Robert {
8719c2daa00SOllivier Robert 	struct jheader *hp;
8729c2daa00SOllivier Robert 	char *cp;
873c0b746e5SOllivier Robert 
874c0b746e5SOllivier Robert 	hp = &canmsg;
875c0b746e5SOllivier Robert 	hp->id = putshort(id);
8762d4e511cSCy Schubert 	if ((cp = jupiter_send(up, hp)) != NULL)
8772d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "%u: %s", id, cp);
878c0b746e5SOllivier Robert }
879c0b746e5SOllivier Robert 
880c0b746e5SOllivier Robert /* Request a single message output */
881c0b746e5SOllivier Robert static struct jheader reqonemsg = {
882c0b746e5SOllivier Robert 	putshort(JUPITER_SYNC), 0, 0, 0,
883ea906c41SOllivier Robert 	JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY,
884c0b746e5SOllivier Robert 	0
885c0b746e5SOllivier Robert };
886c0b746e5SOllivier Robert 
887c0b746e5SOllivier Robert static void
8882d4e511cSCy Schubert jupiter_reqonemsg(
8892d4e511cSCy Schubert 	struct instance * const up,
8902d4e511cSCy Schubert 	u_int                   id
8912d4e511cSCy Schubert 	)
892c0b746e5SOllivier Robert {
8939c2daa00SOllivier Robert 	struct jheader *hp;
8949c2daa00SOllivier Robert 	char *cp;
895c0b746e5SOllivier Robert 
896c0b746e5SOllivier Robert 	hp = &reqonemsg;
897c0b746e5SOllivier Robert 	hp->id = putshort(id);
8982d4e511cSCy Schubert 	if ((cp = jupiter_send(up, hp)) != NULL)
8992d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "%u: %s", id, cp);
900c0b746e5SOllivier Robert }
901c0b746e5SOllivier Robert 
902c0b746e5SOllivier Robert /* Set the platform dynamics */
903c0b746e5SOllivier Robert static struct {
904c0b746e5SOllivier Robert 	struct jheader jheader;
905c0b746e5SOllivier Robert 	struct jplat jplat;
906c0b746e5SOllivier Robert } platmsg = {
907c0b746e5SOllivier Robert 	{ putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT),
908c0b746e5SOllivier Robert 	    putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0,
909ea906c41SOllivier Robert 	    JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 },
910c0b746e5SOllivier Robert 	{ 0, 0, 0 }
911c0b746e5SOllivier Robert };
912c0b746e5SOllivier Robert 
913c0b746e5SOllivier Robert static void
9142d4e511cSCy Schubert jupiter_platform(
9152d4e511cSCy Schubert 	struct instance * const up,
9162d4e511cSCy Schubert 	u_int             platform
9172d4e511cSCy Schubert 	)
918c0b746e5SOllivier Robert {
9199c2daa00SOllivier Robert 	struct jheader *hp;
9209c2daa00SOllivier Robert 	struct jplat *pp;
9219c2daa00SOllivier Robert 	char *cp;
922c0b746e5SOllivier Robert 
923c0b746e5SOllivier Robert 	hp = &platmsg.jheader;
924c0b746e5SOllivier Robert 	pp = &platmsg.jplat;
925c0b746e5SOllivier Robert 	pp->platform = putshort(platform);
9262d4e511cSCy Schubert 	if ((cp = jupiter_send(up, hp)) != NULL)
9272d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "%u: %s", platform, cp);
928c0b746e5SOllivier Robert }
929c0b746e5SOllivier Robert 
930c0b746e5SOllivier Robert /* Checksum "len" shorts */
931c0b746e5SOllivier Robert static u_short
9329c2daa00SOllivier Robert jupiter_cksum(u_short *sp, u_int len)
933c0b746e5SOllivier Robert {
9349c2daa00SOllivier Robert 	u_short sum, x;
935c0b746e5SOllivier Robert 
936c0b746e5SOllivier Robert 	sum = 0;
937c0b746e5SOllivier Robert 	while (len-- > 0) {
938c0b746e5SOllivier Robert 		x = *sp++;
939c0b746e5SOllivier Robert 		sum += getshort(x);
940c0b746e5SOllivier Robert 	}
941c0b746e5SOllivier Robert 	return (~sum + 1);
942c0b746e5SOllivier Robert }
943c0b746e5SOllivier Robert 
944c0b746e5SOllivier Robert /* Return the size of the next message (or zero if we don't have it all yet) */
945c0b746e5SOllivier Robert static int
9462d4e511cSCy Schubert jupiter_recv(
9472d4e511cSCy Schubert 	struct instance * const up
9482d4e511cSCy Schubert 	)
949c0b746e5SOllivier Robert {
9509c2daa00SOllivier Robert 	int n, len, size, cc;
9519c2daa00SOllivier Robert 	struct jheader *hp;
9529c2daa00SOllivier Robert 	u_char *bp;
9539c2daa00SOllivier Robert 	u_short *sp;
954c0b746e5SOllivier Robert 
955c0b746e5SOllivier Robert 	/* Must have at least a header's worth */
956c0b746e5SOllivier Robert 	cc = sizeof(*hp);
9572d4e511cSCy Schubert 	size = up->ssize;
958c0b746e5SOllivier Robert 	if (size < cc)
959c0b746e5SOllivier Robert 		return (0);
960c0b746e5SOllivier Robert 
961c0b746e5SOllivier Robert 	/* Search for the sync short if missing */
9622d4e511cSCy Schubert 	sp = up->sbuf;
963c0b746e5SOllivier Robert 	hp = (struct jheader *)sp;
964c0b746e5SOllivier Robert 	if (getshort(hp->sync) != JUPITER_SYNC) {
965c0b746e5SOllivier Robert 		/* Wasn't at the front, sync up */
9662d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "syncing");
967c0b746e5SOllivier Robert 		bp = (u_char *)sp;
968c0b746e5SOllivier Robert 		n = size;
969c0b746e5SOllivier Robert 		while (n >= 2) {
970c0b746e5SOllivier Robert 			if (bp[0] != (JUPITER_SYNC & 0xff)) {
9719c2daa00SOllivier Robert 				/*
9722d4e511cSCy Schubert 				jupiter_debug(up->peer, __func__,
9732b15cb3dSCy Schubert 				    "{0x%x}", bp[0]);
9749c2daa00SOllivier Robert 				*/
975c0b746e5SOllivier Robert 				++bp;
976c0b746e5SOllivier Robert 				--n;
977c0b746e5SOllivier Robert 				continue;
978c0b746e5SOllivier Robert 			}
979c0b746e5SOllivier Robert 			if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff))
980c0b746e5SOllivier Robert 				break;
9819c2daa00SOllivier Robert 			/*
9822d4e511cSCy Schubert 			jupiter_debug(up->peer, __func__,
9832b15cb3dSCy Schubert 			    "{0x%x 0x%x}", bp[0], bp[1]);
9849c2daa00SOllivier Robert 			*/
985c0b746e5SOllivier Robert 			bp += 2;
986c0b746e5SOllivier Robert 			n -= 2;
987c0b746e5SOllivier Robert 		}
9889c2daa00SOllivier Robert 		/*
9892d4e511cSCy Schubert 		jupiter_debug(up->peer, __func__, "\n");
9909c2daa00SOllivier Robert 		*/
991c0b746e5SOllivier Robert 		/* Shuffle data to front of input buffer */
992c0b746e5SOllivier Robert 		if (n > 0)
993c0b746e5SOllivier Robert 			memcpy(sp, bp, n);
994c0b746e5SOllivier Robert 		size = n;
9952d4e511cSCy Schubert 		up->ssize = size;
996c0b746e5SOllivier Robert 		if (size < cc || hp->sync != JUPITER_SYNC)
997c0b746e5SOllivier Robert 			return (0);
998c0b746e5SOllivier Robert 	}
999c0b746e5SOllivier Robert 
1000c0b746e5SOllivier Robert 	if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) !=
1001c0b746e5SOllivier Robert 	    getshort(hp->hsum)) {
10022d4e511cSCy Schubert 	    jupiter_debug(up->peer, __func__, "bad header checksum!");
1003c0b746e5SOllivier Robert 		/* This is drastic but checksum errors should be rare */
10042d4e511cSCy Schubert 		up->ssize = 0;
1005c0b746e5SOllivier Robert 		return (0);
1006c0b746e5SOllivier Robert 	}
1007c0b746e5SOllivier Robert 
1008c0b746e5SOllivier Robert 	/* Check for a payload */
1009c0b746e5SOllivier Robert 	len = getshort(hp->len);
1010c0b746e5SOllivier Robert 	if (len > 0) {
1011c0b746e5SOllivier Robert 		n = (len + 1) * sizeof(u_short);
1012c0b746e5SOllivier Robert 		/* Not enough data yet */
1013c0b746e5SOllivier Robert 		if (size < cc + n)
1014c0b746e5SOllivier Robert 			return (0);
1015c0b746e5SOllivier Robert 
1016c0b746e5SOllivier Robert 		/* Check payload checksum */
1017c0b746e5SOllivier Robert 		sp = (u_short *)(hp + 1);
1018c0b746e5SOllivier Robert 		if (jupiter_cksum(sp, len) != getshort(sp[len])) {
10192d4e511cSCy Schubert 			jupiter_debug(up->peer,
10202b15cb3dSCy Schubert 			    __func__, "bad payload checksum!");
1021c0b746e5SOllivier Robert 			/* This is drastic but checksum errors should be rare */
10222d4e511cSCy Schubert 			up->ssize = 0;
1023c0b746e5SOllivier Robert 			return (0);
1024c0b746e5SOllivier Robert 		}
1025c0b746e5SOllivier Robert 		cc += n;
1026c0b746e5SOllivier Robert 	}
1027c0b746e5SOllivier Robert 	return (cc);
1028c0b746e5SOllivier Robert }
1029c0b746e5SOllivier Robert 
10309c2daa00SOllivier Robert #else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
1031*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
10329c2daa00SOllivier Robert #endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
1033