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