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 38358659Scy/* This clock *REQUIRES* the PPS API to be available */ 39132451Sroberto#if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI) 4054359Sroberto 4154359Sroberto#include "ntpd.h" 4254359Sroberto#include "ntp_io.h" 4354359Sroberto#include "ntp_refclock.h" 4454359Sroberto#include "ntp_unixtime.h" 4554359Sroberto#include "ntp_stdlib.h" 46358659Scy#include "ntp_calendar.h" 47358659Scy#include "ntp_calgps.h" 48358659Scy#include "timespecops.h" 4954359Sroberto 5082498Sroberto#include <stdio.h> 5182498Sroberto#include <ctype.h> 5282498Sroberto 5354359Sroberto#include "jupiter.h" 54358659Scy#include "ppsapi_timepps.h" 5554359Sroberto 56280849Scy#ifdef WORDS_BIGENDIAN 5754359Sroberto#define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 5854359Sroberto#define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 5954359Sroberto#else 60280849Scy#define getshort(s) ((u_short)(s)) 61280849Scy#define putshort(s) ((u_short)(s)) 6254359Sroberto#endif 6354359Sroberto 6454359Sroberto/* 6554359Sroberto * This driver supports the Rockwell Jupiter GPS Receiver board 6654359Sroberto * adapted to precision timing applications. It requires the 6754359Sroberto * ppsclock line discipline or streams module described in the 6854359Sroberto * Line Disciplines and Streams Drivers page. It also requires a 6954359Sroberto * gadget box and 1-PPS level converter, such as described in the 7054359Sroberto * Pulse-per-second (PPS) Signal Interfacing page. 7154359Sroberto * 7254359Sroberto * It may work (with minor modifications) with other Rockwell GPS 7354359Sroberto * receivers such as the CityTracker. 7454359Sroberto */ 7554359Sroberto 7654359Sroberto/* 7754359Sroberto * GPS Definitions 7854359Sroberto */ 7954359Sroberto#define DEVICE "/dev/gps%d" /* device name and unit */ 8054359Sroberto#define SPEED232 B9600 /* baud */ 8154359Sroberto 8254359Sroberto/* 8354359Sroberto * Radio interface parameters 8454359Sroberto */ 8554359Sroberto#define PRECISION (-18) /* precision assumed (about 4 us) */ 8654359Sroberto#define REFID "GPS\0" /* reference id */ 8754359Sroberto#define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */ 8854359Sroberto#define DEFFUDGETIME 0 /* default fudge time (ms) */ 8954359Sroberto 9054359Sroberto/* Unix timestamp for the GPS epoch: January 6, 1980 */ 9154359Sroberto#define GPS_EPOCH 315964800 9254359Sroberto 93309007Sdelphij/* Rata Die Number of first day of GPS epoch. This is the number of days 94309007Sdelphij * since 0000-12-31 to 1980-01-06 in the proleptic Gregorian Calendar. 95309007Sdelphij */ 96309007Sdelphij#define RDN_GPS_EPOCH (4*146097 + 138431 + 1) 97309007Sdelphij 9854359Sroberto/* Double short to unsigned int */ 9954359Sroberto#define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 10054359Sroberto 10154359Sroberto/* Double short to signed int */ 10254359Sroberto#define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 10354359Sroberto 10454359Sroberto/* One week's worth of seconds */ 10554359Sroberto#define WEEKSECS (7 * 24 * 60 * 60) 10654359Sroberto 10754359Sroberto/* 10854359Sroberto * Jupiter unit control structure. 10954359Sroberto */ 110132451Srobertostruct instance { 111132451Sroberto struct peer *peer; /* peer */ 112358659Scy 113132451Sroberto pps_params_t pps_params; /* pps parameters */ 114132451Sroberto pps_info_t pps_info; /* last pps data */ 115132451Sroberto pps_handle_t pps_handle; /* pps handle */ 116358659Scy u_int assert; /* pps edge to use */ 117358659Scy u_int hardpps; /* enable kernel mode */ 118358659Scy l_fp rcv_pps; /* last pps timestamp */ 119358659Scy l_fp rcv_next; /* rcv time of next reftime */ 120358659Scy TGpsDatum ref_next; /* next GPS time stamp to use with PPS */ 121358659Scy TGpsDatum piv_next; /* pivot for week date unfolding */ 122358659Scy uint16_t piv_hold; /* TTL for pivot value */ 123358659Scy uint16_t rcvtout; /* receive timeout ticker */ 12454359Sroberto int wantid; /* don't reconfig on channel id msg */ 12554359Sroberto u_int moving; /* mobile platform? */ 126132451Sroberto u_char sloppyclockflag; /* fudge flags */ 12754359Sroberto u_short sbuf[512]; /* local input buffer */ 12854359Sroberto int ssize; /* space used in sbuf */ 12954359Sroberto}; 13054359Sroberto 13154359Sroberto/* 13254359Sroberto * Function prototypes 13354359Sroberto */ 134358659Scystatic void jupiter_canmsg (struct instance * const, u_int); 135280849Scystatic u_short jupiter_cksum (u_short *, u_int); 136358659Scystatic int jupiter_config (struct instance * const); 137280849Scystatic void jupiter_debug (struct peer *, const char *, 138338530Sdelphij const char *, ...) NTP_PRINTF(3, 4); 139358659Scystatic const char * jupiter_parse_t (struct instance * const, u_short *, l_fp); 140358659Scystatic const char * jupiter_parse_gpos(struct instance * const, u_short *); 141358659Scystatic void jupiter_platform(struct instance * const, u_int); 142280849Scystatic void jupiter_poll (int, struct peer *); 143280849Scystatic void jupiter_control (int, const struct refclockstat *, 144280849Scy struct refclockstat *, struct peer *); 145358659Scystatic int jupiter_ppsapi (struct instance * const); 146358659Scystatic int jupiter_pps (struct instance * const); 147358659Scystatic int jupiter_recv (struct instance * const); 148358659Scystatic void jupiter_receive (struct recvbuf * const rbufp); 149358659Scystatic void jupiter_reqmsg (struct instance * const, u_int, u_int); 150358659Scystatic void jupiter_reqonemsg(struct instance * const, u_int); 151358659Scystatic char * jupiter_send (struct instance * const, struct jheader *); 152280849Scystatic void jupiter_shutdown(int, struct peer *); 153280849Scystatic int jupiter_start (int, struct peer *); 154358659Scystatic void jupiter_ticker (int, struct peer *); 15554359Sroberto 15654359Sroberto/* 15754359Sroberto * Transfer vector 15854359Sroberto */ 15954359Srobertostruct refclock refclock_jupiter = { 16054359Sroberto jupiter_start, /* start up driver */ 16154359Sroberto jupiter_shutdown, /* shut down driver */ 16254359Sroberto jupiter_poll, /* transmit poll message */ 163132451Sroberto jupiter_control, /* (clock control) */ 16454359Sroberto noentry, /* (clock init) */ 16554359Sroberto noentry, /* (clock buginfo) */ 166358659Scy jupiter_ticker /* 1HZ ticker */ 16754359Sroberto}; 16854359Sroberto 16954359Sroberto/* 17054359Sroberto * jupiter_start - open the devices and initialize data for processing 17154359Sroberto */ 17254359Srobertostatic int 17354359Srobertojupiter_start( 174132451Sroberto int unit, 175132451Sroberto struct peer *peer 17654359Sroberto ) 17754359Sroberto{ 178358659Scy struct refclockproc * const pp = peer->procptr; 179358659Scy struct instance * up; 180280849Scy int fd; 18154359Sroberto char gpsdev[20]; 18254359Sroberto 18354359Sroberto /* 18454359Sroberto * Open serial port 18554359Sroberto */ 186280849Scy snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 187132451Sroberto fd = refclock_open(gpsdev, SPEED232, LDISC_RAW); 188280849Scy if (fd <= 0) { 189280849Scy jupiter_debug(peer, "jupiter_start", "open %s: %m", 190280849Scy gpsdev); 19154359Sroberto return (0); 19254359Sroberto } 19354359Sroberto 19454359Sroberto /* Allocate unit structure */ 195358659Scy up = emalloc_zero(sizeof(*up)); 196358659Scy up->peer = peer; 19754359Sroberto pp->io.clock_recv = jupiter_receive; 198280849Scy pp->io.srcclock = peer; 19954359Sroberto pp->io.datalen = 0; 20054359Sroberto pp->io.fd = fd; 20154359Sroberto if (!io_addclock(&pp->io)) { 202280849Scy close(fd); 203280849Scy pp->io.fd = -1; 204358659Scy free(up); 20554359Sroberto return (0); 20654359Sroberto } 207358659Scy pp->unitptr = up; 20854359Sroberto 20954359Sroberto /* 21054359Sroberto * Initialize miscellaneous variables 21154359Sroberto */ 21254359Sroberto peer->precision = PRECISION; 21354359Sroberto pp->clockdesc = DESCRIPTION; 21454359Sroberto memcpy((char *)&pp->refid, REFID, 4); 21554359Sroberto 216358659Scy up->assert = 1; 217358659Scy up->hardpps = 0; 218132451Sroberto /* 219132451Sroberto * Start the PPSAPI interface if it is there. Default to use 220132451Sroberto * the assert edge and do not enable the kernel hardpps. 221132451Sroberto */ 222358659Scy if (time_pps_create(fd, &up->pps_handle) < 0) { 223358659Scy up->pps_handle = 0; 224132451Sroberto msyslog(LOG_ERR, 225132451Sroberto "refclock_jupiter: time_pps_create failed: %m"); 226132451Sroberto } 227358659Scy else if (!jupiter_ppsapi(up)) 228132451Sroberto goto clean_up; 229358659Scy 23054359Sroberto /* Ensure the receiver is properly configured */ 231358659Scy if (!jupiter_config(up)) 232132451Sroberto goto clean_up; 23354359Sroberto 234358659Scy jupiter_pps(up); /* get current PPS state */ 23554359Sroberto return (1); 236132451Sroberto 237132451Srobertoclean_up: 238132451Sroberto jupiter_shutdown(unit, peer); 239132451Sroberto pp->unitptr = 0; 240132451Sroberto return (0); 24154359Sroberto} 24254359Sroberto 24354359Sroberto/* 24454359Sroberto * jupiter_shutdown - shut down the clock 24554359Sroberto */ 24654359Srobertostatic void 247132451Srobertojupiter_shutdown(int unit, struct peer *peer) 24854359Sroberto{ 249358659Scy struct refclockproc * const pp = peer->procptr; 250358659Scy struct instance * const up = pp->unitptr; 251358659Scy 252358659Scy if (!up) 253132451Sroberto return; 254132451Sroberto 255358659Scy if (up->pps_handle) { 256358659Scy time_pps_destroy(up->pps_handle); 257358659Scy up->pps_handle = 0; 258132451Sroberto } 259132451Sroberto 260280849Scy if (pp->io.fd != -1) 261280849Scy io_closeclock(&pp->io); 262358659Scy free(up); 26354359Sroberto} 26454359Sroberto 26554359Sroberto/* 26654359Sroberto * jupiter_config - Configure the receiver 26754359Sroberto */ 268132451Srobertostatic int 269358659Scyjupiter_config(struct instance * const up) 27054359Sroberto{ 271358659Scy jupiter_debug(up->peer, __func__, "init receiver"); 27254359Sroberto 27354359Sroberto /* 27454359Sroberto * Initialize the unit variables 27554359Sroberto */ 276358659Scy up->sloppyclockflag = up->peer->procptr->sloppyclockflag; 277358659Scy up->moving = !!(up->sloppyclockflag & CLK_FLAG2); 278358659Scy if (up->moving) 279358659Scy jupiter_debug(up->peer, __func__, "mobile platform"); 28054359Sroberto 281358659Scy ZERO(up->rcv_next); 282358659Scy ZERO(up->ref_next); 283358659Scy ZERO(up->piv_next); 284358659Scy up->ssize = 0; 28554359Sroberto 28654359Sroberto /* Stop outputting all messages */ 287358659Scy jupiter_canmsg(up, JUPITER_ALL); 28854359Sroberto 28954359Sroberto /* Request the receiver id so we can syslog the firmware version */ 290358659Scy jupiter_reqonemsg(up, JUPITER_O_ID); 29154359Sroberto 29254359Sroberto /* Flag that this the id was requested (so we don't get called again) */ 293358659Scy up->wantid = 1; 29454359Sroberto 29554359Sroberto /* Request perodic time mark pulse messages */ 296358659Scy jupiter_reqmsg(up, JUPITER_O_PULSE, 1); 29754359Sroberto 298132451Sroberto /* Request perodic geodetic position status */ 299358659Scy jupiter_reqmsg(up, JUPITER_O_GPOS, 1); 300132451Sroberto 30154359Sroberto /* Set application platform type */ 302358659Scy if (up->moving) 303358659Scy jupiter_platform(up, JUPITER_I_PLAT_MED); 30454359Sroberto else 305358659Scy jupiter_platform(up, JUPITER_I_PLAT_LOW); 306132451Sroberto 307132451Sroberto return (1); 30854359Sroberto} 30954359Sroberto 310358659Scystatic void 311358659Scyjupiter_checkpps( 312358659Scy struct refclockproc * const pp, 313358659Scy struct instance * const up 314358659Scy ) 315358659Scy{ 316358659Scy l_fp tstamp, delta; 317358659Scy struct calendar cd; 318358659Scy 319358659Scy if (jupiter_pps(up) || !up->piv_next.weeks) 320358659Scy return; 321358659Scy 322358659Scy /* check delay between pulse message and pulse. */ 323358659Scy delta = up->rcv_pps; /* set by jupiter_pps() */ 324358659Scy L_SUB(&delta, &up->rcv_next); /* recv time pulse message */ 325358659Scy if (delta.l_ui != 0 || delta.l_uf >= 0xC0000000) { 326358659Scy up->ref_next.weeks = 0; /* consider as consumed... */ 327358659Scy return; 328358659Scy } 329358659Scy 330358659Scy pp->lastrec = up->rcv_pps; 331358659Scy tstamp = ntpfp_from_gpsdatum(&up->ref_next); 332358659Scy refclock_process_offset(pp, tstamp, up->rcv_pps, pp->fudgetime1); 333358659Scy up->rcvtout = 2; 334358659Scy 335358659Scy gpscal_to_calendar(&cd, &up->ref_next); 336358659Scy refclock_save_lcode(pp, ntpcal_iso8601std(NULL, 0, &cd), 337358659Scy (size_t)-1); 338358659Scy up->ref_next.weeks = 0; /* consumed... */ 339358659Scy} 340358659Scy 34154359Sroberto/* 342358659Scy * jupiter_ticker - process periodic checks 343358659Scy */ 344358659Scystatic void 345358659Scyjupiter_ticker(int unit, struct peer *peer) 346358659Scy{ 347358659Scy struct refclockproc * const pp = peer->procptr; 348358659Scy struct instance * const up = pp->unitptr; 349358659Scy 350358659Scy if (!up) 351358659Scy return; 352358659Scy 353358659Scy /* check if we can add another sample now */ 354358659Scy jupiter_checkpps(pp, up); 355358659Scy 356358659Scy /* check the pivot update cycle */ 357358659Scy if (up->piv_hold && !--up->piv_hold) 358358659Scy ZERO(up->piv_next); 359358659Scy 360358659Scy if (up->rcvtout) 361358659Scy --up->rcvtout; 362358659Scy else if (pp->coderecv != pp->codeproc) 363358659Scy refclock_samples_expire(pp, 1); 364358659Scy} 365358659Scy 366358659Scy/* 367132451Sroberto * Initialize PPSAPI 368132451Sroberto */ 369132451Srobertoint 370132451Srobertojupiter_ppsapi( 371358659Scy struct instance * const up /* unit structure pointer */ 372132451Sroberto ) 373132451Sroberto{ 374132451Sroberto int capability; 375132451Sroberto 376358659Scy if (time_pps_getcap(up->pps_handle, &capability) < 0) { 377132451Sroberto msyslog(LOG_ERR, 378132451Sroberto "refclock_jupiter: time_pps_getcap failed: %m"); 379132451Sroberto return (0); 380132451Sroberto } 381358659Scy memset(&up->pps_params, 0, sizeof(pps_params_t)); 382358659Scy if (!up->assert) 383358659Scy up->pps_params.mode = capability & PPS_CAPTURECLEAR; 384132451Sroberto else 385358659Scy up->pps_params.mode = capability & PPS_CAPTUREASSERT; 386358659Scy if (!(up->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { 387132451Sroberto msyslog(LOG_ERR, 388132451Sroberto "refclock_jupiter: invalid capture edge %d", 389358659Scy up->assert); 390132451Sroberto return (0); 391132451Sroberto } 392358659Scy up->pps_params.mode |= PPS_TSFMT_TSPEC; 393358659Scy if (time_pps_setparams(up->pps_handle, &up->pps_params) < 0) { 394132451Sroberto msyslog(LOG_ERR, 395132451Sroberto "refclock_jupiter: time_pps_setparams failed: %m"); 396132451Sroberto return (0); 397132451Sroberto } 398358659Scy if (up->hardpps) { 399358659Scy if (time_pps_kcbind(up->pps_handle, PPS_KC_HARDPPS, 400358659Scy up->pps_params.mode & ~PPS_TSFMT_TSPEC, 401132451Sroberto PPS_TSFMT_TSPEC) < 0) { 402132451Sroberto msyslog(LOG_ERR, 403132451Sroberto "refclock_jupiter: time_pps_kcbind failed: %m"); 404132451Sroberto return (0); 405132451Sroberto } 406280849Scy hardpps_enable = 1; 407132451Sroberto } 408358659Scy/* up->peer->precision = PPS_PRECISION; */ 409132451Sroberto 410132451Sroberto#if DEBUG 411132451Sroberto if (debug) { 412358659Scy time_pps_getparams(up->pps_handle, &up->pps_params); 413358659Scy jupiter_debug(up->peer, __func__, 414132451Sroberto "pps capability 0x%x version %d mode 0x%x kern %d", 415358659Scy capability, up->pps_params.api_version, 416358659Scy up->pps_params.mode, up->hardpps); 417132451Sroberto } 418132451Sroberto#endif 419132451Sroberto 420132451Sroberto return (1); 421132451Sroberto} 422132451Sroberto 423132451Sroberto/* 424132451Sroberto * Get PPSAPI timestamps. 425132451Sroberto * 426132451Sroberto * Return 0 on failure and 1 on success. 427132451Sroberto */ 428132451Srobertostatic int 429358659Scyjupiter_pps(struct instance * const up) 430132451Sroberto{ 431132451Sroberto pps_info_t pps_info; 432132451Sroberto struct timespec timeout, ts; 433132451Sroberto l_fp tstmp; 434132451Sroberto 435132451Sroberto /* 436132451Sroberto * Convert the timespec nanoseconds field to ntp l_fp units. 437132451Sroberto */ 438358659Scy if (up->pps_handle == 0) 439132451Sroberto return 1; 440132451Sroberto timeout.tv_sec = 0; 441132451Sroberto timeout.tv_nsec = 0; 442358659Scy memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t)); 443358659Scy if (time_pps_fetch(up->pps_handle, PPS_TSFMT_TSPEC, &up->pps_info, 444132451Sroberto &timeout) < 0) 445132451Sroberto return 1; 446358659Scy if (up->pps_params.mode & PPS_CAPTUREASSERT) { 447132451Sroberto if (pps_info.assert_sequence == 448358659Scy up->pps_info.assert_sequence) 449132451Sroberto return 1; 450358659Scy ts = up->pps_info.assert_timestamp; 451358659Scy } else if (up->pps_params.mode & PPS_CAPTURECLEAR) { 452132451Sroberto if (pps_info.clear_sequence == 453358659Scy up->pps_info.clear_sequence) 454132451Sroberto return 1; 455358659Scy ts = up->pps_info.clear_timestamp; 456132451Sroberto } else { 457132451Sroberto return 1; 458132451Sroberto } 459358659Scy 460358659Scy tstmp = tspec_stamp_to_lfp(ts); 461358659Scy if (L_ISEQU(&tstmp, &up->rcv_pps)) 462132451Sroberto return 1; 463132451Sroberto 464358659Scy up->rcv_pps = tstmp; 465132451Sroberto return 0; 466132451Sroberto} 467132451Sroberto 468132451Sroberto/* 46954359Sroberto * jupiter_poll - jupiter watchdog routine 47054359Sroberto */ 47154359Srobertostatic void 472132451Srobertojupiter_poll(int unit, struct peer *peer) 47354359Sroberto{ 474358659Scy struct refclockproc * const pp = peer->procptr; 475358659Scy struct instance * const up = pp->unitptr; 47654359Sroberto 477358659Scy pp->polls++; 47854359Sroberto 47954359Sroberto /* 480358659Scy * If we have new samples since last poll, everything is fine. 481358659Scy * if not, blarb loudly. 48254359Sroberto */ 483358659Scy if (pp->coderecv != pp->codeproc) { 484358659Scy refclock_receive(peer); 485358659Scy refclock_report(peer, CEVNT_NOMINAL); 48654359Sroberto } else { 48754359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 48854359Sroberto 48954359Sroberto /* Request the receiver id to trigger a reconfig */ 490358659Scy jupiter_reqonemsg(up, JUPITER_O_ID); 491358659Scy up->wantid = 0; 49254359Sroberto } 49354359Sroberto} 49454359Sroberto 49554359Sroberto/* 496132451Sroberto * jupiter_control - fudge control 49754359Sroberto */ 49854359Srobertostatic void 499132451Srobertojupiter_control( 500132451Sroberto int unit, /* unit (not used) */ 501280849Scy const struct refclockstat *in, /* input parameters (not used) */ 502132451Sroberto struct refclockstat *out, /* output parameters (not used) */ 503132451Sroberto struct peer *peer /* peer structure pointer */ 504132451Sroberto ) 50554359Sroberto{ 506358659Scy struct refclockproc * const pp = peer->procptr; 507358659Scy struct instance * const up = pp->unitptr; 508358659Scy 509132451Sroberto u_char sloppyclockflag; 51054359Sroberto 511358659Scy up->assert = !(pp->sloppyclockflag & CLK_FLAG3); 512358659Scy jupiter_ppsapi(up); 51354359Sroberto 514358659Scy sloppyclockflag = up->sloppyclockflag; 515358659Scy up->sloppyclockflag = pp->sloppyclockflag; 516358659Scy if ((up->sloppyclockflag & CLK_FLAG2) != 51754359Sroberto (sloppyclockflag & CLK_FLAG2)) { 518280849Scy jupiter_debug(peer, __func__, 519132451Sroberto "mode switch: reset receiver"); 520358659Scy jupiter_config(up); 52154359Sroberto return; 52254359Sroberto } 523132451Sroberto} 52454359Sroberto 525132451Sroberto/* 526132451Sroberto * jupiter_receive - receive gps data 527132451Sroberto * Gag me! 528132451Sroberto */ 529132451Srobertostatic void 530358659Scyjupiter_receive(struct recvbuf * const rbufp) 531132451Sroberto{ 532358659Scy struct peer * const peer = rbufp->recv_peer; 533358659Scy struct refclockproc * const pp = peer->procptr; 534358659Scy struct instance * const up = pp->unitptr; 535358659Scy 536280849Scy size_t bpcnt; 537358659Scy int cc, size; 538280849Scy const char *cp; 539132451Sroberto u_char *bp; 540132451Sroberto u_short *sp; 541132451Sroberto struct jid *ip; 542132451Sroberto struct jheader *hp; 54354359Sroberto 544132451Sroberto /* Initialize pointers and read the timecode and timestamp */ 54554359Sroberto bp = (u_char *)rbufp->recv_buffer; 54654359Sroberto bpcnt = rbufp->recv_length; 54754359Sroberto 54854359Sroberto /* This shouldn't happen */ 549358659Scy if (bpcnt > sizeof(up->sbuf) - up->ssize) 550358659Scy bpcnt = sizeof(up->sbuf) - up->ssize; 55154359Sroberto 55254359Sroberto /* Append to input buffer */ 553358659Scy memcpy((u_char *)up->sbuf + up->ssize, bp, bpcnt); 554358659Scy up->ssize += bpcnt; 55554359Sroberto 556132451Sroberto /* While there's at least a header and we parse an intact message */ 557358659Scy while (up->ssize > (int)sizeof(*hp) && (cc = jupiter_recv(up)) > 0) { 558358659Scy hp = (struct jheader *)up->sbuf; 55954359Sroberto sp = (u_short *)(hp + 1); 56054359Sroberto size = cc - sizeof(*hp); 56154359Sroberto switch (getshort(hp->id)) { 56254359Sroberto 56354359Sroberto case JUPITER_O_PULSE: 564358659Scy /* first see if we can push another sample: */ 565358659Scy jupiter_checkpps(pp, up); 566358659Scy 56754359Sroberto if (size != sizeof(struct jpulse)) { 568280849Scy jupiter_debug(peer, __func__, 569280849Scy "pulse: len %d != %u", 57054359Sroberto size, (int)sizeof(struct jpulse)); 57154359Sroberto refclock_report(peer, CEVNT_BADREPLY); 57254359Sroberto break; 57354359Sroberto } 574358659Scy 575358659Scy /* Parse timecode (even when there's no pps) 576358659Scy * 577358659Scy * There appears to be a firmware bug related to 578358659Scy * the pulse message; in addition to the one per 579358659Scy * second messages, we get an extra pulse 58054359Sroberto * message once an hour (on the anniversary of 58154359Sroberto * the cold start). It seems to come 200 ms 582358659Scy * after the one requested. 583358659Scy * 584358659Scy * But since we feed samples only when a new PPS 585358659Scy * pulse is found we can simply ignore that and 586358659Scy * aggregate/update any existing timing message. 58754359Sroberto */ 588358659Scy if ((cp = jupiter_parse_t(up, sp, rbufp->recv_time)) != NULL) { 589280849Scy jupiter_debug(peer, __func__, 590280849Scy "pulse: %s", cp); 59154359Sroberto } 59254359Sroberto break; 59354359Sroberto 594132451Sroberto case JUPITER_O_GPOS: 595132451Sroberto if (size != sizeof(struct jgpos)) { 596280849Scy jupiter_debug(peer, __func__, 597280849Scy "gpos: len %d != %u", 598132451Sroberto size, (int)sizeof(struct jgpos)); 599132451Sroberto refclock_report(peer, CEVNT_BADREPLY); 600132451Sroberto break; 601132451Sroberto } 602132451Sroberto 603358659Scy if ((cp = jupiter_parse_gpos(up, sp)) != NULL) { 604280849Scy jupiter_debug(peer, __func__, 605280849Scy "gpos: %s", cp); 606132451Sroberto break; 607132451Sroberto } 608132451Sroberto break; 609132451Sroberto 61054359Sroberto case JUPITER_O_ID: 61154359Sroberto if (size != sizeof(struct jid)) { 612280849Scy jupiter_debug(peer, __func__, 613280849Scy "id: len %d != %u", 61454359Sroberto size, (int)sizeof(struct jid)); 61554359Sroberto refclock_report(peer, CEVNT_BADREPLY); 61654359Sroberto break; 61754359Sroberto } 61854359Sroberto /* 61954359Sroberto * If we got this message because the Jupiter 620132451Sroberto * just powered instance, it needs to be reconfigured. 62154359Sroberto */ 62254359Sroberto ip = (struct jid *)sp; 623280849Scy jupiter_debug(peer, __func__, 624280849Scy "%s chan ver %s, %s (%s)", 62554359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 62654359Sroberto msyslog(LOG_DEBUG, 627182007Sroberto "jupiter_receive: %s chan ver %s, %s (%s)", 62854359Sroberto ip->chans, ip->vers, ip->date, ip->opts); 629358659Scy if (up->wantid) 630358659Scy up->wantid = 0; 63154359Sroberto else { 632280849Scy jupiter_debug(peer, __func__, "reset receiver"); 633358659Scy jupiter_config(up); 634132451Sroberto /* 635132451Sroberto * Restore since jupiter_config() just 636132451Sroberto * zeroed it 637132451Sroberto */ 638358659Scy up->ssize = cc; 63954359Sroberto } 64054359Sroberto break; 64154359Sroberto 64254359Sroberto default: 643280849Scy jupiter_debug(peer, __func__, "unknown message id %d", 64454359Sroberto getshort(hp->id)); 64554359Sroberto break; 64654359Sroberto } 647358659Scy up->ssize -= cc; 648358659Scy if (up->ssize < 0) { 64954359Sroberto fprintf(stderr, "jupiter_recv: negative ssize!\n"); 65054359Sroberto abort(); 651358659Scy } else if (up->ssize > 0) 652358659Scy memcpy(up->sbuf, (u_char *)up->sbuf + cc, up->ssize); 65354359Sroberto } 65454359Sroberto} 65554359Sroberto 656280849Scystatic const char * 657358659Scyjupiter_parse_t( 658358659Scy struct instance * const up, 659358659Scy u_short * sp, 660358659Scy l_fp rcvtime 661358659Scy ) 66254359Sroberto{ 663132451Sroberto struct jpulse *jp; 664132451Sroberto u_int32 sweek; 665132451Sroberto u_short flags; 666358659Scy l_fp fofs; 667358659Scy 66854359Sroberto jp = (struct jpulse *)sp; 669358659Scy flags = getshort(jp->flags); 67054359Sroberto 671358659Scy /* Toss if not designated "valid" by the gps. 672358659Scy * !!NOTE!! do *not* kill data received so far! 673358659Scy */ 674358659Scy if ((flags & JUPITER_O_PULSE_VALID) == 0) { 675358659Scy refclock_report(up->peer, CEVNT_BADTIME); 676358659Scy return ("time mark not valid"); 677358659Scy } 678358659Scy 679358659Scy up->rcv_next = rcvtime; /* remember when this happened */ 680358659Scy 68154359Sroberto /* The timecode is presented as seconds into the current GPS week */ 682132451Sroberto sweek = DS2UI(jp->sweek) % WEEKSECS; 683358659Scy /* check if we have to apply the UTC offset ourselves */ 684358659Scy if ((flags & JUPITER_O_PULSE_UTC) == 0) { 685358659Scy struct timespec tofs; 686358659Scy tofs.tv_sec = getshort(jp->offs); 687358659Scy tofs.tv_nsec = DS2I(jp->offns); 688358659Scy fofs = tspec_intv_to_lfp(tofs); 689358659Scy L_NEG(&fofs); 690358659Scy } else { 691358659Scy ZERO(fofs); 692358659Scy } 693358659Scy 69454359Sroberto /* 69554359Sroberto * If we don't know the current GPS week, calculate it from the 69654359Sroberto * current time. (It's too bad they didn't include this 697358659Scy * important value in the pulse message). 698358659Scy * 699358659Scy * So we pick the pivot value from the other messages like gpos 700358659Scy * or chan if we can. Of course, the PULSE message can be in UTC 701358659Scy * or GPS time scale, and the other messages are simply always 702358659Scy * GPS time. 70354359Sroberto * 704358659Scy * But as long as the difference between the time stamps is less 705358659Scy * than a half week, the unfolding of a week time is unambigeous 706358659Scy * and well suited for the problem we have here. And we won't 707358659Scy * see *that* many leap seconds, ever. 70854359Sroberto */ 709358659Scy if (up->piv_next.weeks) { 710358659Scy up->ref_next = gpscal_from_weektime2( 711358659Scy sweek, fofs, &up->piv_next); 712358659Scy up->piv_next = up->ref_next; 713358659Scy } else { 714358659Scy up->ref_next = gpscal_from_weektime1( 715358659Scy sweek, fofs, rcvtime); 71654359Sroberto } 717358659Scy 71854359Sroberto 71954359Sroberto 72054359Sroberto return (NULL); 72154359Sroberto} 72254359Sroberto 723280849Scystatic const char * 724358659Scyjupiter_parse_gpos( 725358659Scy struct instance * const up, 726358659Scy u_short * sp 727358659Scy ) 72854359Sroberto{ 729132451Sroberto struct jgpos *jg; 730358659Scy struct calendar tref; 731132451Sroberto char *cp; 732358659Scy struct timespec tofs; 733358659Scy uint16_t raw_week; 734358659Scy uint32_t raw_secs; 73554359Sroberto 736132451Sroberto jg = (struct jgpos *)sp; 73754359Sroberto 738132451Sroberto if (jg->navval != 0) { 739132451Sroberto /* 740132451Sroberto * Solution not valid. Use caution and refuse 741132451Sroberto * to determine GPS week from this message. 742132451Sroberto */ 743132451Sroberto return ("Navigation solution not valid"); 74454359Sroberto } 74554359Sroberto 746358659Scy raw_week = getshort(jg->gweek); 747358659Scy raw_secs = DS2UI(jg->sweek); 748358659Scy tofs.tv_sec = 0; 749358659Scy tofs.tv_nsec = DS2UI(jg->nsweek); 750358659Scy up->piv_next = gpscal_from_gpsweek(raw_week, raw_secs, 751358659Scy tspec_intv_to_lfp(tofs)); 752358659Scy up->piv_hold = 60; 753309007Sdelphij 754358659Scy gpscal_to_calendar(&tref, &up->piv_next); 755358659Scy cp = ntpcal_iso8601std(NULL, 0, &tref); 756358659Scy jupiter_debug(up->peer, __func__, 757358659Scy "GPS %s (gweek/sweek %hu/%u)", 758358659Scy cp, (unsigned short)raw_week, (unsigned int)raw_secs); 759132451Sroberto return (NULL); 76054359Sroberto} 76154359Sroberto 76254359Sroberto/* 76354359Sroberto * jupiter_debug - print debug messages 76454359Sroberto */ 76554359Srobertostatic void 766280849Scyjupiter_debug( 767280849Scy struct peer * peer, 768280849Scy const char * function, 769280849Scy const char * fmt, 770280849Scy ... 771280849Scy ) 77254359Sroberto{ 773280849Scy char buffer[200]; 774280849Scy va_list ap; 77554359Sroberto 776132451Sroberto va_start(ap, fmt); 777132451Sroberto /* 778132451Sroberto * Print debug message to stdout 779132451Sroberto * In the future, we may want to get get more creative... 780132451Sroberto */ 781280849Scy mvsnprintf(buffer, sizeof(buffer), fmt, ap); 782280849Scy record_clock_stats(&peer->srcadr, buffer); 783182007Sroberto#ifdef DEBUG 784132451Sroberto if (debug) { 785280849Scy printf("%s: %s\n", function, buffer); 786132451Sroberto fflush(stdout); 787132451Sroberto } 788182007Sroberto#endif 78954359Sroberto 790132451Sroberto va_end(ap); 79154359Sroberto} 79254359Sroberto 79354359Sroberto/* Checksum and transmit a message to the Jupiter */ 79454359Srobertostatic char * 795358659Scyjupiter_send( 796358659Scy struct instance * const up, 797358659Scy struct jheader * hp 798358659Scy ) 79954359Sroberto{ 800132451Sroberto u_int len, size; 801280849Scy ssize_t cc; 802132451Sroberto u_short *sp; 80354359Sroberto static char errstr[132]; 80454359Sroberto 80554359Sroberto size = sizeof(*hp); 80654359Sroberto hp->hsum = putshort(jupiter_cksum((u_short *)hp, 80754359Sroberto (size / sizeof(u_short)) - 1)); 80854359Sroberto len = getshort(hp->len); 80954359Sroberto if (len > 0) { 81054359Sroberto sp = (u_short *)(hp + 1); 81154359Sroberto sp[len] = putshort(jupiter_cksum(sp, len)); 81254359Sroberto size += (len + 1) * sizeof(u_short); 81354359Sroberto } 81454359Sroberto 815358659Scy if ((cc = write(up->peer->procptr->io.fd, (char *)hp, size)) < 0) { 816280849Scy msnprintf(errstr, sizeof(errstr), "write: %m"); 81754359Sroberto return (errstr); 818280849Scy } else if (cc != (int)size) { 819280849Scy snprintf(errstr, sizeof(errstr), "short write (%zd != %u)", cc, size); 82054359Sroberto return (errstr); 82154359Sroberto } 82254359Sroberto return (NULL); 82354359Sroberto} 82454359Sroberto 82554359Sroberto/* Request periodic message output */ 82654359Srobertostatic struct { 82754359Sroberto struct jheader jheader; 82854359Sroberto struct jrequest jrequest; 82954359Sroberto} reqmsg = { 83054359Sroberto { putshort(JUPITER_SYNC), 0, 83154359Sroberto putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1), 832182007Sroberto 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | 833182007Sroberto JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 }, 83454359Sroberto { 0, 0, 0, 0 } 83554359Sroberto}; 83654359Sroberto 83754359Sroberto/* An interval of zero means to output on trigger */ 83854359Srobertostatic void 839358659Scyjupiter_reqmsg( 840358659Scy struct instance * const up, 841358659Scy u_int id, 842358659Scy u_int interval 843358659Scy ) 84454359Sroberto{ 845132451Sroberto struct jheader *hp; 846132451Sroberto struct jrequest *rp; 847132451Sroberto char *cp; 84854359Sroberto 84954359Sroberto hp = &reqmsg.jheader; 85054359Sroberto hp->id = putshort(id); 85154359Sroberto rp = &reqmsg.jrequest; 85254359Sroberto rp->trigger = putshort(interval == 0); 85354359Sroberto rp->interval = putshort(interval); 854358659Scy if ((cp = jupiter_send(up, hp)) != NULL) 855358659Scy jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 85654359Sroberto} 85754359Sroberto 85854359Sroberto/* Cancel periodic message output */ 85954359Srobertostatic struct jheader canmsg = { 86054359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 861182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC, 86254359Sroberto 0 86354359Sroberto}; 86454359Sroberto 86554359Srobertostatic void 866358659Scyjupiter_canmsg( 867358659Scy struct instance * const up, 868358659Scy u_int id 869358659Scy ) 87054359Sroberto{ 871132451Sroberto struct jheader *hp; 872132451Sroberto char *cp; 87354359Sroberto 87454359Sroberto hp = &canmsg; 87554359Sroberto hp->id = putshort(id); 876358659Scy if ((cp = jupiter_send(up, hp)) != NULL) 877358659Scy jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 87854359Sroberto} 87954359Sroberto 88054359Sroberto/* Request a single message output */ 88154359Srobertostatic struct jheader reqonemsg = { 88254359Sroberto putshort(JUPITER_SYNC), 0, 0, 0, 883182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY, 88454359Sroberto 0 88554359Sroberto}; 88654359Sroberto 88754359Srobertostatic void 888358659Scyjupiter_reqonemsg( 889358659Scy struct instance * const up, 890358659Scy u_int id 891358659Scy ) 89254359Sroberto{ 893132451Sroberto struct jheader *hp; 894132451Sroberto char *cp; 89554359Sroberto 89654359Sroberto hp = &reqonemsg; 89754359Sroberto hp->id = putshort(id); 898358659Scy if ((cp = jupiter_send(up, hp)) != NULL) 899358659Scy jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 90054359Sroberto} 90154359Sroberto 90254359Sroberto/* Set the platform dynamics */ 90354359Srobertostatic struct { 90454359Sroberto struct jheader jheader; 90554359Sroberto struct jplat jplat; 90654359Sroberto} platmsg = { 90754359Sroberto { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT), 90854359Sroberto putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0, 909182007Sroberto JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 }, 91054359Sroberto { 0, 0, 0 } 91154359Sroberto}; 91254359Sroberto 91354359Srobertostatic void 914358659Scyjupiter_platform( 915358659Scy struct instance * const up, 916358659Scy u_int platform 917358659Scy ) 91854359Sroberto{ 919132451Sroberto struct jheader *hp; 920132451Sroberto struct jplat *pp; 921132451Sroberto char *cp; 92254359Sroberto 92354359Sroberto hp = &platmsg.jheader; 92454359Sroberto pp = &platmsg.jplat; 92554359Sroberto pp->platform = putshort(platform); 926358659Scy if ((cp = jupiter_send(up, hp)) != NULL) 927358659Scy jupiter_debug(up->peer, __func__, "%u: %s", platform, cp); 92854359Sroberto} 92954359Sroberto 93054359Sroberto/* Checksum "len" shorts */ 93154359Srobertostatic u_short 932132451Srobertojupiter_cksum(u_short *sp, u_int len) 93354359Sroberto{ 934132451Sroberto u_short sum, x; 93554359Sroberto 93654359Sroberto sum = 0; 93754359Sroberto while (len-- > 0) { 93854359Sroberto x = *sp++; 93954359Sroberto sum += getshort(x); 94054359Sroberto } 94154359Sroberto return (~sum + 1); 94254359Sroberto} 94354359Sroberto 94454359Sroberto/* Return the size of the next message (or zero if we don't have it all yet) */ 94554359Srobertostatic int 946358659Scyjupiter_recv( 947358659Scy struct instance * const up 948358659Scy ) 94954359Sroberto{ 950132451Sroberto int n, len, size, cc; 951132451Sroberto struct jheader *hp; 952132451Sroberto u_char *bp; 953132451Sroberto u_short *sp; 95454359Sroberto 95554359Sroberto /* Must have at least a header's worth */ 95654359Sroberto cc = sizeof(*hp); 957358659Scy size = up->ssize; 95854359Sroberto if (size < cc) 95954359Sroberto return (0); 96054359Sroberto 96154359Sroberto /* Search for the sync short if missing */ 962358659Scy sp = up->sbuf; 96354359Sroberto hp = (struct jheader *)sp; 96454359Sroberto if (getshort(hp->sync) != JUPITER_SYNC) { 96554359Sroberto /* Wasn't at the front, sync up */ 966358659Scy jupiter_debug(up->peer, __func__, "syncing"); 96754359Sroberto bp = (u_char *)sp; 96854359Sroberto n = size; 96954359Sroberto while (n >= 2) { 97054359Sroberto if (bp[0] != (JUPITER_SYNC & 0xff)) { 971132451Sroberto /* 972358659Scy jupiter_debug(up->peer, __func__, 973280849Scy "{0x%x}", bp[0]); 974132451Sroberto */ 97554359Sroberto ++bp; 97654359Sroberto --n; 97754359Sroberto continue; 97854359Sroberto } 97954359Sroberto if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff)) 98054359Sroberto break; 981132451Sroberto /* 982358659Scy jupiter_debug(up->peer, __func__, 983280849Scy "{0x%x 0x%x}", bp[0], bp[1]); 984132451Sroberto */ 98554359Sroberto bp += 2; 98654359Sroberto n -= 2; 98754359Sroberto } 988132451Sroberto /* 989358659Scy jupiter_debug(up->peer, __func__, "\n"); 990132451Sroberto */ 99154359Sroberto /* Shuffle data to front of input buffer */ 99254359Sroberto if (n > 0) 99354359Sroberto memcpy(sp, bp, n); 99454359Sroberto size = n; 995358659Scy up->ssize = size; 99654359Sroberto if (size < cc || hp->sync != JUPITER_SYNC) 99754359Sroberto return (0); 99854359Sroberto } 99954359Sroberto 100054359Sroberto if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) != 100154359Sroberto getshort(hp->hsum)) { 1002358659Scy jupiter_debug(up->peer, __func__, "bad header checksum!"); 100354359Sroberto /* This is drastic but checksum errors should be rare */ 1004358659Scy up->ssize = 0; 100554359Sroberto return (0); 100654359Sroberto } 100754359Sroberto 100854359Sroberto /* Check for a payload */ 100954359Sroberto len = getshort(hp->len); 101054359Sroberto if (len > 0) { 101154359Sroberto n = (len + 1) * sizeof(u_short); 101254359Sroberto /* Not enough data yet */ 101354359Sroberto if (size < cc + n) 101454359Sroberto return (0); 101554359Sroberto 101654359Sroberto /* Check payload checksum */ 101754359Sroberto sp = (u_short *)(hp + 1); 101854359Sroberto if (jupiter_cksum(sp, len) != getshort(sp[len])) { 1019358659Scy jupiter_debug(up->peer, 1020280849Scy __func__, "bad payload checksum!"); 102154359Sroberto /* This is drastic but checksum errors should be rare */ 1022358659Scy up->ssize = 0; 102354359Sroberto return (0); 102454359Sroberto } 102554359Sroberto cc += n; 102654359Sroberto } 102754359Sroberto return (cc); 102854359Sroberto} 102954359Sroberto 1030132451Sroberto#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 103154359Srobertoint refclock_jupiter_bs; 1032132451Sroberto#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 1033