File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_mx4200.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:37 2012 UTC (12 years, 1 month ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>