154359Sroberto/* 2132451Sroberto * Copyright (c) 1997, 1998, 2003 354359Sroberto * The Regents of the University of California. All rights reserved. 454359Sroberto * 554359Sroberto * Redistribution and use in source and binary forms, with or without 654359Sroberto * modification, are permitted provided that the following conditions 754359Sroberto * are met: 854359Sroberto * 1. Redistributions of source code must retain the above copyright 954359Sroberto * notice, this list of conditions and the following disclaimer. 1054359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1154359Sroberto * notice, this list of conditions and the following disclaimer in the 1254359Sroberto * documentation and/or other materials provided with the distribution. 1354359Sroberto * 3. All advertising materials mentioning features or use of this software 1454359Sroberto * must display the following acknowledgement: 1554359Sroberto * This product includes software developed by the University of 1654359Sroberto * California, Lawrence Berkeley Laboratory. 1754359Sroberto * 4. The name of the University may not be used to endorse or promote 1854359Sroberto * products derived from this software without specific prior 1954359Sroberto * written permission. 2054359Sroberto * 2154359Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2254359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2354359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2454359Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2554359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2654359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2754359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2854359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2954359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3054359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3154359Sroberto * SUCH DAMAGE. 3254359Sroberto */ 3354359Sroberto 3454359Sroberto#ifdef HAVE_CONFIG_H 3554359Sroberto# include <config.h> 3654359Sroberto#endif 3754359Sroberto 38132451Sroberto#if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI) 3954359Sroberto 4054359Sroberto#include "ntpd.h" 4154359Sroberto#include "ntp_io.h" 4254359Sroberto#include "ntp_refclock.h" 4354359Sroberto#include "ntp_unixtime.h" 4454359Sroberto#include "ntp_stdlib.h" 4554359Sroberto 4682498Sroberto#include <stdio.h> 4782498Sroberto#include <ctype.h> 4882498Sroberto 4954359Sroberto#include "jupiter.h" 5054359Sroberto 51132451Sroberto#ifdef HAVE_PPSAPI 52182007Sroberto# include "ppsapi_timepps.h" 53132451Sroberto#endif 5454359Sroberto 55285612Sdelphij#ifdef WORDS_BIGENDIAN 5654359Sroberto#define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 5754359Sroberto#define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 5854359Sroberto#else 59285612Sdelphij#define getshort(s) ((u_short)(s)) 60285612Sdelphij#define putshort(s) ((u_short)(s)) 6154359Sroberto#endif 6254359Sroberto 6354359Sroberto/* 6454359Sroberto * This driver supports the Rockwell Jupiter GPS Receiver board 6554359Sroberto * adapted to precision timing applications. It requires the 6654359Sroberto * ppsclock line discipline or streams module described in the 6754359Sroberto * Line Disciplines and Streams Drivers page. It also requires a 6854359Sroberto * gadget box and 1-PPS level converter, such as described in the 6954359Sroberto * Pulse-per-second (PPS) Signal Interfacing page. 7054359Sroberto * 7154359Sroberto * It may work (with minor modifications) with other Rockwell GPS 7254359Sroberto * receivers such as the CityTracker. 7354359Sroberto */ 7454359Sroberto 7554359Sroberto/* 7654359Sroberto * GPS Definitions 7754359Sroberto */ 7854359Sroberto#define DEVICE "/dev/gps%d" /* device name and unit */ 7954359Sroberto#define SPEED232 B9600 /* baud */ 8054359Sroberto 8154359Sroberto/* 8254359Sroberto * Radio interface parameters 8354359Sroberto */ 8454359Sroberto#define PRECISION (-18) /* precision assumed (about 4 us) */ 8554359Sroberto#define REFID "GPS\0" /* reference id */ 8654359Sroberto#define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */ 8754359Sroberto#define DEFFUDGETIME 0 /* default fudge time (ms) */ 8854359Sroberto 8954359Sroberto/* Unix timestamp for the GPS epoch: January 6, 1980 */ 9054359Sroberto#define GPS_EPOCH 315964800 9154359Sroberto 92309008Sdelphij/* Rata Die Number of first day of GPS epoch. This is the number of days 93309008Sdelphij * since 0000-12-31 to 1980-01-06 in the proleptic Gregorian Calendar. 94309008Sdelphij */ 95309008Sdelphij#define RDN_GPS_EPOCH (4*146097 + 138431 + 1) 96309008Sdelphij 9754359Sroberto/* Double short to unsigned int */ 9854359Sroberto#define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 9954359Sroberto 10054359Sroberto/* Double short to signed int */ 10154359Sroberto#define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 10254359Sroberto 10354359Sroberto/* One week's worth of seconds */ 10454359Sroberto#define WEEKSECS (7 * 24 * 60 * 60) 10554359Sroberto 10654359Sroberto/* 10754359Sroberto * Jupiter unit control structure. 10854359Sroberto */ 109132451Srobertostruct instance { 110132451Sroberto struct peer *peer; /* peer */ 11154359Sroberto u_int pollcnt; /* poll message counter */ 11254359Sroberto u_int polled; /* Hand in a time sample? */ 113132451Sroberto#ifdef HAVE_PPSAPI 114132451Sroberto pps_params_t pps_params; /* pps parameters */ 115132451Sroberto pps_info_t pps_info; /* last pps data */ 116132451Sroberto pps_handle_t pps_handle; /* pps handle */ 117132451Sroberto u_int assert; /* pps edge to use */ 118182007Sroberto u_int hardpps; /* enable kernel mode */ 119132451Sroberto struct timespec ts; /* last timestamp */ 120132451Sroberto#endif 121132451Sroberto l_fp limit; 122132451Sroberto u_int gpos_gweek; /* Current GPOS GPS week number */ 123132451Sroberto u_int gpos_sweek; /* Current GPOS GPS seconds into week */ 12454359Sroberto u_int gweek; /* current GPS week number */ 12554359Sroberto u_int32 lastsweek; /* last seconds into GPS week */ 126132451Sroberto time_t timecode; /* current ntp timecode */ 12754359Sroberto u_int32 stime; /* used to detect firmware bug */ 12854359Sroberto int wantid; /* don't reconfig on channel id msg */ 12954359Sroberto u_int moving; /* mobile platform? */ 130132451Sroberto u_char sloppyclockflag; /* fudge flags */ 13154359Sroberto u_short sbuf[512]; /* local input buffer */ 13254359Sroberto int ssize; /* space used in sbuf */ 13354359Sroberto}; 13454359Sroberto 13554359Sroberto/* 13654359Sroberto * Function prototypes 13754359Sroberto */ 138285612Sdelphijstatic void jupiter_canmsg (struct instance *, u_int); 139285612Sdelphijstatic u_short jupiter_cksum (u_short *, u_int); 140285612Sdelphijstatic int jupiter_config (struct instance *); 141285612Sdelphijstatic void jupiter_debug (struct peer *, const char *, 142338531Sdelphij const char *, ...) NTP_PRINTF(3, 4); 143285612Sdelphijstatic const char * jupiter_parse_t (struct instance *, u_short *); 144285612Sdelphijstatic const char * jupiter_parse_gpos (struct instance *, u_short *); 145285612Sdelphijstatic void jupiter_platform (struct instance *, u_int); 146285612Sdelphijstatic void jupiter_poll (int, struct peer *); 147285612Sdelphijstatic void jupiter_control (int, const struct refclockstat *, 148285612Sdelphij struct refclockstat *, struct peer *); 149132451Sroberto#ifdef HAVE_PPSAPI 150285612Sdelphijstatic int jupiter_ppsapi (struct instance *); 151285612Sdelphijstatic int jupiter_pps (struct instance *); 152132451Sroberto#endif /* HAVE_PPSAPI */ 153285612Sdelphijstatic int jupiter_recv (struct instance *); 154285612Sdelphijstatic void jupiter_receive (struct recvbuf *rbufp); 155285612Sdelphijstatic void jupiter_reqmsg (struct instance *, u_int, u_int); 156285612Sdelphijstatic void jupiter_reqonemsg(struct instance *, u_int); 157285612Sdelphijstatic char * jupiter_send (struct instance *, struct jheader *); 158285612Sdelphijstatic void jupiter_shutdown(int, struct peer *); 159285612Sdelphijstatic int jupiter_start (int, struct peer *); 16054359Sroberto 161309008Sdelphijstatic u_int get_full_week(u_int base_week, u_int gpos_week); 162309008Sdelphijstatic u_int get_base_week(void); 163309008Sdelphij 164309008Sdelphij 16554359Sroberto/* 16654359Sroberto * Transfer vector 16754359Sroberto */ 16854359Srobertostruct refclock refclock_jupiter = { 16954359Sroberto jupiter_start, /* start up driver */ 17054359Sroberto jupiter_shutdown, /* shut down driver */ 17154359Sroberto jupiter_poll, /* transmit poll message */ 172132451Sroberto jupiter_control, /* (clock control) */ 17354359Sroberto noentry, /* (clock init) */ 17454359Sroberto noentry, /* (clock buginfo) */ 17554359Sroberto NOFLAGS /* not used */ 17654359Sroberto}; 17754359Sroberto 17854359Sroberto/* 17954359Sroberto * jupiter_start - open the devices and initialize data for processing 18054359Sroberto */ 18154359Srobertostatic int 18254359Srobertojupiter_start( 183132451Sroberto int unit, 184132451Sroberto struct peer *peer 18554359Sroberto ) 18654359Sroberto{ 18754359Sroberto struct refclockproc *pp; 188132451Sroberto struct instance *instance; 189285612Sdelphij int fd; 19054359Sroberto char gpsdev[20]; 19154359Sroberto 19254359Sroberto /* 19354359Sroberto * Open serial port 19454359Sroberto */ 195285612Sdelphij snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 196132451Sroberto fd = refclock_open(gpsdev, SPEED232, LDISC_RAW); 197285612Sdelphij if (fd <= 0) { 198285612Sdelphij jupiter_debug(peer, "jupiter_start", "open %s: %m", 199285612Sdelphij gpsdev); 20054359Sroberto return (0); 20154359Sroberto } 20254359Sroberto 20354359Sroberto /* Allocate unit structure */ 204285612Sdelphij instance = emalloc_zero(sizeof(*instance)); 205132451Sroberto instance->peer = peer; 20654359Sroberto pp = peer->procptr; 20754359Sroberto pp->io.clock_recv = jupiter_receive; 208285612Sdelphij pp->io.srcclock = peer; 20954359Sroberto pp->io.datalen = 0; 21054359Sroberto pp->io.fd = fd; 21154359Sroberto if (!io_addclock(&pp->io)) { 212285612Sdelphij close(fd); 213285612Sdelphij pp->io.fd = -1; 214132451Sroberto free(instance); 21554359Sroberto return (0); 21654359Sroberto } 217285612Sdelphij pp->unitptr = instance; 21854359Sroberto 21954359Sroberto /* 22054359Sroberto * Initialize miscellaneous variables 22154359Sroberto */ 22254359Sroberto peer->precision = PRECISION; 22354359Sroberto pp->clockdesc = DESCRIPTION; 22454359Sroberto memcpy((char *)&pp->refid, REFID, 4); 22554359Sroberto 226132451Sroberto#ifdef HAVE_PPSAPI 227182007Sroberto instance->assert = 1; 228182007Sroberto instance->hardpps = 0; 229132451Sroberto /* 230132451Sroberto * Start the PPSAPI interface if it is there. Default to use 231132451Sroberto * the assert edge and do not enable the kernel hardpps. 232132451Sroberto */ 233132451Sroberto if (time_pps_create(fd, &instance->pps_handle) < 0) { 234132451Sroberto instance->pps_handle = 0; 235132451Sroberto msyslog(LOG_ERR, 236132451Sroberto "refclock_jupiter: time_pps_create failed: %m"); 237132451Sroberto } 238182007Sroberto else if (!jupiter_ppsapi(instance)) 239132451Sroberto goto clean_up; 240132451Sroberto#endif /* HAVE_PPSAPI */ 24154359Sroberto 24254359Sroberto /* Ensure the receiver is properly configured */ 243132451Sroberto if (!jupiter_config(instance)) 244132451Sroberto goto clean_up; 24554359Sroberto 24654359Sroberto return (1); 247132451Sroberto 248132451Srobertoclean_up: 249132451Sroberto jupiter_shutdown(unit, peer); 250132451Sroberto pp->unitptr = 0; 251132451Sroberto return (0); 25254359Sroberto} 25354359Sroberto 25454359Sroberto/* 25554359Sroberto * jupiter_shutdown - shut down the clock 25654359Sroberto */ 25754359Srobertostatic void 258132451Srobertojupiter_shutdown(int unit, struct peer *peer) 25954359Sroberto{ 260132451Sroberto struct instance *instance; 26154359Sroberto struct refclockproc *pp; 26254359Sroberto 26354359Sroberto pp = peer->procptr; 264285612Sdelphij instance = pp->unitptr; 265182007Sroberto if (!instance) 266132451Sroberto return; 267132451Sroberto 268132451Sroberto#ifdef HAVE_PPSAPI 269132451Sroberto if (instance->pps_handle) { 270132451Sroberto time_pps_destroy(instance->pps_handle); 271132451Sroberto instance->pps_handle = 0; 272132451Sroberto } 273132451Sroberto#endif /* HAVE_PPSAPI */ 274132451Sroberto 275285612Sdelphij if (pp->io.fd != -1) 276285612Sdelphij io_closeclock(&pp->io); 277132451Sroberto free(instance); 27854359Sroberto} 27954359Sroberto 28054359Sroberto/* 28154359Sroberto * jupiter_config - Configure the receiver 28254359Sroberto */ 283132451Srobertostatic int 284132451Srobertojupiter_config(struct instance *instance) 28554359Sroberto{ 286285612Sdelphij jupiter_debug(instance->peer, __func__, "init receiver"); 28754359Sroberto 28854359Sroberto /* 28954359Sroberto * Initialize the unit variables 29054359Sroberto */ 291132451Sroberto instance->sloppyclockflag = instance->peer->procptr->sloppyclockflag; 292132451Sroberto instance->moving = !!(instance->sloppyclockflag & CLK_FLAG2); 293132451Sroberto if (instance->moving) 294285612Sdelphij jupiter_debug(instance->peer, __func__, "mobile platform"); 29554359Sroberto 296132451Sroberto instance->pollcnt = 2; 297132451Sroberto instance->polled = 0; 298132451Sroberto instance->gpos_gweek = 0; 299132451Sroberto instance->gpos_sweek = 0; 300132451Sroberto instance->gweek = 0; 301132451Sroberto instance->lastsweek = 2 * WEEKSECS; 302132451Sroberto instance->timecode = 0; 303132451Sroberto instance->stime = 0; 304132451Sroberto instance->ssize = 0; 30554359Sroberto 30654359Sroberto /* Stop outputting all messages */ 307132451Sroberto jupiter_canmsg(instance, JUPITER_ALL); 30854359Sroberto 30954359Sroberto /* Request the receiver id so we can syslog the firmware version */ 310132451Sroberto jupiter_reqonemsg(instance, JUPITER_O_ID); 31154359Sroberto 31254359Sroberto /* Flag that this the id was requested (so we don't get called again) */ 313132451Sroberto instance->wantid = 1; 31454359Sroberto 31554359Sroberto /* Request perodic time mark pulse messages */ 316132451Sroberto jupiter_reqmsg(instance, JUPITER_O_PULSE, 1); 31754359Sroberto 318132451Sroberto /* Request perodic geodetic position status */ 319132451Sroberto jupiter_reqmsg(instance, JUPITER_O_GPOS, 1); 320132451Sroberto 32154359Sroberto /* Set application platform type */ 322132451Sroberto if (instance->moving) 323132451Sroberto jupiter_platform(instance, JUPITER_I_PLAT_MED); 32454359Sroberto else 325132451Sroberto jupiter_platform(instance, JUPITER_I_PLAT_LOW); 326132451Sroberto 327132451Sroberto return (1); 32854359Sroberto} 32954359Sroberto 330132451Sroberto#ifdef HAVE_PPSAPI 33154359Sroberto/* 332132451Sroberto * Initialize PPSAPI 333132451Sroberto */ 334132451Srobertoint 335132451Srobertojupiter_ppsapi( 336182007Sroberto struct instance *instance /* unit structure pointer */ 337132451Sroberto ) 338132451Sroberto{ 339132451Sroberto int capability; 340132451Sroberto 341132451Sroberto if (time_pps_getcap(instance->pps_handle, &capability) < 0) { 342132451Sroberto msyslog(LOG_ERR, 343132451Sroberto "refclock_jupiter: time_pps_getcap failed: %m"); 344132451Sroberto return (0); 345132451Sroberto } 346132451Sroberto memset(&instance->pps_params, 0, sizeof(pps_params_t)); 347182007Sroberto if (!instance->assert) 348132451Sroberto instance->pps_params.mode = capability & PPS_CAPTURECLEAR; 349132451Sroberto else 350132451Sroberto instance->pps_params.mode = capability & PPS_CAPTUREASSERT; 351132451Sroberto if (!(instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { 352132451Sroberto msyslog(LOG_ERR, 353132451Sroberto "refclock_jupiter: invalid capture edge %d", 354182007Sroberto instance->assert); 355132451Sroberto return (0); 356132451Sroberto } 357132451Sroberto instance->pps_params.mode |= PPS_TSFMT_TSPEC; 358132451Sroberto if (time_pps_setparams(instance->pps_handle, &instance->pps_params) < 0) { 359132451Sroberto msyslog(LOG_ERR, 360132451Sroberto "refclock_jupiter: time_pps_setparams failed: %m"); 361132451Sroberto return (0); 362132451Sroberto } 363182007Sroberto if (instance->hardpps) { 364132451Sroberto if (time_pps_kcbind(instance->pps_handle, PPS_KC_HARDPPS, 365182007Sroberto instance->pps_params.mode & ~PPS_TSFMT_TSPEC, 366132451Sroberto PPS_TSFMT_TSPEC) < 0) { 367132451Sroberto msyslog(LOG_ERR, 368132451Sroberto "refclock_jupiter: time_pps_kcbind failed: %m"); 369132451Sroberto return (0); 370132451Sroberto } 371285612Sdelphij hardpps_enable = 1; 372132451Sroberto } 373132451Sroberto/* instance->peer->precision = PPS_PRECISION; */ 374132451Sroberto 375132451Sroberto#if DEBUG 376132451Sroberto if (debug) { 377132451Sroberto time_pps_getparams(instance->pps_handle, &instance->pps_params); 378285612Sdelphij jupiter_debug(instance->peer, __func__, 379132451Sroberto "pps capability 0x%x version %d mode 0x%x kern %d", 380132451Sroberto capability, instance->pps_params.api_version, 381182007Sroberto instance->pps_params.mode, instance->hardpps); 382132451Sroberto } 383132451Sroberto#endif 384132451Sroberto 385132451Sroberto return (1); 386132451Sroberto} 387132451Sroberto 388132451Sroberto/* 389132451Sroberto * Get PPSAPI timestamps. 390132451Sroberto * 391132451Sroberto * Return 0 on failure and 1 on success. 392132451Sroberto */ 393132451Srobertostatic int 394132451Srobertojupiter_pps(struct instance *instance) 395132451Sroberto{ 396132451Sroberto pps_info_t pps_info; 397132451Sroberto struct timespec timeout, ts; 398132451Sroberto double dtemp; 399132451Sroberto l_fp tstmp; 400132451Sroberto 401132451Sroberto /* 402132451Sroberto * Convert the timespec nanoseconds field to ntp l_fp units. 403132451Sroberto */ 404132451Sroberto if (instance->pps_handle == 0) 405132451Sroberto return 1; 406132451Sroberto timeout.tv_sec = 0; 407132451Sroberto timeout.tv_nsec = 0; 408132451Sroberto memcpy(&pps_info, &instance->pps_info, sizeof(pps_info_t)); 409132451Sroberto if (time_pps_fetch(instance->pps_handle, PPS_TSFMT_TSPEC, &instance->pps_info, 410132451Sroberto &timeout) < 0) 411132451Sroberto return 1; 412132451Sroberto if (instance->pps_params.mode & PPS_CAPTUREASSERT) { 413132451Sroberto if (pps_info.assert_sequence == 414132451Sroberto instance->pps_info.assert_sequence) 415132451Sroberto return 1; 416132451Sroberto ts = instance->pps_info.assert_timestamp; 417132451Sroberto } else if (instance->pps_params.mode & PPS_CAPTURECLEAR) { 418132451Sroberto if (pps_info.clear_sequence == 419132451Sroberto instance->pps_info.clear_sequence) 420132451Sroberto return 1; 421132451Sroberto ts = instance->pps_info.clear_timestamp; 422132451Sroberto } else { 423132451Sroberto return 1; 424132451Sroberto } 425132451Sroberto if ((instance->ts.tv_sec == ts.tv_sec) && (instance->ts.tv_nsec == ts.tv_nsec)) 426132451Sroberto return 1; 427132451Sroberto instance->ts = ts; 428132451Sroberto 429285612Sdelphij tstmp.l_ui = (u_int32)ts.tv_sec + JAN_1970; 430132451Sroberto dtemp = ts.tv_nsec * FRAC / 1e9; 431132451Sroberto tstmp.l_uf = (u_int32)dtemp; 432132451Sroberto instance->peer->procptr->lastrec = tstmp; 433132451Sroberto return 0; 434132451Sroberto} 435132451Sroberto#endif /* HAVE_PPSAPI */ 436132451Sroberto 437132451Sroberto/* 43854359Sroberto * jupiter_poll - jupiter watchdog routine 43954359Sroberto */ 44054359Srobertostatic void 441132451Srobertojupiter_poll(int unit, struct peer *peer) 44254359Sroberto{ 443132451Sroberto struct instance *instance; 444132451Sroberto struct refclockproc *pp; 44554359Sroberto 44654359Sroberto pp = peer->procptr; 447285612Sdelphij instance = pp->unitptr; 44854359Sroberto 44954359Sroberto /* 45054359Sroberto * You don't need to poll this clock. It puts out timecodes 45154359Sroberto * once per second. If asked for a timestamp, take note. 45254359Sroberto * The next time a timecode comes in, it will be fed back. 45354359Sroberto */ 45454359Sroberto 45554359Sroberto /* 45654359Sroberto * If we haven't had a response in a while, reset the receiver. 45754359Sroberto */ 458132451Sroberto if (instance->pollcnt > 0) { 459132451Sroberto instance->pollcnt--; 46054359Sroberto } else { 46154359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 46254359Sroberto 46354359Sroberto /* Request the receiver id to trigger a reconfig */ 464132451Sroberto jupiter_reqonemsg(instance, JUPITER_O_ID); 465132451Sroberto instance->wantid = 0; 46654359Sroberto } 46754359Sroberto 46854359Sroberto /* 46954359Sroberto * polled every 64 seconds. Ask jupiter_receive to hand in 47054359Sroberto * a timestamp. 47154359Sroberto */ 472132451Sroberto instance->polled = 1; 47354359Sroberto pp->polls++; 47454359Sroberto} 47554359Sroberto 47654359Sroberto/* 477132451Sroberto * jupiter_control - fudge control 47854359Sroberto */ 47954359Srobertostatic void 480132451Srobertojupiter_control( 481132451Sroberto int unit, /* unit (not used) */ 482285612Sdelphij const struct refclockstat *in, /* input parameters (not used) */ 483132451Sroberto struct refclockstat *out, /* output parameters (not used) */ 484132451Sroberto struct peer *peer /* peer structure pointer */ 485132451Sroberto ) 48654359Sroberto{ 487132451Sroberto struct refclockproc *pp; 488132451Sroberto struct instance *instance; 489132451Sroberto u_char sloppyclockflag; 49054359Sroberto 49154359Sroberto pp = peer->procptr; 492285612Sdelphij instance = pp->unitptr; 49354359Sroberto 494132451Sroberto DTOLFP(pp->fudgetime2, &instance->limit); 495132451Sroberto /* Force positive value. */ 496132451Sroberto if (L_ISNEG(&instance->limit)) 497132451Sroberto L_NEG(&instance->limit); 498132451Sroberto 499132451Sroberto#ifdef HAVE_PPSAPI 500132451Sroberto instance->assert = !(pp->sloppyclockflag & CLK_FLAG3); 501182007Sroberto jupiter_ppsapi(instance); 502132451Sroberto#endif /* HAVE_PPSAPI */ 503132451Sroberto 504132451Sroberto sloppyclockflag = instance->sloppyclockflag; 505132451Sroberto instance->sloppyclockflag = pp->sloppyclockflag; 506132451Sroberto if ((instance->sloppyclockflag & CLK_FLAG2) != 50754359Sroberto (sloppyclockflag & CLK_FLAG2)) { 508285612Sdelphij jupiter_debug(peer, __func__, 509132451Sroberto "mode switch: reset receiver"); 510132451Sroberto jupiter_config(instance); 51154359Sroberto return; 51254359Sroberto } 513132451Sroberto} 51454359Sroberto 515132451Sroberto/* 516132451Sroberto * jupiter_receive - receive gps data 517132451Sroberto * Gag me! 518132451Sroberto */ 519132451Srobertostatic void 520132451Srobertojupiter_receive(struct recvbuf *rbufp) 521132451Sroberto{ 522285612Sdelphij size_t bpcnt; 523285612Sdelphij int cc, size, ppsret; 524132451Sroberto time_t last_timecode; 525132451Sroberto u_int32 laststime; 526285612Sdelphij const char *cp; 527132451Sroberto u_char *bp; 528132451Sroberto u_short *sp; 529132451Sroberto struct jid *ip; 530132451Sroberto struct jheader *hp; 531132451Sroberto struct peer *peer; 532132451Sroberto struct refclockproc *pp; 533132451Sroberto struct instance *instance; 534132451Sroberto l_fp tstamp; 53554359Sroberto 536132451Sroberto /* Initialize pointers and read the timecode and timestamp */ 537285612Sdelphij peer = rbufp->recv_peer; 538132451Sroberto pp = peer->procptr; 539285612Sdelphij instance = pp->unitptr; 540132451Sroberto 54154359Sroberto bp = (u_char *)rbufp->recv_buffer; 54254359Sroberto bpcnt = rbufp->recv_length; 54354359Sroberto 54454359Sroberto /* This shouldn't happen */ 545132451Sroberto if (bpcnt > sizeof(instance->sbuf) - instance->ssize) 546132451Sroberto bpcnt = sizeof(instance->sbuf) - instance->ssize; 54754359Sroberto 54854359Sroberto /* Append to input buffer */ 549132451Sroberto memcpy((u_char *)instance->sbuf + instance->ssize, bp, bpcnt); 550132451Sroberto instance->ssize += bpcnt; 55154359Sroberto 552132451Sroberto /* While there's at least a header and we parse an intact message */ 553285612Sdelphij while (instance->ssize > (int)sizeof(*hp) && (cc = jupiter_recv(instance)) > 0) { 554132451Sroberto instance->pollcnt = 2; 555132451Sroberto 556132451Sroberto tstamp = rbufp->recv_time; 557132451Sroberto hp = (struct jheader *)instance->sbuf; 55854359Sroberto sp = (u_short *)(hp + 1); 55954359Sroberto size = cc - sizeof(*hp); 56054359Sroberto switch (getshort(hp->id)) { 56154359Sroberto 56254359Sroberto case JUPITER_O_PULSE: 56354359Sroberto if (size != sizeof(struct jpulse)) { 564285612Sdelphij jupiter_debug(peer, __func__, 565285612Sdelphij "pulse: len %d != %u", 56654359Sroberto size, (int)sizeof(struct jpulse)); 56754359Sroberto refclock_report(peer, CEVNT_BADREPLY); 56854359Sroberto break; 56954359Sroberto } 57054359Sroberto 57154359Sroberto /* 57254359Sroberto * There appears to be a firmware bug related 57354359Sroberto * to the pulse message; in addition to the one 57454359Sroberto * per second messages, we get an extra pulse 57554359Sroberto * message once an hour (on the anniversary of 57654359Sroberto * the cold start). It seems to come 200 ms 57754359Sroberto * after the one requested. So if we've seen a 57854359Sroberto * pulse message in the last 210 ms, we skip 57954359Sroberto * this one. 58054359Sroberto */ 581132451Sroberto laststime = instance->stime; 582132451Sroberto instance->stime = DS2UI(((struct jpulse *)sp)->stime); 583132451Sroberto if (laststime != 0 && instance->stime - laststime <= 21) { 584285612Sdelphij jupiter_debug(peer, __func__, 585132451Sroberto "avoided firmware bug (stime %.2f, laststime %.2f)", 586132451Sroberto (double)instance->stime * 0.01, (double)laststime * 0.01); 58754359Sroberto break; 58854359Sroberto } 58954359Sroberto 59054359Sroberto /* Retrieve pps timestamp */ 591132451Sroberto ppsret = jupiter_pps(instance); 59254359Sroberto 593132451Sroberto /* 594132451Sroberto * Add one second if msg received early 595132451Sroberto * (i.e. before limit, a.k.a. fudgetime2) in 596132451Sroberto * the second. 597132451Sroberto */ 598132451Sroberto L_SUB(&tstamp, &pp->lastrec); 599132451Sroberto if (!L_ISGEQ(&tstamp, &instance->limit)) 600132451Sroberto ++pp->lastrec.l_ui; 601132451Sroberto 60254359Sroberto /* Parse timecode (even when there's no pps) */ 603132451Sroberto last_timecode = instance->timecode; 604132451Sroberto if ((cp = jupiter_parse_t(instance, sp)) != NULL) { 605285612Sdelphij jupiter_debug(peer, __func__, 606285612Sdelphij "pulse: %s", cp); 60754359Sroberto break; 60854359Sroberto } 60954359Sroberto 61054359Sroberto /* Bail if we didn't get a pps timestamp */ 61154359Sroberto if (ppsret) 61254359Sroberto break; 61354359Sroberto 61454359Sroberto /* Bail if we don't have the last timecode yet */ 61554359Sroberto if (last_timecode == 0) 61654359Sroberto break; 61754359Sroberto 61854359Sroberto /* Add the new sample to a median filter */ 619285612Sdelphij tstamp.l_ui = JAN_1970 + (u_int32)last_timecode; 620132451Sroberto tstamp.l_uf = 0; 62154359Sroberto 622132451Sroberto refclock_process_offset(pp, tstamp, pp->lastrec, pp->fudgetime1); 623132451Sroberto 62454359Sroberto /* 62554359Sroberto * The clock will blurt a timecode every second 62654359Sroberto * but we only want one when polled. If we 62754359Sroberto * havn't been polled, bail out. 62854359Sroberto */ 629132451Sroberto if (!instance->polled) 63054359Sroberto break; 631132451Sroberto instance->polled = 0; 63254359Sroberto 63354359Sroberto /* 63454359Sroberto * It's a live one! Remember this time. 63554359Sroberto */ 63654359Sroberto 637132451Sroberto pp->lastref = pp->lastrec; 638132451Sroberto refclock_receive(peer); 639132451Sroberto 64054359Sroberto /* 641132451Sroberto * If we get here - what we got from the clock is 642132451Sroberto * OK, so say so 64354359Sroberto */ 644132451Sroberto refclock_report(peer, CEVNT_NOMINAL); 64554359Sroberto 64654359Sroberto /* 64754359Sroberto * We have succeeded in answering the poll. 64854359Sroberto * Turn off the flag and return 64954359Sroberto */ 650132451Sroberto instance->polled = 0; 65154359Sroberto break; 65254359Sroberto 653132451Sroberto case JUPITER_O_GPOS: 654132451Sroberto if (size != sizeof(struct jgpos)) { 655285612Sdelphij jupiter_debug(peer, __func__, 656285612Sdelphij "gpos: len %d != %u", 657132451Sroberto size, (int)sizeof(struct jgpos)); 658132451Sroberto refclock_report(peer, CEVNT_BADREPLY); 659132451Sroberto break; 660132451Sroberto } 661132451Sroberto 662132451Sroberto if ((cp = jupiter_parse_gpos(instance, sp)) != NULL) { 663285612Sdelphij jupiter_debug(peer, __func__, 664285612Sdelphij "gpos: %s", cp); 665132451Sroberto break; 666132451Sroberto } 667132451Sroberto break; 668132451Sroberto 66954359Sroberto case JUPITER_O_ID: 67054359Sroberto if (size != sizeof(struct jid)) { 671285612Sdelphij jupiter_debug(peer, __func__, 672285612Sdelphij "id: len %d != %u", 67354359Sroberto size, (int)sizeof(struct jid)); 67454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 67554359Sroberto break; 67654359Sroberto } 67754359Sroberto /* 67854359Sroberto * If we got this message because the Jupiter 679132451Sroberto * just powered instance, it needs to be reconfigured. 68054359Sroberto */ 68154359Sroberto ip = (struct jid *)sp; 682285612Sdelphij jupiter_debug(peer, __func__, 683285612Sdelphij "%s chan ver %s, %s (%s)", 68454359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 68554359Sroberto msyslog(LOG_DEBUG, 686182007Sroberto "jupiter_receive: %s chan ver %s, %s (%s)", 68754359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 688132451Sroberto if (instance->wantid) 689132451Sroberto instance->wantid = 0; 69054359Sroberto else { 691285612Sdelphij jupiter_debug(peer, __func__, "reset receiver"); 692132451Sroberto jupiter_config(instance); 693132451Sroberto /* 694132451Sroberto * Restore since jupiter_config() just 695132451Sroberto * zeroed it 696132451Sroberto */ 697132451Sroberto instance->ssize = cc; 69854359Sroberto } 69954359Sroberto break; 70054359Sroberto 70154359Sroberto default: 702285612Sdelphij jupiter_debug(peer, __func__, "unknown message id %d", 70354359Sroberto getshort(hp->id)); 70454359Sroberto break; 70554359Sroberto } 706132451Sroberto instance->ssize -= cc; 707132451Sroberto if (instance->ssize < 0) { 70854359Sroberto fprintf(stderr, "jupiter_recv: negative ssize!\n"); 70954359Sroberto abort(); 710132451Sroberto } else if (instance->ssize > 0) 711132451Sroberto memcpy(instance->sbuf, (u_char *)instance->sbuf + cc, instance->ssize); 71254359Sroberto } 71354359Sroberto} 71454359Sroberto 715285612Sdelphijstatic const char * 716132451Srobertojupiter_parse_t(struct instance *instance, u_short *sp) 71754359Sroberto{ 718132451Sroberto struct tm *tm; 719132451Sroberto char *cp; 720132451Sroberto struct jpulse *jp; 721132451Sroberto u_int32 sweek; 722132451Sroberto time_t last_timecode; 723132451Sroberto u_short flags; 72454359Sroberto 72554359Sroberto jp = (struct jpulse *)sp; 72654359Sroberto 72754359Sroberto /* The timecode is presented as seconds into the current GPS week */ 728132451Sroberto sweek = DS2UI(jp->sweek) % WEEKSECS; 72954359Sroberto 73054359Sroberto /* 73154359Sroberto * If we don't know the current GPS week, calculate it from the 73254359Sroberto * current time. (It's too bad they didn't include this 73354359Sroberto * important value in the pulse message). We'd like to pick it 73454359Sroberto * up from one of the other messages like gpos or chan but they 73554359Sroberto * don't appear to be synchronous with time keeping and changes 73654359Sroberto * too soon (something like 10 seconds before the new GPS 73754359Sroberto * week). 73854359Sroberto * 73954359Sroberto * If we already know the current GPS week, increment it when 74054359Sroberto * we wrap into a new week. 74154359Sroberto */ 742132451Sroberto if (instance->gweek == 0) { 743132451Sroberto if (!instance->gpos_gweek) { 744132451Sroberto return ("jupiter_parse_t: Unknown gweek"); 745132451Sroberto } 746132451Sroberto 747132451Sroberto instance->gweek = instance->gpos_gweek; 748132451Sroberto 749132451Sroberto /* 750132451Sroberto * Fix warps. GPOS has GPS time and PULSE has UTC. 751132451Sroberto * Plus, GPOS need not be completely in synch with 752132451Sroberto * the PPS signal. 753132451Sroberto */ 754132451Sroberto if (instance->gpos_sweek >= sweek) { 755132451Sroberto if ((instance->gpos_sweek - sweek) > WEEKSECS / 2) 756132451Sroberto ++instance->gweek; 757132451Sroberto } 758132451Sroberto else { 759132451Sroberto if ((sweek - instance->gpos_sweek) > WEEKSECS / 2) 760132451Sroberto --instance->gweek; 761132451Sroberto } 76254359Sroberto } 763132451Sroberto else if (sweek == 0 && instance->lastsweek == WEEKSECS - 1) { 764132451Sroberto ++instance->gweek; 765285612Sdelphij jupiter_debug(instance->peer, __func__, 766285612Sdelphij "NEW gps week %u", instance->gweek); 767132451Sroberto } 76854359Sroberto 76954359Sroberto /* 77054359Sroberto * See if the sweek stayed the same (this happens when there is 77154359Sroberto * no pps pulse). 77254359Sroberto * 77354359Sroberto * Otherwise, look for time warps: 77454359Sroberto * 77554359Sroberto * - we have stored at least one lastsweek and 77654359Sroberto * - the sweek didn't increase by one and 77754359Sroberto * - we didn't wrap to a new GPS week 77854359Sroberto * 77954359Sroberto * Then we warped. 78054359Sroberto */ 781132451Sroberto if (instance->lastsweek == sweek) 782285612Sdelphij jupiter_debug(instance->peer, __func__, 783285612Sdelphij "gps sweek not incrementing (%d)", 78454359Sroberto sweek); 785132451Sroberto else if (instance->lastsweek != 2 * WEEKSECS && 786132451Sroberto instance->lastsweek + 1 != sweek && 787132451Sroberto !(sweek == 0 && instance->lastsweek == WEEKSECS - 1)) 788285612Sdelphij jupiter_debug(instance->peer, __func__, 789285612Sdelphij "gps sweek jumped (was %d, now %d)", 790132451Sroberto instance->lastsweek, sweek); 791132451Sroberto instance->lastsweek = sweek; 79254359Sroberto 79354359Sroberto /* This timecode describes next pulse */ 794132451Sroberto last_timecode = instance->timecode; 795132451Sroberto instance->timecode = 796132451Sroberto GPS_EPOCH + (instance->gweek * WEEKSECS) + sweek; 79754359Sroberto 79854359Sroberto if (last_timecode == 0) 79954359Sroberto /* XXX debugging */ 800285612Sdelphij jupiter_debug(instance->peer, __func__, 801285612Sdelphij "UTC <none> (gweek/sweek %u/%u)", 802132451Sroberto instance->gweek, sweek); 80354359Sroberto else { 80454359Sroberto /* XXX debugging */ 805132451Sroberto tm = gmtime(&last_timecode); 80654359Sroberto cp = asctime(tm); 80754359Sroberto 808285612Sdelphij jupiter_debug(instance->peer, __func__, 809285612Sdelphij "UTC %.24s (gweek/sweek %u/%u)", 810132451Sroberto cp, instance->gweek, sweek); 81154359Sroberto 81254359Sroberto /* Billboard last_timecode (which is now the current time) */ 813132451Sroberto instance->peer->procptr->year = tm->tm_year + 1900; 814132451Sroberto instance->peer->procptr->day = tm->tm_yday + 1; 815132451Sroberto instance->peer->procptr->hour = tm->tm_hour; 816132451Sroberto instance->peer->procptr->minute = tm->tm_min; 817132451Sroberto instance->peer->procptr->second = tm->tm_sec; 81854359Sroberto } 81954359Sroberto 82054359Sroberto flags = getshort(jp->flags); 82154359Sroberto 82254359Sroberto /* Toss if not designated "valid" by the gps */ 82354359Sroberto if ((flags & JUPITER_O_PULSE_VALID) == 0) { 824132451Sroberto refclock_report(instance->peer, CEVNT_BADTIME); 82554359Sroberto return ("time mark not valid"); 82654359Sroberto } 82754359Sroberto 82854359Sroberto /* We better be sync'ed to UTC... */ 82954359Sroberto if ((flags & JUPITER_O_PULSE_UTC) == 0) { 830132451Sroberto refclock_report(instance->peer, CEVNT_BADTIME); 83154359Sroberto return ("time mark not sync'ed to UTC"); 83254359Sroberto } 83354359Sroberto 83454359Sroberto return (NULL); 83554359Sroberto} 83654359Sroberto 837285612Sdelphijstatic const char * 838132451Srobertojupiter_parse_gpos(struct instance *instance, u_short *sp) 83954359Sroberto{ 840132451Sroberto struct jgpos *jg; 841132451Sroberto time_t t; 842132451Sroberto struct tm *tm; 843132451Sroberto char *cp; 84454359Sroberto 845132451Sroberto jg = (struct jgpos *)sp; 84654359Sroberto 847132451Sroberto if (jg->navval != 0) { 848132451Sroberto /* 849132451Sroberto * Solution not valid. Use caution and refuse 850132451Sroberto * to determine GPS week from this message. 851132451Sroberto */ 852132451Sroberto instance->gpos_gweek = 0; 853132451Sroberto instance->gpos_sweek = 0; 854132451Sroberto return ("Navigation solution not valid"); 85554359Sroberto } 85654359Sroberto 857132451Sroberto instance->gpos_sweek = DS2UI(jg->sweek); 858309008Sdelphij instance->gpos_gweek = get_full_week(get_base_week(), 859309008Sdelphij getshort(jg->gweek)); 860309008Sdelphij 861309008Sdelphij /* according to the protocol spec, the seconds-in-week cannot 862309008Sdelphij * exceed the nominal value: Is it really necessary to normalise 863309008Sdelphij * the seconds??? 864309008Sdelphij */ 865132451Sroberto while(instance->gpos_sweek >= WEEKSECS) { 866132451Sroberto instance->gpos_sweek -= WEEKSECS; 867132451Sroberto ++instance->gpos_gweek; 86854359Sroberto } 869132451Sroberto instance->gweek = 0; 87054359Sroberto 871132451Sroberto t = GPS_EPOCH + (instance->gpos_gweek * WEEKSECS) + instance->gpos_sweek; 872132451Sroberto tm = gmtime(&t); 873132451Sroberto cp = asctime(tm); 87454359Sroberto 875285612Sdelphij jupiter_debug(instance->peer, __func__, 876285612Sdelphij "GPS %.24s (gweek/sweek %u/%u)", 877132451Sroberto cp, instance->gpos_gweek, instance->gpos_sweek); 878132451Sroberto return (NULL); 87954359Sroberto} 88054359Sroberto 88154359Sroberto/* 88254359Sroberto * jupiter_debug - print debug messages 88354359Sroberto */ 88454359Srobertostatic void 885285612Sdelphijjupiter_debug( 886285612Sdelphij struct peer * peer, 887285612Sdelphij const char * function, 888285612Sdelphij const char * fmt, 889285612Sdelphij ... 890285612Sdelphij ) 89154359Sroberto{ 892285612Sdelphij char buffer[200]; 893285612Sdelphij va_list ap; 89454359Sroberto 895132451Sroberto va_start(ap, fmt); 896132451Sroberto /* 897132451Sroberto * Print debug message to stdout 898132451Sroberto * In the future, we may want to get get more creative... 899132451Sroberto */ 900285612Sdelphij mvsnprintf(buffer, sizeof(buffer), fmt, ap); 901285612Sdelphij record_clock_stats(&peer->srcadr, buffer); 902182007Sroberto#ifdef DEBUG 903132451Sroberto if (debug) { 904285612Sdelphij printf("%s: %s\n", function, buffer); 905132451Sroberto fflush(stdout); 906132451Sroberto } 907182007Sroberto#endif 90854359Sroberto 909132451Sroberto va_end(ap); 91054359Sroberto} 91154359Sroberto 91254359Sroberto/* Checksum and transmit a message to the Jupiter */ 91354359Srobertostatic char * 914132451Srobertojupiter_send(struct instance *instance, struct jheader *hp) 91554359Sroberto{ 916132451Sroberto u_int len, size; 917285612Sdelphij ssize_t cc; 918132451Sroberto u_short *sp; 91954359Sroberto static char errstr[132]; 92054359Sroberto 92154359Sroberto size = sizeof(*hp); 92254359Sroberto hp->hsum = putshort(jupiter_cksum((u_short *)hp, 92354359Sroberto (size / sizeof(u_short)) - 1)); 92454359Sroberto len = getshort(hp->len); 92554359Sroberto if (len > 0) { 92654359Sroberto sp = (u_short *)(hp + 1); 92754359Sroberto sp[len] = putshort(jupiter_cksum(sp, len)); 92854359Sroberto size += (len + 1) * sizeof(u_short); 92954359Sroberto } 93054359Sroberto 931132451Sroberto if ((cc = write(instance->peer->procptr->io.fd, (char *)hp, size)) < 0) { 932285612Sdelphij msnprintf(errstr, sizeof(errstr), "write: %m"); 93354359Sroberto return (errstr); 934285612Sdelphij } else if (cc != (int)size) { 935285612Sdelphij snprintf(errstr, sizeof(errstr), "short write (%zd != %u)", cc, size); 93654359Sroberto return (errstr); 93754359Sroberto } 93854359Sroberto return (NULL); 93954359Sroberto} 94054359Sroberto 94154359Sroberto/* Request periodic message output */ 94254359Srobertostatic struct { 94354359Sroberto struct jheader jheader; 94454359Sroberto struct jrequest jrequest; 94554359Sroberto} reqmsg = { 94654359Sroberto { putshort(JUPITER_SYNC), 0, 94754359Sroberto putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1), 948182007Sroberto 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | 949182007Sroberto JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 }, 95054359Sroberto { 0, 0, 0, 0 } 95154359Sroberto}; 95254359Sroberto 95354359Sroberto/* An interval of zero means to output on trigger */ 95454359Srobertostatic void 955132451Srobertojupiter_reqmsg(struct instance *instance, u_int id, 956132451Sroberto u_int interval) 95754359Sroberto{ 958132451Sroberto struct jheader *hp; 959132451Sroberto struct jrequest *rp; 960132451Sroberto char *cp; 96154359Sroberto 96254359Sroberto hp = &reqmsg.jheader; 96354359Sroberto hp->id = putshort(id); 96454359Sroberto rp = &reqmsg.jrequest; 96554359Sroberto rp->trigger = putshort(interval == 0); 96654359Sroberto rp->interval = putshort(interval); 967132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 968285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 96954359Sroberto} 97054359Sroberto 97154359Sroberto/* Cancel periodic message output */ 97254359Srobertostatic struct jheader canmsg = { 97354359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 974182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC, 97554359Sroberto 0 97654359Sroberto}; 97754359Sroberto 97854359Srobertostatic void 979132451Srobertojupiter_canmsg(struct instance *instance, u_int id) 98054359Sroberto{ 981132451Sroberto struct jheader *hp; 982132451Sroberto char *cp; 98354359Sroberto 98454359Sroberto hp = &canmsg; 98554359Sroberto hp->id = putshort(id); 986132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 987285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 98854359Sroberto} 98954359Sroberto 99054359Sroberto/* Request a single message output */ 99154359Srobertostatic struct jheader reqonemsg = { 99254359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 993182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY, 99454359Sroberto 0 99554359Sroberto}; 99654359Sroberto 99754359Srobertostatic void 998132451Srobertojupiter_reqonemsg(struct instance *instance, u_int id) 99954359Sroberto{ 1000132451Sroberto struct jheader *hp; 1001132451Sroberto char *cp; 100254359Sroberto 100354359Sroberto hp = &reqonemsg; 100454359Sroberto hp->id = putshort(id); 1005132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 1006285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 100754359Sroberto} 100854359Sroberto 100954359Sroberto/* Set the platform dynamics */ 101054359Srobertostatic struct { 101154359Sroberto struct jheader jheader; 101254359Sroberto struct jplat jplat; 101354359Sroberto} platmsg = { 101454359Sroberto { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT), 101554359Sroberto putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0, 1016182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 }, 101754359Sroberto { 0, 0, 0 } 101854359Sroberto}; 101954359Sroberto 102054359Srobertostatic void 1021132451Srobertojupiter_platform(struct instance *instance, u_int platform) 102254359Sroberto{ 1023132451Sroberto struct jheader *hp; 1024132451Sroberto struct jplat *pp; 1025132451Sroberto char *cp; 102654359Sroberto 102754359Sroberto hp = &platmsg.jheader; 102854359Sroberto pp = &platmsg.jplat; 102954359Sroberto pp->platform = putshort(platform); 1030132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 1031285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", platform, cp); 103254359Sroberto} 103354359Sroberto 103454359Sroberto/* Checksum "len" shorts */ 103554359Srobertostatic u_short 1036132451Srobertojupiter_cksum(u_short *sp, u_int len) 103754359Sroberto{ 1038132451Sroberto u_short sum, x; 103954359Sroberto 104054359Sroberto sum = 0; 104154359Sroberto while (len-- > 0) { 104254359Sroberto x = *sp++; 104354359Sroberto sum += getshort(x); 104454359Sroberto } 104554359Sroberto return (~sum + 1); 104654359Sroberto} 104754359Sroberto 104854359Sroberto/* Return the size of the next message (or zero if we don't have it all yet) */ 104954359Srobertostatic int 1050132451Srobertojupiter_recv(struct instance *instance) 105154359Sroberto{ 1052132451Sroberto int n, len, size, cc; 1053132451Sroberto struct jheader *hp; 1054132451Sroberto u_char *bp; 1055132451Sroberto u_short *sp; 105654359Sroberto 105754359Sroberto /* Must have at least a header's worth */ 105854359Sroberto cc = sizeof(*hp); 1059132451Sroberto size = instance->ssize; 106054359Sroberto if (size < cc) 106154359Sroberto return (0); 106254359Sroberto 106354359Sroberto /* Search for the sync short if missing */ 1064132451Sroberto sp = instance->sbuf; 106554359Sroberto hp = (struct jheader *)sp; 106654359Sroberto if (getshort(hp->sync) != JUPITER_SYNC) { 106754359Sroberto /* Wasn't at the front, sync up */ 1068285612Sdelphij jupiter_debug(instance->peer, __func__, "syncing"); 106954359Sroberto bp = (u_char *)sp; 107054359Sroberto n = size; 107154359Sroberto while (n >= 2) { 107254359Sroberto if (bp[0] != (JUPITER_SYNC & 0xff)) { 1073132451Sroberto /* 1074285612Sdelphij jupiter_debug(instance->peer, __func__, 1075285612Sdelphij "{0x%x}", bp[0]); 1076132451Sroberto */ 107754359Sroberto ++bp; 107854359Sroberto --n; 107954359Sroberto continue; 108054359Sroberto } 108154359Sroberto if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff)) 108254359Sroberto break; 1083132451Sroberto /* 1084285612Sdelphij jupiter_debug(instance->peer, __func__, 1085285612Sdelphij "{0x%x 0x%x}", bp[0], bp[1]); 1086132451Sroberto */ 108754359Sroberto bp += 2; 108854359Sroberto n -= 2; 108954359Sroberto } 1090132451Sroberto /* 1091285612Sdelphij jupiter_debug(instance->peer, __func__, "\n"); 1092132451Sroberto */ 109354359Sroberto /* Shuffle data to front of input buffer */ 109454359Sroberto if (n > 0) 109554359Sroberto memcpy(sp, bp, n); 109654359Sroberto size = n; 1097132451Sroberto instance->ssize = size; 109854359Sroberto if (size < cc || hp->sync != JUPITER_SYNC) 109954359Sroberto return (0); 110054359Sroberto } 110154359Sroberto 110254359Sroberto if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) != 110354359Sroberto getshort(hp->hsum)) { 1104285612Sdelphij jupiter_debug(instance->peer, __func__, "bad header checksum!"); 110554359Sroberto /* This is drastic but checksum errors should be rare */ 1106132451Sroberto instance->ssize = 0; 110754359Sroberto return (0); 110854359Sroberto } 110954359Sroberto 111054359Sroberto /* Check for a payload */ 111154359Sroberto len = getshort(hp->len); 111254359Sroberto if (len > 0) { 111354359Sroberto n = (len + 1) * sizeof(u_short); 111454359Sroberto /* Not enough data yet */ 111554359Sroberto if (size < cc + n) 111654359Sroberto return (0); 111754359Sroberto 111854359Sroberto /* Check payload checksum */ 111954359Sroberto sp = (u_short *)(hp + 1); 112054359Sroberto if (jupiter_cksum(sp, len) != getshort(sp[len])) { 1121132451Sroberto jupiter_debug(instance->peer, 1122285612Sdelphij __func__, "bad payload checksum!"); 112354359Sroberto /* This is drastic but checksum errors should be rare */ 1124132451Sroberto instance->ssize = 0; 112554359Sroberto return (0); 112654359Sroberto } 112754359Sroberto cc += n; 112854359Sroberto } 112954359Sroberto return (cc); 113054359Sroberto} 113154359Sroberto 1132309008Sdelphijstatic u_int 1133309008Sdelphijget_base_week(void) 1134309008Sdelphij{ 1135309008Sdelphij static int init_done /* = 0 */; 1136309008Sdelphij static u_int base_week; 1137309008Sdelphij 1138309008Sdelphij /* Get the build date, convert to days since GPS epoch and 1139309008Sdelphij * finally weeks since GPS epoch. Note that the build stamp is 1140309008Sdelphij * trusted once it is fetched -- only dates before the GPS epoch 1141309008Sdelphij * are not permitted. This will permit proper synchronisation 1142309008Sdelphij * for a time range of 1024 weeks starting with 00:00:00 of the 1143309008Sdelphij * last Sunday on or before the build time. 1144309008Sdelphij * 1145309008Sdelphij * If the impossible happens and fetching the build date fails, 1146309008Sdelphij * a 1024-week cycle starting with 2016-01-03 is assumed to 1147309008Sdelphij * avoid catastropic errors. This will work until 2035-08-19. 1148309008Sdelphij */ 1149309008Sdelphij if (!init_done) { 1150309008Sdelphij struct calendar bd; 1151309008Sdelphij if (ntpcal_get_build_date(&bd)) { 1152309008Sdelphij int32_t days = ntpcal_date_to_rd(&bd); 1153309008Sdelphij if (days > RDN_GPS_EPOCH) 1154309008Sdelphij days -= RDN_GPS_EPOCH; 1155309008Sdelphij else 1156309008Sdelphij days = 0; 1157309008Sdelphij base_week = days / 7; 1158309008Sdelphij } else { 1159309008Sdelphij base_week = 1878; /* 2016-01-03, Sunday */ 1160309008Sdelphij msyslog(LOG_ERR, 1161309008Sdelphij "refclock_jupiter: ntpcal_get_build_date() failed: %s", 1162309008Sdelphij "using 2016-01-03 as GPS base!"); 1163309008Sdelphij } 1164309008Sdelphij init_done = 1; 1165309008Sdelphij } 1166309008Sdelphij return base_week; 1167309008Sdelphij} 1168309008Sdelphij 1169309008Sdelphijstatic u_int 1170309008Sdelphijget_full_week( 1171309008Sdelphij u_int base_week, 1172309008Sdelphij u_int gpos_week 1173309008Sdelphij ) 1174309008Sdelphij{ 1175309008Sdelphij /* Periodic extension on base week. Since the period is 1024 1176309008Sdelphij * weeks and we do unsigned arithmetic here, we can do wonderful 1177309008Sdelphij * things with masks and the well-defined overflow behaviour. 1178309008Sdelphij */ 1179309008Sdelphij return base_week + ((gpos_week - base_week) & 1023); 1180309008Sdelphij} 1181309008Sdelphij 1182132451Sroberto#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 118354359Srobertoint refclock_jupiter_bs; 1184132451Sroberto#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 1185