PageRenderTime 118ms CodeModel.GetById 55ms app.highlight 51ms RepoModel.GetById 3ms app.codeStats 0ms

/contrib/ntp/ntpd/refclock_mx4200.c

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