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 92310419Sdelphij/* Rata Die Number of first day of GPS epoch. This is the number of days 93310419Sdelphij * since 0000-12-31 to 1980-01-06 in the proleptic Gregorian Calendar. 94310419Sdelphij */ 95310419Sdelphij#define RDN_GPS_EPOCH (4*146097 + 138431 + 1) 96310419Sdelphij 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 *, 142285612Sdelphij const char *, ...) 143285612Sdelphij __attribute__ ((format (printf, 3, 4))); 144285612Sdelphijstatic const char * jupiter_parse_t (struct instance *, u_short *); 145285612Sdelphijstatic const char * jupiter_parse_gpos (struct instance *, u_short *); 146285612Sdelphijstatic void jupiter_platform (struct instance *, u_int); 147285612Sdelphijstatic void jupiter_poll (int, struct peer *); 148285612Sdelphijstatic void jupiter_control (int, const struct refclockstat *, 149285612Sdelphij struct refclockstat *, struct peer *); 150132451Sroberto#ifdef HAVE_PPSAPI 151285612Sdelphijstatic int jupiter_ppsapi (struct instance *); 152285612Sdelphijstatic int jupiter_pps (struct instance *); 153132451Sroberto#endif /* HAVE_PPSAPI */ 154285612Sdelphijstatic int jupiter_recv (struct instance *); 155285612Sdelphijstatic void jupiter_receive (struct recvbuf *rbufp); 156285612Sdelphijstatic void jupiter_reqmsg (struct instance *, u_int, u_int); 157285612Sdelphijstatic void jupiter_reqonemsg(struct instance *, u_int); 158285612Sdelphijstatic char * jupiter_send (struct instance *, struct jheader *); 159285612Sdelphijstatic void jupiter_shutdown(int, struct peer *); 160285612Sdelphijstatic int jupiter_start (int, struct peer *); 16154359Sroberto 162310419Sdelphijstatic u_int get_full_week(u_int base_week, u_int gpos_week); 163310419Sdelphijstatic u_int get_base_week(void); 164310419Sdelphij 165310419Sdelphij 16654359Sroberto/* 16754359Sroberto * Transfer vector 16854359Sroberto */ 16954359Srobertostruct refclock refclock_jupiter = { 17054359Sroberto jupiter_start, /* start up driver */ 17154359Sroberto jupiter_shutdown, /* shut down driver */ 17254359Sroberto jupiter_poll, /* transmit poll message */ 173132451Sroberto jupiter_control, /* (clock control) */ 17454359Sroberto noentry, /* (clock init) */ 17554359Sroberto noentry, /* (clock buginfo) */ 17654359Sroberto NOFLAGS /* not used */ 17754359Sroberto}; 17854359Sroberto 17954359Sroberto/* 18054359Sroberto * jupiter_start - open the devices and initialize data for processing 18154359Sroberto */ 18254359Srobertostatic int 18354359Srobertojupiter_start( 184132451Sroberto int unit, 185132451Sroberto struct peer *peer 18654359Sroberto ) 18754359Sroberto{ 18854359Sroberto struct refclockproc *pp; 189132451Sroberto struct instance *instance; 190285612Sdelphij int fd; 19154359Sroberto char gpsdev[20]; 19254359Sroberto 19354359Sroberto /* 19454359Sroberto * Open serial port 19554359Sroberto */ 196285612Sdelphij snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 197132451Sroberto fd = refclock_open(gpsdev, SPEED232, LDISC_RAW); 198285612Sdelphij if (fd <= 0) { 199285612Sdelphij jupiter_debug(peer, "jupiter_start", "open %s: %m", 200285612Sdelphij gpsdev); 20154359Sroberto return (0); 20254359Sroberto } 20354359Sroberto 20454359Sroberto /* Allocate unit structure */ 205285612Sdelphij instance = emalloc_zero(sizeof(*instance)); 206132451Sroberto instance->peer = peer; 20754359Sroberto pp = peer->procptr; 20854359Sroberto pp->io.clock_recv = jupiter_receive; 209285612Sdelphij pp->io.srcclock = peer; 21054359Sroberto pp->io.datalen = 0; 21154359Sroberto pp->io.fd = fd; 21254359Sroberto if (!io_addclock(&pp->io)) { 213285612Sdelphij close(fd); 214285612Sdelphij pp->io.fd = -1; 215132451Sroberto free(instance); 21654359Sroberto return (0); 21754359Sroberto } 218285612Sdelphij pp->unitptr = instance; 21954359Sroberto 22054359Sroberto /* 22154359Sroberto * Initialize miscellaneous variables 22254359Sroberto */ 22354359Sroberto peer->precision = PRECISION; 22454359Sroberto pp->clockdesc = DESCRIPTION; 22554359Sroberto memcpy((char *)&pp->refid, REFID, 4); 22654359Sroberto 227132451Sroberto#ifdef HAVE_PPSAPI 228182007Sroberto instance->assert = 1; 229182007Sroberto instance->hardpps = 0; 230132451Sroberto /* 231132451Sroberto * Start the PPSAPI interface if it is there. Default to use 232132451Sroberto * the assert edge and do not enable the kernel hardpps. 233132451Sroberto */ 234132451Sroberto if (time_pps_create(fd, &instance->pps_handle) < 0) { 235132451Sroberto instance->pps_handle = 0; 236132451Sroberto msyslog(LOG_ERR, 237132451Sroberto "refclock_jupiter: time_pps_create failed: %m"); 238132451Sroberto } 239182007Sroberto else if (!jupiter_ppsapi(instance)) 240132451Sroberto goto clean_up; 241132451Sroberto#endif /* HAVE_PPSAPI */ 24254359Sroberto 24354359Sroberto /* Ensure the receiver is properly configured */ 244132451Sroberto if (!jupiter_config(instance)) 245132451Sroberto goto clean_up; 24654359Sroberto 24754359Sroberto return (1); 248132451Sroberto 249132451Srobertoclean_up: 250132451Sroberto jupiter_shutdown(unit, peer); 251132451Sroberto pp->unitptr = 0; 252132451Sroberto return (0); 25354359Sroberto} 25454359Sroberto 25554359Sroberto/* 25654359Sroberto * jupiter_shutdown - shut down the clock 25754359Sroberto */ 25854359Srobertostatic void 259132451Srobertojupiter_shutdown(int unit, struct peer *peer) 26054359Sroberto{ 261132451Sroberto struct instance *instance; 26254359Sroberto struct refclockproc *pp; 26354359Sroberto 26454359Sroberto pp = peer->procptr; 265285612Sdelphij instance = pp->unitptr; 266182007Sroberto if (!instance) 267132451Sroberto return; 268132451Sroberto 269132451Sroberto#ifdef HAVE_PPSAPI 270132451Sroberto if (instance->pps_handle) { 271132451Sroberto time_pps_destroy(instance->pps_handle); 272132451Sroberto instance->pps_handle = 0; 273132451Sroberto } 274132451Sroberto#endif /* HAVE_PPSAPI */ 275132451Sroberto 276285612Sdelphij if (pp->io.fd != -1) 277285612Sdelphij io_closeclock(&pp->io); 278132451Sroberto free(instance); 27954359Sroberto} 28054359Sroberto 28154359Sroberto/* 28254359Sroberto * jupiter_config - Configure the receiver 28354359Sroberto */ 284132451Srobertostatic int 285132451Srobertojupiter_config(struct instance *instance) 28654359Sroberto{ 287285612Sdelphij jupiter_debug(instance->peer, __func__, "init receiver"); 28854359Sroberto 28954359Sroberto /* 29054359Sroberto * Initialize the unit variables 29154359Sroberto */ 292132451Sroberto instance->sloppyclockflag = instance->peer->procptr->sloppyclockflag; 293132451Sroberto instance->moving = !!(instance->sloppyclockflag & CLK_FLAG2); 294132451Sroberto if (instance->moving) 295285612Sdelphij jupiter_debug(instance->peer, __func__, "mobile platform"); 29654359Sroberto 297132451Sroberto instance->pollcnt = 2; 298132451Sroberto instance->polled = 0; 299132451Sroberto instance->gpos_gweek = 0; 300132451Sroberto instance->gpos_sweek = 0; 301132451Sroberto instance->gweek = 0; 302132451Sroberto instance->lastsweek = 2 * WEEKSECS; 303132451Sroberto instance->timecode = 0; 304132451Sroberto instance->stime = 0; 305132451Sroberto instance->ssize = 0; 30654359Sroberto 30754359Sroberto /* Stop outputting all messages */ 308132451Sroberto jupiter_canmsg(instance, JUPITER_ALL); 30954359Sroberto 31054359Sroberto /* Request the receiver id so we can syslog the firmware version */ 311132451Sroberto jupiter_reqonemsg(instance, JUPITER_O_ID); 31254359Sroberto 31354359Sroberto /* Flag that this the id was requested (so we don't get called again) */ 314132451Sroberto instance->wantid = 1; 31554359Sroberto 31654359Sroberto /* Request perodic time mark pulse messages */ 317132451Sroberto jupiter_reqmsg(instance, JUPITER_O_PULSE, 1); 31854359Sroberto 319132451Sroberto /* Request perodic geodetic position status */ 320132451Sroberto jupiter_reqmsg(instance, JUPITER_O_GPOS, 1); 321132451Sroberto 32254359Sroberto /* Set application platform type */ 323132451Sroberto if (instance->moving) 324132451Sroberto jupiter_platform(instance, JUPITER_I_PLAT_MED); 32554359Sroberto else 326132451Sroberto jupiter_platform(instance, JUPITER_I_PLAT_LOW); 327132451Sroberto 328132451Sroberto return (1); 32954359Sroberto} 33054359Sroberto 331132451Sroberto#ifdef HAVE_PPSAPI 33254359Sroberto/* 333132451Sroberto * Initialize PPSAPI 334132451Sroberto */ 335132451Srobertoint 336132451Srobertojupiter_ppsapi( 337182007Sroberto struct instance *instance /* unit structure pointer */ 338132451Sroberto ) 339132451Sroberto{ 340132451Sroberto int capability; 341132451Sroberto 342132451Sroberto if (time_pps_getcap(instance->pps_handle, &capability) < 0) { 343132451Sroberto msyslog(LOG_ERR, 344132451Sroberto "refclock_jupiter: time_pps_getcap failed: %m"); 345132451Sroberto return (0); 346132451Sroberto } 347132451Sroberto memset(&instance->pps_params, 0, sizeof(pps_params_t)); 348182007Sroberto if (!instance->assert) 349132451Sroberto instance->pps_params.mode = capability & PPS_CAPTURECLEAR; 350132451Sroberto else 351132451Sroberto instance->pps_params.mode = capability & PPS_CAPTUREASSERT; 352132451Sroberto if (!(instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { 353132451Sroberto msyslog(LOG_ERR, 354132451Sroberto "refclock_jupiter: invalid capture edge %d", 355182007Sroberto instance->assert); 356132451Sroberto return (0); 357132451Sroberto } 358132451Sroberto instance->pps_params.mode |= PPS_TSFMT_TSPEC; 359132451Sroberto if (time_pps_setparams(instance->pps_handle, &instance->pps_params) < 0) { 360132451Sroberto msyslog(LOG_ERR, 361132451Sroberto "refclock_jupiter: time_pps_setparams failed: %m"); 362132451Sroberto return (0); 363132451Sroberto } 364182007Sroberto if (instance->hardpps) { 365132451Sroberto if (time_pps_kcbind(instance->pps_handle, PPS_KC_HARDPPS, 366182007Sroberto instance->pps_params.mode & ~PPS_TSFMT_TSPEC, 367132451Sroberto PPS_TSFMT_TSPEC) < 0) { 368132451Sroberto msyslog(LOG_ERR, 369132451Sroberto "refclock_jupiter: time_pps_kcbind failed: %m"); 370132451Sroberto return (0); 371132451Sroberto } 372285612Sdelphij hardpps_enable = 1; 373132451Sroberto } 374132451Sroberto/* instance->peer->precision = PPS_PRECISION; */ 375132451Sroberto 376132451Sroberto#if DEBUG 377132451Sroberto if (debug) { 378132451Sroberto time_pps_getparams(instance->pps_handle, &instance->pps_params); 379285612Sdelphij jupiter_debug(instance->peer, __func__, 380132451Sroberto "pps capability 0x%x version %d mode 0x%x kern %d", 381132451Sroberto capability, instance->pps_params.api_version, 382182007Sroberto instance->pps_params.mode, instance->hardpps); 383132451Sroberto } 384132451Sroberto#endif 385132451Sroberto 386132451Sroberto return (1); 387132451Sroberto} 388132451Sroberto 389132451Sroberto/* 390132451Sroberto * Get PPSAPI timestamps. 391132451Sroberto * 392132451Sroberto * Return 0 on failure and 1 on success. 393132451Sroberto */ 394132451Srobertostatic int 395132451Srobertojupiter_pps(struct instance *instance) 396132451Sroberto{ 397132451Sroberto pps_info_t pps_info; 398132451Sroberto struct timespec timeout, ts; 399132451Sroberto double dtemp; 400132451Sroberto l_fp tstmp; 401132451Sroberto 402132451Sroberto /* 403132451Sroberto * Convert the timespec nanoseconds field to ntp l_fp units. 404132451Sroberto */ 405132451Sroberto if (instance->pps_handle == 0) 406132451Sroberto return 1; 407132451Sroberto timeout.tv_sec = 0; 408132451Sroberto timeout.tv_nsec = 0; 409132451Sroberto memcpy(&pps_info, &instance->pps_info, sizeof(pps_info_t)); 410132451Sroberto if (time_pps_fetch(instance->pps_handle, PPS_TSFMT_TSPEC, &instance->pps_info, 411132451Sroberto &timeout) < 0) 412132451Sroberto return 1; 413132451Sroberto if (instance->pps_params.mode & PPS_CAPTUREASSERT) { 414132451Sroberto if (pps_info.assert_sequence == 415132451Sroberto instance->pps_info.assert_sequence) 416132451Sroberto return 1; 417132451Sroberto ts = instance->pps_info.assert_timestamp; 418132451Sroberto } else if (instance->pps_params.mode & PPS_CAPTURECLEAR) { 419132451Sroberto if (pps_info.clear_sequence == 420132451Sroberto instance->pps_info.clear_sequence) 421132451Sroberto return 1; 422132451Sroberto ts = instance->pps_info.clear_timestamp; 423132451Sroberto } else { 424132451Sroberto return 1; 425132451Sroberto } 426132451Sroberto if ((instance->ts.tv_sec == ts.tv_sec) && (instance->ts.tv_nsec == ts.tv_nsec)) 427132451Sroberto return 1; 428132451Sroberto instance->ts = ts; 429132451Sroberto 430285612Sdelphij tstmp.l_ui = (u_int32)ts.tv_sec + JAN_1970; 431132451Sroberto dtemp = ts.tv_nsec * FRAC / 1e9; 432132451Sroberto tstmp.l_uf = (u_int32)dtemp; 433132451Sroberto instance->peer->procptr->lastrec = tstmp; 434132451Sroberto return 0; 435132451Sroberto} 436132451Sroberto#endif /* HAVE_PPSAPI */ 437132451Sroberto 438132451Sroberto/* 43954359Sroberto * jupiter_poll - jupiter watchdog routine 44054359Sroberto */ 44154359Srobertostatic void 442132451Srobertojupiter_poll(int unit, struct peer *peer) 44354359Sroberto{ 444132451Sroberto struct instance *instance; 445132451Sroberto struct refclockproc *pp; 44654359Sroberto 44754359Sroberto pp = peer->procptr; 448285612Sdelphij instance = pp->unitptr; 44954359Sroberto 45054359Sroberto /* 45154359Sroberto * You don't need to poll this clock. It puts out timecodes 45254359Sroberto * once per second. If asked for a timestamp, take note. 45354359Sroberto * The next time a timecode comes in, it will be fed back. 45454359Sroberto */ 45554359Sroberto 45654359Sroberto /* 45754359Sroberto * If we haven't had a response in a while, reset the receiver. 45854359Sroberto */ 459132451Sroberto if (instance->pollcnt > 0) { 460132451Sroberto instance->pollcnt--; 46154359Sroberto } else { 46254359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 46354359Sroberto 46454359Sroberto /* Request the receiver id to trigger a reconfig */ 465132451Sroberto jupiter_reqonemsg(instance, JUPITER_O_ID); 466132451Sroberto instance->wantid = 0; 46754359Sroberto } 46854359Sroberto 46954359Sroberto /* 47054359Sroberto * polled every 64 seconds. Ask jupiter_receive to hand in 47154359Sroberto * a timestamp. 47254359Sroberto */ 473132451Sroberto instance->polled = 1; 47454359Sroberto pp->polls++; 47554359Sroberto} 47654359Sroberto 47754359Sroberto/* 478132451Sroberto * jupiter_control - fudge control 47954359Sroberto */ 48054359Srobertostatic void 481132451Srobertojupiter_control( 482132451Sroberto int unit, /* unit (not used) */ 483285612Sdelphij const struct refclockstat *in, /* input parameters (not used) */ 484132451Sroberto struct refclockstat *out, /* output parameters (not used) */ 485132451Sroberto struct peer *peer /* peer structure pointer */ 486132451Sroberto ) 48754359Sroberto{ 488132451Sroberto struct refclockproc *pp; 489132451Sroberto struct instance *instance; 490132451Sroberto u_char sloppyclockflag; 49154359Sroberto 49254359Sroberto pp = peer->procptr; 493285612Sdelphij instance = pp->unitptr; 49454359Sroberto 495132451Sroberto DTOLFP(pp->fudgetime2, &instance->limit); 496132451Sroberto /* Force positive value. */ 497132451Sroberto if (L_ISNEG(&instance->limit)) 498132451Sroberto L_NEG(&instance->limit); 499132451Sroberto 500132451Sroberto#ifdef HAVE_PPSAPI 501132451Sroberto instance->assert = !(pp->sloppyclockflag & CLK_FLAG3); 502182007Sroberto jupiter_ppsapi(instance); 503132451Sroberto#endif /* HAVE_PPSAPI */ 504132451Sroberto 505132451Sroberto sloppyclockflag = instance->sloppyclockflag; 506132451Sroberto instance->sloppyclockflag = pp->sloppyclockflag; 507132451Sroberto if ((instance->sloppyclockflag & CLK_FLAG2) != 50854359Sroberto (sloppyclockflag & CLK_FLAG2)) { 509285612Sdelphij jupiter_debug(peer, __func__, 510132451Sroberto "mode switch: reset receiver"); 511132451Sroberto jupiter_config(instance); 51254359Sroberto return; 51354359Sroberto } 514132451Sroberto} 51554359Sroberto 516132451Sroberto/* 517132451Sroberto * jupiter_receive - receive gps data 518132451Sroberto * Gag me! 519132451Sroberto */ 520132451Srobertostatic void 521132451Srobertojupiter_receive(struct recvbuf *rbufp) 522132451Sroberto{ 523285612Sdelphij size_t bpcnt; 524285612Sdelphij int cc, size, ppsret; 525132451Sroberto time_t last_timecode; 526132451Sroberto u_int32 laststime; 527285612Sdelphij const char *cp; 528132451Sroberto u_char *bp; 529132451Sroberto u_short *sp; 530132451Sroberto struct jid *ip; 531132451Sroberto struct jheader *hp; 532132451Sroberto struct peer *peer; 533132451Sroberto struct refclockproc *pp; 534132451Sroberto struct instance *instance; 535132451Sroberto l_fp tstamp; 53654359Sroberto 537132451Sroberto /* Initialize pointers and read the timecode and timestamp */ 538285612Sdelphij peer = rbufp->recv_peer; 539132451Sroberto pp = peer->procptr; 540285612Sdelphij instance = pp->unitptr; 541132451Sroberto 54254359Sroberto bp = (u_char *)rbufp->recv_buffer; 54354359Sroberto bpcnt = rbufp->recv_length; 54454359Sroberto 54554359Sroberto /* This shouldn't happen */ 546132451Sroberto if (bpcnt > sizeof(instance->sbuf) - instance->ssize) 547132451Sroberto bpcnt = sizeof(instance->sbuf) - instance->ssize; 54854359Sroberto 54954359Sroberto /* Append to input buffer */ 550132451Sroberto memcpy((u_char *)instance->sbuf + instance->ssize, bp, bpcnt); 551132451Sroberto instance->ssize += bpcnt; 55254359Sroberto 553132451Sroberto /* While there's at least a header and we parse an intact message */ 554285612Sdelphij while (instance->ssize > (int)sizeof(*hp) && (cc = jupiter_recv(instance)) > 0) { 555132451Sroberto instance->pollcnt = 2; 556132451Sroberto 557132451Sroberto tstamp = rbufp->recv_time; 558132451Sroberto hp = (struct jheader *)instance->sbuf; 55954359Sroberto sp = (u_short *)(hp + 1); 56054359Sroberto size = cc - sizeof(*hp); 56154359Sroberto switch (getshort(hp->id)) { 56254359Sroberto 56354359Sroberto case JUPITER_O_PULSE: 56454359Sroberto if (size != sizeof(struct jpulse)) { 565285612Sdelphij jupiter_debug(peer, __func__, 566285612Sdelphij "pulse: len %d != %u", 56754359Sroberto size, (int)sizeof(struct jpulse)); 56854359Sroberto refclock_report(peer, CEVNT_BADREPLY); 56954359Sroberto break; 57054359Sroberto } 57154359Sroberto 57254359Sroberto /* 57354359Sroberto * There appears to be a firmware bug related 57454359Sroberto * to the pulse message; in addition to the one 57554359Sroberto * per second messages, we get an extra pulse 57654359Sroberto * message once an hour (on the anniversary of 57754359Sroberto * the cold start). It seems to come 200 ms 57854359Sroberto * after the one requested. So if we've seen a 57954359Sroberto * pulse message in the last 210 ms, we skip 58054359Sroberto * this one. 58154359Sroberto */ 582132451Sroberto laststime = instance->stime; 583132451Sroberto instance->stime = DS2UI(((struct jpulse *)sp)->stime); 584132451Sroberto if (laststime != 0 && instance->stime - laststime <= 21) { 585285612Sdelphij jupiter_debug(peer, __func__, 586132451Sroberto "avoided firmware bug (stime %.2f, laststime %.2f)", 587132451Sroberto (double)instance->stime * 0.01, (double)laststime * 0.01); 58854359Sroberto break; 58954359Sroberto } 59054359Sroberto 59154359Sroberto /* Retrieve pps timestamp */ 592132451Sroberto ppsret = jupiter_pps(instance); 59354359Sroberto 594132451Sroberto /* 595132451Sroberto * Add one second if msg received early 596132451Sroberto * (i.e. before limit, a.k.a. fudgetime2) in 597132451Sroberto * the second. 598132451Sroberto */ 599132451Sroberto L_SUB(&tstamp, &pp->lastrec); 600132451Sroberto if (!L_ISGEQ(&tstamp, &instance->limit)) 601132451Sroberto ++pp->lastrec.l_ui; 602132451Sroberto 60354359Sroberto /* Parse timecode (even when there's no pps) */ 604132451Sroberto last_timecode = instance->timecode; 605132451Sroberto if ((cp = jupiter_parse_t(instance, sp)) != NULL) { 606285612Sdelphij jupiter_debug(peer, __func__, 607285612Sdelphij "pulse: %s", cp); 60854359Sroberto break; 60954359Sroberto } 61054359Sroberto 61154359Sroberto /* Bail if we didn't get a pps timestamp */ 61254359Sroberto if (ppsret) 61354359Sroberto break; 61454359Sroberto 61554359Sroberto /* Bail if we don't have the last timecode yet */ 61654359Sroberto if (last_timecode == 0) 61754359Sroberto break; 61854359Sroberto 61954359Sroberto /* Add the new sample to a median filter */ 620285612Sdelphij tstamp.l_ui = JAN_1970 + (u_int32)last_timecode; 621132451Sroberto tstamp.l_uf = 0; 62254359Sroberto 623132451Sroberto refclock_process_offset(pp, tstamp, pp->lastrec, pp->fudgetime1); 624132451Sroberto 62554359Sroberto /* 62654359Sroberto * The clock will blurt a timecode every second 62754359Sroberto * but we only want one when polled. If we 62854359Sroberto * havn't been polled, bail out. 62954359Sroberto */ 630132451Sroberto if (!instance->polled) 63154359Sroberto break; 632132451Sroberto instance->polled = 0; 63354359Sroberto 63454359Sroberto /* 63554359Sroberto * It's a live one! Remember this time. 63654359Sroberto */ 63754359Sroberto 638132451Sroberto pp->lastref = pp->lastrec; 639132451Sroberto refclock_receive(peer); 640132451Sroberto 64154359Sroberto /* 642132451Sroberto * If we get here - what we got from the clock is 643132451Sroberto * OK, so say so 64454359Sroberto */ 645132451Sroberto refclock_report(peer, CEVNT_NOMINAL); 64654359Sroberto 64754359Sroberto /* 64854359Sroberto * We have succeeded in answering the poll. 64954359Sroberto * Turn off the flag and return 65054359Sroberto */ 651132451Sroberto instance->polled = 0; 65254359Sroberto break; 65354359Sroberto 654132451Sroberto case JUPITER_O_GPOS: 655132451Sroberto if (size != sizeof(struct jgpos)) { 656285612Sdelphij jupiter_debug(peer, __func__, 657285612Sdelphij "gpos: len %d != %u", 658132451Sroberto size, (int)sizeof(struct jgpos)); 659132451Sroberto refclock_report(peer, CEVNT_BADREPLY); 660132451Sroberto break; 661132451Sroberto } 662132451Sroberto 663132451Sroberto if ((cp = jupiter_parse_gpos(instance, sp)) != NULL) { 664285612Sdelphij jupiter_debug(peer, __func__, 665285612Sdelphij "gpos: %s", cp); 666132451Sroberto break; 667132451Sroberto } 668132451Sroberto break; 669132451Sroberto 67054359Sroberto case JUPITER_O_ID: 67154359Sroberto if (size != sizeof(struct jid)) { 672285612Sdelphij jupiter_debug(peer, __func__, 673285612Sdelphij "id: len %d != %u", 67454359Sroberto size, (int)sizeof(struct jid)); 67554359Sroberto refclock_report(peer, CEVNT_BADREPLY); 67654359Sroberto break; 67754359Sroberto } 67854359Sroberto /* 67954359Sroberto * If we got this message because the Jupiter 680132451Sroberto * just powered instance, it needs to be reconfigured. 68154359Sroberto */ 68254359Sroberto ip = (struct jid *)sp; 683285612Sdelphij jupiter_debug(peer, __func__, 684285612Sdelphij "%s chan ver %s, %s (%s)", 68554359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 68654359Sroberto msyslog(LOG_DEBUG, 687182007Sroberto "jupiter_receive: %s chan ver %s, %s (%s)", 68854359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 689132451Sroberto if (instance->wantid) 690132451Sroberto instance->wantid = 0; 69154359Sroberto else { 692285612Sdelphij jupiter_debug(peer, __func__, "reset receiver"); 693132451Sroberto jupiter_config(instance); 694132451Sroberto /* 695132451Sroberto * Restore since jupiter_config() just 696132451Sroberto * zeroed it 697132451Sroberto */ 698132451Sroberto instance->ssize = cc; 69954359Sroberto } 70054359Sroberto break; 70154359Sroberto 70254359Sroberto default: 703285612Sdelphij jupiter_debug(peer, __func__, "unknown message id %d", 70454359Sroberto getshort(hp->id)); 70554359Sroberto break; 70654359Sroberto } 707132451Sroberto instance->ssize -= cc; 708132451Sroberto if (instance->ssize < 0) { 70954359Sroberto fprintf(stderr, "jupiter_recv: negative ssize!\n"); 71054359Sroberto abort(); 711132451Sroberto } else if (instance->ssize > 0) 712132451Sroberto memcpy(instance->sbuf, (u_char *)instance->sbuf + cc, instance->ssize); 71354359Sroberto } 71454359Sroberto} 71554359Sroberto 716285612Sdelphijstatic const char * 717132451Srobertojupiter_parse_t(struct instance *instance, u_short *sp) 71854359Sroberto{ 719132451Sroberto struct tm *tm; 720132451Sroberto char *cp; 721132451Sroberto struct jpulse *jp; 722132451Sroberto u_int32 sweek; 723132451Sroberto time_t last_timecode; 724132451Sroberto u_short flags; 72554359Sroberto 72654359Sroberto jp = (struct jpulse *)sp; 72754359Sroberto 72854359Sroberto /* The timecode is presented as seconds into the current GPS week */ 729132451Sroberto sweek = DS2UI(jp->sweek) % WEEKSECS; 73054359Sroberto 73154359Sroberto /* 73254359Sroberto * If we don't know the current GPS week, calculate it from the 73354359Sroberto * current time. (It's too bad they didn't include this 73454359Sroberto * important value in the pulse message). We'd like to pick it 73554359Sroberto * up from one of the other messages like gpos or chan but they 73654359Sroberto * don't appear to be synchronous with time keeping and changes 73754359Sroberto * too soon (something like 10 seconds before the new GPS 73854359Sroberto * week). 73954359Sroberto * 74054359Sroberto * If we already know the current GPS week, increment it when 74154359Sroberto * we wrap into a new week. 74254359Sroberto */ 743132451Sroberto if (instance->gweek == 0) { 744132451Sroberto if (!instance->gpos_gweek) { 745132451Sroberto return ("jupiter_parse_t: Unknown gweek"); 746132451Sroberto } 747132451Sroberto 748132451Sroberto instance->gweek = instance->gpos_gweek; 749132451Sroberto 750132451Sroberto /* 751132451Sroberto * Fix warps. GPOS has GPS time and PULSE has UTC. 752132451Sroberto * Plus, GPOS need not be completely in synch with 753132451Sroberto * the PPS signal. 754132451Sroberto */ 755132451Sroberto if (instance->gpos_sweek >= sweek) { 756132451Sroberto if ((instance->gpos_sweek - sweek) > WEEKSECS / 2) 757132451Sroberto ++instance->gweek; 758132451Sroberto } 759132451Sroberto else { 760132451Sroberto if ((sweek - instance->gpos_sweek) > WEEKSECS / 2) 761132451Sroberto --instance->gweek; 762132451Sroberto } 76354359Sroberto } 764132451Sroberto else if (sweek == 0 && instance->lastsweek == WEEKSECS - 1) { 765132451Sroberto ++instance->gweek; 766285612Sdelphij jupiter_debug(instance->peer, __func__, 767285612Sdelphij "NEW gps week %u", instance->gweek); 768132451Sroberto } 76954359Sroberto 77054359Sroberto /* 77154359Sroberto * See if the sweek stayed the same (this happens when there is 77254359Sroberto * no pps pulse). 77354359Sroberto * 77454359Sroberto * Otherwise, look for time warps: 77554359Sroberto * 77654359Sroberto * - we have stored at least one lastsweek and 77754359Sroberto * - the sweek didn't increase by one and 77854359Sroberto * - we didn't wrap to a new GPS week 77954359Sroberto * 78054359Sroberto * Then we warped. 78154359Sroberto */ 782132451Sroberto if (instance->lastsweek == sweek) 783285612Sdelphij jupiter_debug(instance->peer, __func__, 784285612Sdelphij "gps sweek not incrementing (%d)", 78554359Sroberto sweek); 786132451Sroberto else if (instance->lastsweek != 2 * WEEKSECS && 787132451Sroberto instance->lastsweek + 1 != sweek && 788132451Sroberto !(sweek == 0 && instance->lastsweek == WEEKSECS - 1)) 789285612Sdelphij jupiter_debug(instance->peer, __func__, 790285612Sdelphij "gps sweek jumped (was %d, now %d)", 791132451Sroberto instance->lastsweek, sweek); 792132451Sroberto instance->lastsweek = sweek; 79354359Sroberto 79454359Sroberto /* This timecode describes next pulse */ 795132451Sroberto last_timecode = instance->timecode; 796132451Sroberto instance->timecode = 797132451Sroberto GPS_EPOCH + (instance->gweek * WEEKSECS) + sweek; 79854359Sroberto 79954359Sroberto if (last_timecode == 0) 80054359Sroberto /* XXX debugging */ 801285612Sdelphij jupiter_debug(instance->peer, __func__, 802285612Sdelphij "UTC <none> (gweek/sweek %u/%u)", 803132451Sroberto instance->gweek, sweek); 80454359Sroberto else { 80554359Sroberto /* XXX debugging */ 806132451Sroberto tm = gmtime(&last_timecode); 80754359Sroberto cp = asctime(tm); 80854359Sroberto 809285612Sdelphij jupiter_debug(instance->peer, __func__, 810285612Sdelphij "UTC %.24s (gweek/sweek %u/%u)", 811132451Sroberto cp, instance->gweek, sweek); 81254359Sroberto 81354359Sroberto /* Billboard last_timecode (which is now the current time) */ 814132451Sroberto instance->peer->procptr->year = tm->tm_year + 1900; 815132451Sroberto instance->peer->procptr->day = tm->tm_yday + 1; 816132451Sroberto instance->peer->procptr->hour = tm->tm_hour; 817132451Sroberto instance->peer->procptr->minute = tm->tm_min; 818132451Sroberto instance->peer->procptr->second = tm->tm_sec; 81954359Sroberto } 82054359Sroberto 82154359Sroberto flags = getshort(jp->flags); 82254359Sroberto 82354359Sroberto /* Toss if not designated "valid" by the gps */ 82454359Sroberto if ((flags & JUPITER_O_PULSE_VALID) == 0) { 825132451Sroberto refclock_report(instance->peer, CEVNT_BADTIME); 82654359Sroberto return ("time mark not valid"); 82754359Sroberto } 82854359Sroberto 82954359Sroberto /* We better be sync'ed to UTC... */ 83054359Sroberto if ((flags & JUPITER_O_PULSE_UTC) == 0) { 831132451Sroberto refclock_report(instance->peer, CEVNT_BADTIME); 83254359Sroberto return ("time mark not sync'ed to UTC"); 83354359Sroberto } 83454359Sroberto 83554359Sroberto return (NULL); 83654359Sroberto} 83754359Sroberto 838285612Sdelphijstatic const char * 839132451Srobertojupiter_parse_gpos(struct instance *instance, u_short *sp) 84054359Sroberto{ 841132451Sroberto struct jgpos *jg; 842132451Sroberto time_t t; 843132451Sroberto struct tm *tm; 844132451Sroberto char *cp; 84554359Sroberto 846132451Sroberto jg = (struct jgpos *)sp; 84754359Sroberto 848132451Sroberto if (jg->navval != 0) { 849132451Sroberto /* 850132451Sroberto * Solution not valid. Use caution and refuse 851132451Sroberto * to determine GPS week from this message. 852132451Sroberto */ 853132451Sroberto instance->gpos_gweek = 0; 854132451Sroberto instance->gpos_sweek = 0; 855132451Sroberto return ("Navigation solution not valid"); 85654359Sroberto } 85754359Sroberto 858132451Sroberto instance->gpos_sweek = DS2UI(jg->sweek); 859310419Sdelphij instance->gpos_gweek = get_full_week(get_base_week(), 860310419Sdelphij getshort(jg->gweek)); 861310419Sdelphij 862310419Sdelphij /* according to the protocol spec, the seconds-in-week cannot 863310419Sdelphij * exceed the nominal value: Is it really necessary to normalise 864310419Sdelphij * the seconds??? 865310419Sdelphij */ 866132451Sroberto while(instance->gpos_sweek >= WEEKSECS) { 867132451Sroberto instance->gpos_sweek -= WEEKSECS; 868132451Sroberto ++instance->gpos_gweek; 86954359Sroberto } 870132451Sroberto instance->gweek = 0; 87154359Sroberto 872132451Sroberto t = GPS_EPOCH + (instance->gpos_gweek * WEEKSECS) + instance->gpos_sweek; 873132451Sroberto tm = gmtime(&t); 874132451Sroberto cp = asctime(tm); 87554359Sroberto 876285612Sdelphij jupiter_debug(instance->peer, __func__, 877285612Sdelphij "GPS %.24s (gweek/sweek %u/%u)", 878132451Sroberto cp, instance->gpos_gweek, instance->gpos_sweek); 879132451Sroberto return (NULL); 88054359Sroberto} 88154359Sroberto 88254359Sroberto/* 88354359Sroberto * jupiter_debug - print debug messages 88454359Sroberto */ 88554359Srobertostatic void 886285612Sdelphijjupiter_debug( 887285612Sdelphij struct peer * peer, 888285612Sdelphij const char * function, 889285612Sdelphij const char * fmt, 890285612Sdelphij ... 891285612Sdelphij ) 89254359Sroberto{ 893285612Sdelphij char buffer[200]; 894285612Sdelphij va_list ap; 89554359Sroberto 896132451Sroberto va_start(ap, fmt); 897132451Sroberto /* 898132451Sroberto * Print debug message to stdout 899132451Sroberto * In the future, we may want to get get more creative... 900132451Sroberto */ 901285612Sdelphij mvsnprintf(buffer, sizeof(buffer), fmt, ap); 902285612Sdelphij record_clock_stats(&peer->srcadr, buffer); 903182007Sroberto#ifdef DEBUG 904132451Sroberto if (debug) { 905285612Sdelphij printf("%s: %s\n", function, buffer); 906132451Sroberto fflush(stdout); 907132451Sroberto } 908182007Sroberto#endif 90954359Sroberto 910132451Sroberto va_end(ap); 91154359Sroberto} 91254359Sroberto 91354359Sroberto/* Checksum and transmit a message to the Jupiter */ 91454359Srobertostatic char * 915132451Srobertojupiter_send(struct instance *instance, struct jheader *hp) 91654359Sroberto{ 917132451Sroberto u_int len, size; 918285612Sdelphij ssize_t cc; 919132451Sroberto u_short *sp; 92054359Sroberto static char errstr[132]; 92154359Sroberto 92254359Sroberto size = sizeof(*hp); 92354359Sroberto hp->hsum = putshort(jupiter_cksum((u_short *)hp, 92454359Sroberto (size / sizeof(u_short)) - 1)); 92554359Sroberto len = getshort(hp->len); 92654359Sroberto if (len > 0) { 92754359Sroberto sp = (u_short *)(hp + 1); 92854359Sroberto sp[len] = putshort(jupiter_cksum(sp, len)); 92954359Sroberto size += (len + 1) * sizeof(u_short); 93054359Sroberto } 93154359Sroberto 932132451Sroberto if ((cc = write(instance->peer->procptr->io.fd, (char *)hp, size)) < 0) { 933285612Sdelphij msnprintf(errstr, sizeof(errstr), "write: %m"); 93454359Sroberto return (errstr); 935285612Sdelphij } else if (cc != (int)size) { 936285612Sdelphij snprintf(errstr, sizeof(errstr), "short write (%zd != %u)", cc, size); 93754359Sroberto return (errstr); 93854359Sroberto } 93954359Sroberto return (NULL); 94054359Sroberto} 94154359Sroberto 94254359Sroberto/* Request periodic message output */ 94354359Srobertostatic struct { 94454359Sroberto struct jheader jheader; 94554359Sroberto struct jrequest jrequest; 94654359Sroberto} reqmsg = { 94754359Sroberto { putshort(JUPITER_SYNC), 0, 94854359Sroberto putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1), 949182007Sroberto 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | 950182007Sroberto JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 }, 95154359Sroberto { 0, 0, 0, 0 } 95254359Sroberto}; 95354359Sroberto 95454359Sroberto/* An interval of zero means to output on trigger */ 95554359Srobertostatic void 956132451Srobertojupiter_reqmsg(struct instance *instance, u_int id, 957132451Sroberto u_int interval) 95854359Sroberto{ 959132451Sroberto struct jheader *hp; 960132451Sroberto struct jrequest *rp; 961132451Sroberto char *cp; 96254359Sroberto 96354359Sroberto hp = &reqmsg.jheader; 96454359Sroberto hp->id = putshort(id); 96554359Sroberto rp = &reqmsg.jrequest; 96654359Sroberto rp->trigger = putshort(interval == 0); 96754359Sroberto rp->interval = putshort(interval); 968132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 969285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 97054359Sroberto} 97154359Sroberto 97254359Sroberto/* Cancel periodic message output */ 97354359Srobertostatic struct jheader canmsg = { 97454359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 975182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC, 97654359Sroberto 0 97754359Sroberto}; 97854359Sroberto 97954359Srobertostatic void 980132451Srobertojupiter_canmsg(struct instance *instance, u_int id) 98154359Sroberto{ 982132451Sroberto struct jheader *hp; 983132451Sroberto char *cp; 98454359Sroberto 98554359Sroberto hp = &canmsg; 98654359Sroberto hp->id = putshort(id); 987132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 988285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 98954359Sroberto} 99054359Sroberto 99154359Sroberto/* Request a single message output */ 99254359Srobertostatic struct jheader reqonemsg = { 99354359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 994182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY, 99554359Sroberto 0 99654359Sroberto}; 99754359Sroberto 99854359Srobertostatic void 999132451Srobertojupiter_reqonemsg(struct instance *instance, u_int id) 100054359Sroberto{ 1001132451Sroberto struct jheader *hp; 1002132451Sroberto char *cp; 100354359Sroberto 100454359Sroberto hp = &reqonemsg; 100554359Sroberto hp->id = putshort(id); 1006132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 1007285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", id, cp); 100854359Sroberto} 100954359Sroberto 101054359Sroberto/* Set the platform dynamics */ 101154359Srobertostatic struct { 101254359Sroberto struct jheader jheader; 101354359Sroberto struct jplat jplat; 101454359Sroberto} platmsg = { 101554359Sroberto { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT), 101654359Sroberto putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0, 1017182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 }, 101854359Sroberto { 0, 0, 0 } 101954359Sroberto}; 102054359Sroberto 102154359Srobertostatic void 1022132451Srobertojupiter_platform(struct instance *instance, u_int platform) 102354359Sroberto{ 1024132451Sroberto struct jheader *hp; 1025132451Sroberto struct jplat *pp; 1026132451Sroberto char *cp; 102754359Sroberto 102854359Sroberto hp = &platmsg.jheader; 102954359Sroberto pp = &platmsg.jplat; 103054359Sroberto pp->platform = putshort(platform); 1031132451Sroberto if ((cp = jupiter_send(instance, hp)) != NULL) 1032285612Sdelphij jupiter_debug(instance->peer, __func__, "%u: %s", platform, cp); 103354359Sroberto} 103454359Sroberto 103554359Sroberto/* Checksum "len" shorts */ 103654359Srobertostatic u_short 1037132451Srobertojupiter_cksum(u_short *sp, u_int len) 103854359Sroberto{ 1039132451Sroberto u_short sum, x; 104054359Sroberto 104154359Sroberto sum = 0; 104254359Sroberto while (len-- > 0) { 104354359Sroberto x = *sp++; 104454359Sroberto sum += getshort(x); 104554359Sroberto } 104654359Sroberto return (~sum + 1); 104754359Sroberto} 104854359Sroberto 104954359Sroberto/* Return the size of the next message (or zero if we don't have it all yet) */ 105054359Srobertostatic int 1051132451Srobertojupiter_recv(struct instance *instance) 105254359Sroberto{ 1053132451Sroberto int n, len, size, cc; 1054132451Sroberto struct jheader *hp; 1055132451Sroberto u_char *bp; 1056132451Sroberto u_short *sp; 105754359Sroberto 105854359Sroberto /* Must have at least a header's worth */ 105954359Sroberto cc = sizeof(*hp); 1060132451Sroberto size = instance->ssize; 106154359Sroberto if (size < cc) 106254359Sroberto return (0); 106354359Sroberto 106454359Sroberto /* Search for the sync short if missing */ 1065132451Sroberto sp = instance->sbuf; 106654359Sroberto hp = (struct jheader *)sp; 106754359Sroberto if (getshort(hp->sync) != JUPITER_SYNC) { 106854359Sroberto /* Wasn't at the front, sync up */ 1069285612Sdelphij jupiter_debug(instance->peer, __func__, "syncing"); 107054359Sroberto bp = (u_char *)sp; 107154359Sroberto n = size; 107254359Sroberto while (n >= 2) { 107354359Sroberto if (bp[0] != (JUPITER_SYNC & 0xff)) { 1074132451Sroberto /* 1075285612Sdelphij jupiter_debug(instance->peer, __func__, 1076285612Sdelphij "{0x%x}", bp[0]); 1077132451Sroberto */ 107854359Sroberto ++bp; 107954359Sroberto --n; 108054359Sroberto continue; 108154359Sroberto } 108254359Sroberto if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff)) 108354359Sroberto break; 1084132451Sroberto /* 1085285612Sdelphij jupiter_debug(instance->peer, __func__, 1086285612Sdelphij "{0x%x 0x%x}", bp[0], bp[1]); 1087132451Sroberto */ 108854359Sroberto bp += 2; 108954359Sroberto n -= 2; 109054359Sroberto } 1091132451Sroberto /* 1092285612Sdelphij jupiter_debug(instance->peer, __func__, "\n"); 1093132451Sroberto */ 109454359Sroberto /* Shuffle data to front of input buffer */ 109554359Sroberto if (n > 0) 109654359Sroberto memcpy(sp, bp, n); 109754359Sroberto size = n; 1098132451Sroberto instance->ssize = size; 109954359Sroberto if (size < cc || hp->sync != JUPITER_SYNC) 110054359Sroberto return (0); 110154359Sroberto } 110254359Sroberto 110354359Sroberto if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) != 110454359Sroberto getshort(hp->hsum)) { 1105285612Sdelphij jupiter_debug(instance->peer, __func__, "bad header checksum!"); 110654359Sroberto /* This is drastic but checksum errors should be rare */ 1107132451Sroberto instance->ssize = 0; 110854359Sroberto return (0); 110954359Sroberto } 111054359Sroberto 111154359Sroberto /* Check for a payload */ 111254359Sroberto len = getshort(hp->len); 111354359Sroberto if (len > 0) { 111454359Sroberto n = (len + 1) * sizeof(u_short); 111554359Sroberto /* Not enough data yet */ 111654359Sroberto if (size < cc + n) 111754359Sroberto return (0); 111854359Sroberto 111954359Sroberto /* Check payload checksum */ 112054359Sroberto sp = (u_short *)(hp + 1); 112154359Sroberto if (jupiter_cksum(sp, len) != getshort(sp[len])) { 1122132451Sroberto jupiter_debug(instance->peer, 1123285612Sdelphij __func__, "bad payload checksum!"); 112454359Sroberto /* This is drastic but checksum errors should be rare */ 1125132451Sroberto instance->ssize = 0; 112654359Sroberto return (0); 112754359Sroberto } 112854359Sroberto cc += n; 112954359Sroberto } 113054359Sroberto return (cc); 113154359Sroberto} 113254359Sroberto 1133310419Sdelphijstatic u_int 1134310419Sdelphijget_base_week(void) 1135310419Sdelphij{ 1136310419Sdelphij static int init_done /* = 0 */; 1137310419Sdelphij static u_int base_week; 1138310419Sdelphij 1139310419Sdelphij /* Get the build date, convert to days since GPS epoch and 1140310419Sdelphij * finally weeks since GPS epoch. Note that the build stamp is 1141310419Sdelphij * trusted once it is fetched -- only dates before the GPS epoch 1142310419Sdelphij * are not permitted. This will permit proper synchronisation 1143310419Sdelphij * for a time range of 1024 weeks starting with 00:00:00 of the 1144310419Sdelphij * last Sunday on or before the build time. 1145310419Sdelphij * 1146310419Sdelphij * If the impossible happens and fetching the build date fails, 1147310419Sdelphij * a 1024-week cycle starting with 2016-01-03 is assumed to 1148310419Sdelphij * avoid catastropic errors. This will work until 2035-08-19. 1149310419Sdelphij */ 1150310419Sdelphij if (!init_done) { 1151310419Sdelphij struct calendar bd; 1152310419Sdelphij if (ntpcal_get_build_date(&bd)) { 1153310419Sdelphij int32_t days = ntpcal_date_to_rd(&bd); 1154310419Sdelphij if (days > RDN_GPS_EPOCH) 1155310419Sdelphij days -= RDN_GPS_EPOCH; 1156310419Sdelphij else 1157310419Sdelphij days = 0; 1158310419Sdelphij base_week = days / 7; 1159310419Sdelphij } else { 1160310419Sdelphij base_week = 1878; /* 2016-01-03, Sunday */ 1161310419Sdelphij msyslog(LOG_ERR, 1162310419Sdelphij "refclock_jupiter: ntpcal_get_build_date() failed: %s", 1163310419Sdelphij "using 2016-01-03 as GPS base!"); 1164310419Sdelphij } 1165310419Sdelphij init_done = 1; 1166310419Sdelphij } 1167310419Sdelphij return base_week; 1168310419Sdelphij} 1169310419Sdelphij 1170310419Sdelphijstatic u_int 1171310419Sdelphijget_full_week( 1172310419Sdelphij u_int base_week, 1173310419Sdelphij u_int gpos_week 1174310419Sdelphij ) 1175310419Sdelphij{ 1176310419Sdelphij /* Periodic extension on base week. Since the period is 1024 1177310419Sdelphij * weeks and we do unsigned arithmetic here, we can do wonderful 1178310419Sdelphij * things with masks and the well-defined overflow behaviour. 1179310419Sdelphij */ 1180310419Sdelphij return base_week + ((gpos_week - base_week) & 1023); 1181310419Sdelphij} 1182310419Sdelphij 1183132451Sroberto#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 118454359Srobertoint refclock_jupiter_bs; 1185132451Sroberto#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 1186