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