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