Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_mx4200.c
blobf4e576ad673ddb888c44afa695e39e177c8a15c4
1 /* $NetBSD: refclock_mx4200.c,v 1.3 2006/06/11 19:34:12 kardel Exp $ */
3 /*
4 * This software was developed by the Computer Systems Engineering group
5 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
7 * Copyright (c) 1992 The Regents of the University of California.
8 * All rights reserved.
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.
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.
40 * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
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.
48 #ifdef HAVE_CONFIG_H
49 # include <config.h>
50 #endif
52 #if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
54 #include "ntpd.h"
55 #include "ntp_io.h"
56 #include "ntp_refclock.h"
57 #include "ntp_unixtime.h"
58 #include "ntp_stdlib.h"
60 #include <stdio.h>
61 #include <ctype.h>
63 #include "mx4200.h"
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
72 #include "ntp_sprintf.h"
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;
83 #endif /* ! HAVE_STRUCT_PPSCLOCKEV */
85 #ifdef HAVE_PPSAPI
86 # include "ppsapi_timepps.h"
87 #endif /* HAVE_PPSAPI */
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.
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.
102 * Check this every time you edit the code!
104 #define YEAR_LAST_MODIFIED 2000
107 * GPS Definitions
109 #define DEVICE "/dev/gps%d" /* device name and unit */
110 #define SPEED232 B4800 /* baud */
113 * Radio interface parameters
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) */
120 #define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
123 * Position Averaging.
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 */
130 * MX4200 unit control structure.
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;
153 static char pmvxg[] = "PMVXG";
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 */
170 * Function prototypes
172 static int mx4200_start P((int, struct peer *));
173 static void mx4200_shutdown P((int, struct peer *));
174 static void mx4200_receive P((struct recvbuf *));
175 static void mx4200_poll P((int, struct peer *));
177 static char * mx4200_parse_t P((struct peer *));
178 static char * mx4200_parse_p P((struct peer *));
179 static char * mx4200_parse_s P((struct peer *));
180 #ifdef QSORT_USES_VOID_P
181 int mx4200_cmpl_fp P((const void *, const void *));
182 #else
183 int mx4200_cmpl_fp P((const l_fp *, const l_fp *));
184 #endif /* not QSORT_USES_VOID_P */
185 static int mx4200_config P((struct peer *));
186 static void mx4200_ref P((struct peer *));
187 static void mx4200_send P((struct peer *, char *, ...))
188 __attribute__ ((format (printf, 2, 3)));
189 static u_char mx4200_cksum P((char *, int));
190 static int mx4200_jday P((int, int, int));
191 static void mx4200_debug P((struct peer *, char *, ...))
192 __attribute__ ((format (printf, 2, 3)));
193 static int mx4200_pps P((struct peer *));
196 * Transfer vector
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 */
211 * mx4200_start - open the devices and initialize data for processing
213 static int
214 mx4200_start(
215 int unit,
216 struct peer *peer
219 register struct mx4200unit *up;
220 struct refclockproc *pp;
221 int fd;
222 char gpsdev[20];
225 * Open serial port
227 (void)sprintf(gpsdev, DEVICE, unit);
228 if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
229 return (0);
233 * Allocate unit structure
235 if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
236 perror("emalloc");
237 (void) close(fd);
238 return (0);
240 memset((char *)up, 0, sizeof(struct mx4200unit));
241 pp = peer->procptr;
242 pp->io.clock_recv = mx4200_receive;
243 pp->io.srcclock = (caddr_t)peer;
244 pp->io.datalen = 0;
245 pp->io.fd = fd;
246 if (!io_addclock(&pp->io)) {
247 (void) close(fd);
248 free(up);
249 return (0);
251 pp->unitptr = (caddr_t)up;
254 * Initialize miscellaneous variables
256 peer->precision = PRECISION;
257 pp->clockdesc = DESCRIPTION;
258 memcpy((char *)&pp->refid, REFID, 4);
260 /* Ensure the receiver is properly configured */
261 return mx4200_config(peer);
266 * mx4200_shutdown - shut down the clock
268 static void
269 mx4200_shutdown(
270 int unit,
271 struct peer *peer
274 register struct mx4200unit *up;
275 struct refclockproc *pp;
277 pp = peer->procptr;
278 up = (struct mx4200unit *)pp->unitptr;
279 io_closeclock(&pp->io);
280 free(up);
285 * mx4200_config - Configure the receiver
287 static int
288 mx4200_config(
289 struct peer *peer
292 char tr_mode;
293 int add_mode;
294 register struct mx4200unit *up;
295 struct refclockproc *pp;
296 int mode;
298 pp = peer->procptr;
299 up = (struct mx4200unit *)pp->unitptr;
302 * Initialize the unit variables
304 * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
305 * at the time mx4200_start is called. These are set later,
306 * and so the code must be prepared to handle changing flags.
308 up->sloppyclockflag = pp->sloppyclockflag;
309 if (pp->sloppyclockflag & CLK_FLAG2) {
310 up->moving = 1; /* Receiver on mobile platform */
311 msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
312 } else {
313 up->moving = 0; /* Static Installation */
315 up->pollcnt = 2;
316 up->polled = 0;
317 up->known = 0;
318 up->avg_lat = 0.0;
319 up->avg_lon = 0.0;
320 up->avg_alt = 0.0;
321 up->central_meridian = NOT_INITIALIZED;
322 up->N_fixes = 0.0;
323 up->last_leap = 0; /* LEAP_NOWARNING */
324 up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
325 up->log_time = current_time + SLEEPTIME;
327 if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
328 perror("time_pps_create");
329 msyslog(LOG_ERR,
330 "mx4200_config: time_pps_create failed: %m");
331 return (0);
333 if (time_pps_getcap(up->pps_h, &mode) < 0) {
334 msyslog(LOG_ERR,
335 "mx4200_config: time_pps_getcap failed: %m");
336 return (0);
339 if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
340 msyslog(LOG_ERR,
341 "mx4200_config: time_pps_getparams failed: %m");
342 return (0);
345 /* nb. only turn things on, if someone else has turned something
346 * on before we get here, leave it alone!
349 up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
350 up->pps_p.mode &= mode; /* only set what is legal */
352 if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
353 perror("time_pps_setparams");
354 msyslog(LOG_ERR,
355 "mx4200_config: time_pps_setparams failed: %m");
356 exit(1);
359 if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
360 PPS_TSFMT_TSPEC) < 0) {
361 perror("time_pps_kcbind");
362 msyslog(LOG_ERR,
363 "mx4200_config: time_pps_kcbind failed: %m");
364 exit(1);
369 * "007" Control Port Configuration
370 * Zero the output list (do it twice to flush possible junk)
372 mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
373 PMVXG_S_PORTCONF,
374 /* control port output block Label */
375 1); /* clear current output control list (1=yes) */
376 /* add/delete sentences from list */
377 /* must be null */
378 /* sentence output rate (sec) */
379 /* precision for position output */
380 /* nmea version for cga & gll output */
381 /* pass-through control */
382 mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
383 PMVXG_S_PORTCONF, 1);
386 * Request software configuration so we can syslog the firmware version
388 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
391 * "001" Initialization/Mode Control, Part A
392 * Where ARE we?
394 mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
395 PMVXG_S_INITMODEA);
396 /* day of month */
397 /* month of year */
398 /* year */
399 /* gmt */
400 /* latitude DDMM.MMMM */
401 /* north/south */
402 /* longitude DDDMM.MMMM */
403 /* east/west */
404 /* height */
405 /* Altitude Reference 1=MSL */
408 * "001" Initialization/Mode Control, Part B
409 * Start off in 2d/3d coast mode, holding altitude to last known
410 * value if only 3 satellites available.
412 mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
413 pmvxg, PMVXG_S_INITMODEB,
414 3, /* 2d/3d coast */
415 /* reserved */
416 0.1, /* hor accel fact as per Steve (m/s**2) */
417 0.1, /* ver accel fact as per Steve (m/s**2) */
418 10, /* vdop */
419 10, /* hdop limit as per Steve */
420 5, /* elevation limit as per Steve (deg) */
421 'U', /* time output mode (UTC) */
422 0); /* local time offset from gmt (HHHMM) */
425 * "023" Time Recovery Configuration
426 * Get UTC time from a stationary receiver.
427 * (Set field 1 'D' == dynamic if we are on a moving platform).
428 * (Set field 1 'S' == static if we are not moving).
429 * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
432 if (pp->sloppyclockflag & CLK_FLAG2)
433 up->moving = 1; /* Receiver on mobile platform */
434 else
435 up->moving = 0; /* Static Installation */
437 up->pollcnt = 2;
438 if (up->moving) {
439 /* dynamic: solve for pos, alt, time, while moving */
440 tr_mode = 'D';
441 } else {
442 /* static: solve for pos, alt, time, while stationary */
443 tr_mode = 'S';
445 mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
446 PMVXG_S_TRECOVCONF,
447 tr_mode, /* time recovery mode (see above ) */
448 'U', /* synchronize to UTC */
449 'A', /* always output a time pulse */
450 500, /* max time error in ns */
451 0, /* user bias in ns */
452 1); /* output "830" sentences to control port */
453 /* Multi-satellite mode */
456 * Output position information (to calculate fixed installation
457 * location) only if we are not moving
459 if (up->moving) {
460 add_mode = 2; /* delete from list */
461 } else {
462 add_mode = 1; /* add to list */
467 * "007" Control Port Configuration
468 * Output "021" position, height, velocity reports
470 mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
471 PMVXG_S_PORTCONF,
472 PMVXG_D_PHV, /* control port output block Label */
473 0, /* clear current output control list (0=no) */
474 add_mode, /* add/delete sentences from list (1=add, 2=del) */
475 /* must be null */
476 INTERVAL); /* sentence output rate (sec) */
477 /* precision for position output */
478 /* nmea version for cga & gll output */
479 /* pass-through control */
481 return (1);
485 * mx4200_ref - Reconfigure unit as a reference station at a known position.
487 static void
488 mx4200_ref(
489 struct peer *peer
492 register struct mx4200unit *up;
493 struct refclockproc *pp;
494 double minute, lat, lon, alt;
495 char lats[16], lons[16];
496 char nsc, ewc;
498 pp = peer->procptr;
499 up = (struct mx4200unit *)pp->unitptr;
501 /* Should never happen! */
502 if (up->moving) return;
505 * Set up to output status information in the near future
507 up->log_time = current_time + SLEEPTIME;
510 * "007" Control Port Configuration
511 * Stop outputting "021" position, height, velocity reports
513 mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
514 PMVXG_S_PORTCONF,
515 PMVXG_D_PHV, /* control port output block Label */
516 0, /* clear current output control list (0=no) */
517 2); /* add/delete sentences from list (2=delete) */
518 /* must be null */
519 /* sentence output rate (sec) */
520 /* precision for position output */
521 /* nmea version for cga & gll output */
522 /* pass-through control */
525 * "001" Initialization/Mode Control, Part B
526 * Put receiver in fully-constrained 2d nav mode
528 mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
529 pmvxg, PMVXG_S_INITMODEB,
530 2, /* 2d nav */
531 /* reserved */
532 0.1, /* hor accel fact as per Steve (m/s**2) */
533 0.1, /* ver accel fact as per Steve (m/s**2) */
534 10, /* vdop */
535 10, /* hdop limit as per Steve */
536 5, /* elevation limit as per Steve (deg) */
537 'U', /* time output mode (UTC) */
538 0); /* local time offset from gmt (HHHMM) */
541 * "023" Time Recovery Configuration
542 * Get UTC time from a stationary receiver. Solve for time only.
543 * This should improve the time resolution dramatically.
545 mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
546 PMVXG_S_TRECOVCONF,
547 'K', /* known position: solve for time only */
548 'U', /* synchronize to UTC */
549 'A', /* always output a time pulse */
550 500, /* max time error in ns */
551 0, /* user bias in ns */
552 1); /* output "830" sentences to control port */
553 /* Multi-satellite mode */
556 * "000" Initialization/Mode Control - Part A
557 * Fix to our averaged position.
559 if (up->central_meridian != NOT_INITIALIZED) {
560 up->avg_lon += up->central_meridian;
561 if (up->avg_lon < -180.0) up->avg_lon += 360.0;
562 if (up->avg_lon > 180.0) up->avg_lon -= 360.0;
565 if (up->avg_lat >= 0.0) {
566 lat = up->avg_lat;
567 nsc = 'N';
568 } else {
569 lat = up->avg_lat * (-1.0);
570 nsc = 'S';
572 if (up->avg_lon >= 0.0) {
573 lon = up->avg_lon;
574 ewc = 'E';
575 } else {
576 lon = up->avg_lon * (-1.0);
577 ewc = 'W';
579 alt = up->avg_alt;
580 minute = (lat - (double)(int)lat) * 60.0;
581 sprintf(lats,"%02d%02.4f", (int)lat, minute);
582 minute = (lon - (double)(int)lon) * 60.0;
583 sprintf(lons,"%03d%02.4f", (int)lon, minute);
585 mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
586 PMVXG_S_INITMODEA,
587 /* day of month */
588 /* month of year */
589 /* year */
590 /* gmt */
591 lats, /* latitude DDMM.MMMM */
592 nsc, /* north/south */
593 lons, /* longitude DDDMM.MMMM */
594 ewc, /* east/west */
595 alt, /* Altitude */
596 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
598 msyslog(LOG_DEBUG,
599 "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
600 lats, nsc, lons, ewc, alt );
605 * mx4200_poll - mx4200 watchdog routine
607 static void
608 mx4200_poll(
609 int unit,
610 struct peer *peer
613 register struct mx4200unit *up;
614 struct refclockproc *pp;
616 pp = peer->procptr;
617 up = (struct mx4200unit *)pp->unitptr;
620 * You don't need to poll this clock. It puts out timecodes
621 * once per second. If asked for a timestamp, take note.
622 * The next time a timecode comes in, it will be fed back.
626 * If we haven't had a response in a while, reset the receiver.
628 if (up->pollcnt > 0) {
629 up->pollcnt--;
630 } else {
631 refclock_report(peer, CEVNT_TIMEOUT);
634 * Request a "000" status message which should trigger a
635 * reconfig
637 mx4200_send(peer, "%s,%03d",
638 "CDGPQ", /* query from CDU to GPS */
639 PMVXG_D_STATUS); /* label of desired sentence */
643 * polled every 64 seconds. Ask mx4200_receive to hand in
644 * a timestamp.
646 up->polled = 1;
647 pp->polls++;
650 * Output receiver status information.
652 if ((up->log_time > 0) && (current_time > up->log_time)) {
653 up->log_time = 0;
655 * Output the following messages once, for debugging.
656 * "004" Mode Data
657 * "523" Time Recovery Parameters
659 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
660 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
664 static char char2hex[] = "0123456789ABCDEF";
667 * mx4200_receive - receive gps data
669 static void
670 mx4200_receive(
671 struct recvbuf *rbufp
674 register struct mx4200unit *up;
675 struct refclockproc *pp;
676 struct peer *peer;
677 char *cp;
678 int sentence_type;
679 u_char ck;
682 * Initialize pointers and read the timecode and timestamp.
684 peer = (struct peer *)rbufp->recv_srcclock;
685 pp = peer->procptr;
686 up = (struct mx4200unit *)pp->unitptr;
689 * If operating mode has been changed, then reinitialize the receiver
690 * before doing anything else.
692 if ((pp->sloppyclockflag & CLK_FLAG2) !=
693 (up->sloppyclockflag & CLK_FLAG2)) {
694 up->sloppyclockflag = pp->sloppyclockflag;
695 mx4200_debug(peer,
696 "mx4200_receive: mode switch: reset receiver\n");
697 mx4200_config(peer);
698 return;
700 up->sloppyclockflag = pp->sloppyclockflag;
703 * Read clock output. Automatically handles STREAMS, CLKLDISC.
705 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
708 * There is a case where <cr><lf> generates 2 timestamps.
710 if (pp->lencode == 0)
711 return;
713 up->pollcnt = 2;
714 pp->a_lastcode[pp->lencode] = '\0';
715 record_clock_stats(&peer->srcadr, pp->a_lastcode);
716 mx4200_debug(peer, "mx4200_receive: %d %s\n",
717 pp->lencode, pp->a_lastcode);
720 * The structure of the control port sentences is based on the
721 * NMEA-0183 Standard for interfacing Marine Electronics
722 * Navigation Devices (Version 1.5)
724 * $PMVXG,XXX, ....................*CK<cr><lf>
726 * $ Sentence Start Identifier (reserved char)
727 * (Start-of-Sentence Identifier)
728 * P Special ID (Proprietary)
729 * MVX Originator ID (Magnavox)
730 * G Interface ID (GPS)
731 * , Field Delimiters (reserved char)
732 * XXX Sentence Type
733 * ...... Data
734 * * Checksum Field Delimiter (reserved char)
735 * CK Checksum
736 * <cr><lf> Carriage-Return/Line Feed (reserved chars)
737 * (End-of-Sentence Identifier)
739 * Reject if any important landmarks are missing.
741 cp = pp->a_lastcode + pp->lencode - 3;
742 if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
743 mx4200_debug(peer, "mx4200_receive: bad format\n");
744 refclock_report(peer, CEVNT_BADREPLY);
745 return;
749 * Check and discard the checksum
751 ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
752 if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
753 mx4200_debug(peer, "mx4200_receive: bad checksum\n");
754 refclock_report(peer, CEVNT_BADREPLY);
755 return;
757 *cp = '\0';
760 * Get the sentence type.
762 sentence_type = 0;
763 if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
764 mx4200_debug(peer, "mx4200_receive: no sentence\n");
765 refclock_report(peer, CEVNT_BADREPLY);
766 return;
768 cp++;
769 sentence_type = strtol(cp, &cp, 10);
772 * Process the sentence according to its type.
774 switch (sentence_type) {
777 * "000" Status message
779 case PMVXG_D_STATUS:
781 * XXX
782 * Since we configure the receiver to not give us status
783 * messages and since the receiver outputs status messages by
784 * default after being reset to factory defaults when sent the
785 * "$PMVXG,018,C\r\n" message, any status message we get
786 * indicates the reciever needs to be initialized; thus, it is
787 * not necessary to decode the status message.
789 if ((cp = mx4200_parse_s(peer)) != NULL) {
790 mx4200_debug(peer,
791 "mx4200_receive: status: %s\n", cp);
793 mx4200_debug(peer, "mx4200_receive: reset receiver\n");
794 mx4200_config(peer);
795 break;
798 * "021" Position, Height, Velocity message,
799 * if we are still averaging our position
801 case PMVXG_D_PHV:
802 if (!up->known) {
804 * Parse the message, calculating our averaged position.
806 if ((cp = mx4200_parse_p(peer)) != NULL) {
807 mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
808 return;
810 mx4200_debug(peer,
811 "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
812 up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
814 * Reinitialize as a reference station
815 * if position is well known.
817 if (current_time > up->clamp_time) {
818 up->known++;
819 mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
820 mx4200_ref(peer);
823 break;
826 * Print to the syslog:
827 * "004" Mode Data
828 * "030" Software Configuration
829 * "523" Time Recovery Parameters Currently in Use
831 case PMVXG_D_MODEDATA:
832 case PMVXG_D_SOFTCONF:
833 case PMVXG_D_TRECOVUSEAGE:
835 if ((cp = mx4200_parse_s(peer)) != NULL) {
836 mx4200_debug(peer,
837 "mx4200_receive: multi-record: %s\n", cp);
839 break;
842 * "830" Time Recovery Results message
844 case PMVXG_D_TRECOVOUT:
847 * Capture the last PPS signal.
848 * Precision timestamp is returned in pp->lastrec
850 if (mx4200_pps(peer) != NULL) {
851 mx4200_debug(peer, "mx4200_receive: pps failure\n");
852 refclock_report(peer, CEVNT_FAULT);
853 return;
858 * Parse the time recovery message, and keep the info
859 * to print the pretty billboards.
861 if ((cp = mx4200_parse_t(peer)) != NULL) {
862 mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
863 refclock_report(peer, CEVNT_BADREPLY);
864 return;
868 * Add the new sample to a median filter.
870 if (!refclock_process(pp)) {
871 mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
872 pp->offset);
873 refclock_report(peer, CEVNT_BADTIME);
874 return;
878 * The clock will blurt a timecode every second but we only
879 * want one when polled. If we havn't been polled, bail out.
881 if (!up->polled)
882 return;
885 * Return offset and dispersion to control module. We use
886 * lastrec as both the reference time and receive time in
887 * order to avoid being cute, like setting the reference time
888 * later than the receive time, which may cause a paranoid
889 * protocol module to chuck out the data.
891 mx4200_debug(peer, "mx4200_receive: process time: ");
892 mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
893 pp->year, pp->day, pp->hour, pp->minute, pp->second,
894 prettydate(&pp->lastrec), pp->offset);
895 pp->lastref = pp->lastrec;
896 refclock_receive(peer);
899 * We have succeeded in answering the poll.
900 * Turn off the flag and return
902 up->polled = 0;
903 break;
906 * Ignore all other sentence types
908 default:
909 break;
911 } /* switch (sentence_type) */
913 return;
918 * Parse a mx4200 time recovery message. Returns a string if error.
920 * A typical message looks like this. Checksum has already been stripped.
922 * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
924 * Field Field Contents
925 * ----- --------------
926 * Block Label: $PMVXG
927 * Sentence Type: 830=Time Recovery Results
928 * This sentence is output approximately 1 second
929 * preceding the 1PPS output. It indicates the
930 * exact time of the next pulse, whether or not the
931 * time mark will be valid (based on operator-specified
932 * error tolerance), the time to which the pulse is
933 * synchronized, the receiver operating mode,
934 * and the time error of the *last* 1PPS output.
935 * 1 char Time Mark Valid: T=Valid, F=Not Valid
936 * 2 int Year: 1993-
937 * 3 int Month of Year: 1-12
938 * 4 int Day of Month: 1-31
939 * 5 int Time of Day: HH:MM:SS
940 * 6 char Time Synchronization: U=UTC, G=GPS
941 * 7 char Time Recovery Mode: D=Dynamic, S=Static,
942 * K=Known Position, N=No Time Recovery
943 * 8 int Oscillator Offset: The filter's estimate of the oscillator
944 * frequency error, in parts per billion (ppb).
945 * 9 int Time Mark Error: The computed error of the *last* pulse
946 * output, in nanoseconds.
947 * 10 int User Time Bias: Operator specified bias, in nanoseconds
948 * 11 int Leap Second Flag: Indicates that a leap second will
949 * occur. This value is usually zero, except during
950 * the week prior to the leap second occurrence, when
951 * this value will be set to +1 or -1. A value of
952 * +1 indicates that GPS time will be 1 second
953 * further ahead of UTC time.
956 static char *
957 mx4200_parse_t(
958 struct peer *peer
961 struct refclockproc *pp;
962 struct mx4200unit *up;
963 char time_mark_valid, time_sync, op_mode;
964 int sentence_type, valid;
965 int year, day_of_year, month, day_of_month;
966 int hour, minute, second, leapsec;
967 int oscillator_offset, time_mark_error, time_bias;
969 pp = peer->procptr;
970 up = (struct mx4200unit *)pp->unitptr;
972 leapsec = 0; /* Not all receivers output leap second warnings (!) */
973 sscanf(pp->a_lastcode,
974 "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
975 &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
976 &hour, &minute, &second, &time_sync, &op_mode,
977 &oscillator_offset, &time_mark_error, &time_bias, &leapsec);
979 if (sentence_type != PMVXG_D_TRECOVOUT)
980 return ("wrong rec-type");
982 switch (time_mark_valid) {
983 case 'T':
984 valid = 1;
985 break;
986 case 'F':
987 valid = 0;
988 break;
989 default:
990 return ("bad pulse-valid");
993 switch (time_sync) {
994 case 'G':
995 return ("synchronized to GPS; should be UTC");
996 case 'U':
997 break; /* UTC -> ok */
998 default:
999 return ("not synchronized to UTC");
1003 * Check for insane time (allow for possible leap seconds)
1005 if (second > 60 || minute > 59 || hour > 23 ||
1006 second < 0 || minute < 0 || hour < 0) {
1007 mx4200_debug(peer,
1008 "mx4200_parse_t: bad time %02d:%02d:%02d",
1009 hour, minute, second);
1010 if (leapsec != 0)
1011 mx4200_debug(peer, " (leap %+d\n)", leapsec);
1012 mx4200_debug(peer, "\n");
1013 refclock_report(peer, CEVNT_BADTIME);
1014 return ("bad time");
1016 if ( second == 60 ) {
1017 msyslog(LOG_DEBUG,
1018 "mx4200: leap second! %02d:%02d:%02d",
1019 hour, minute, second);
1023 * Check for insane date
1024 * (Certainly can't be any year before this code was last altered!)
1026 if (day_of_month > 31 || month > 12 ||
1027 day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) {
1028 mx4200_debug(peer,
1029 "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
1030 year, month, day_of_month);
1031 refclock_report(peer, CEVNT_BADDATE);
1032 return ("bad date");
1036 * Silly Hack for MX4200:
1037 * ASCII message is for *next* 1PPS signal, but we have the
1038 * timestamp for the *last* 1PPS signal. So we have to subtract
1039 * a second. Discard if we are on a month boundary to avoid
1040 * possible leap seconds and leap days.
1042 second--;
1043 if (second < 0) {
1044 second = 59;
1045 minute--;
1046 if (minute < 0) {
1047 minute = 59;
1048 hour--;
1049 if (hour < 0) {
1050 hour = 23;
1051 day_of_month--;
1052 if (day_of_month < 1) {
1053 return ("sorry, month boundary");
1060 * Calculate Julian date
1062 if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
1063 mx4200_debug(peer,
1064 "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
1065 day_of_year, year, month, day_of_month);
1066 refclock_report(peer, CEVNT_BADDATE);
1067 return("invalid julian date");
1071 * Setup leap second indicator
1073 switch (leapsec) {
1074 case 0:
1075 pp->leap = LEAP_NOWARNING;
1076 break;
1077 case 1:
1078 pp->leap = LEAP_ADDSECOND;
1079 break;
1080 case -1:
1081 pp->leap = LEAP_DELSECOND;
1082 break;
1083 default:
1084 pp->leap = LEAP_NOTINSYNC;
1088 * Any change to the leap second warning status?
1090 if (leapsec != up->last_leap ) {
1091 msyslog(LOG_DEBUG,
1092 "mx4200: leap second warning: %d to %d (%d)",
1093 up->last_leap, leapsec, pp->leap);
1095 up->last_leap = leapsec;
1098 * Copy time data for billboard monitoring.
1101 pp->year = year;
1102 pp->day = day_of_year;
1103 pp->hour = hour;
1104 pp->minute = minute;
1105 pp->second = second;
1108 * Toss if sentence is marked invalid
1110 if (!valid || pp->leap == LEAP_NOTINSYNC) {
1111 mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
1112 refclock_report(peer, CEVNT_BADTIME);
1113 return ("pulse invalid");
1116 return (NULL);
1120 * Calculate the checksum
1122 static u_char
1123 mx4200_cksum(
1124 register char *cp,
1125 register int n
1128 register u_char ck;
1130 for (ck = 0; n-- > 0; cp++)
1131 ck ^= *cp;
1132 return (ck);
1136 * Tables to compute the day of year. Viva la leap.
1138 static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1139 static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1142 * Calculate the the Julian Day
1144 static int
1145 mx4200_jday(
1146 int year,
1147 int month,
1148 int day_of_month
1151 register int day, i;
1152 int leap_year;
1155 * Is this a leap year ?
1157 if (year % 4) {
1158 leap_year = 0; /* FALSE */
1159 } else {
1160 if (year % 100) {
1161 leap_year = 1; /* TRUE */
1162 } else {
1163 if (year % 400) {
1164 leap_year = 0; /* FALSE */
1165 } else {
1166 leap_year = 1; /* TRUE */
1172 * Calculate the Julian Date
1174 day = day_of_month;
1176 if (leap_year) {
1177 /* a leap year */
1178 if (day > day2tab[month - 1]) {
1179 return (0);
1181 for (i = 0; i < month - 1; i++)
1182 day += day2tab[i];
1183 } else {
1184 /* not a leap year */
1185 if (day > day1tab[month - 1]) {
1186 return (0);
1188 for (i = 0; i < month - 1; i++)
1189 day += day1tab[i];
1191 return (day);
1195 * Parse a mx4200 position/height/velocity sentence.
1197 * A typical message looks like this. Checksum has already been stripped.
1199 * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
1201 * Field Field Contents
1202 * ----- --------------
1203 * Block Label: $PMVXG
1204 * Sentence Type: 021=Position, Height Velocity Data
1205 * This sentence gives the receiver position, height,
1206 * navigation mode, and velocity north/east.
1207 * *This sentence is intended for post-analysis
1208 * applications.*
1209 * 1 float UTC measurement time (seconds into week)
1210 * 2 float WGS-84 Lattitude (degrees, minutes)
1211 * 3 char N=North, S=South
1212 * 4 float WGS-84 Longitude (degrees, minutes)
1213 * 5 char E=East, W=West
1214 * 6 float Altitude (meters above mean sea level)
1215 * 7 float Geoidal height (meters)
1216 * 8 float East velocity (m/sec)
1217 * 9 float West Velocity (m/sec)
1218 * 10 int Navigation Mode
1219 * Mode if navigating:
1220 * 1 = Position from remote device
1221 * 2 = 2-D position
1222 * 3 = 3-D position
1223 * 4 = 2-D differential position
1224 * 5 = 3-D differential position
1225 * 6 = Static
1226 * 8 = Position known -- reference station
1227 * 9 = Position known -- Navigator
1228 * Mode if not navigating:
1229 * 51 = Too few satellites
1230 * 52 = DOPs too large
1231 * 53 = Position STD too large
1232 * 54 = Velocity STD too large
1233 * 55 = Too many iterations for velocity
1234 * 56 = Too many iterations for position
1235 * 57 = 3 sat startup failed
1236 * 58 = Command abort
1238 static char *
1239 mx4200_parse_p(
1240 struct peer *peer
1243 struct refclockproc *pp;
1244 struct mx4200unit *up;
1245 int sentence_type, mode;
1246 double mtime, lat, lon, alt, geoid, vele, veln;
1247 char north_south, east_west;
1249 pp = peer->procptr;
1250 up = (struct mx4200unit *)pp->unitptr;
1252 /* Should never happen! */
1253 if (up->moving) return ("mobile platform - no pos!");
1255 sscanf ( pp->a_lastcode,
1256 "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
1257 &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
1258 &alt, &geoid, &vele, &veln, &mode);
1260 /* Sentence type */
1261 if (sentence_type != PMVXG_D_PHV)
1262 return ("wrong rec-type");
1265 * return if not navigating
1267 if (mode > 10)
1268 return ("not navigating");
1269 if (mode != 3 && mode != 5)
1270 return ("not navigating in 3D");
1272 /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
1273 if (lat < 0.0) return ("negative latitude");
1274 if (lat > 9000.0) lat = 9000.0;
1275 lat *= 0.01;
1276 lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
1278 /* North/South */
1279 switch (north_south) {
1280 case 'N':
1281 break;
1282 case 'S':
1283 lat *= -1.0;
1284 break;
1285 default:
1286 return ("invalid north/south indicator");
1289 /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
1290 if (lon < 0.0) return ("negative longitude");
1291 if (lon > 180.0) lon = 180.0;
1292 lon *= 0.01;
1293 lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
1295 /* East/West */
1296 switch (east_west) {
1297 case 'E':
1298 break;
1299 case 'W':
1300 lon *= -1.0;
1301 break;
1302 default:
1303 return ("invalid east/west indicator");
1307 * Normalize longitude to near 0 degrees.
1308 * Assume all data are clustered around first reading.
1310 if (up->central_meridian == NOT_INITIALIZED) {
1311 up->central_meridian = lon;
1312 mx4200_debug(peer,
1313 "mx4200_receive: central meridian = %.9f \n",
1314 up->central_meridian);
1316 lon -= up->central_meridian;
1317 if (lon < -180.0) lon += 360.0;
1318 if (lon > 180.0) lon -= 360.0;
1321 * Calculate running averages
1324 up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
1325 up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
1326 up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
1328 up->N_fixes += 1.0;
1330 up->avg_lon /= up->N_fixes;
1331 up->avg_lat /= up->N_fixes;
1332 up->avg_alt /= up->N_fixes;
1334 mx4200_debug(peer,
1335 "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
1336 up->N_fixes, lat, lon, alt, up->central_meridian);
1338 return (NULL);
1342 * Parse a mx4200 Status sentence
1343 * Parse a mx4200 Mode Data sentence
1344 * Parse a mx4200 Software Configuration sentence
1345 * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
1346 * (used only for logging raw strings)
1348 * A typical message looks like this. Checksum has already been stripped.
1350 * $PMVXG,000,XXX,XX,X,HHMM,X
1352 * Field Field Contents
1353 * ----- --------------
1354 * Block Label: $PMVXG
1355 * Sentence Type: 000=Status.
1356 * Returns status of the receiver to the controller.
1357 * 1 Current Receiver Status:
1358 * ACQ = Satellite re-acquisition
1359 * ALT = Constellation selection
1360 * COR = Providing corrections (for reference stations only)
1361 * IAC = Initial acquisition
1362 * IDL = Idle, no satellites
1363 * NAV = Navigation
1364 * STS = Search the Sky (no almanac available)
1365 * TRK = Tracking
1366 * 2 Number of satellites that should be visible
1367 * 3 Number of satellites being tracked
1368 * 4 Time since last navigation status if not currently navigating
1369 * (hours, minutes)
1370 * 5 Initialization status:
1371 * 0 = Waiting for initialization parameters
1372 * 1 = Initialization completed
1374 * A typical message looks like this. Checksum has already been stripped.
1376 * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
1378 * Field Field Contents
1379 * ----- --------------
1380 * Block Label: $PMVXG
1381 * Sentence Type: 004=Software Configuration.
1382 * Defines the navigation mode and criteria for
1383 * acceptable navigation for the receiver.
1384 * 1 Constrain Altitude Mode:
1385 * 0 = Auto. Constrain altitude (2-D solution) and use
1386 * manual altitude input when 3 sats avalable. Do
1387 * not constrain altitude (3-D solution) when 4 sats
1388 * available.
1389 * 1 = Always constrain altitude (2-D solution).
1390 * 2 = Never constrain altitude (3-D solution).
1391 * 3 = Coast. Constrain altitude (2-D solution) and use
1392 * last GPS altitude calculation when 3 sats avalable.
1393 * Do not constrain altitude (3-D solution) when 4 sats
1394 * available.
1395 * 2 Altitude Reference: (always 0 for MX4200)
1396 * 0 = Ellipsoid
1397 * 1 = Geoid (MSL)
1398 * 3 Differential Navigation Control:
1399 * 0 = Disabled
1400 * 1 = Enabled
1401 * 4 Horizontal Acceleration Constant (m/sec**2)
1402 * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
1403 * 6 Tracking Elevation Limit (degrees)
1404 * 7 HDOP Limit
1405 * 8 VDOP Limit
1406 * 9 Time Output Mode:
1407 * U = UTC
1408 * L = Local time
1409 * 10 Local Time Offset (minutes) (absent on MX4200)
1411 * A typical message looks like this. Checksum has already been stripped.
1413 * $PMVXG,030,NNNN,FFF
1415 * Field Field Contents
1416 * ----- --------------
1417 * Block Label: $PMVXG
1418 * Sentence Type: 030=Software Configuration.
1419 * This sentence contains the navigation processor
1420 * and baseband firmware version numbers.
1421 * 1 Nav Processor Version Number
1422 * 2 Baseband Firmware Version Number
1424 * A typical message looks like this. Checksum has already been stripped.
1426 * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
1428 * Field Field Contents
1429 * ----- --------------
1430 * Block Label: $PMVXG
1431 * Sentence Type: 523=Time Recovery Parameters Currently in Use.
1432 * This sentence contains the configuration of the
1433 * time recovery feature of the receiver.
1434 * 1 Time Recovery Mode:
1435 * D = Dynamic; solve for position and time while moving
1436 * S = Static; solve for position and time while stationary
1437 * K = Known position input, solve for time only
1438 * N = No time recovery
1439 * 2 Time Synchronization:
1440 * U = UTC time
1441 * G = GPS time
1442 * 3 Time Mark Mode:
1443 * A = Always output a time pulse
1444 * V = Only output time pulse if time is valid (as determined
1445 * by Maximum Time Error)
1446 * 4 Maximum Time Error - the maximum error (in nanoseconds) for
1447 * which a time mark will be considered valid.
1448 * 5 User Time Bias - external bias in nanoseconds
1449 * 6 Time Message Control:
1450 * 0 = Do not output the time recovery message
1451 * 1 = Output the time recovery message (record 830) to
1452 * Control port
1453 * 2 = Output the time recovery message (record 830) to
1454 * Equipment port
1455 * 7 Reserved
1456 * 8 Position Known PRN (absent on MX 4200)
1459 static char *
1460 mx4200_parse_s(
1461 struct peer *peer
1464 struct refclockproc *pp;
1465 struct mx4200unit *up;
1466 int sentence_type;
1468 pp = peer->procptr;
1469 up = (struct mx4200unit *)pp->unitptr;
1471 sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
1473 /* Sentence type */
1474 switch (sentence_type) {
1476 case PMVXG_D_STATUS:
1477 msyslog(LOG_DEBUG,
1478 "mx4200: status: %s", pp->a_lastcode);
1479 break;
1480 case PMVXG_D_MODEDATA:
1481 msyslog(LOG_DEBUG,
1482 "mx4200: mode data: %s", pp->a_lastcode);
1483 break;
1484 case PMVXG_D_SOFTCONF:
1485 msyslog(LOG_DEBUG,
1486 "mx4200: firmware configuration: %s", pp->a_lastcode);
1487 break;
1488 case PMVXG_D_TRECOVUSEAGE:
1489 msyslog(LOG_DEBUG,
1490 "mx4200: time recovery parms: %s", pp->a_lastcode);
1491 break;
1492 default:
1493 return ("wrong rec-type");
1496 return (NULL);
1500 * Process a PPS signal, placing a timestamp in pp->lastrec.
1502 static int
1503 mx4200_pps(
1504 struct peer *peer
1507 int temp_serial;
1508 struct refclockproc *pp;
1509 struct mx4200unit *up;
1511 struct timespec timeout;
1513 pp = peer->procptr;
1514 up = (struct mx4200unit *)pp->unitptr;
1517 * Grab the timestamp of the PPS signal.
1519 temp_serial = up->pps_i.assert_sequence;
1520 timeout.tv_sec = 0;
1521 timeout.tv_nsec = 0;
1522 if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
1523 &timeout) < 0) {
1524 mx4200_debug(peer,
1525 "mx4200_pps: time_pps_fetch: serial=%ul, %s\n",
1526 (unsigned long)up->pps_i.assert_sequence, strerror(errno));
1527 refclock_report(peer, CEVNT_FAULT);
1528 return(1);
1530 if (temp_serial == up->pps_i.assert_sequence) {
1531 mx4200_debug(peer,
1532 "mx4200_pps: assert_sequence serial not incrementing: %ul\n",
1533 (unsigned long)up->pps_i.assert_sequence);
1534 refclock_report(peer, CEVNT_FAULT);
1535 return(1);
1538 * Check pps serial number against last one
1540 if (up->lastserial + 1 != up->pps_i.assert_sequence &&
1541 up->lastserial != 0) {
1542 if (up->pps_i.assert_sequence == up->lastserial) {
1543 mx4200_debug(peer, "mx4200_pps: no new pps event\n");
1544 } else {
1545 mx4200_debug(peer, "mx4200_pps: missed %ul pps events\n",
1546 up->pps_i.assert_sequence - up->lastserial - 1UL);
1548 refclock_report(peer, CEVNT_FAULT);
1550 up->lastserial = up->pps_i.assert_sequence;
1553 * Return the timestamp in pp->lastrec
1556 pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
1557 (u_int32) JAN_1970;
1558 pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
1559 4.2949672960) + 0.5;
1561 return(0);
1565 * mx4200_debug - print debug messages
1567 #if defined(__STDC__)
1568 static void
1569 mx4200_debug(struct peer *peer, char *fmt, ...)
1570 #else
1571 static void
1572 mx4200_debug(peer, fmt, va_alist)
1573 struct peer *peer;
1574 char *fmt;
1575 #endif /* __STDC__ */
1577 #ifdef DEBUG
1578 va_list ap;
1579 struct refclockproc *pp;
1580 struct mx4200unit *up;
1582 if (debug) {
1584 #if defined(__STDC__)
1585 va_start(ap, fmt);
1586 #else
1587 va_start(ap);
1588 #endif /* __STDC__ */
1590 pp = peer->procptr;
1591 up = (struct mx4200unit *)pp->unitptr;
1595 * Print debug message to stdout
1596 * In the future, we may want to get get more creative...
1598 vprintf(fmt, ap);
1600 va_end(ap);
1602 #endif
1606 * Send a character string to the receiver. Checksum is appended here.
1608 #if defined(__STDC__)
1609 static void
1610 mx4200_send(struct peer *peer, char *fmt, ...)
1611 #else
1612 static void
1613 mx4200_send(peer, fmt, va_alist)
1614 struct peer *peer;
1615 char *fmt;
1616 va_dcl
1617 #endif /* __STDC__ */
1619 struct refclockproc *pp;
1620 struct mx4200unit *up;
1622 register char *cp;
1623 register int n, m;
1624 va_list ap;
1625 char buf[1024];
1626 u_char ck;
1628 #if defined(__STDC__)
1629 va_start(ap, fmt);
1630 #else
1631 va_start(ap);
1632 #endif /* __STDC__ */
1634 pp = peer->procptr;
1635 up = (struct mx4200unit *)pp->unitptr;
1637 cp = buf;
1638 *cp++ = '$';
1639 n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
1640 ck = mx4200_cksum(cp, n);
1641 cp += n;
1642 ++n;
1643 n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
1645 m = write(pp->io.fd, buf, (unsigned)n);
1646 if (m < 0)
1647 msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
1648 mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
1649 va_end(ap);
1652 #else
1653 int refclock_mx4200_bs;
1654 #endif /* REFCLOCK */