turns printfs back on
[freebsd-src/fkvm-freebsd.git] / contrib / ntp / ntpd / refclock_mx4200.c
blob68150b7c4240289f5707ca6ea8c4bf64f0363ca7
1 /*
2 * This software was developed by the Computer Systems Engineering group
3 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
5 * Copyright (c) 1992 The Regents of the University of California.
6 * All rights reserved.
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.
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.
38 * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
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.
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif
50 #if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
52 #include "ntpd.h"
53 #include "ntp_io.h"
54 #include "ntp_refclock.h"
55 #include "ntp_unixtime.h"
56 #include "ntp_stdlib.h"
58 #include <stdio.h>
59 #include <ctype.h>
61 #include "mx4200.h"
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
70 #include "ntp_sprintf.h"
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;
81 #endif /* ! HAVE_STRUCT_PPSCLOCKEV */
83 #ifdef HAVE_PPSAPI
84 # include "ppsapi_timepps.h"
85 #endif /* HAVE_PPSAPI */
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.
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.
100 * Check this every time you edit the code!
102 #define YEAR_LAST_MODIFIED 2000
105 * GPS Definitions
107 #define DEVICE "/dev/gps%d" /* device name and unit */
108 #define SPEED232 B4800 /* baud */
111 * Radio interface parameters
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) */
118 #define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
121 * Position Averaging.
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 */
128 * MX4200 unit control structure.
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;
151 static char pmvxg[] = "PMVXG";
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 */
168 * Function prototypes
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 *));
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 *));
194 * Transfer vector
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 */
209 * mx4200_start - open the devices and initialize data for processing
211 static int
212 mx4200_start(
213 int unit,
214 struct peer *peer
217 register struct mx4200unit *up;
218 struct refclockproc *pp;
219 int fd;
220 char gpsdev[20];
223 * Open serial port
225 (void)sprintf(gpsdev, DEVICE, unit);
226 if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
227 return (0);
231 * Allocate unit structure
233 if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
234 perror("emalloc");
235 (void) close(fd);
236 return (0);
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);
249 pp->unitptr = (caddr_t)up;
252 * Initialize miscellaneous variables
254 peer->precision = PRECISION;
255 pp->clockdesc = DESCRIPTION;
256 memcpy((char *)&pp->refid, REFID, 4);
258 /* Ensure the receiver is properly configured */
259 return mx4200_config(peer);
264 * mx4200_shutdown - shut down the clock
266 static void
267 mx4200_shutdown(
268 int unit,
269 struct peer *peer
272 register struct mx4200unit *up;
273 struct refclockproc *pp;
275 pp = peer->procptr;
276 up = (struct mx4200unit *)pp->unitptr;
277 io_closeclock(&pp->io);
278 free(up);
283 * mx4200_config - Configure the receiver
285 static int
286 mx4200_config(
287 struct peer *peer
290 char tr_mode;
291 int add_mode;
292 register struct mx4200unit *up;
293 struct refclockproc *pp;
294 int mode;
296 pp = peer->procptr;
297 up = (struct mx4200unit *)pp->unitptr;
300 * Initialize the unit variables
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.
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 */
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;
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);
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);
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);
343 /* nb. only turn things on, if someone else has turned something
344 * on before we get here, leave it alone!
347 up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
348 up->pps_p.mode &= mode; /* only set what is legal */
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);
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);
367 * "007" Control Port Configuration
368 * Zero the output list (do it twice to flush possible junk)
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);
384 * Request software configuration so we can syslog the firmware version
386 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
389 * "001" Initialization/Mode Control, Part A
390 * Where ARE we?
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 */
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.
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) */
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).
430 if (pp->sloppyclockflag & CLK_FLAG2)
431 up->moving = 1; /* Receiver on mobile platform */
432 else
433 up->moving = 0; /* Static Installation */
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';
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 */
454 * Output position information (to calculate fixed installation
455 * location) only if we are not moving
457 if (up->moving) {
458 add_mode = 2; /* delete from list */
459 } else {
460 add_mode = 1; /* add to list */
465 * "007" Control Port Configuration
466 * Output "021" position, height, velocity reports
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 */
479 return (1);
483 * mx4200_ref - Reconfigure unit as a reference station at a known position.
485 static void
486 mx4200_ref(
487 struct peer *peer
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;
496 pp = peer->procptr;
497 up = (struct mx4200unit *)pp->unitptr;
499 /* Should never happen! */
500 if (up->moving) return;
503 * Set up to output status information in the near future
505 up->log_time = current_time + SLEEPTIME;
508 * "007" Control Port Configuration
509 * Stop outputting "021" position, height, velocity reports
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 */
523 * "001" Initialization/Mode Control, Part B
524 * Put receiver in fully-constrained 2d nav mode
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) */
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.
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 */
554 * "000" Initialization/Mode Control - Part A
555 * Fix to our averaged position.
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;
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';
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';
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);
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)*/
596 msyslog(LOG_DEBUG,
597 "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
598 lats, nsc, lons, ewc, alt );
603 * mx4200_poll - mx4200 watchdog routine
605 static void
606 mx4200_poll(
607 int unit,
608 struct peer *peer
611 register struct mx4200unit *up;
612 struct refclockproc *pp;
614 pp = peer->procptr;
615 up = (struct mx4200unit *)pp->unitptr;
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.
624 * If we haven't had a response in a while, reset the receiver.
626 if (up->pollcnt > 0) {
627 up->pollcnt--;
628 } else {
629 refclock_report(peer, CEVNT_TIMEOUT);
632 * Request a "000" status message which should trigger a
633 * reconfig
635 mx4200_send(peer, "%s,%03d",
636 "CDGPQ", /* query from CDU to GPS */
637 PMVXG_D_STATUS); /* label of desired sentence */
641 * polled every 64 seconds. Ask mx4200_receive to hand in
642 * a timestamp.
644 up->polled = 1;
645 pp->polls++;
648 * Output receiver status information.
650 if ((up->log_time > 0) && (current_time > up->log_time)) {
651 up->log_time = 0;
653 * Output the following messages once, for debugging.
654 * "004" Mode Data
655 * "523" Time Recovery Parameters
657 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
658 mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
662 static char char2hex[] = "0123456789ABCDEF";
665 * mx4200_receive - receive gps data
667 static void
668 mx4200_receive(
669 struct recvbuf *rbufp
672 register struct mx4200unit *up;
673 struct refclockproc *pp;
674 struct peer *peer;
675 char *cp;
676 int sentence_type;
677 u_char ck;
680 * Initialize pointers and read the timecode and timestamp.
682 peer = (struct peer *)rbufp->recv_srcclock;
683 pp = peer->procptr;
684 up = (struct mx4200unit *)pp->unitptr;
687 * If operating mode has been changed, then reinitialize the receiver
688 * before doing anything else.
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;
698 up->sloppyclockflag = pp->sloppyclockflag;
701 * Read clock output. Automatically handles STREAMS, CLKLDISC.
703 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
706 * There is a case where <cr><lf> generates 2 timestamps.
708 if (pp->lencode == 0)
709 return;
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);
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)
722 * $PMVXG,XXX, ....................*CK<cr><lf>
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)
737 * Reject if any important landmarks are missing.
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;
747 * Check and discard the checksum
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;
755 *cp = '\0';
758 * Get the sentence type.
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;
766 cp++;
767 sentence_type = strtol(cp, &cp, 10);
770 * Process the sentence according to its type.
772 switch (sentence_type) {
775 * "000" Status message
777 case PMVXG_D_STATUS:
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.
787 if ((cp = mx4200_parse_s(peer)) != NULL) {
788 mx4200_debug(peer,
789 "mx4200_receive: status: %s\n", cp);
791 mx4200_debug(peer, "mx4200_receive: reset receiver\n");
792 mx4200_config(peer);
793 break;
796 * "021" Position, Height, Velocity message,
797 * if we are still averaging our position
799 case PMVXG_D_PHV:
800 if (!up->known) {
802 * Parse the message, calculating our averaged position.
804 if ((cp = mx4200_parse_p(peer)) != NULL) {
805 mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
806 return;
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);
812 * Reinitialize as a reference station
813 * if position is well known.
815 if (current_time > up->clamp_time) {
816 up->known++;
817 mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
818 mx4200_ref(peer);
821 break;
824 * Print to the syslog:
825 * "004" Mode Data
826 * "030" Software Configuration
827 * "523" Time Recovery Parameters Currently in Use
829 case PMVXG_D_MODEDATA:
830 case PMVXG_D_SOFTCONF:
831 case PMVXG_D_TRECOVUSEAGE:
833 if ((cp = mx4200_parse_s(peer)) != NULL) {
834 mx4200_debug(peer,
835 "mx4200_receive: multi-record: %s\n", cp);
837 break;
840 * "830" Time Recovery Results message
842 case PMVXG_D_TRECOVOUT:
845 * Capture the last PPS signal.
846 * Precision timestamp is returned in pp->lastrec
848 if (mx4200_pps(peer) != NULL) {
849 mx4200_debug(peer, "mx4200_receive: pps failure\n");
850 refclock_report(peer, CEVNT_FAULT);
851 return;
856 * Parse the time recovery message, and keep the info
857 * to print the pretty billboards.
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;
866 * Add the new sample to a median filter.
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;
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.
879 if (!up->polled)
880 return;
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.
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);
897 * We have succeeded in answering the poll.
898 * Turn off the flag and return
900 up->polled = 0;
901 break;
904 * Ignore all other sentence types
906 default:
907 break;
909 } /* switch (sentence_type) */
911 return;
916 * Parse a mx4200 time recovery message. Returns a string if error.
918 * A typical message looks like this. Checksum has already been stripped.
920 * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
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.
954 static char *
955 mx4200_parse_t(
956 struct peer *peer
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;
967 pp = peer->procptr;
968 up = (struct mx4200unit *)pp->unitptr;
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);
977 if (sentence_type != PMVXG_D_TRECOVOUT)
978 return ("wrong rec-type");
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");
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");
1001 * Check for insane time (allow for possible leap seconds)
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");
1014 if ( second == 60 ) {
1015 msyslog(LOG_DEBUG,
1016 "mx4200: leap second! %02d:%02d:%02d",
1017 hour, minute, second);
1021 * Check for insane date
1022 * (Certainly can't be any year before this code was last altered!)
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");
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.
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");
1058 * Calculate Julian date
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");
1069 * Setup leap second indicator
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;
1086 * Any change to the leap second warning status?
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);
1093 up->last_leap = leapsec;
1096 * Copy time data for billboard monitoring.
1099 pp->year = year;
1100 pp->day = day_of_year;
1101 pp->hour = hour;
1102 pp->minute = minute;
1103 pp->second = second;
1106 * Toss if sentence is marked invalid
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");
1114 return (NULL);
1118 * Calculate the checksum
1120 static u_char
1121 mx4200_cksum(
1122 register char *cp,
1123 register int n
1126 register u_char ck;
1128 for (ck = 0; n-- > 0; cp++)
1129 ck ^= *cp;
1130 return (ck);
1134 * Tables to compute the day of year. Viva la leap.
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};
1140 * Calculate the the Julian Day
1142 static int
1143 mx4200_jday(
1144 int year,
1145 int month,
1146 int day_of_month
1149 register int day, i;
1150 int leap_year;
1153 * Is this a leap year ?
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 */
1170 * Calculate the Julian Date
1172 day = day_of_month;
1174 if (leap_year) {
1175 /* a leap year */
1176 if (day > day2tab[month - 1]) {
1177 return (0);
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);
1186 for (i = 0; i < month - 1; i++)
1187 day += day1tab[i];
1189 return (day);
1193 * Parse a mx4200 position/height/velocity sentence.
1195 * A typical message looks like this. Checksum has already been stripped.
1197 * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
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
1236 static char *
1237 mx4200_parse_p(
1238 struct peer *peer
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;
1247 pp = peer->procptr;
1248 up = (struct mx4200unit *)pp->unitptr;
1250 /* Should never happen! */
1251 if (up->moving) return ("mobile platform - no pos!");
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);
1258 /* Sentence type */
1259 if (sentence_type != PMVXG_D_PHV)
1260 return ("wrong rec-type");
1263 * return if not navigating
1265 if (mode > 10)
1266 return ("not navigating");
1267 if (mode != 3 && mode != 5)
1268 return ("not navigating in 3D");
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);
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");
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);
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");
1305 * Normalize longitude to near 0 degrees.
1306 * Assume all data are clustered around first reading.
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);
1314 lon -= up->central_meridian;
1315 if (lon < -180.0) lon += 360.0;
1316 if (lon > 180.0) lon -= 360.0;
1319 * Calculate running averages
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;
1326 up->N_fixes += 1.0;
1328 up->avg_lon /= up->N_fixes;
1329 up->avg_lat /= up->N_fixes;
1330 up->avg_alt /= up->N_fixes;
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);
1336 return (NULL);
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)
1346 * A typical message looks like this. Checksum has already been stripped.
1348 * $PMVXG,000,XXX,XX,X,HHMM,X
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
1372 * A typical message looks like this. Checksum has already been stripped.
1374 * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
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)
1409 * A typical message looks like this. Checksum has already been stripped.
1411 * $PMVXG,030,NNNN,FFF
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
1422 * A typical message looks like this. Checksum has already been stripped.
1424 * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
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)
1457 static char *
1458 mx4200_parse_s(
1459 struct peer *peer
1462 struct refclockproc *pp;
1463 struct mx4200unit *up;
1464 int sentence_type;
1466 pp = peer->procptr;
1467 up = (struct mx4200unit *)pp->unitptr;
1469 sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
1471 /* Sentence type */
1472 switch (sentence_type) {
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");
1494 return (NULL);
1498 * Process a PPS signal, placing a timestamp in pp->lastrec.
1500 static int
1501 mx4200_pps(
1502 struct peer *peer
1505 int temp_serial;
1506 struct refclockproc *pp;
1507 struct mx4200unit *up;
1509 struct timespec timeout;
1511 pp = peer->procptr;
1512 up = (struct mx4200unit *)pp->unitptr;
1515 * Grab the timestamp of the PPS signal.
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);
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);
1536 * Check pps serial number against last one
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);
1546 refclock_report(peer, CEVNT_FAULT);
1548 up->lastserial = up->pps_i.assert_sequence;
1551 * Return the timestamp in pp->lastrec
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;
1559 return(0);
1563 * mx4200_debug - print debug messages
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__ */
1575 #ifdef DEBUG
1576 va_list ap;
1577 struct refclockproc *pp;
1578 struct mx4200unit *up;
1580 if (debug) {
1582 #if defined(__STDC__)
1583 va_start(ap, fmt);
1584 #else
1585 va_start(ap);
1586 #endif /* __STDC__ */
1588 pp = peer->procptr;
1589 up = (struct mx4200unit *)pp->unitptr;
1593 * Print debug message to stdout
1594 * In the future, we may want to get get more creative...
1596 vprintf(fmt, ap);
1598 va_end(ap);
1600 #endif
1604 * Send a character string to the receiver. Checksum is appended here.
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__ */
1617 struct refclockproc *pp;
1618 struct mx4200unit *up;
1620 register char *cp;
1621 register int n, m;
1622 va_list ap;
1623 char buf[1024];
1624 u_char ck;
1626 #if defined(__STDC__)
1627 va_start(ap, fmt);
1628 #else
1629 va_start(ap);
1630 #endif /* __STDC__ */
1632 pp = peer->procptr;
1633 up = (struct mx4200unit *)pp->unitptr;
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));
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);
1650 #else
1651 int refclock_mx4200_bs;
1652 #endif /* REFCLOCK */