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