1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * This software was developed by the Computer Systems Engineering group 3c0b746e5SOllivier Robert * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66. 4c0b746e5SOllivier Robert * 5c0b746e5SOllivier Robert * Copyright (c) 1992 The Regents of the University of California. 6c0b746e5SOllivier Robert * All rights reserved. 7c0b746e5SOllivier Robert * 8c0b746e5SOllivier Robert * Redistribution and use in source and binary forms, with or without 9c0b746e5SOllivier Robert * modification, are permitted provided that the following conditions 10c0b746e5SOllivier Robert * are met: 11c0b746e5SOllivier Robert * 1. Redistributions of source code must retain the above copyright 12c0b746e5SOllivier Robert * notice, this list of conditions and the following disclaimer. 13c0b746e5SOllivier Robert * 2. Redistributions in binary form must reproduce the above copyright 14c0b746e5SOllivier Robert * notice, this list of conditions and the following disclaimer in the 15c0b746e5SOllivier Robert * documentation and/or other materials provided with the distribution. 16c0b746e5SOllivier Robert * 3. All advertising materials mentioning features or use of this software 17c0b746e5SOllivier Robert * must display the following acknowledgement: 18c0b746e5SOllivier Robert * This product includes software developed by the University of 19c0b746e5SOllivier Robert * California, Lawrence Berkeley Laboratory. 20c0b746e5SOllivier Robert * 4. The name of the University may not be used to endorse or promote 21c0b746e5SOllivier Robert * products derived from this software without specific prior 22c0b746e5SOllivier Robert * written permission. 23c0b746e5SOllivier Robert * 24c0b746e5SOllivier Robert * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25c0b746e5SOllivier Robert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26c0b746e5SOllivier Robert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27c0b746e5SOllivier Robert * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28c0b746e5SOllivier Robert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29c0b746e5SOllivier Robert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30c0b746e5SOllivier Robert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31c0b746e5SOllivier Robert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32c0b746e5SOllivier Robert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33c0b746e5SOllivier Robert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34c0b746e5SOllivier Robert * SUCH DAMAGE. 35c0b746e5SOllivier Robert */ 36c0b746e5SOllivier Robert 37c0b746e5SOllivier Robert /* 38c0b746e5SOllivier Robert * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999. 39c0b746e5SOllivier Robert * 40c0b746e5SOllivier Robert * 1. Added support for alternate PPS schemes, with code mostly 41c0b746e5SOllivier Robert * copied from the Oncore driver (Thanks, Poul-Henning Kamp). 42c0b746e5SOllivier Robert * This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7. 43c0b746e5SOllivier Robert */ 44c0b746e5SOllivier Robert 45c0b746e5SOllivier Robert 46c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 47c0b746e5SOllivier Robert # include <config.h> 48c0b746e5SOllivier Robert #endif 49c0b746e5SOllivier Robert 50224ba2bdSOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI) 51c0b746e5SOllivier Robert 52c0b746e5SOllivier Robert #include "ntpd.h" 53c0b746e5SOllivier Robert #include "ntp_io.h" 54c0b746e5SOllivier Robert #include "ntp_refclock.h" 55c0b746e5SOllivier Robert #include "ntp_unixtime.h" 56c0b746e5SOllivier Robert #include "ntp_stdlib.h" 57c0b746e5SOllivier Robert 58224ba2bdSOllivier Robert #include <stdio.h> 59224ba2bdSOllivier Robert #include <ctype.h> 60224ba2bdSOllivier Robert 61c0b746e5SOllivier Robert #include "mx4200.h" 62c0b746e5SOllivier Robert 63c0b746e5SOllivier Robert #ifdef HAVE_SYS_TERMIOS_H 64c0b746e5SOllivier Robert # include <sys/termios.h> 65c0b746e5SOllivier Robert #endif 66c0b746e5SOllivier Robert #ifdef HAVE_SYS_PPSCLOCK_H 67c0b746e5SOllivier Robert # include <sys/ppsclock.h> 68c0b746e5SOllivier Robert #endif 69c0b746e5SOllivier Robert 70c0b746e5SOllivier Robert #ifndef HAVE_STRUCT_PPSCLOCKEV 71c0b746e5SOllivier Robert struct ppsclockev { 72224ba2bdSOllivier Robert # ifdef HAVE_STRUCT_TIMESPEC 73c0b746e5SOllivier Robert struct timespec tv; 74c0b746e5SOllivier Robert # else 75c0b746e5SOllivier Robert struct timeval tv; 76c0b746e5SOllivier Robert # endif 77c0b746e5SOllivier Robert u_int serial; 78c0b746e5SOllivier Robert }; 79c0b746e5SOllivier Robert #endif /* ! HAVE_STRUCT_PPSCLOCKEV */ 80c0b746e5SOllivier Robert 81ea906c41SOllivier Robert #ifdef HAVE_PPSAPI 82ea906c41SOllivier Robert # include "ppsapi_timepps.h" 83ea906c41SOllivier Robert #endif /* HAVE_PPSAPI */ 84224ba2bdSOllivier Robert 85c0b746e5SOllivier Robert /* 86c0b746e5SOllivier Robert * This driver supports the Magnavox Model MX 4200 GPS Receiver 87c0b746e5SOllivier Robert * adapted to precision timing applications. It requires the 88c0b746e5SOllivier Robert * ppsclock line discipline or streams module described in the 89c0b746e5SOllivier Robert * Line Disciplines and Streams Drivers page. It also requires a 90c0b746e5SOllivier Robert * gadget box and 1-PPS level converter, such as described in the 91c0b746e5SOllivier Robert * Pulse-per-second (PPS) Signal Interfacing page. 92c0b746e5SOllivier Robert * 93c0b746e5SOllivier Robert * It's likely that other compatible Magnavox receivers such as the 94c0b746e5SOllivier Robert * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code. 95c0b746e5SOllivier Robert */ 96c0b746e5SOllivier Robert 97c0b746e5SOllivier Robert /* 98c0b746e5SOllivier Robert * Check this every time you edit the code! 99c0b746e5SOllivier Robert */ 100224ba2bdSOllivier Robert #define YEAR_LAST_MODIFIED 2000 101c0b746e5SOllivier Robert 102c0b746e5SOllivier Robert /* 103c0b746e5SOllivier Robert * GPS Definitions 104c0b746e5SOllivier Robert */ 105c0b746e5SOllivier Robert #define DEVICE "/dev/gps%d" /* device name and unit */ 106c0b746e5SOllivier Robert #define SPEED232 B4800 /* baud */ 107c0b746e5SOllivier Robert 108c0b746e5SOllivier Robert /* 109c0b746e5SOllivier Robert * Radio interface parameters 110c0b746e5SOllivier Robert */ 111c0b746e5SOllivier Robert #define PRECISION (-18) /* precision assumed (about 4 us) */ 112c0b746e5SOllivier Robert #define REFID "GPS\0" /* reference id */ 113c0b746e5SOllivier Robert #define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */ 114c0b746e5SOllivier Robert #define DEFFUDGETIME 0 /* default fudge time (ms) */ 115c0b746e5SOllivier Robert 116c0b746e5SOllivier Robert #define SLEEPTIME 32 /* seconds to wait for reconfig to complete */ 117c0b746e5SOllivier Robert 118c0b746e5SOllivier Robert /* 119c0b746e5SOllivier Robert * Position Averaging. 120c0b746e5SOllivier Robert */ 121c0b746e5SOllivier Robert #define INTERVAL 1 /* Interval between position measurements (s) */ 122c0b746e5SOllivier Robert #define AVGING_TIME 24 /* Number of hours to average */ 123c0b746e5SOllivier Robert #define NOT_INITIALIZED -9999. /* initial pivot longitude */ 124c0b746e5SOllivier Robert 125c0b746e5SOllivier Robert /* 126c0b746e5SOllivier Robert * MX4200 unit control structure. 127c0b746e5SOllivier Robert */ 128c0b746e5SOllivier Robert struct mx4200unit { 129c0b746e5SOllivier Robert u_int pollcnt; /* poll message counter */ 130c0b746e5SOllivier Robert u_int polled; /* Hand in a time sample? */ 131c0b746e5SOllivier Robert u_int lastserial; /* last pps serial number */ 132c0b746e5SOllivier Robert struct ppsclockev ppsev; /* PPS control structure */ 133c0b746e5SOllivier Robert double avg_lat; /* average latitude */ 134c0b746e5SOllivier Robert double avg_lon; /* average longitude */ 135c0b746e5SOllivier Robert double avg_alt; /* average height */ 136c0b746e5SOllivier Robert double central_meridian; /* central meridian */ 137224ba2bdSOllivier Robert double N_fixes; /* Number of position measurements */ 138c0b746e5SOllivier Robert int last_leap; /* leap second warning */ 139c0b746e5SOllivier Robert u_int moving; /* mobile platform? */ 140c0b746e5SOllivier Robert u_long sloppyclockflag; /* fudge flags */ 141c0b746e5SOllivier Robert u_int known; /* position known yet? */ 142c0b746e5SOllivier Robert u_long clamp_time; /* when to stop postion averaging */ 143c0b746e5SOllivier Robert u_long log_time; /* when to print receiver status */ 144224ba2bdSOllivier Robert pps_handle_t pps_h; 145224ba2bdSOllivier Robert pps_params_t pps_p; 146224ba2bdSOllivier Robert pps_info_t pps_i; 147c0b746e5SOllivier Robert }; 148c0b746e5SOllivier Robert 149c0b746e5SOllivier Robert static char pmvxg[] = "PMVXG"; 150c0b746e5SOllivier Robert 151c0b746e5SOllivier Robert /* XXX should be somewhere else */ 152c0b746e5SOllivier Robert #ifdef __GNUC__ 153c0b746e5SOllivier Robert #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) 154c0b746e5SOllivier Robert #ifndef __attribute__ 155c0b746e5SOllivier Robert #define __attribute__(args) 156a151a66cSOllivier Robert #endif /* __attribute__ */ 157a151a66cSOllivier Robert #endif /* __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */ 158c0b746e5SOllivier Robert #else 159c0b746e5SOllivier Robert #ifndef __attribute__ 160c0b746e5SOllivier Robert #define __attribute__(args) 161a151a66cSOllivier Robert #endif /* __attribute__ */ 162a151a66cSOllivier Robert #endif /* __GNUC__ */ 163c0b746e5SOllivier Robert /* XXX end */ 164c0b746e5SOllivier Robert 165c0b746e5SOllivier Robert /* 166c0b746e5SOllivier Robert * Function prototypes 167c0b746e5SOllivier Robert */ 1682b15cb3dSCy Schubert static int mx4200_start (int, struct peer *); 1692b15cb3dSCy Schubert static void mx4200_shutdown (int, struct peer *); 1702b15cb3dSCy Schubert static void mx4200_receive (struct recvbuf *); 1712b15cb3dSCy Schubert static void mx4200_poll (int, struct peer *); 172c0b746e5SOllivier Robert 1732b15cb3dSCy Schubert static char * mx4200_parse_t (struct peer *); 1742b15cb3dSCy Schubert static char * mx4200_parse_p (struct peer *); 1752b15cb3dSCy Schubert static char * mx4200_parse_s (struct peer *); 1762b15cb3dSCy Schubert int mx4200_cmpl_fp (const void *, const void *); 1772b15cb3dSCy Schubert static int mx4200_config (struct peer *); 1782b15cb3dSCy Schubert static void mx4200_ref (struct peer *); 1792b15cb3dSCy Schubert static void mx4200_send (struct peer *, char *, ...) 180c0b746e5SOllivier Robert __attribute__ ((format (printf, 2, 3))); 1812b15cb3dSCy Schubert static u_char mx4200_cksum (char *, int); 1822b15cb3dSCy Schubert static int mx4200_jday (int, int, int); 1832b15cb3dSCy Schubert static void mx4200_debug (struct peer *, char *, ...) 184c0b746e5SOllivier Robert __attribute__ ((format (printf, 2, 3))); 1852b15cb3dSCy Schubert static int mx4200_pps (struct peer *); 186c0b746e5SOllivier Robert 187c0b746e5SOllivier Robert /* 188c0b746e5SOllivier Robert * Transfer vector 189c0b746e5SOllivier Robert */ 190c0b746e5SOllivier Robert struct refclock refclock_mx4200 = { 191c0b746e5SOllivier Robert mx4200_start, /* start up driver */ 192c0b746e5SOllivier Robert mx4200_shutdown, /* shut down driver */ 193c0b746e5SOllivier Robert mx4200_poll, /* transmit poll message */ 194c0b746e5SOllivier Robert noentry, /* not used (old mx4200_control) */ 195c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 196c0b746e5SOllivier Robert noentry, /* not used (old mx4200_buginfo) */ 197c0b746e5SOllivier Robert NOFLAGS /* not used */ 198c0b746e5SOllivier Robert }; 199c0b746e5SOllivier Robert 200c0b746e5SOllivier Robert 201c0b746e5SOllivier Robert 202c0b746e5SOllivier Robert /* 203c0b746e5SOllivier Robert * mx4200_start - open the devices and initialize data for processing 204c0b746e5SOllivier Robert */ 205c0b746e5SOllivier Robert static int 206c0b746e5SOllivier Robert mx4200_start( 207c0b746e5SOllivier Robert int unit, 208c0b746e5SOllivier Robert struct peer *peer 209c0b746e5SOllivier Robert ) 210c0b746e5SOllivier Robert { 211c0b746e5SOllivier Robert register struct mx4200unit *up; 212c0b746e5SOllivier Robert struct refclockproc *pp; 213c0b746e5SOllivier Robert int fd; 214c0b746e5SOllivier Robert char gpsdev[20]; 215c0b746e5SOllivier Robert 216c0b746e5SOllivier Robert /* 217c0b746e5SOllivier Robert * Open serial port 218c0b746e5SOllivier Robert */ 2192b15cb3dSCy Schubert snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 220a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_PPS); 2212b15cb3dSCy Schubert if (fd <= 0) 2222b15cb3dSCy Schubert return 0; 223c0b746e5SOllivier Robert 224c0b746e5SOllivier Robert /* 225c0b746e5SOllivier Robert * Allocate unit structure 226c0b746e5SOllivier Robert */ 2272b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 228c0b746e5SOllivier Robert pp = peer->procptr; 229c0b746e5SOllivier Robert pp->io.clock_recv = mx4200_receive; 2302b15cb3dSCy Schubert pp->io.srcclock = peer; 231c0b746e5SOllivier Robert pp->io.datalen = 0; 232c0b746e5SOllivier Robert pp->io.fd = fd; 233c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 2342b15cb3dSCy Schubert close(fd); 2352b15cb3dSCy Schubert pp->io.fd = -1; 236c0b746e5SOllivier Robert free(up); 237c0b746e5SOllivier Robert return (0); 238c0b746e5SOllivier Robert } 2392b15cb3dSCy Schubert pp->unitptr = up; 240c0b746e5SOllivier Robert 241c0b746e5SOllivier Robert /* 242c0b746e5SOllivier Robert * Initialize miscellaneous variables 243c0b746e5SOllivier Robert */ 244c0b746e5SOllivier Robert peer->precision = PRECISION; 245c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 246c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 247c0b746e5SOllivier Robert 248c0b746e5SOllivier Robert /* Ensure the receiver is properly configured */ 249224ba2bdSOllivier Robert return mx4200_config(peer); 250c0b746e5SOllivier Robert } 251c0b746e5SOllivier Robert 252c0b746e5SOllivier Robert 253c0b746e5SOllivier Robert /* 254c0b746e5SOllivier Robert * mx4200_shutdown - shut down the clock 255c0b746e5SOllivier Robert */ 256c0b746e5SOllivier Robert static void 257c0b746e5SOllivier Robert mx4200_shutdown( 258c0b746e5SOllivier Robert int unit, 259c0b746e5SOllivier Robert struct peer *peer 260c0b746e5SOllivier Robert ) 261c0b746e5SOllivier Robert { 262c0b746e5SOllivier Robert register struct mx4200unit *up; 263c0b746e5SOllivier Robert struct refclockproc *pp; 264c0b746e5SOllivier Robert 265c0b746e5SOllivier Robert pp = peer->procptr; 2662b15cb3dSCy Schubert up = pp->unitptr; 2672b15cb3dSCy Schubert if (-1 != pp->io.fd) 268c0b746e5SOllivier Robert io_closeclock(&pp->io); 2692b15cb3dSCy Schubert if (NULL != up) 270c0b746e5SOllivier Robert free(up); 271c0b746e5SOllivier Robert } 272c0b746e5SOllivier Robert 273c0b746e5SOllivier Robert 274c0b746e5SOllivier Robert /* 275c0b746e5SOllivier Robert * mx4200_config - Configure the receiver 276c0b746e5SOllivier Robert */ 277224ba2bdSOllivier Robert static int 278c0b746e5SOllivier Robert mx4200_config( 279c0b746e5SOllivier Robert struct peer *peer 280c0b746e5SOllivier Robert ) 281c0b746e5SOllivier Robert { 282c0b746e5SOllivier Robert char tr_mode; 283c0b746e5SOllivier Robert int add_mode; 284c0b746e5SOllivier Robert register struct mx4200unit *up; 285c0b746e5SOllivier Robert struct refclockproc *pp; 286224ba2bdSOllivier Robert int mode; 287c0b746e5SOllivier Robert 288c0b746e5SOllivier Robert pp = peer->procptr; 2892b15cb3dSCy Schubert up = pp->unitptr; 290c0b746e5SOllivier Robert 291c0b746e5SOllivier Robert /* 292c0b746e5SOllivier Robert * Initialize the unit variables 293c0b746e5SOllivier Robert * 294c0b746e5SOllivier Robert * STRANGE BEHAVIOUR WARNING: The fudge flags are not available 295c0b746e5SOllivier Robert * at the time mx4200_start is called. These are set later, 296c0b746e5SOllivier Robert * and so the code must be prepared to handle changing flags. 297c0b746e5SOllivier Robert */ 298c0b746e5SOllivier Robert up->sloppyclockflag = pp->sloppyclockflag; 299c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG2) { 300c0b746e5SOllivier Robert up->moving = 1; /* Receiver on mobile platform */ 301c0b746e5SOllivier Robert msyslog(LOG_DEBUG, "mx4200_config: mobile platform"); 302c0b746e5SOllivier Robert } else { 303c0b746e5SOllivier Robert up->moving = 0; /* Static Installation */ 304c0b746e5SOllivier Robert } 305c0b746e5SOllivier Robert up->pollcnt = 2; 306c0b746e5SOllivier Robert up->polled = 0; 307c0b746e5SOllivier Robert up->known = 0; 308c0b746e5SOllivier Robert up->avg_lat = 0.0; 309c0b746e5SOllivier Robert up->avg_lon = 0.0; 310c0b746e5SOllivier Robert up->avg_alt = 0.0; 311c0b746e5SOllivier Robert up->central_meridian = NOT_INITIALIZED; 312224ba2bdSOllivier Robert up->N_fixes = 0.0; 313c0b746e5SOllivier Robert up->last_leap = 0; /* LEAP_NOWARNING */ 314c0b746e5SOllivier Robert up->clamp_time = current_time + (AVGING_TIME * 60 * 60); 315c0b746e5SOllivier Robert up->log_time = current_time + SLEEPTIME; 316c0b746e5SOllivier Robert 317224ba2bdSOllivier Robert if (time_pps_create(pp->io.fd, &up->pps_h) < 0) { 318224ba2bdSOllivier Robert perror("time_pps_create"); 319224ba2bdSOllivier Robert msyslog(LOG_ERR, 320224ba2bdSOllivier Robert "mx4200_config: time_pps_create failed: %m"); 321224ba2bdSOllivier Robert return (0); 322224ba2bdSOllivier Robert } 323224ba2bdSOllivier Robert if (time_pps_getcap(up->pps_h, &mode) < 0) { 324224ba2bdSOllivier Robert msyslog(LOG_ERR, 325224ba2bdSOllivier Robert "mx4200_config: time_pps_getcap failed: %m"); 326224ba2bdSOllivier Robert return (0); 327224ba2bdSOllivier Robert } 328224ba2bdSOllivier Robert 329224ba2bdSOllivier Robert if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) { 330224ba2bdSOllivier Robert msyslog(LOG_ERR, 331224ba2bdSOllivier Robert "mx4200_config: time_pps_getparams failed: %m"); 332224ba2bdSOllivier Robert return (0); 333224ba2bdSOllivier Robert } 334224ba2bdSOllivier Robert 335224ba2bdSOllivier Robert /* nb. only turn things on, if someone else has turned something 336224ba2bdSOllivier Robert * on before we get here, leave it alone! 337224ba2bdSOllivier Robert */ 338224ba2bdSOllivier Robert 339224ba2bdSOllivier Robert up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; 340224ba2bdSOllivier Robert up->pps_p.mode &= mode; /* only set what is legal */ 341224ba2bdSOllivier Robert 342224ba2bdSOllivier Robert if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) { 343224ba2bdSOllivier Robert perror("time_pps_setparams"); 344224ba2bdSOllivier Robert msyslog(LOG_ERR, 345224ba2bdSOllivier Robert "mx4200_config: time_pps_setparams failed: %m"); 346224ba2bdSOllivier Robert exit(1); 347224ba2bdSOllivier Robert } 348224ba2bdSOllivier Robert 349224ba2bdSOllivier Robert if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT, 350224ba2bdSOllivier Robert PPS_TSFMT_TSPEC) < 0) { 351224ba2bdSOllivier Robert perror("time_pps_kcbind"); 352224ba2bdSOllivier Robert msyslog(LOG_ERR, 353224ba2bdSOllivier Robert "mx4200_config: time_pps_kcbind failed: %m"); 354224ba2bdSOllivier Robert exit(1); 355224ba2bdSOllivier Robert } 356224ba2bdSOllivier Robert 357224ba2bdSOllivier Robert 358c0b746e5SOllivier Robert /* 359c0b746e5SOllivier Robert * "007" Control Port Configuration 360c0b746e5SOllivier Robert * Zero the output list (do it twice to flush possible junk) 361c0b746e5SOllivier Robert */ 362c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg, 363c0b746e5SOllivier Robert PMVXG_S_PORTCONF, 364c0b746e5SOllivier Robert /* control port output block Label */ 365c0b746e5SOllivier Robert 1); /* clear current output control list (1=yes) */ 366c0b746e5SOllivier Robert /* add/delete sentences from list */ 367c0b746e5SOllivier Robert /* must be null */ 368c0b746e5SOllivier Robert /* sentence output rate (sec) */ 369c0b746e5SOllivier Robert /* precision for position output */ 370c0b746e5SOllivier Robert /* nmea version for cga & gll output */ 371c0b746e5SOllivier Robert /* pass-through control */ 372c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg, 373c0b746e5SOllivier Robert PMVXG_S_PORTCONF, 1); 374c0b746e5SOllivier Robert 375c0b746e5SOllivier Robert /* 376c0b746e5SOllivier Robert * Request software configuration so we can syslog the firmware version 377c0b746e5SOllivier Robert */ 378c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF); 379c0b746e5SOllivier Robert 380c0b746e5SOllivier Robert /* 381c0b746e5SOllivier Robert * "001" Initialization/Mode Control, Part A 382c0b746e5SOllivier Robert * Where ARE we? 383c0b746e5SOllivier Robert */ 384c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg, 385c0b746e5SOllivier Robert PMVXG_S_INITMODEA); 386c0b746e5SOllivier Robert /* day of month */ 387c0b746e5SOllivier Robert /* month of year */ 388c0b746e5SOllivier Robert /* year */ 389c0b746e5SOllivier Robert /* gmt */ 390c0b746e5SOllivier Robert /* latitude DDMM.MMMM */ 391c0b746e5SOllivier Robert /* north/south */ 392c0b746e5SOllivier Robert /* longitude DDDMM.MMMM */ 393c0b746e5SOllivier Robert /* east/west */ 394c0b746e5SOllivier Robert /* height */ 395c0b746e5SOllivier Robert /* Altitude Reference 1=MSL */ 396c0b746e5SOllivier Robert 397c0b746e5SOllivier Robert /* 398c0b746e5SOllivier Robert * "001" Initialization/Mode Control, Part B 399c0b746e5SOllivier Robert * Start off in 2d/3d coast mode, holding altitude to last known 400c0b746e5SOllivier Robert * value if only 3 satellites available. 401c0b746e5SOllivier Robert */ 402c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d", 403c0b746e5SOllivier Robert pmvxg, PMVXG_S_INITMODEB, 404c0b746e5SOllivier Robert 3, /* 2d/3d coast */ 405c0b746e5SOllivier Robert /* reserved */ 406c0b746e5SOllivier Robert 0.1, /* hor accel fact as per Steve (m/s**2) */ 407c0b746e5SOllivier Robert 0.1, /* ver accel fact as per Steve (m/s**2) */ 408c0b746e5SOllivier Robert 10, /* vdop */ 409c0b746e5SOllivier Robert 10, /* hdop limit as per Steve */ 410c0b746e5SOllivier Robert 5, /* elevation limit as per Steve (deg) */ 411c0b746e5SOllivier Robert 'U', /* time output mode (UTC) */ 412c0b746e5SOllivier Robert 0); /* local time offset from gmt (HHHMM) */ 413c0b746e5SOllivier Robert 414c0b746e5SOllivier Robert /* 415c0b746e5SOllivier Robert * "023" Time Recovery Configuration 416c0b746e5SOllivier Robert * Get UTC time from a stationary receiver. 417c0b746e5SOllivier Robert * (Set field 1 'D' == dynamic if we are on a moving platform). 418c0b746e5SOllivier Robert * (Set field 1 'S' == static if we are not moving). 419c0b746e5SOllivier Robert * (Set field 1 'K' == known position if we can initialize lat/lon/alt). 420c0b746e5SOllivier Robert */ 421c0b746e5SOllivier Robert 422c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG2) 423c0b746e5SOllivier Robert up->moving = 1; /* Receiver on mobile platform */ 424c0b746e5SOllivier Robert else 425c0b746e5SOllivier Robert up->moving = 0; /* Static Installation */ 426c0b746e5SOllivier Robert 427c0b746e5SOllivier Robert up->pollcnt = 2; 428c0b746e5SOllivier Robert if (up->moving) { 429c0b746e5SOllivier Robert /* dynamic: solve for pos, alt, time, while moving */ 430c0b746e5SOllivier Robert tr_mode = 'D'; 431c0b746e5SOllivier Robert } else { 432c0b746e5SOllivier Robert /* static: solve for pos, alt, time, while stationary */ 433c0b746e5SOllivier Robert tr_mode = 'S'; 434c0b746e5SOllivier Robert } 435c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg, 436c0b746e5SOllivier Robert PMVXG_S_TRECOVCONF, 437c0b746e5SOllivier Robert tr_mode, /* time recovery mode (see above ) */ 438c0b746e5SOllivier Robert 'U', /* synchronize to UTC */ 439c0b746e5SOllivier Robert 'A', /* always output a time pulse */ 440c0b746e5SOllivier Robert 500, /* max time error in ns */ 441c0b746e5SOllivier Robert 0, /* user bias in ns */ 442c0b746e5SOllivier Robert 1); /* output "830" sentences to control port */ 443c0b746e5SOllivier Robert /* Multi-satellite mode */ 444c0b746e5SOllivier Robert 445c0b746e5SOllivier Robert /* 446c0b746e5SOllivier Robert * Output position information (to calculate fixed installation 447c0b746e5SOllivier Robert * location) only if we are not moving 448c0b746e5SOllivier Robert */ 449c0b746e5SOllivier Robert if (up->moving) { 450c0b746e5SOllivier Robert add_mode = 2; /* delete from list */ 451c0b746e5SOllivier Robert } else { 452c0b746e5SOllivier Robert add_mode = 1; /* add to list */ 453c0b746e5SOllivier Robert } 454c0b746e5SOllivier Robert 455c0b746e5SOllivier Robert 456c0b746e5SOllivier Robert /* 457c0b746e5SOllivier Robert * "007" Control Port Configuration 458c0b746e5SOllivier Robert * Output "021" position, height, velocity reports 459c0b746e5SOllivier Robert */ 460c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg, 461c0b746e5SOllivier Robert PMVXG_S_PORTCONF, 462c0b746e5SOllivier Robert PMVXG_D_PHV, /* control port output block Label */ 463c0b746e5SOllivier Robert 0, /* clear current output control list (0=no) */ 464c0b746e5SOllivier Robert add_mode, /* add/delete sentences from list (1=add, 2=del) */ 465c0b746e5SOllivier Robert /* must be null */ 466c0b746e5SOllivier Robert INTERVAL); /* sentence output rate (sec) */ 467c0b746e5SOllivier Robert /* precision for position output */ 468c0b746e5SOllivier Robert /* nmea version for cga & gll output */ 469c0b746e5SOllivier Robert /* pass-through control */ 470224ba2bdSOllivier Robert 471224ba2bdSOllivier Robert return (1); 472c0b746e5SOllivier Robert } 473c0b746e5SOllivier Robert 474c0b746e5SOllivier Robert /* 475c0b746e5SOllivier Robert * mx4200_ref - Reconfigure unit as a reference station at a known position. 476c0b746e5SOllivier Robert */ 477c0b746e5SOllivier Robert static void 478c0b746e5SOllivier Robert mx4200_ref( 479c0b746e5SOllivier Robert struct peer *peer 480c0b746e5SOllivier Robert ) 481c0b746e5SOllivier Robert { 482c0b746e5SOllivier Robert register struct mx4200unit *up; 483c0b746e5SOllivier Robert struct refclockproc *pp; 484c0b746e5SOllivier Robert double minute, lat, lon, alt; 485c0b746e5SOllivier Robert char lats[16], lons[16]; 486c0b746e5SOllivier Robert char nsc, ewc; 487c0b746e5SOllivier Robert 488c0b746e5SOllivier Robert pp = peer->procptr; 4892b15cb3dSCy Schubert up = pp->unitptr; 490c0b746e5SOllivier Robert 491c0b746e5SOllivier Robert /* Should never happen! */ 492c0b746e5SOllivier Robert if (up->moving) return; 493c0b746e5SOllivier Robert 494c0b746e5SOllivier Robert /* 495c0b746e5SOllivier Robert * Set up to output status information in the near future 496c0b746e5SOllivier Robert */ 497c0b746e5SOllivier Robert up->log_time = current_time + SLEEPTIME; 498c0b746e5SOllivier Robert 499c0b746e5SOllivier Robert /* 500c0b746e5SOllivier Robert * "007" Control Port Configuration 501c0b746e5SOllivier Robert * Stop outputting "021" position, height, velocity reports 502c0b746e5SOllivier Robert */ 503c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg, 504c0b746e5SOllivier Robert PMVXG_S_PORTCONF, 505c0b746e5SOllivier Robert PMVXG_D_PHV, /* control port output block Label */ 506c0b746e5SOllivier Robert 0, /* clear current output control list (0=no) */ 507c0b746e5SOllivier Robert 2); /* add/delete sentences from list (2=delete) */ 508c0b746e5SOllivier Robert /* must be null */ 509c0b746e5SOllivier Robert /* sentence output rate (sec) */ 510c0b746e5SOllivier Robert /* precision for position output */ 511c0b746e5SOllivier Robert /* nmea version for cga & gll output */ 512c0b746e5SOllivier Robert /* pass-through control */ 513c0b746e5SOllivier Robert 514c0b746e5SOllivier Robert /* 515c0b746e5SOllivier Robert * "001" Initialization/Mode Control, Part B 516c0b746e5SOllivier Robert * Put receiver in fully-constrained 2d nav mode 517c0b746e5SOllivier Robert */ 518c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d", 519c0b746e5SOllivier Robert pmvxg, PMVXG_S_INITMODEB, 520c0b746e5SOllivier Robert 2, /* 2d nav */ 521c0b746e5SOllivier Robert /* reserved */ 522c0b746e5SOllivier Robert 0.1, /* hor accel fact as per Steve (m/s**2) */ 523c0b746e5SOllivier Robert 0.1, /* ver accel fact as per Steve (m/s**2) */ 524c0b746e5SOllivier Robert 10, /* vdop */ 525c0b746e5SOllivier Robert 10, /* hdop limit as per Steve */ 526c0b746e5SOllivier Robert 5, /* elevation limit as per Steve (deg) */ 527c0b746e5SOllivier Robert 'U', /* time output mode (UTC) */ 528c0b746e5SOllivier Robert 0); /* local time offset from gmt (HHHMM) */ 529c0b746e5SOllivier Robert 530c0b746e5SOllivier Robert /* 531c0b746e5SOllivier Robert * "023" Time Recovery Configuration 532c0b746e5SOllivier Robert * Get UTC time from a stationary receiver. Solve for time only. 533c0b746e5SOllivier Robert * This should improve the time resolution dramatically. 534c0b746e5SOllivier Robert */ 535c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg, 536c0b746e5SOllivier Robert PMVXG_S_TRECOVCONF, 537c0b746e5SOllivier Robert 'K', /* known position: solve for time only */ 538c0b746e5SOllivier Robert 'U', /* synchronize to UTC */ 539c0b746e5SOllivier Robert 'A', /* always output a time pulse */ 540c0b746e5SOllivier Robert 500, /* max time error in ns */ 541c0b746e5SOllivier Robert 0, /* user bias in ns */ 542c0b746e5SOllivier Robert 1); /* output "830" sentences to control port */ 543c0b746e5SOllivier Robert /* Multi-satellite mode */ 544c0b746e5SOllivier Robert 545c0b746e5SOllivier Robert /* 546c0b746e5SOllivier Robert * "000" Initialization/Mode Control - Part A 547c0b746e5SOllivier Robert * Fix to our averaged position. 548c0b746e5SOllivier Robert */ 549c0b746e5SOllivier Robert if (up->central_meridian != NOT_INITIALIZED) { 550c0b746e5SOllivier Robert up->avg_lon += up->central_meridian; 551c0b746e5SOllivier Robert if (up->avg_lon < -180.0) up->avg_lon += 360.0; 552c0b746e5SOllivier Robert if (up->avg_lon > 180.0) up->avg_lon -= 360.0; 553c0b746e5SOllivier Robert } 554c0b746e5SOllivier Robert 555c0b746e5SOllivier Robert if (up->avg_lat >= 0.0) { 556c0b746e5SOllivier Robert lat = up->avg_lat; 557c0b746e5SOllivier Robert nsc = 'N'; 558c0b746e5SOllivier Robert } else { 559c0b746e5SOllivier Robert lat = up->avg_lat * (-1.0); 560c0b746e5SOllivier Robert nsc = 'S'; 561c0b746e5SOllivier Robert } 562c0b746e5SOllivier Robert if (up->avg_lon >= 0.0) { 563c0b746e5SOllivier Robert lon = up->avg_lon; 564c0b746e5SOllivier Robert ewc = 'E'; 565c0b746e5SOllivier Robert } else { 566c0b746e5SOllivier Robert lon = up->avg_lon * (-1.0); 567c0b746e5SOllivier Robert ewc = 'W'; 568c0b746e5SOllivier Robert } 569c0b746e5SOllivier Robert alt = up->avg_alt; 570c0b746e5SOllivier Robert minute = (lat - (double)(int)lat) * 60.0; 5712b15cb3dSCy Schubert snprintf(lats, sizeof(lats), "%02d%02.4f", (int)lat, minute); 572c0b746e5SOllivier Robert minute = (lon - (double)(int)lon) * 60.0; 5732b15cb3dSCy Schubert snprintf(lons, sizeof(lons), "%03d%02.4f", (int)lon, minute); 574c0b746e5SOllivier Robert 575a151a66cSOllivier Robert mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg, 576c0b746e5SOllivier Robert PMVXG_S_INITMODEA, 577c0b746e5SOllivier Robert /* day of month */ 578c0b746e5SOllivier Robert /* month of year */ 579c0b746e5SOllivier Robert /* year */ 580c0b746e5SOllivier Robert /* gmt */ 581c0b746e5SOllivier Robert lats, /* latitude DDMM.MMMM */ 582c0b746e5SOllivier Robert nsc, /* north/south */ 583c0b746e5SOllivier Robert lons, /* longitude DDDMM.MMMM */ 584c0b746e5SOllivier Robert ewc, /* east/west */ 585a151a66cSOllivier Robert alt, /* Altitude */ 586a151a66cSOllivier Robert 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/ 587c0b746e5SOllivier Robert 588c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 589c0b746e5SOllivier Robert "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m", 590c0b746e5SOllivier Robert lats, nsc, lons, ewc, alt ); 591c0b746e5SOllivier Robert 592c0b746e5SOllivier Robert } 593c0b746e5SOllivier Robert 594c0b746e5SOllivier Robert /* 595c0b746e5SOllivier Robert * mx4200_poll - mx4200 watchdog routine 596c0b746e5SOllivier Robert */ 597c0b746e5SOllivier Robert static void 598c0b746e5SOllivier Robert mx4200_poll( 599c0b746e5SOllivier Robert int unit, 600c0b746e5SOllivier Robert struct peer *peer 601c0b746e5SOllivier Robert ) 602c0b746e5SOllivier Robert { 603c0b746e5SOllivier Robert register struct mx4200unit *up; 604c0b746e5SOllivier Robert struct refclockproc *pp; 605c0b746e5SOllivier Robert 606c0b746e5SOllivier Robert pp = peer->procptr; 6072b15cb3dSCy Schubert up = pp->unitptr; 608c0b746e5SOllivier Robert 609c0b746e5SOllivier Robert /* 610c0b746e5SOllivier Robert * You don't need to poll this clock. It puts out timecodes 611c0b746e5SOllivier Robert * once per second. If asked for a timestamp, take note. 612c0b746e5SOllivier Robert * The next time a timecode comes in, it will be fed back. 613c0b746e5SOllivier Robert */ 614c0b746e5SOllivier Robert 615c0b746e5SOllivier Robert /* 616c0b746e5SOllivier Robert * If we haven't had a response in a while, reset the receiver. 617c0b746e5SOllivier Robert */ 618c0b746e5SOllivier Robert if (up->pollcnt > 0) { 619c0b746e5SOllivier Robert up->pollcnt--; 620c0b746e5SOllivier Robert } else { 621c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 622c0b746e5SOllivier Robert 623c0b746e5SOllivier Robert /* 624c0b746e5SOllivier Robert * Request a "000" status message which should trigger a 625c0b746e5SOllivier Robert * reconfig 626c0b746e5SOllivier Robert */ 627c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d", 628c0b746e5SOllivier Robert "CDGPQ", /* query from CDU to GPS */ 629c0b746e5SOllivier Robert PMVXG_D_STATUS); /* label of desired sentence */ 630c0b746e5SOllivier Robert } 631c0b746e5SOllivier Robert 632c0b746e5SOllivier Robert /* 633c0b746e5SOllivier Robert * polled every 64 seconds. Ask mx4200_receive to hand in 634c0b746e5SOllivier Robert * a timestamp. 635c0b746e5SOllivier Robert */ 636c0b746e5SOllivier Robert up->polled = 1; 637c0b746e5SOllivier Robert pp->polls++; 638c0b746e5SOllivier Robert 639c0b746e5SOllivier Robert /* 640c0b746e5SOllivier Robert * Output receiver status information. 641c0b746e5SOllivier Robert */ 642c0b746e5SOllivier Robert if ((up->log_time > 0) && (current_time > up->log_time)) { 643c0b746e5SOllivier Robert up->log_time = 0; 644c0b746e5SOllivier Robert /* 645c0b746e5SOllivier Robert * Output the following messages once, for debugging. 646c0b746e5SOllivier Robert * "004" Mode Data 647c0b746e5SOllivier Robert * "523" Time Recovery Parameters 648c0b746e5SOllivier Robert */ 649c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA); 650c0b746e5SOllivier Robert mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE); 651c0b746e5SOllivier Robert } 652c0b746e5SOllivier Robert } 653c0b746e5SOllivier Robert 654c0b746e5SOllivier Robert static char char2hex[] = "0123456789ABCDEF"; 655c0b746e5SOllivier Robert 656c0b746e5SOllivier Robert /* 657c0b746e5SOllivier Robert * mx4200_receive - receive gps data 658c0b746e5SOllivier Robert */ 659c0b746e5SOllivier Robert static void 660c0b746e5SOllivier Robert mx4200_receive( 661c0b746e5SOllivier Robert struct recvbuf *rbufp 662c0b746e5SOllivier Robert ) 663c0b746e5SOllivier Robert { 664c0b746e5SOllivier Robert register struct mx4200unit *up; 665c0b746e5SOllivier Robert struct refclockproc *pp; 666c0b746e5SOllivier Robert struct peer *peer; 667c0b746e5SOllivier Robert char *cp; 668c0b746e5SOllivier Robert int sentence_type; 669c0b746e5SOllivier Robert u_char ck; 670c0b746e5SOllivier Robert 671c0b746e5SOllivier Robert /* 672c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp. 673c0b746e5SOllivier Robert */ 6742b15cb3dSCy Schubert peer = rbufp->recv_peer; 675c0b746e5SOllivier Robert pp = peer->procptr; 6762b15cb3dSCy Schubert up = pp->unitptr; 677c0b746e5SOllivier Robert 678c0b746e5SOllivier Robert /* 679c0b746e5SOllivier Robert * If operating mode has been changed, then reinitialize the receiver 680c0b746e5SOllivier Robert * before doing anything else. 681c0b746e5SOllivier Robert */ 682c0b746e5SOllivier Robert if ((pp->sloppyclockflag & CLK_FLAG2) != 683c0b746e5SOllivier Robert (up->sloppyclockflag & CLK_FLAG2)) { 684c0b746e5SOllivier Robert up->sloppyclockflag = pp->sloppyclockflag; 685c0b746e5SOllivier Robert mx4200_debug(peer, 686c0b746e5SOllivier Robert "mx4200_receive: mode switch: reset receiver\n"); 687c0b746e5SOllivier Robert mx4200_config(peer); 688c0b746e5SOllivier Robert return; 689c0b746e5SOllivier Robert } 690c0b746e5SOllivier Robert up->sloppyclockflag = pp->sloppyclockflag; 691c0b746e5SOllivier Robert 692c0b746e5SOllivier Robert /* 693c0b746e5SOllivier Robert * Read clock output. Automatically handles STREAMS, CLKLDISC. 694c0b746e5SOllivier Robert */ 695c0b746e5SOllivier Robert pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 696c0b746e5SOllivier Robert 697c0b746e5SOllivier Robert /* 698c0b746e5SOllivier Robert * There is a case where <cr><lf> generates 2 timestamps. 699c0b746e5SOllivier Robert */ 700c0b746e5SOllivier Robert if (pp->lencode == 0) 701c0b746e5SOllivier Robert return; 702c0b746e5SOllivier Robert 703c0b746e5SOllivier Robert up->pollcnt = 2; 704c0b746e5SOllivier Robert pp->a_lastcode[pp->lencode] = '\0'; 705c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 706c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: %d %s\n", 707c0b746e5SOllivier Robert pp->lencode, pp->a_lastcode); 708c0b746e5SOllivier Robert 709c0b746e5SOllivier Robert /* 710c0b746e5SOllivier Robert * The structure of the control port sentences is based on the 711c0b746e5SOllivier Robert * NMEA-0183 Standard for interfacing Marine Electronics 712c0b746e5SOllivier Robert * Navigation Devices (Version 1.5) 713c0b746e5SOllivier Robert * 714c0b746e5SOllivier Robert * $PMVXG,XXX, ....................*CK<cr><lf> 715c0b746e5SOllivier Robert * 716c0b746e5SOllivier Robert * $ Sentence Start Identifier (reserved char) 717c0b746e5SOllivier Robert * (Start-of-Sentence Identifier) 718c0b746e5SOllivier Robert * P Special ID (Proprietary) 719c0b746e5SOllivier Robert * MVX Originator ID (Magnavox) 720c0b746e5SOllivier Robert * G Interface ID (GPS) 721c0b746e5SOllivier Robert * , Field Delimiters (reserved char) 722c0b746e5SOllivier Robert * XXX Sentence Type 723c0b746e5SOllivier Robert * ...... Data 724c0b746e5SOllivier Robert * * Checksum Field Delimiter (reserved char) 725c0b746e5SOllivier Robert * CK Checksum 726c0b746e5SOllivier Robert * <cr><lf> Carriage-Return/Line Feed (reserved chars) 727c0b746e5SOllivier Robert * (End-of-Sentence Identifier) 728c0b746e5SOllivier Robert * 729c0b746e5SOllivier Robert * Reject if any important landmarks are missing. 730c0b746e5SOllivier Robert */ 731c0b746e5SOllivier Robert cp = pp->a_lastcode + pp->lencode - 3; 732c0b746e5SOllivier Robert if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) { 733c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: bad format\n"); 734c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 735c0b746e5SOllivier Robert return; 736c0b746e5SOllivier Robert } 737c0b746e5SOllivier Robert 738c0b746e5SOllivier Robert /* 739c0b746e5SOllivier Robert * Check and discard the checksum 740c0b746e5SOllivier Robert */ 741c0b746e5SOllivier Robert ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4); 742c0b746e5SOllivier Robert if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) { 743c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: bad checksum\n"); 744c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 745c0b746e5SOllivier Robert return; 746c0b746e5SOllivier Robert } 747c0b746e5SOllivier Robert *cp = '\0'; 748c0b746e5SOllivier Robert 749c0b746e5SOllivier Robert /* 750c0b746e5SOllivier Robert * Get the sentence type. 751c0b746e5SOllivier Robert */ 752c0b746e5SOllivier Robert sentence_type = 0; 753c0b746e5SOllivier Robert if ((cp = strchr(pp->a_lastcode, ',')) == NULL) { 754c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: no sentence\n"); 755c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 756c0b746e5SOllivier Robert return; 757c0b746e5SOllivier Robert } 758c0b746e5SOllivier Robert cp++; 759c0b746e5SOllivier Robert sentence_type = strtol(cp, &cp, 10); 760c0b746e5SOllivier Robert 761c0b746e5SOllivier Robert /* 762224ba2bdSOllivier Robert * Process the sentence according to its type. 763224ba2bdSOllivier Robert */ 764224ba2bdSOllivier Robert switch (sentence_type) { 765224ba2bdSOllivier Robert 766224ba2bdSOllivier Robert /* 767c0b746e5SOllivier Robert * "000" Status message 768c0b746e5SOllivier Robert */ 769224ba2bdSOllivier Robert case PMVXG_D_STATUS: 770c0b746e5SOllivier Robert /* 771c0b746e5SOllivier Robert * XXX 772c0b746e5SOllivier Robert * Since we configure the receiver to not give us status 773c0b746e5SOllivier Robert * messages and since the receiver outputs status messages by 774c0b746e5SOllivier Robert * default after being reset to factory defaults when sent the 775c0b746e5SOllivier Robert * "$PMVXG,018,C\r\n" message, any status message we get 776c0b746e5SOllivier Robert * indicates the reciever needs to be initialized; thus, it is 777c0b746e5SOllivier Robert * not necessary to decode the status message. 778c0b746e5SOllivier Robert */ 779c0b746e5SOllivier Robert if ((cp = mx4200_parse_s(peer)) != NULL) { 780c0b746e5SOllivier Robert mx4200_debug(peer, 781c0b746e5SOllivier Robert "mx4200_receive: status: %s\n", cp); 782c0b746e5SOllivier Robert } 783c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: reset receiver\n"); 784c0b746e5SOllivier Robert mx4200_config(peer); 785224ba2bdSOllivier Robert break; 786c0b746e5SOllivier Robert 787c0b746e5SOllivier Robert /* 788c0b746e5SOllivier Robert * "021" Position, Height, Velocity message, 789c0b746e5SOllivier Robert * if we are still averaging our position 790c0b746e5SOllivier Robert */ 791224ba2bdSOllivier Robert case PMVXG_D_PHV: 792224ba2bdSOllivier Robert if (!up->known) { 793c0b746e5SOllivier Robert /* 794c0b746e5SOllivier Robert * Parse the message, calculating our averaged position. 795c0b746e5SOllivier Robert */ 796c0b746e5SOllivier Robert if ((cp = mx4200_parse_p(peer)) != NULL) { 797c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp); 798c0b746e5SOllivier Robert return; 799c0b746e5SOllivier Robert } 800c0b746e5SOllivier Robert mx4200_debug(peer, 801224ba2bdSOllivier Robert "mx4200_receive: position avg %f %.9f %.9f %.4f\n", 802224ba2bdSOllivier Robert up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt); 803c0b746e5SOllivier Robert /* 804c0b746e5SOllivier Robert * Reinitialize as a reference station 805c0b746e5SOllivier Robert * if position is well known. 806c0b746e5SOllivier Robert */ 807c0b746e5SOllivier Robert if (current_time > up->clamp_time) { 808c0b746e5SOllivier Robert up->known++; 809c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: reconfiguring!\n"); 810c0b746e5SOllivier Robert mx4200_ref(peer); 811c0b746e5SOllivier Robert } 812c0b746e5SOllivier Robert } 813224ba2bdSOllivier Robert break; 814c0b746e5SOllivier Robert 815c0b746e5SOllivier Robert /* 816c0b746e5SOllivier Robert * Print to the syslog: 817c0b746e5SOllivier Robert * "004" Mode Data 818c0b746e5SOllivier Robert * "030" Software Configuration 819c0b746e5SOllivier Robert * "523" Time Recovery Parameters Currently in Use 820c0b746e5SOllivier Robert */ 821224ba2bdSOllivier Robert case PMVXG_D_MODEDATA: 822224ba2bdSOllivier Robert case PMVXG_D_SOFTCONF: 823224ba2bdSOllivier Robert case PMVXG_D_TRECOVUSEAGE: 824224ba2bdSOllivier Robert 825c0b746e5SOllivier Robert if ((cp = mx4200_parse_s(peer)) != NULL) { 826c0b746e5SOllivier Robert mx4200_debug(peer, 827c0b746e5SOllivier Robert "mx4200_receive: multi-record: %s\n", cp); 828c0b746e5SOllivier Robert } 829224ba2bdSOllivier Robert break; 830c0b746e5SOllivier Robert 831c0b746e5SOllivier Robert /* 832c0b746e5SOllivier Robert * "830" Time Recovery Results message 833c0b746e5SOllivier Robert */ 834224ba2bdSOllivier Robert case PMVXG_D_TRECOVOUT: 835c0b746e5SOllivier Robert 836c0b746e5SOllivier Robert /* 837c0b746e5SOllivier Robert * Capture the last PPS signal. 838c0b746e5SOllivier Robert * Precision timestamp is returned in pp->lastrec 839c0b746e5SOllivier Robert */ 8402b15cb3dSCy Schubert if (0 != mx4200_pps(peer)) { 841c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: pps failure\n"); 842c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 843c0b746e5SOllivier Robert return; 844c0b746e5SOllivier Robert } 845c0b746e5SOllivier Robert 846c0b746e5SOllivier Robert 847c0b746e5SOllivier Robert /* 848c0b746e5SOllivier Robert * Parse the time recovery message, and keep the info 849c0b746e5SOllivier Robert * to print the pretty billboards. 850c0b746e5SOllivier Robert */ 851c0b746e5SOllivier Robert if ((cp = mx4200_parse_t(peer)) != NULL) { 852c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: time: %s\n", cp); 853c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 854c0b746e5SOllivier Robert return; 855c0b746e5SOllivier Robert } 856c0b746e5SOllivier Robert 857c0b746e5SOllivier Robert /* 858c0b746e5SOllivier Robert * Add the new sample to a median filter. 859c0b746e5SOllivier Robert */ 860c0b746e5SOllivier Robert if (!refclock_process(pp)) { 861c0b746e5SOllivier Robert mx4200_debug(peer,"mx4200_receive: offset: %.6f\n", 862c0b746e5SOllivier Robert pp->offset); 863c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 864c0b746e5SOllivier Robert return; 865c0b746e5SOllivier Robert } 866c0b746e5SOllivier Robert 867c0b746e5SOllivier Robert /* 868c0b746e5SOllivier Robert * The clock will blurt a timecode every second but we only 869c0b746e5SOllivier Robert * want one when polled. If we havn't been polled, bail out. 870c0b746e5SOllivier Robert */ 871c0b746e5SOllivier Robert if (!up->polled) 872c0b746e5SOllivier Robert return; 873c0b746e5SOllivier Robert 874c0b746e5SOllivier Robert /* 875c0b746e5SOllivier Robert * Return offset and dispersion to control module. We use 876c0b746e5SOllivier Robert * lastrec as both the reference time and receive time in 877c0b746e5SOllivier Robert * order to avoid being cute, like setting the reference time 878c0b746e5SOllivier Robert * later than the receive time, which may cause a paranoid 879c0b746e5SOllivier Robert * protocol module to chuck out the data. 880c0b746e5SOllivier Robert */ 881c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_receive: process time: "); 882c0b746e5SOllivier Robert mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n", 883c0b746e5SOllivier Robert pp->year, pp->day, pp->hour, pp->minute, pp->second, 884c0b746e5SOllivier Robert prettydate(&pp->lastrec), pp->offset); 8859c2daa00SOllivier Robert pp->lastref = pp->lastrec; 886c0b746e5SOllivier Robert refclock_receive(peer); 887c0b746e5SOllivier Robert 888c0b746e5SOllivier Robert /* 889c0b746e5SOllivier Robert * We have succeeded in answering the poll. 890c0b746e5SOllivier Robert * Turn off the flag and return 891c0b746e5SOllivier Robert */ 892c0b746e5SOllivier Robert up->polled = 0; 893224ba2bdSOllivier Robert break; 894c0b746e5SOllivier Robert 895c0b746e5SOllivier Robert /* 896c0b746e5SOllivier Robert * Ignore all other sentence types 897c0b746e5SOllivier Robert */ 898224ba2bdSOllivier Robert default: 899224ba2bdSOllivier Robert break; 900224ba2bdSOllivier Robert 901224ba2bdSOllivier Robert } /* switch (sentence_type) */ 902224ba2bdSOllivier Robert 903c0b746e5SOllivier Robert return; 904c0b746e5SOllivier Robert } 905c0b746e5SOllivier Robert 906c0b746e5SOllivier Robert 907c0b746e5SOllivier Robert /* 908c0b746e5SOllivier Robert * Parse a mx4200 time recovery message. Returns a string if error. 909c0b746e5SOllivier Robert * 910c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 911c0b746e5SOllivier Robert * 912c0b746e5SOllivier Robert * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL 913c0b746e5SOllivier Robert * 914c0b746e5SOllivier Robert * Field Field Contents 915c0b746e5SOllivier Robert * ----- -------------- 916c0b746e5SOllivier Robert * Block Label: $PMVXG 917c0b746e5SOllivier Robert * Sentence Type: 830=Time Recovery Results 918c0b746e5SOllivier Robert * This sentence is output approximately 1 second 919c0b746e5SOllivier Robert * preceding the 1PPS output. It indicates the 920c0b746e5SOllivier Robert * exact time of the next pulse, whether or not the 921c0b746e5SOllivier Robert * time mark will be valid (based on operator-specified 922c0b746e5SOllivier Robert * error tolerance), the time to which the pulse is 923c0b746e5SOllivier Robert * synchronized, the receiver operating mode, 924c0b746e5SOllivier Robert * and the time error of the *last* 1PPS output. 925c0b746e5SOllivier Robert * 1 char Time Mark Valid: T=Valid, F=Not Valid 926c0b746e5SOllivier Robert * 2 int Year: 1993- 927c0b746e5SOllivier Robert * 3 int Month of Year: 1-12 928c0b746e5SOllivier Robert * 4 int Day of Month: 1-31 929c0b746e5SOllivier Robert * 5 int Time of Day: HH:MM:SS 930c0b746e5SOllivier Robert * 6 char Time Synchronization: U=UTC, G=GPS 931c0b746e5SOllivier Robert * 7 char Time Recovery Mode: D=Dynamic, S=Static, 932c0b746e5SOllivier Robert * K=Known Position, N=No Time Recovery 933c0b746e5SOllivier Robert * 8 int Oscillator Offset: The filter's estimate of the oscillator 934c0b746e5SOllivier Robert * frequency error, in parts per billion (ppb). 935c0b746e5SOllivier Robert * 9 int Time Mark Error: The computed error of the *last* pulse 936c0b746e5SOllivier Robert * output, in nanoseconds. 937c0b746e5SOllivier Robert * 10 int User Time Bias: Operator specified bias, in nanoseconds 938c0b746e5SOllivier Robert * 11 int Leap Second Flag: Indicates that a leap second will 939c0b746e5SOllivier Robert * occur. This value is usually zero, except during 9409c2daa00SOllivier Robert * the week prior to the leap second occurrence, when 941c0b746e5SOllivier Robert * this value will be set to +1 or -1. A value of 942c0b746e5SOllivier Robert * +1 indicates that GPS time will be 1 second 943c0b746e5SOllivier Robert * further ahead of UTC time. 944c0b746e5SOllivier Robert * 945c0b746e5SOllivier Robert */ 946c0b746e5SOllivier Robert static char * 947c0b746e5SOllivier Robert mx4200_parse_t( 948c0b746e5SOllivier Robert struct peer *peer 949c0b746e5SOllivier Robert ) 950c0b746e5SOllivier Robert { 951c0b746e5SOllivier Robert struct refclockproc *pp; 952c0b746e5SOllivier Robert struct mx4200unit *up; 953c0b746e5SOllivier Robert char time_mark_valid, time_sync, op_mode; 954c0b746e5SOllivier Robert int sentence_type, valid; 955224ba2bdSOllivier Robert int year, day_of_year, month, day_of_month; 9562b15cb3dSCy Schubert int hour, minute, second, leapsec_warn; 957c0b746e5SOllivier Robert int oscillator_offset, time_mark_error, time_bias; 958c0b746e5SOllivier Robert 959c0b746e5SOllivier Robert pp = peer->procptr; 9602b15cb3dSCy Schubert up = pp->unitptr; 961c0b746e5SOllivier Robert 9622b15cb3dSCy Schubert leapsec_warn = 0; /* Not all receivers output leap second warnings (!) */ 963224ba2bdSOllivier Robert sscanf(pp->a_lastcode, 964224ba2bdSOllivier Robert "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d", 965c0b746e5SOllivier Robert &sentence_type, &time_mark_valid, &year, &month, &day_of_month, 966224ba2bdSOllivier Robert &hour, &minute, &second, &time_sync, &op_mode, 9672b15cb3dSCy Schubert &oscillator_offset, &time_mark_error, &time_bias, &leapsec_warn); 968c0b746e5SOllivier Robert 969c0b746e5SOllivier Robert if (sentence_type != PMVXG_D_TRECOVOUT) 970c0b746e5SOllivier Robert return ("wrong rec-type"); 971c0b746e5SOllivier Robert 972c0b746e5SOllivier Robert switch (time_mark_valid) { 973c0b746e5SOllivier Robert case 'T': 974c0b746e5SOllivier Robert valid = 1; 975c0b746e5SOllivier Robert break; 976c0b746e5SOllivier Robert case 'F': 977c0b746e5SOllivier Robert valid = 0; 978c0b746e5SOllivier Robert break; 979c0b746e5SOllivier Robert default: 980c0b746e5SOllivier Robert return ("bad pulse-valid"); 981c0b746e5SOllivier Robert } 982c0b746e5SOllivier Robert 983c0b746e5SOllivier Robert switch (time_sync) { 984c0b746e5SOllivier Robert case 'G': 985c0b746e5SOllivier Robert return ("synchronized to GPS; should be UTC"); 986c0b746e5SOllivier Robert case 'U': 987c0b746e5SOllivier Robert break; /* UTC -> ok */ 988c0b746e5SOllivier Robert default: 989c0b746e5SOllivier Robert return ("not synchronized to UTC"); 990c0b746e5SOllivier Robert } 991c0b746e5SOllivier Robert 992c0b746e5SOllivier Robert /* 993c0b746e5SOllivier Robert * Check for insane time (allow for possible leap seconds) 994c0b746e5SOllivier Robert */ 995c0b746e5SOllivier Robert if (second > 60 || minute > 59 || hour > 23 || 996c0b746e5SOllivier Robert second < 0 || minute < 0 || hour < 0) { 997c0b746e5SOllivier Robert mx4200_debug(peer, 998c0b746e5SOllivier Robert "mx4200_parse_t: bad time %02d:%02d:%02d", 999c0b746e5SOllivier Robert hour, minute, second); 10002b15cb3dSCy Schubert if (leapsec_warn != 0) 10012b15cb3dSCy Schubert mx4200_debug(peer, " (leap %+d\n)", leapsec_warn); 1002c0b746e5SOllivier Robert mx4200_debug(peer, "\n"); 1003c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1004c0b746e5SOllivier Robert return ("bad time"); 1005c0b746e5SOllivier Robert } 1006c0b746e5SOllivier Robert if ( second == 60 ) { 1007c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1008c0b746e5SOllivier Robert "mx4200: leap second! %02d:%02d:%02d", 1009c0b746e5SOllivier Robert hour, minute, second); 1010c0b746e5SOllivier Robert } 1011c0b746e5SOllivier Robert 1012c0b746e5SOllivier Robert /* 1013c0b746e5SOllivier Robert * Check for insane date 1014c0b746e5SOllivier Robert * (Certainly can't be any year before this code was last altered!) 1015c0b746e5SOllivier Robert */ 1016c0b746e5SOllivier Robert if (day_of_month > 31 || month > 12 || 1017224ba2bdSOllivier Robert day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) { 1018c0b746e5SOllivier Robert mx4200_debug(peer, 1019c0b746e5SOllivier Robert "mx4200_parse_t: bad date (%4d-%02d-%02d)\n", 1020c0b746e5SOllivier Robert year, month, day_of_month); 1021c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADDATE); 1022c0b746e5SOllivier Robert return ("bad date"); 1023c0b746e5SOllivier Robert } 1024c0b746e5SOllivier Robert 1025c0b746e5SOllivier Robert /* 1026c0b746e5SOllivier Robert * Silly Hack for MX4200: 1027c0b746e5SOllivier Robert * ASCII message is for *next* 1PPS signal, but we have the 1028c0b746e5SOllivier Robert * timestamp for the *last* 1PPS signal. So we have to subtract 1029c0b746e5SOllivier Robert * a second. Discard if we are on a month boundary to avoid 1030c0b746e5SOllivier Robert * possible leap seconds and leap days. 1031c0b746e5SOllivier Robert */ 1032c0b746e5SOllivier Robert second--; 1033c0b746e5SOllivier Robert if (second < 0) { 1034c0b746e5SOllivier Robert second = 59; 1035c0b746e5SOllivier Robert minute--; 1036c0b746e5SOllivier Robert if (minute < 0) { 1037c0b746e5SOllivier Robert minute = 59; 1038c0b746e5SOllivier Robert hour--; 1039c0b746e5SOllivier Robert if (hour < 0) { 1040c0b746e5SOllivier Robert hour = 23; 1041c0b746e5SOllivier Robert day_of_month--; 1042c0b746e5SOllivier Robert if (day_of_month < 1) { 1043c0b746e5SOllivier Robert return ("sorry, month boundary"); 1044c0b746e5SOllivier Robert } 1045c0b746e5SOllivier Robert } 1046c0b746e5SOllivier Robert } 1047c0b746e5SOllivier Robert } 1048c0b746e5SOllivier Robert 1049c0b746e5SOllivier Robert /* 1050c0b746e5SOllivier Robert * Calculate Julian date 1051c0b746e5SOllivier Robert */ 1052c0b746e5SOllivier Robert if (!(day_of_year = mx4200_jday(year, month, day_of_month))) { 1053c0b746e5SOllivier Robert mx4200_debug(peer, 1054c0b746e5SOllivier Robert "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n", 1055c0b746e5SOllivier Robert day_of_year, year, month, day_of_month); 1056c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADDATE); 1057c0b746e5SOllivier Robert return("invalid julian date"); 1058c0b746e5SOllivier Robert } 1059c0b746e5SOllivier Robert 1060c0b746e5SOllivier Robert /* 1061c0b746e5SOllivier Robert * Setup leap second indicator 1062c0b746e5SOllivier Robert */ 10632b15cb3dSCy Schubert switch (leapsec_warn) { 1064c0b746e5SOllivier Robert case 0: 1065c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 1066c0b746e5SOllivier Robert break; 1067c0b746e5SOllivier Robert case 1: 1068c0b746e5SOllivier Robert pp->leap = LEAP_ADDSECOND; 1069c0b746e5SOllivier Robert break; 1070c0b746e5SOllivier Robert case -1: 1071c0b746e5SOllivier Robert pp->leap = LEAP_DELSECOND; 1072c0b746e5SOllivier Robert break; 1073c0b746e5SOllivier Robert default: 1074c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; 1075c0b746e5SOllivier Robert } 1076c0b746e5SOllivier Robert 1077c0b746e5SOllivier Robert /* 1078c0b746e5SOllivier Robert * Any change to the leap second warning status? 1079c0b746e5SOllivier Robert */ 10802b15cb3dSCy Schubert if (leapsec_warn != up->last_leap ) { 1081c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1082c0b746e5SOllivier Robert "mx4200: leap second warning: %d to %d (%d)", 10832b15cb3dSCy Schubert up->last_leap, leapsec_warn, pp->leap); 1084c0b746e5SOllivier Robert } 10852b15cb3dSCy Schubert up->last_leap = leapsec_warn; 1086c0b746e5SOllivier Robert 1087c0b746e5SOllivier Robert /* 1088c0b746e5SOllivier Robert * Copy time data for billboard monitoring. 1089c0b746e5SOllivier Robert */ 1090c0b746e5SOllivier Robert 1091c0b746e5SOllivier Robert pp->year = year; 1092c0b746e5SOllivier Robert pp->day = day_of_year; 1093c0b746e5SOllivier Robert pp->hour = hour; 1094c0b746e5SOllivier Robert pp->minute = minute; 1095c0b746e5SOllivier Robert pp->second = second; 1096c0b746e5SOllivier Robert 1097c0b746e5SOllivier Robert /* 1098c0b746e5SOllivier Robert * Toss if sentence is marked invalid 1099c0b746e5SOllivier Robert */ 1100c0b746e5SOllivier Robert if (!valid || pp->leap == LEAP_NOTINSYNC) { 1101c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n"); 1102c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1103c0b746e5SOllivier Robert return ("pulse invalid"); 1104c0b746e5SOllivier Robert } 1105c0b746e5SOllivier Robert 1106c0b746e5SOllivier Robert return (NULL); 1107c0b746e5SOllivier Robert } 1108c0b746e5SOllivier Robert 1109c0b746e5SOllivier Robert /* 1110c0b746e5SOllivier Robert * Calculate the checksum 1111c0b746e5SOllivier Robert */ 1112c0b746e5SOllivier Robert static u_char 1113c0b746e5SOllivier Robert mx4200_cksum( 1114c0b746e5SOllivier Robert register char *cp, 1115c0b746e5SOllivier Robert register int n 1116c0b746e5SOllivier Robert ) 1117c0b746e5SOllivier Robert { 1118c0b746e5SOllivier Robert register u_char ck; 1119c0b746e5SOllivier Robert 1120c0b746e5SOllivier Robert for (ck = 0; n-- > 0; cp++) 1121c0b746e5SOllivier Robert ck ^= *cp; 1122c0b746e5SOllivier Robert return (ck); 1123c0b746e5SOllivier Robert } 1124c0b746e5SOllivier Robert 1125c0b746e5SOllivier Robert /* 1126c0b746e5SOllivier Robert * Tables to compute the day of year. Viva la leap. 1127c0b746e5SOllivier Robert */ 1128c0b746e5SOllivier Robert static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 1129c0b746e5SOllivier Robert static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 1130c0b746e5SOllivier Robert 1131c0b746e5SOllivier Robert /* 1132c0b746e5SOllivier Robert * Calculate the the Julian Day 1133c0b746e5SOllivier Robert */ 1134c0b746e5SOllivier Robert static int 1135c0b746e5SOllivier Robert mx4200_jday( 1136c0b746e5SOllivier Robert int year, 1137c0b746e5SOllivier Robert int month, 1138c0b746e5SOllivier Robert int day_of_month 1139c0b746e5SOllivier Robert ) 1140c0b746e5SOllivier Robert { 1141c0b746e5SOllivier Robert register int day, i; 1142c0b746e5SOllivier Robert int leap_year; 1143c0b746e5SOllivier Robert 1144c0b746e5SOllivier Robert /* 1145c0b746e5SOllivier Robert * Is this a leap year ? 1146c0b746e5SOllivier Robert */ 1147c0b746e5SOllivier Robert if (year % 4) { 1148c0b746e5SOllivier Robert leap_year = 0; /* FALSE */ 1149c0b746e5SOllivier Robert } else { 1150c0b746e5SOllivier Robert if (year % 100) { 1151c0b746e5SOllivier Robert leap_year = 1; /* TRUE */ 1152c0b746e5SOllivier Robert } else { 1153c0b746e5SOllivier Robert if (year % 400) { 1154c0b746e5SOllivier Robert leap_year = 0; /* FALSE */ 1155c0b746e5SOllivier Robert } else { 1156c0b746e5SOllivier Robert leap_year = 1; /* TRUE */ 1157c0b746e5SOllivier Robert } 1158c0b746e5SOllivier Robert } 1159c0b746e5SOllivier Robert } 1160c0b746e5SOllivier Robert 1161c0b746e5SOllivier Robert /* 1162c0b746e5SOllivier Robert * Calculate the Julian Date 1163c0b746e5SOllivier Robert */ 1164c0b746e5SOllivier Robert day = day_of_month; 1165c0b746e5SOllivier Robert 1166c0b746e5SOllivier Robert if (leap_year) { 1167c0b746e5SOllivier Robert /* a leap year */ 1168c0b746e5SOllivier Robert if (day > day2tab[month - 1]) { 1169c0b746e5SOllivier Robert return (0); 1170c0b746e5SOllivier Robert } 1171c0b746e5SOllivier Robert for (i = 0; i < month - 1; i++) 1172c0b746e5SOllivier Robert day += day2tab[i]; 1173c0b746e5SOllivier Robert } else { 1174c0b746e5SOllivier Robert /* not a leap year */ 1175c0b746e5SOllivier Robert if (day > day1tab[month - 1]) { 1176c0b746e5SOllivier Robert return (0); 1177c0b746e5SOllivier Robert } 1178c0b746e5SOllivier Robert for (i = 0; i < month - 1; i++) 1179c0b746e5SOllivier Robert day += day1tab[i]; 1180c0b746e5SOllivier Robert } 1181c0b746e5SOllivier Robert return (day); 1182c0b746e5SOllivier Robert } 1183c0b746e5SOllivier Robert 1184c0b746e5SOllivier Robert /* 1185c0b746e5SOllivier Robert * Parse a mx4200 position/height/velocity sentence. 1186c0b746e5SOllivier Robert * 1187c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 1188c0b746e5SOllivier Robert * 1189c0b746e5SOllivier Robert * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM 1190c0b746e5SOllivier Robert * 1191c0b746e5SOllivier Robert * Field Field Contents 1192c0b746e5SOllivier Robert * ----- -------------- 1193c0b746e5SOllivier Robert * Block Label: $PMVXG 1194c0b746e5SOllivier Robert * Sentence Type: 021=Position, Height Velocity Data 1195c0b746e5SOllivier Robert * This sentence gives the receiver position, height, 1196c0b746e5SOllivier Robert * navigation mode, and velocity north/east. 1197c0b746e5SOllivier Robert * *This sentence is intended for post-analysis 1198c0b746e5SOllivier Robert * applications.* 1199c0b746e5SOllivier Robert * 1 float UTC measurement time (seconds into week) 1200c0b746e5SOllivier Robert * 2 float WGS-84 Lattitude (degrees, minutes) 1201c0b746e5SOllivier Robert * 3 char N=North, S=South 1202c0b746e5SOllivier Robert * 4 float WGS-84 Longitude (degrees, minutes) 1203c0b746e5SOllivier Robert * 5 char E=East, W=West 1204c0b746e5SOllivier Robert * 6 float Altitude (meters above mean sea level) 1205c0b746e5SOllivier Robert * 7 float Geoidal height (meters) 1206c0b746e5SOllivier Robert * 8 float East velocity (m/sec) 1207c0b746e5SOllivier Robert * 9 float West Velocity (m/sec) 1208c0b746e5SOllivier Robert * 10 int Navigation Mode 1209c0b746e5SOllivier Robert * Mode if navigating: 1210c0b746e5SOllivier Robert * 1 = Position from remote device 1211c0b746e5SOllivier Robert * 2 = 2-D position 1212c0b746e5SOllivier Robert * 3 = 3-D position 1213c0b746e5SOllivier Robert * 4 = 2-D differential position 1214c0b746e5SOllivier Robert * 5 = 3-D differential position 1215c0b746e5SOllivier Robert * 6 = Static 1216c0b746e5SOllivier Robert * 8 = Position known -- reference station 1217c0b746e5SOllivier Robert * 9 = Position known -- Navigator 1218c0b746e5SOllivier Robert * Mode if not navigating: 1219c0b746e5SOllivier Robert * 51 = Too few satellites 1220c0b746e5SOllivier Robert * 52 = DOPs too large 1221c0b746e5SOllivier Robert * 53 = Position STD too large 1222c0b746e5SOllivier Robert * 54 = Velocity STD too large 1223c0b746e5SOllivier Robert * 55 = Too many iterations for velocity 1224c0b746e5SOllivier Robert * 56 = Too many iterations for position 1225c0b746e5SOllivier Robert * 57 = 3 sat startup failed 1226c0b746e5SOllivier Robert * 58 = Command abort 1227c0b746e5SOllivier Robert */ 1228c0b746e5SOllivier Robert static char * 1229c0b746e5SOllivier Robert mx4200_parse_p( 1230c0b746e5SOllivier Robert struct peer *peer 1231c0b746e5SOllivier Robert ) 1232c0b746e5SOllivier Robert { 1233c0b746e5SOllivier Robert struct refclockproc *pp; 1234c0b746e5SOllivier Robert struct mx4200unit *up; 1235c0b746e5SOllivier Robert int sentence_type, mode; 1236224ba2bdSOllivier Robert double mtime, lat, lon, alt, geoid, vele, veln; 1237c0b746e5SOllivier Robert char north_south, east_west; 1238c0b746e5SOllivier Robert 1239c0b746e5SOllivier Robert pp = peer->procptr; 12402b15cb3dSCy Schubert up = pp->unitptr; 1241c0b746e5SOllivier Robert 1242c0b746e5SOllivier Robert /* Should never happen! */ 1243c0b746e5SOllivier Robert if (up->moving) return ("mobile platform - no pos!"); 1244c0b746e5SOllivier Robert 1245224ba2bdSOllivier Robert sscanf ( pp->a_lastcode, 1246224ba2bdSOllivier Robert "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d", 1247224ba2bdSOllivier Robert &sentence_type, &mtime, &lat, &north_south, &lon, &east_west, 1248224ba2bdSOllivier Robert &alt, &geoid, &vele, &veln, &mode); 1249c0b746e5SOllivier Robert 1250c0b746e5SOllivier Robert /* Sentence type */ 1251c0b746e5SOllivier Robert if (sentence_type != PMVXG_D_PHV) 1252c0b746e5SOllivier Robert return ("wrong rec-type"); 1253c0b746e5SOllivier Robert 1254c0b746e5SOllivier Robert /* 1255c0b746e5SOllivier Robert * return if not navigating 1256c0b746e5SOllivier Robert */ 1257c0b746e5SOllivier Robert if (mode > 10) 1258c0b746e5SOllivier Robert return ("not navigating"); 1259c0b746e5SOllivier Robert if (mode != 3 && mode != 5) 1260c0b746e5SOllivier Robert return ("not navigating in 3D"); 1261c0b746e5SOllivier Robert 1262c0b746e5SOllivier Robert /* Latitude (always +ve) and convert DDMM.MMMM to decimal */ 1263c0b746e5SOllivier Robert if (lat < 0.0) return ("negative latitude"); 1264c0b746e5SOllivier Robert if (lat > 9000.0) lat = 9000.0; 1265c0b746e5SOllivier Robert lat *= 0.01; 1266c0b746e5SOllivier Robert lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666); 1267c0b746e5SOllivier Robert 1268c0b746e5SOllivier Robert /* North/South */ 1269c0b746e5SOllivier Robert switch (north_south) { 1270c0b746e5SOllivier Robert case 'N': 1271c0b746e5SOllivier Robert break; 1272c0b746e5SOllivier Robert case 'S': 1273c0b746e5SOllivier Robert lat *= -1.0; 1274c0b746e5SOllivier Robert break; 1275c0b746e5SOllivier Robert default: 1276c0b746e5SOllivier Robert return ("invalid north/south indicator"); 1277c0b746e5SOllivier Robert } 1278c0b746e5SOllivier Robert 1279c0b746e5SOllivier Robert /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */ 1280c0b746e5SOllivier Robert if (lon < 0.0) return ("negative longitude"); 1281c0b746e5SOllivier Robert if (lon > 180.0) lon = 180.0; 1282c0b746e5SOllivier Robert lon *= 0.01; 1283c0b746e5SOllivier Robert lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666); 1284c0b746e5SOllivier Robert 1285c0b746e5SOllivier Robert /* East/West */ 1286c0b746e5SOllivier Robert switch (east_west) { 1287c0b746e5SOllivier Robert case 'E': 1288c0b746e5SOllivier Robert break; 1289c0b746e5SOllivier Robert case 'W': 1290c0b746e5SOllivier Robert lon *= -1.0; 1291c0b746e5SOllivier Robert break; 1292c0b746e5SOllivier Robert default: 1293c0b746e5SOllivier Robert return ("invalid east/west indicator"); 1294c0b746e5SOllivier Robert } 1295c0b746e5SOllivier Robert 1296c0b746e5SOllivier Robert /* 1297c0b746e5SOllivier Robert * Normalize longitude to near 0 degrees. 1298c0b746e5SOllivier Robert * Assume all data are clustered around first reading. 1299c0b746e5SOllivier Robert */ 1300c0b746e5SOllivier Robert if (up->central_meridian == NOT_INITIALIZED) { 1301c0b746e5SOllivier Robert up->central_meridian = lon; 1302c0b746e5SOllivier Robert mx4200_debug(peer, 1303c0b746e5SOllivier Robert "mx4200_receive: central meridian = %.9f \n", 1304c0b746e5SOllivier Robert up->central_meridian); 1305c0b746e5SOllivier Robert } 1306c0b746e5SOllivier Robert lon -= up->central_meridian; 1307c0b746e5SOllivier Robert if (lon < -180.0) lon += 360.0; 1308c0b746e5SOllivier Robert if (lon > 180.0) lon -= 360.0; 1309c0b746e5SOllivier Robert 1310c0b746e5SOllivier Robert /* 1311224ba2bdSOllivier Robert * Calculate running averages 1312c0b746e5SOllivier Robert */ 1313c0b746e5SOllivier Robert 1314224ba2bdSOllivier Robert up->avg_lon = (up->N_fixes * up->avg_lon) + lon; 1315224ba2bdSOllivier Robert up->avg_lat = (up->N_fixes * up->avg_lat) + lat; 1316224ba2bdSOllivier Robert up->avg_alt = (up->N_fixes * up->avg_alt) + alt; 1317c0b746e5SOllivier Robert 1318224ba2bdSOllivier Robert up->N_fixes += 1.0; 1319224ba2bdSOllivier Robert 1320224ba2bdSOllivier Robert up->avg_lon /= up->N_fixes; 1321224ba2bdSOllivier Robert up->avg_lat /= up->N_fixes; 1322224ba2bdSOllivier Robert up->avg_alt /= up->N_fixes; 1323c0b746e5SOllivier Robert 1324c0b746e5SOllivier Robert mx4200_debug(peer, 1325224ba2bdSOllivier Robert "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n", 1326224ba2bdSOllivier Robert up->N_fixes, lat, lon, alt, up->central_meridian); 1327c0b746e5SOllivier Robert 1328c0b746e5SOllivier Robert return (NULL); 1329c0b746e5SOllivier Robert } 1330c0b746e5SOllivier Robert 1331c0b746e5SOllivier Robert /* 1332c0b746e5SOllivier Robert * Parse a mx4200 Status sentence 1333c0b746e5SOllivier Robert * Parse a mx4200 Mode Data sentence 1334c0b746e5SOllivier Robert * Parse a mx4200 Software Configuration sentence 1335c0b746e5SOllivier Robert * Parse a mx4200 Time Recovery Parameters Currently in Use sentence 1336c0b746e5SOllivier Robert * (used only for logging raw strings) 1337c0b746e5SOllivier Robert * 1338c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 1339c0b746e5SOllivier Robert * 1340c0b746e5SOllivier Robert * $PMVXG,000,XXX,XX,X,HHMM,X 1341c0b746e5SOllivier Robert * 1342c0b746e5SOllivier Robert * Field Field Contents 1343c0b746e5SOllivier Robert * ----- -------------- 1344c0b746e5SOllivier Robert * Block Label: $PMVXG 1345c0b746e5SOllivier Robert * Sentence Type: 000=Status. 1346c0b746e5SOllivier Robert * Returns status of the receiver to the controller. 1347c0b746e5SOllivier Robert * 1 Current Receiver Status: 1348c0b746e5SOllivier Robert * ACQ = Satellite re-acquisition 1349c0b746e5SOllivier Robert * ALT = Constellation selection 1350c0b746e5SOllivier Robert * COR = Providing corrections (for reference stations only) 1351c0b746e5SOllivier Robert * IAC = Initial acquisition 1352c0b746e5SOllivier Robert * IDL = Idle, no satellites 1353c0b746e5SOllivier Robert * NAV = Navigation 1354c0b746e5SOllivier Robert * STS = Search the Sky (no almanac available) 1355c0b746e5SOllivier Robert * TRK = Tracking 1356c0b746e5SOllivier Robert * 2 Number of satellites that should be visible 1357c0b746e5SOllivier Robert * 3 Number of satellites being tracked 1358c0b746e5SOllivier Robert * 4 Time since last navigation status if not currently navigating 1359c0b746e5SOllivier Robert * (hours, minutes) 1360c0b746e5SOllivier Robert * 5 Initialization status: 1361c0b746e5SOllivier Robert * 0 = Waiting for initialization parameters 1362c0b746e5SOllivier Robert * 1 = Initialization completed 1363c0b746e5SOllivier Robert * 1364c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 1365c0b746e5SOllivier Robert * 1366c0b746e5SOllivier Robert * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T 1367c0b746e5SOllivier Robert * 1368c0b746e5SOllivier Robert * Field Field Contents 1369c0b746e5SOllivier Robert * ----- -------------- 1370c0b746e5SOllivier Robert * Block Label: $PMVXG 1371c0b746e5SOllivier Robert * Sentence Type: 004=Software Configuration. 1372c0b746e5SOllivier Robert * Defines the navigation mode and criteria for 1373c0b746e5SOllivier Robert * acceptable navigation for the receiver. 1374c0b746e5SOllivier Robert * 1 Constrain Altitude Mode: 1375c0b746e5SOllivier Robert * 0 = Auto. Constrain altitude (2-D solution) and use 1376c0b746e5SOllivier Robert * manual altitude input when 3 sats avalable. Do 1377c0b746e5SOllivier Robert * not constrain altitude (3-D solution) when 4 sats 1378c0b746e5SOllivier Robert * available. 1379c0b746e5SOllivier Robert * 1 = Always constrain altitude (2-D solution). 1380c0b746e5SOllivier Robert * 2 = Never constrain altitude (3-D solution). 1381c0b746e5SOllivier Robert * 3 = Coast. Constrain altitude (2-D solution) and use 1382c0b746e5SOllivier Robert * last GPS altitude calculation when 3 sats avalable. 1383c0b746e5SOllivier Robert * Do not constrain altitude (3-D solution) when 4 sats 1384c0b746e5SOllivier Robert * available. 1385c0b746e5SOllivier Robert * 2 Altitude Reference: (always 0 for MX4200) 1386c0b746e5SOllivier Robert * 0 = Ellipsoid 1387c0b746e5SOllivier Robert * 1 = Geoid (MSL) 1388c0b746e5SOllivier Robert * 3 Differential Navigation Control: 1389c0b746e5SOllivier Robert * 0 = Disabled 1390c0b746e5SOllivier Robert * 1 = Enabled 1391c0b746e5SOllivier Robert * 4 Horizontal Acceleration Constant (m/sec**2) 1392c0b746e5SOllivier Robert * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200) 1393c0b746e5SOllivier Robert * 6 Tracking Elevation Limit (degrees) 1394c0b746e5SOllivier Robert * 7 HDOP Limit 1395c0b746e5SOllivier Robert * 8 VDOP Limit 1396c0b746e5SOllivier Robert * 9 Time Output Mode: 1397c0b746e5SOllivier Robert * U = UTC 1398c0b746e5SOllivier Robert * L = Local time 1399c0b746e5SOllivier Robert * 10 Local Time Offset (minutes) (absent on MX4200) 1400c0b746e5SOllivier Robert * 1401c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 1402c0b746e5SOllivier Robert * 1403c0b746e5SOllivier Robert * $PMVXG,030,NNNN,FFF 1404c0b746e5SOllivier Robert * 1405c0b746e5SOllivier Robert * Field Field Contents 1406c0b746e5SOllivier Robert * ----- -------------- 1407c0b746e5SOllivier Robert * Block Label: $PMVXG 1408c0b746e5SOllivier Robert * Sentence Type: 030=Software Configuration. 1409c0b746e5SOllivier Robert * This sentence contains the navigation processor 1410c0b746e5SOllivier Robert * and baseband firmware version numbers. 1411c0b746e5SOllivier Robert * 1 Nav Processor Version Number 1412c0b746e5SOllivier Robert * 2 Baseband Firmware Version Number 1413c0b746e5SOllivier Robert * 1414c0b746e5SOllivier Robert * A typical message looks like this. Checksum has already been stripped. 1415c0b746e5SOllivier Robert * 1416c0b746e5SOllivier Robert * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R 1417c0b746e5SOllivier Robert * 1418c0b746e5SOllivier Robert * Field Field Contents 1419c0b746e5SOllivier Robert * ----- -------------- 1420c0b746e5SOllivier Robert * Block Label: $PMVXG 1421c0b746e5SOllivier Robert * Sentence Type: 523=Time Recovery Parameters Currently in Use. 1422c0b746e5SOllivier Robert * This sentence contains the configuration of the 1423c0b746e5SOllivier Robert * time recovery feature of the receiver. 1424c0b746e5SOllivier Robert * 1 Time Recovery Mode: 1425c0b746e5SOllivier Robert * D = Dynamic; solve for position and time while moving 1426c0b746e5SOllivier Robert * S = Static; solve for position and time while stationary 1427c0b746e5SOllivier Robert * K = Known position input, solve for time only 1428c0b746e5SOllivier Robert * N = No time recovery 1429c0b746e5SOllivier Robert * 2 Time Synchronization: 1430c0b746e5SOllivier Robert * U = UTC time 1431c0b746e5SOllivier Robert * G = GPS time 1432c0b746e5SOllivier Robert * 3 Time Mark Mode: 1433c0b746e5SOllivier Robert * A = Always output a time pulse 1434c0b746e5SOllivier Robert * V = Only output time pulse if time is valid (as determined 1435c0b746e5SOllivier Robert * by Maximum Time Error) 1436c0b746e5SOllivier Robert * 4 Maximum Time Error - the maximum error (in nanoseconds) for 1437c0b746e5SOllivier Robert * which a time mark will be considered valid. 1438c0b746e5SOllivier Robert * 5 User Time Bias - external bias in nanoseconds 1439c0b746e5SOllivier Robert * 6 Time Message Control: 1440c0b746e5SOllivier Robert * 0 = Do not output the time recovery message 1441c0b746e5SOllivier Robert * 1 = Output the time recovery message (record 830) to 1442c0b746e5SOllivier Robert * Control port 1443c0b746e5SOllivier Robert * 2 = Output the time recovery message (record 830) to 1444c0b746e5SOllivier Robert * Equipment port 1445c0b746e5SOllivier Robert * 7 Reserved 1446c0b746e5SOllivier Robert * 8 Position Known PRN (absent on MX 4200) 1447c0b746e5SOllivier Robert * 1448c0b746e5SOllivier Robert */ 1449c0b746e5SOllivier Robert static char * 1450c0b746e5SOllivier Robert mx4200_parse_s( 1451c0b746e5SOllivier Robert struct peer *peer 1452c0b746e5SOllivier Robert ) 1453c0b746e5SOllivier Robert { 1454c0b746e5SOllivier Robert struct refclockproc *pp; 1455c0b746e5SOllivier Robert struct mx4200unit *up; 1456c0b746e5SOllivier Robert int sentence_type; 1457c0b746e5SOllivier Robert 1458c0b746e5SOllivier Robert pp = peer->procptr; 14592b15cb3dSCy Schubert up = pp->unitptr; 1460c0b746e5SOllivier Robert 1461c0b746e5SOllivier Robert sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type); 1462c0b746e5SOllivier Robert 1463c0b746e5SOllivier Robert /* Sentence type */ 1464c0b746e5SOllivier Robert switch (sentence_type) { 1465c0b746e5SOllivier Robert 1466c0b746e5SOllivier Robert case PMVXG_D_STATUS: 1467c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1468c0b746e5SOllivier Robert "mx4200: status: %s", pp->a_lastcode); 1469c0b746e5SOllivier Robert break; 1470c0b746e5SOllivier Robert case PMVXG_D_MODEDATA: 1471c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1472c0b746e5SOllivier Robert "mx4200: mode data: %s", pp->a_lastcode); 1473c0b746e5SOllivier Robert break; 1474c0b746e5SOllivier Robert case PMVXG_D_SOFTCONF: 1475c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1476c0b746e5SOllivier Robert "mx4200: firmware configuration: %s", pp->a_lastcode); 1477c0b746e5SOllivier Robert break; 1478c0b746e5SOllivier Robert case PMVXG_D_TRECOVUSEAGE: 1479c0b746e5SOllivier Robert msyslog(LOG_DEBUG, 1480c0b746e5SOllivier Robert "mx4200: time recovery parms: %s", pp->a_lastcode); 1481c0b746e5SOllivier Robert break; 1482c0b746e5SOllivier Robert default: 1483c0b746e5SOllivier Robert return ("wrong rec-type"); 1484c0b746e5SOllivier Robert } 1485c0b746e5SOllivier Robert 1486c0b746e5SOllivier Robert return (NULL); 1487c0b746e5SOllivier Robert } 1488c0b746e5SOllivier Robert 1489c0b746e5SOllivier Robert /* 1490224ba2bdSOllivier Robert * Process a PPS signal, placing a timestamp in pp->lastrec. 1491c0b746e5SOllivier Robert */ 1492c0b746e5SOllivier Robert static int 1493c0b746e5SOllivier Robert mx4200_pps( 1494c0b746e5SOllivier Robert struct peer *peer 1495c0b746e5SOllivier Robert ) 1496c0b746e5SOllivier Robert { 1497c0b746e5SOllivier Robert int temp_serial; 1498c0b746e5SOllivier Robert struct refclockproc *pp; 1499c0b746e5SOllivier Robert struct mx4200unit *up; 1500c0b746e5SOllivier Robert 1501224ba2bdSOllivier Robert struct timespec timeout; 1502c0b746e5SOllivier Robert 1503c0b746e5SOllivier Robert pp = peer->procptr; 15042b15cb3dSCy Schubert up = pp->unitptr; 1505c0b746e5SOllivier Robert 1506c0b746e5SOllivier Robert /* 1507c0b746e5SOllivier Robert * Grab the timestamp of the PPS signal. 1508c0b746e5SOllivier Robert */ 1509224ba2bdSOllivier Robert temp_serial = up->pps_i.assert_sequence; 1510224ba2bdSOllivier Robert timeout.tv_sec = 0; 1511224ba2bdSOllivier Robert timeout.tv_nsec = 0; 1512224ba2bdSOllivier Robert if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i), 1513224ba2bdSOllivier Robert &timeout) < 0) { 1514c0b746e5SOllivier Robert mx4200_debug(peer, 15152b15cb3dSCy Schubert "mx4200_pps: time_pps_fetch: serial=%lu, %m\n", 15162b15cb3dSCy Schubert (unsigned long)up->pps_i.assert_sequence); 1517c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1518c0b746e5SOllivier Robert return(1); 1519c0b746e5SOllivier Robert } 1520224ba2bdSOllivier Robert if (temp_serial == up->pps_i.assert_sequence) { 1521c0b746e5SOllivier Robert mx4200_debug(peer, 15222b15cb3dSCy Schubert "mx4200_pps: assert_sequence serial not incrementing: %lu\n", 1523ea906c41SOllivier Robert (unsigned long)up->pps_i.assert_sequence); 1524c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1525c0b746e5SOllivier Robert return(1); 1526c0b746e5SOllivier Robert } 1527c0b746e5SOllivier Robert /* 1528c0b746e5SOllivier Robert * Check pps serial number against last one 1529c0b746e5SOllivier Robert */ 1530224ba2bdSOllivier Robert if (up->lastserial + 1 != up->pps_i.assert_sequence && 1531224ba2bdSOllivier Robert up->lastserial != 0) { 1532224ba2bdSOllivier Robert if (up->pps_i.assert_sequence == up->lastserial) { 1533c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_pps: no new pps event\n"); 1534224ba2bdSOllivier Robert } else { 15352b15cb3dSCy Schubert mx4200_debug(peer, "mx4200_pps: missed %lu pps events\n", 1536ea906c41SOllivier Robert up->pps_i.assert_sequence - up->lastserial - 1UL); 1537224ba2bdSOllivier Robert } 1538c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1539c0b746e5SOllivier Robert } 1540224ba2bdSOllivier Robert up->lastserial = up->pps_i.assert_sequence; 1541c0b746e5SOllivier Robert 1542c0b746e5SOllivier Robert /* 1543c0b746e5SOllivier Robert * Return the timestamp in pp->lastrec 1544c0b746e5SOllivier Robert */ 1545224ba2bdSOllivier Robert 1546224ba2bdSOllivier Robert pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec + 1547224ba2bdSOllivier Robert (u_int32) JAN_1970; 1548224ba2bdSOllivier Robert pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) * 1549224ba2bdSOllivier Robert 4.2949672960) + 0.5; 1550c0b746e5SOllivier Robert 1551c0b746e5SOllivier Robert return(0); 1552c0b746e5SOllivier Robert } 1553c0b746e5SOllivier Robert 1554c0b746e5SOllivier Robert /* 1555c0b746e5SOllivier Robert * mx4200_debug - print debug messages 1556c0b746e5SOllivier Robert */ 1557c0b746e5SOllivier Robert static void 1558c0b746e5SOllivier Robert mx4200_debug(struct peer *peer, char *fmt, ...) 1559c0b746e5SOllivier Robert { 1560ea906c41SOllivier Robert #ifdef DEBUG 1561c0b746e5SOllivier Robert va_list ap; 1562c0b746e5SOllivier Robert struct refclockproc *pp; 1563c0b746e5SOllivier Robert struct mx4200unit *up; 1564c0b746e5SOllivier Robert 1565c0b746e5SOllivier Robert if (debug) { 1566c0b746e5SOllivier Robert va_start(ap, fmt); 1567c0b746e5SOllivier Robert 1568c0b746e5SOllivier Robert pp = peer->procptr; 15692b15cb3dSCy Schubert up = pp->unitptr; 1570c0b746e5SOllivier Robert 1571c0b746e5SOllivier Robert /* 1572c0b746e5SOllivier Robert * Print debug message to stdout 1573c0b746e5SOllivier Robert * In the future, we may want to get get more creative... 1574c0b746e5SOllivier Robert */ 15752b15cb3dSCy Schubert mvprintf(fmt, ap); 1576c0b746e5SOllivier Robert 1577c0b746e5SOllivier Robert va_end(ap); 1578c0b746e5SOllivier Robert } 1579ea906c41SOllivier Robert #endif 1580c0b746e5SOllivier Robert } 1581c0b746e5SOllivier Robert 1582c0b746e5SOllivier Robert /* 1583c0b746e5SOllivier Robert * Send a character string to the receiver. Checksum is appended here. 1584c0b746e5SOllivier Robert */ 1585a151a66cSOllivier Robert #if defined(__STDC__) 1586c0b746e5SOllivier Robert static void 1587c0b746e5SOllivier Robert mx4200_send(struct peer *peer, char *fmt, ...) 1588c0b746e5SOllivier Robert #else 1589a151a66cSOllivier Robert static void 1590c0b746e5SOllivier Robert mx4200_send(peer, fmt, va_alist) 1591c0b746e5SOllivier Robert struct peer *peer; 1592c0b746e5SOllivier Robert char *fmt; 1593c0b746e5SOllivier Robert va_dcl 1594c0b746e5SOllivier Robert #endif /* __STDC__ */ 1595c0b746e5SOllivier Robert { 1596c0b746e5SOllivier Robert struct refclockproc *pp; 1597c0b746e5SOllivier Robert struct mx4200unit *up; 1598c0b746e5SOllivier Robert 1599f0574f5cSXin LI register char *cp, *ep; 1600c0b746e5SOllivier Robert register int n, m; 1601c0b746e5SOllivier Robert va_list ap; 1602c0b746e5SOllivier Robert char buf[1024]; 1603c0b746e5SOllivier Robert u_char ck; 1604c0b746e5SOllivier Robert 1605f0574f5cSXin LI pp = peer->procptr; 1606f0574f5cSXin LI up = pp->unitptr; 1607f0574f5cSXin LI 1608f0574f5cSXin LI cp = buf; 1609f0574f5cSXin LI ep = cp + sizeof(buf); 1610f0574f5cSXin LI *cp++ = '$'; 1611f0574f5cSXin LI 1612a151a66cSOllivier Robert #if defined(__STDC__) 1613c0b746e5SOllivier Robert va_start(ap, fmt); 1614c0b746e5SOllivier Robert #else 1615c0b746e5SOllivier Robert va_start(ap); 1616c0b746e5SOllivier Robert #endif /* __STDC__ */ 1617f0574f5cSXin LI n = VSNPRINTF((cp, (size_t)(ep - cp), fmt, ap)); 1618f0574f5cSXin LI va_end(ap); 1619f0574f5cSXin LI if (n < 0 || (size_t)n >= (size_t)(ep - cp)) 1620f0574f5cSXin LI goto overflow; 1621c0b746e5SOllivier Robert 1622c0b746e5SOllivier Robert ck = mx4200_cksum(cp, n); 1623c0b746e5SOllivier Robert cp += n; 1624f0574f5cSXin LI n = SNPRINTF((cp, (size_t)(ep - cp), "*%02X\r\n", ck)); 1625f0574f5cSXin LI if (n < 0 || (size_t)n >= (size_t)(ep - cp)) 1626f0574f5cSXin LI goto overflow; 1627f0574f5cSXin LI cp += n; 1628f0574f5cSXin LI m = write(pp->io.fd, buf, (unsigned)(cp - buf)); 1629c0b746e5SOllivier Robert if (m < 0) 1630c0b746e5SOllivier Robert msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf); 1631c0b746e5SOllivier Robert mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf); 1632f0574f5cSXin LI 1633f0574f5cSXin LI overflow: 1634f0574f5cSXin LI msyslog(LOG_ERR, "mx4200_send: %s", "data exceeds buffer size"); 1635c0b746e5SOllivier Robert } 1636c0b746e5SOllivier Robert 1637c0b746e5SOllivier Robert #else 1638*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 1639c0b746e5SOllivier Robert #endif /* REFCLOCK */ 1640