/contrib/ntp/ntpd/refclock_mx4200.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 1652 lines · 867 code · 168 blank · 617 comment · 133 complexity · 5c31728c7c3c5f68db581ef581850003 MD5 · raw file
- /*
- * This software was developed by the Computer Systems Engineering group
- * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
- *
- * Copyright (c) 1992 The Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Lawrence Berkeley Laboratory.
- * 4. The name of the University may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /*
- * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
- *
- * 1. Added support for alternate PPS schemes, with code mostly
- * copied from the Oncore driver (Thanks, Poul-Henning Kamp).
- * This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7.
- */
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
- #include "ntpd.h"
- #include "ntp_io.h"
- #include "ntp_refclock.h"
- #include "ntp_unixtime.h"
- #include "ntp_stdlib.h"
- #include <stdio.h>
- #include <ctype.h>
- #include "mx4200.h"
- #ifdef HAVE_SYS_TERMIOS_H
- # include <sys/termios.h>
- #endif
- #ifdef HAVE_SYS_PPSCLOCK_H
- # include <sys/ppsclock.h>
- #endif
- #include "ntp_sprintf.h"
- #ifndef HAVE_STRUCT_PPSCLOCKEV
- struct ppsclockev {
- # ifdef HAVE_STRUCT_TIMESPEC
- struct timespec tv;
- # else
- struct timeval tv;
- # endif
- u_int serial;
- };
- #endif /* ! HAVE_STRUCT_PPSCLOCKEV */
- #ifdef HAVE_PPSAPI
- # include "ppsapi_timepps.h"
- #endif /* HAVE_PPSAPI */
- /*
- * This driver supports the Magnavox Model MX 4200 GPS Receiver
- * adapted to precision timing applications. It requires the
- * ppsclock line discipline or streams module described in the
- * Line Disciplines and Streams Drivers page. It also requires a
- * gadget box and 1-PPS level converter, such as described in the
- * Pulse-per-second (PPS) Signal Interfacing page.
- *
- * It's likely that other compatible Magnavox receivers such as the
- * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code.
- */
- /*
- * Check this every time you edit the code!
- */
- #define YEAR_LAST_MODIFIED 2000
- /*
- * GPS Definitions
- */
- #define DEVICE "/dev/gps%d" /* device name and unit */
- #define SPEED232 B4800 /* baud */
- /*
- * Radio interface parameters
- */
- #define PRECISION (-18) /* precision assumed (about 4 us) */
- #define REFID "GPS\0" /* reference id */
- #define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
- #define DEFFUDGETIME 0 /* default fudge time (ms) */
- #define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
- /*
- * Position Averaging.
- */
- #define INTERVAL 1 /* Interval between position measurements (s) */
- #define AVGING_TIME 24 /* Number of hours to average */
- #define NOT_INITIALIZED -9999. /* initial pivot longitude */
- /*
- * MX4200 unit control structure.
- */
- struct mx4200unit {
- u_int pollcnt; /* poll message counter */
- u_int polled; /* Hand in a time sample? */
- u_int lastserial; /* last pps serial number */
- struct ppsclockev ppsev; /* PPS control structure */
- double avg_lat; /* average latitude */
- double avg_lon; /* average longitude */
- double avg_alt; /* average height */
- double central_meridian; /* central meridian */
- double N_fixes; /* Number of position measurements */
- int last_leap; /* leap second warning */
- u_int moving; /* mobile platform? */
- u_long sloppyclockflag; /* fudge flags */
- u_int known; /* position known yet? */
- u_long clamp_time; /* when to stop postion averaging */
- u_long log_time; /* when to print receiver status */
- pps_handle_t pps_h;
- pps_params_t pps_p;
- pps_info_t pps_i;
- };
- static char pmvxg[] = "PMVXG";
- /* XXX should be somewhere else */
- #ifdef __GNUC__
- #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
- #ifndef __attribute__
- #define __attribute__(args)
- #endif /* __attribute__ */
- #endif /* __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */
- #else
- #ifndef __attribute__
- #define __attribute__(args)
- #endif /* __attribute__ */
- #endif /* __GNUC__ */
- /* XXX end */
- /*
- * Function prototypes
- */
- static int mx4200_start P((int, struct peer *));
- static void mx4200_shutdown P((int, struct peer *));
- static void mx4200_receive P((struct recvbuf *));
- static void mx4200_poll P((int, struct peer *));
- static char * mx4200_parse_t P((struct peer *));
- static char * mx4200_parse_p P((struct peer *));
- static char * mx4200_parse_s P((struct peer *));
- #ifdef QSORT_USES_VOID_P
- int mx4200_cmpl_fp P((const void *, const void *));
- #else
- int mx4200_cmpl_fp P((const l_fp *, const l_fp *));
- #endif /* not QSORT_USES_VOID_P */
- static int mx4200_config P((struct peer *));
- static void mx4200_ref P((struct peer *));
- static void mx4200_send P((struct peer *, char *, ...))
- __attribute__ ((format (printf, 2, 3)));
- static u_char mx4200_cksum P((char *, int));
- static int mx4200_jday P((int, int, int));
- static void mx4200_debug P((struct peer *, char *, ...))
- __attribute__ ((format (printf, 2, 3)));
- static int mx4200_pps P((struct peer *));
- /*
- * Transfer vector
- */
- struct refclock refclock_mx4200 = {
- mx4200_start, /* start up driver */
- mx4200_shutdown, /* shut down driver */
- mx4200_poll, /* transmit poll message */
- noentry, /* not used (old mx4200_control) */
- noentry, /* initialize driver (not used) */
- noentry, /* not used (old mx4200_buginfo) */
- NOFLAGS /* not used */
- };
- /*
- * mx4200_start - open the devices and initialize data for processing
- */
- static int
- mx4200_start(
- int unit,
- struct peer *peer
- )
- {
- register struct mx4200unit *up;
- struct refclockproc *pp;
- int fd;
- char gpsdev[20];
- /*
- * Open serial port
- */
- (void)sprintf(gpsdev, DEVICE, unit);
- if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
- return (0);
- }
- /*
- * Allocate unit structure
- */
- if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
- perror("emalloc");
- (void) close(fd);
- return (0);
- }
- memset((char *)up, 0, sizeof(struct mx4200unit));
- pp = peer->procptr;
- pp->io.clock_recv = mx4200_receive;
- pp->io.srcclock = (caddr_t)peer;
- pp->io.datalen = 0;
- pp->io.fd = fd;
- if (!io_addclock(&pp->io)) {
- (void) close(fd);
- free(up);
- return (0);
- }
- pp->unitptr = (caddr_t)up;
- /*
- * Initialize miscellaneous variables
- */
- peer->precision = PRECISION;
- pp->clockdesc = DESCRIPTION;
- memcpy((char *)&pp->refid, REFID, 4);
- /* Ensure the receiver is properly configured */
- return mx4200_config(peer);
- }
- /*
- * mx4200_shutdown - shut down the clock
- */
- static void
- mx4200_shutdown(
- int unit,
- struct peer *peer
- )
- {
- register struct mx4200unit *up;
- struct refclockproc *pp;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- io_closeclock(&pp->io);
- free(up);
- }
- /*
- * mx4200_config - Configure the receiver
- */
- static int
- mx4200_config(
- struct peer *peer
- )
- {
- char tr_mode;
- int add_mode;
- register struct mx4200unit *up;
- struct refclockproc *pp;
- int mode;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /*
- * Initialize the unit variables
- *
- * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
- * at the time mx4200_start is called. These are set later,
- * and so the code must be prepared to handle changing flags.
- */
- up->sloppyclockflag = pp->sloppyclockflag;
- if (pp->sloppyclockflag & CLK_FLAG2) {
- up->moving = 1; /* Receiver on mobile platform */
- msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
- } else {
- up->moving = 0; /* Static Installation */
- }
- up->pollcnt = 2;
- up->polled = 0;
- up->known = 0;
- up->avg_lat = 0.0;
- up->avg_lon = 0.0;
- up->avg_alt = 0.0;
- up->central_meridian = NOT_INITIALIZED;
- up->N_fixes = 0.0;
- up->last_leap = 0; /* LEAP_NOWARNING */
- up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
- up->log_time = current_time + SLEEPTIME;
- if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
- perror("time_pps_create");
- msyslog(LOG_ERR,
- "mx4200_config: time_pps_create failed: %m");
- return (0);
- }
- if (time_pps_getcap(up->pps_h, &mode) < 0) {
- msyslog(LOG_ERR,
- "mx4200_config: time_pps_getcap failed: %m");
- return (0);
- }
- if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
- msyslog(LOG_ERR,
- "mx4200_config: time_pps_getparams failed: %m");
- return (0);
- }
- /* nb. only turn things on, if someone else has turned something
- * on before we get here, leave it alone!
- */
- up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
- up->pps_p.mode &= mode; /* only set what is legal */
- if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
- perror("time_pps_setparams");
- msyslog(LOG_ERR,
- "mx4200_config: time_pps_setparams failed: %m");
- exit(1);
- }
- if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
- PPS_TSFMT_TSPEC) < 0) {
- perror("time_pps_kcbind");
- msyslog(LOG_ERR,
- "mx4200_config: time_pps_kcbind failed: %m");
- exit(1);
- }
- /*
- * "007" Control Port Configuration
- * Zero the output list (do it twice to flush possible junk)
- */
- mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
- PMVXG_S_PORTCONF,
- /* control port output block Label */
- 1); /* clear current output control list (1=yes) */
- /* add/delete sentences from list */
- /* must be null */
- /* sentence output rate (sec) */
- /* precision for position output */
- /* nmea version for cga & gll output */
- /* pass-through control */
- mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
- PMVXG_S_PORTCONF, 1);
- /*
- * Request software configuration so we can syslog the firmware version
- */
- mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
- /*
- * "001" Initialization/Mode Control, Part A
- * Where ARE we?
- */
- mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
- PMVXG_S_INITMODEA);
- /* day of month */
- /* month of year */
- /* year */
- /* gmt */
- /* latitude DDMM.MMMM */
- /* north/south */
- /* longitude DDDMM.MMMM */
- /* east/west */
- /* height */
- /* Altitude Reference 1=MSL */
- /*
- * "001" Initialization/Mode Control, Part B
- * Start off in 2d/3d coast mode, holding altitude to last known
- * value if only 3 satellites available.
- */
- mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
- pmvxg, PMVXG_S_INITMODEB,
- 3, /* 2d/3d coast */
- /* reserved */
- 0.1, /* hor accel fact as per Steve (m/s**2) */
- 0.1, /* ver accel fact as per Steve (m/s**2) */
- 10, /* vdop */
- 10, /* hdop limit as per Steve */
- 5, /* elevation limit as per Steve (deg) */
- 'U', /* time output mode (UTC) */
- 0); /* local time offset from gmt (HHHMM) */
- /*
- * "023" Time Recovery Configuration
- * Get UTC time from a stationary receiver.
- * (Set field 1 'D' == dynamic if we are on a moving platform).
- * (Set field 1 'S' == static if we are not moving).
- * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
- */
- if (pp->sloppyclockflag & CLK_FLAG2)
- up->moving = 1; /* Receiver on mobile platform */
- else
- up->moving = 0; /* Static Installation */
- up->pollcnt = 2;
- if (up->moving) {
- /* dynamic: solve for pos, alt, time, while moving */
- tr_mode = 'D';
- } else {
- /* static: solve for pos, alt, time, while stationary */
- tr_mode = 'S';
- }
- mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
- PMVXG_S_TRECOVCONF,
- tr_mode, /* time recovery mode (see above ) */
- 'U', /* synchronize to UTC */
- 'A', /* always output a time pulse */
- 500, /* max time error in ns */
- 0, /* user bias in ns */
- 1); /* output "830" sentences to control port */
- /* Multi-satellite mode */
- /*
- * Output position information (to calculate fixed installation
- * location) only if we are not moving
- */
- if (up->moving) {
- add_mode = 2; /* delete from list */
- } else {
- add_mode = 1; /* add to list */
- }
- /*
- * "007" Control Port Configuration
- * Output "021" position, height, velocity reports
- */
- mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
- PMVXG_S_PORTCONF,
- PMVXG_D_PHV, /* control port output block Label */
- 0, /* clear current output control list (0=no) */
- add_mode, /* add/delete sentences from list (1=add, 2=del) */
- /* must be null */
- INTERVAL); /* sentence output rate (sec) */
- /* precision for position output */
- /* nmea version for cga & gll output */
- /* pass-through control */
- return (1);
- }
- /*
- * mx4200_ref - Reconfigure unit as a reference station at a known position.
- */
- static void
- mx4200_ref(
- struct peer *peer
- )
- {
- register struct mx4200unit *up;
- struct refclockproc *pp;
- double minute, lat, lon, alt;
- char lats[16], lons[16];
- char nsc, ewc;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /* Should never happen! */
- if (up->moving) return;
- /*
- * Set up to output status information in the near future
- */
- up->log_time = current_time + SLEEPTIME;
- /*
- * "007" Control Port Configuration
- * Stop outputting "021" position, height, velocity reports
- */
- mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
- PMVXG_S_PORTCONF,
- PMVXG_D_PHV, /* control port output block Label */
- 0, /* clear current output control list (0=no) */
- 2); /* add/delete sentences from list (2=delete) */
- /* must be null */
- /* sentence output rate (sec) */
- /* precision for position output */
- /* nmea version for cga & gll output */
- /* pass-through control */
- /*
- * "001" Initialization/Mode Control, Part B
- * Put receiver in fully-constrained 2d nav mode
- */
- mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
- pmvxg, PMVXG_S_INITMODEB,
- 2, /* 2d nav */
- /* reserved */
- 0.1, /* hor accel fact as per Steve (m/s**2) */
- 0.1, /* ver accel fact as per Steve (m/s**2) */
- 10, /* vdop */
- 10, /* hdop limit as per Steve */
- 5, /* elevation limit as per Steve (deg) */
- 'U', /* time output mode (UTC) */
- 0); /* local time offset from gmt (HHHMM) */
- /*
- * "023" Time Recovery Configuration
- * Get UTC time from a stationary receiver. Solve for time only.
- * This should improve the time resolution dramatically.
- */
- mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
- PMVXG_S_TRECOVCONF,
- 'K', /* known position: solve for time only */
- 'U', /* synchronize to UTC */
- 'A', /* always output a time pulse */
- 500, /* max time error in ns */
- 0, /* user bias in ns */
- 1); /* output "830" sentences to control port */
- /* Multi-satellite mode */
- /*
- * "000" Initialization/Mode Control - Part A
- * Fix to our averaged position.
- */
- if (up->central_meridian != NOT_INITIALIZED) {
- up->avg_lon += up->central_meridian;
- if (up->avg_lon < -180.0) up->avg_lon += 360.0;
- if (up->avg_lon > 180.0) up->avg_lon -= 360.0;
- }
- if (up->avg_lat >= 0.0) {
- lat = up->avg_lat;
- nsc = 'N';
- } else {
- lat = up->avg_lat * (-1.0);
- nsc = 'S';
- }
- if (up->avg_lon >= 0.0) {
- lon = up->avg_lon;
- ewc = 'E';
- } else {
- lon = up->avg_lon * (-1.0);
- ewc = 'W';
- }
- alt = up->avg_alt;
- minute = (lat - (double)(int)lat) * 60.0;
- sprintf(lats,"%02d%02.4f", (int)lat, minute);
- minute = (lon - (double)(int)lon) * 60.0;
- sprintf(lons,"%03d%02.4f", (int)lon, minute);
- mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
- PMVXG_S_INITMODEA,
- /* day of month */
- /* month of year */
- /* year */
- /* gmt */
- lats, /* latitude DDMM.MMMM */
- nsc, /* north/south */
- lons, /* longitude DDDMM.MMMM */
- ewc, /* east/west */
- alt, /* Altitude */
- 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
- msyslog(LOG_DEBUG,
- "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
- lats, nsc, lons, ewc, alt );
- }
- /*
- * mx4200_poll - mx4200 watchdog routine
- */
- static void
- mx4200_poll(
- int unit,
- struct peer *peer
- )
- {
- register struct mx4200unit *up;
- struct refclockproc *pp;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /*
- * You don't need to poll this clock. It puts out timecodes
- * once per second. If asked for a timestamp, take note.
- * The next time a timecode comes in, it will be fed back.
- */
- /*
- * If we haven't had a response in a while, reset the receiver.
- */
- if (up->pollcnt > 0) {
- up->pollcnt--;
- } else {
- refclock_report(peer, CEVNT_TIMEOUT);
- /*
- * Request a "000" status message which should trigger a
- * reconfig
- */
- mx4200_send(peer, "%s,%03d",
- "CDGPQ", /* query from CDU to GPS */
- PMVXG_D_STATUS); /* label of desired sentence */
- }
- /*
- * polled every 64 seconds. Ask mx4200_receive to hand in
- * a timestamp.
- */
- up->polled = 1;
- pp->polls++;
- /*
- * Output receiver status information.
- */
- if ((up->log_time > 0) && (current_time > up->log_time)) {
- up->log_time = 0;
- /*
- * Output the following messages once, for debugging.
- * "004" Mode Data
- * "523" Time Recovery Parameters
- */
- mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
- mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
- }
- }
- static char char2hex[] = "0123456789ABCDEF";
- /*
- * mx4200_receive - receive gps data
- */
- static void
- mx4200_receive(
- struct recvbuf *rbufp
- )
- {
- register struct mx4200unit *up;
- struct refclockproc *pp;
- struct peer *peer;
- char *cp;
- int sentence_type;
- u_char ck;
- /*
- * Initialize pointers and read the timecode and timestamp.
- */
- peer = (struct peer *)rbufp->recv_srcclock;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /*
- * If operating mode has been changed, then reinitialize the receiver
- * before doing anything else.
- */
- if ((pp->sloppyclockflag & CLK_FLAG2) !=
- (up->sloppyclockflag & CLK_FLAG2)) {
- up->sloppyclockflag = pp->sloppyclockflag;
- mx4200_debug(peer,
- "mx4200_receive: mode switch: reset receiver\n");
- mx4200_config(peer);
- return;
- }
- up->sloppyclockflag = pp->sloppyclockflag;
- /*
- * Read clock output. Automatically handles STREAMS, CLKLDISC.
- */
- pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
- /*
- * There is a case where <cr><lf> generates 2 timestamps.
- */
- if (pp->lencode == 0)
- return;
- up->pollcnt = 2;
- pp->a_lastcode[pp->lencode] = '\0';
- record_clock_stats(&peer->srcadr, pp->a_lastcode);
- mx4200_debug(peer, "mx4200_receive: %d %s\n",
- pp->lencode, pp->a_lastcode);
- /*
- * The structure of the control port sentences is based on the
- * NMEA-0183 Standard for interfacing Marine Electronics
- * Navigation Devices (Version 1.5)
- *
- * $PMVXG,XXX, ....................*CK<cr><lf>
- *
- * $ Sentence Start Identifier (reserved char)
- * (Start-of-Sentence Identifier)
- * P Special ID (Proprietary)
- * MVX Originator ID (Magnavox)
- * G Interface ID (GPS)
- * , Field Delimiters (reserved char)
- * XXX Sentence Type
- * ...... Data
- * * Checksum Field Delimiter (reserved char)
- * CK Checksum
- * <cr><lf> Carriage-Return/Line Feed (reserved chars)
- * (End-of-Sentence Identifier)
- *
- * Reject if any important landmarks are missing.
- */
- cp = pp->a_lastcode + pp->lencode - 3;
- if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
- mx4200_debug(peer, "mx4200_receive: bad format\n");
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- /*
- * Check and discard the checksum
- */
- ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
- if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
- mx4200_debug(peer, "mx4200_receive: bad checksum\n");
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- *cp = '\0';
- /*
- * Get the sentence type.
- */
- sentence_type = 0;
- if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
- mx4200_debug(peer, "mx4200_receive: no sentence\n");
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- cp++;
- sentence_type = strtol(cp, &cp, 10);
- /*
- * Process the sentence according to its type.
- */
- switch (sentence_type) {
- /*
- * "000" Status message
- */
- case PMVXG_D_STATUS:
- /*
- * XXX
- * Since we configure the receiver to not give us status
- * messages and since the receiver outputs status messages by
- * default after being reset to factory defaults when sent the
- * "$PMVXG,018,C\r\n" message, any status message we get
- * indicates the reciever needs to be initialized; thus, it is
- * not necessary to decode the status message.
- */
- if ((cp = mx4200_parse_s(peer)) != NULL) {
- mx4200_debug(peer,
- "mx4200_receive: status: %s\n", cp);
- }
- mx4200_debug(peer, "mx4200_receive: reset receiver\n");
- mx4200_config(peer);
- break;
- /*
- * "021" Position, Height, Velocity message,
- * if we are still averaging our position
- */
- case PMVXG_D_PHV:
- if (!up->known) {
- /*
- * Parse the message, calculating our averaged position.
- */
- if ((cp = mx4200_parse_p(peer)) != NULL) {
- mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
- return;
- }
- mx4200_debug(peer,
- "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
- up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
- /*
- * Reinitialize as a reference station
- * if position is well known.
- */
- if (current_time > up->clamp_time) {
- up->known++;
- mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
- mx4200_ref(peer);
- }
- }
- break;
- /*
- * Print to the syslog:
- * "004" Mode Data
- * "030" Software Configuration
- * "523" Time Recovery Parameters Currently in Use
- */
- case PMVXG_D_MODEDATA:
- case PMVXG_D_SOFTCONF:
- case PMVXG_D_TRECOVUSEAGE:
- if ((cp = mx4200_parse_s(peer)) != NULL) {
- mx4200_debug(peer,
- "mx4200_receive: multi-record: %s\n", cp);
- }
- break;
- /*
- * "830" Time Recovery Results message
- */
- case PMVXG_D_TRECOVOUT:
- /*
- * Capture the last PPS signal.
- * Precision timestamp is returned in pp->lastrec
- */
- if (mx4200_pps(peer) != NULL) {
- mx4200_debug(peer, "mx4200_receive: pps failure\n");
- refclock_report(peer, CEVNT_FAULT);
- return;
- }
- /*
- * Parse the time recovery message, and keep the info
- * to print the pretty billboards.
- */
- if ((cp = mx4200_parse_t(peer)) != NULL) {
- mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- /*
- * Add the new sample to a median filter.
- */
- if (!refclock_process(pp)) {
- mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
- pp->offset);
- refclock_report(peer, CEVNT_BADTIME);
- return;
- }
- /*
- * The clock will blurt a timecode every second but we only
- * want one when polled. If we havn't been polled, bail out.
- */
- if (!up->polled)
- return;
- /*
- * Return offset and dispersion to control module. We use
- * lastrec as both the reference time and receive time in
- * order to avoid being cute, like setting the reference time
- * later than the receive time, which may cause a paranoid
- * protocol module to chuck out the data.
- */
- mx4200_debug(peer, "mx4200_receive: process time: ");
- mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
- pp->year, pp->day, pp->hour, pp->minute, pp->second,
- prettydate(&pp->lastrec), pp->offset);
- pp->lastref = pp->lastrec;
- refclock_receive(peer);
- /*
- * We have succeeded in answering the poll.
- * Turn off the flag and return
- */
- up->polled = 0;
- break;
- /*
- * Ignore all other sentence types
- */
- default:
- break;
- } /* switch (sentence_type) */
- return;
- }
- /*
- * Parse a mx4200 time recovery message. Returns a string if error.
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 830=Time Recovery Results
- * This sentence is output approximately 1 second
- * preceding the 1PPS output. It indicates the
- * exact time of the next pulse, whether or not the
- * time mark will be valid (based on operator-specified
- * error tolerance), the time to which the pulse is
- * synchronized, the receiver operating mode,
- * and the time error of the *last* 1PPS output.
- * 1 char Time Mark Valid: T=Valid, F=Not Valid
- * 2 int Year: 1993-
- * 3 int Month of Year: 1-12
- * 4 int Day of Month: 1-31
- * 5 int Time of Day: HH:MM:SS
- * 6 char Time Synchronization: U=UTC, G=GPS
- * 7 char Time Recovery Mode: D=Dynamic, S=Static,
- * K=Known Position, N=No Time Recovery
- * 8 int Oscillator Offset: The filter's estimate of the oscillator
- * frequency error, in parts per billion (ppb).
- * 9 int Time Mark Error: The computed error of the *last* pulse
- * output, in nanoseconds.
- * 10 int User Time Bias: Operator specified bias, in nanoseconds
- * 11 int Leap Second Flag: Indicates that a leap second will
- * occur. This value is usually zero, except during
- * the week prior to the leap second occurrence, when
- * this value will be set to +1 or -1. A value of
- * +1 indicates that GPS time will be 1 second
- * further ahead of UTC time.
- *
- */
- static char *
- mx4200_parse_t(
- struct peer *peer
- )
- {
- struct refclockproc *pp;
- struct mx4200unit *up;
- char time_mark_valid, time_sync, op_mode;
- int sentence_type, valid;
- int year, day_of_year, month, day_of_month;
- int hour, minute, second, leapsec;
- int oscillator_offset, time_mark_error, time_bias;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- leapsec = 0; /* Not all receivers output leap second warnings (!) */
- sscanf(pp->a_lastcode,
- "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
- &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
- &hour, &minute, &second, &time_sync, &op_mode,
- &oscillator_offset, &time_mark_error, &time_bias, &leapsec);
- if (sentence_type != PMVXG_D_TRECOVOUT)
- return ("wrong rec-type");
- switch (time_mark_valid) {
- case 'T':
- valid = 1;
- break;
- case 'F':
- valid = 0;
- break;
- default:
- return ("bad pulse-valid");
- }
- switch (time_sync) {
- case 'G':
- return ("synchronized to GPS; should be UTC");
- case 'U':
- break; /* UTC -> ok */
- default:
- return ("not synchronized to UTC");
- }
- /*
- * Check for insane time (allow for possible leap seconds)
- */
- if (second > 60 || minute > 59 || hour > 23 ||
- second < 0 || minute < 0 || hour < 0) {
- mx4200_debug(peer,
- "mx4200_parse_t: bad time %02d:%02d:%02d",
- hour, minute, second);
- if (leapsec != 0)
- mx4200_debug(peer, " (leap %+d\n)", leapsec);
- mx4200_debug(peer, "\n");
- refclock_report(peer, CEVNT_BADTIME);
- return ("bad time");
- }
- if ( second == 60 ) {
- msyslog(LOG_DEBUG,
- "mx4200: leap second! %02d:%02d:%02d",
- hour, minute, second);
- }
- /*
- * Check for insane date
- * (Certainly can't be any year before this code was last altered!)
- */
- if (day_of_month > 31 || month > 12 ||
- day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) {
- mx4200_debug(peer,
- "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
- year, month, day_of_month);
- refclock_report(peer, CEVNT_BADDATE);
- return ("bad date");
- }
- /*
- * Silly Hack for MX4200:
- * ASCII message is for *next* 1PPS signal, but we have the
- * timestamp for the *last* 1PPS signal. So we have to subtract
- * a second. Discard if we are on a month boundary to avoid
- * possible leap seconds and leap days.
- */
- second--;
- if (second < 0) {
- second = 59;
- minute--;
- if (minute < 0) {
- minute = 59;
- hour--;
- if (hour < 0) {
- hour = 23;
- day_of_month--;
- if (day_of_month < 1) {
- return ("sorry, month boundary");
- }
- }
- }
- }
- /*
- * Calculate Julian date
- */
- if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
- mx4200_debug(peer,
- "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
- day_of_year, year, month, day_of_month);
- refclock_report(peer, CEVNT_BADDATE);
- return("invalid julian date");
- }
- /*
- * Setup leap second indicator
- */
- switch (leapsec) {
- case 0:
- pp->leap = LEAP_NOWARNING;
- break;
- case 1:
- pp->leap = LEAP_ADDSECOND;
- break;
- case -1:
- pp->leap = LEAP_DELSECOND;
- break;
- default:
- pp->leap = LEAP_NOTINSYNC;
- }
- /*
- * Any change to the leap second warning status?
- */
- if (leapsec != up->last_leap ) {
- msyslog(LOG_DEBUG,
- "mx4200: leap second warning: %d to %d (%d)",
- up->last_leap, leapsec, pp->leap);
- }
- up->last_leap = leapsec;
- /*
- * Copy time data for billboard monitoring.
- */
- pp->year = year;
- pp->day = day_of_year;
- pp->hour = hour;
- pp->minute = minute;
- pp->second = second;
- /*
- * Toss if sentence is marked invalid
- */
- if (!valid || pp->leap == LEAP_NOTINSYNC) {
- mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
- refclock_report(peer, CEVNT_BADTIME);
- return ("pulse invalid");
- }
- return (NULL);
- }
- /*
- * Calculate the checksum
- */
- static u_char
- mx4200_cksum(
- register char *cp,
- register int n
- )
- {
- register u_char ck;
- for (ck = 0; n-- > 0; cp++)
- ck ^= *cp;
- return (ck);
- }
- /*
- * Tables to compute the day of year. Viva la leap.
- */
- static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- /*
- * Calculate the the Julian Day
- */
- static int
- mx4200_jday(
- int year,
- int month,
- int day_of_month
- )
- {
- register int day, i;
- int leap_year;
- /*
- * Is this a leap year ?
- */
- if (year % 4) {
- leap_year = 0; /* FALSE */
- } else {
- if (year % 100) {
- leap_year = 1; /* TRUE */
- } else {
- if (year % 400) {
- leap_year = 0; /* FALSE */
- } else {
- leap_year = 1; /* TRUE */
- }
- }
- }
- /*
- * Calculate the Julian Date
- */
- day = day_of_month;
- if (leap_year) {
- /* a leap year */
- if (day > day2tab[month - 1]) {
- return (0);
- }
- for (i = 0; i < month - 1; i++)
- day += day2tab[i];
- } else {
- /* not a leap year */
- if (day > day1tab[month - 1]) {
- return (0);
- }
- for (i = 0; i < month - 1; i++)
- day += day1tab[i];
- }
- return (day);
- }
- /*
- * Parse a mx4200 position/height/velocity sentence.
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 021=Position, Height Velocity Data
- * This sentence gives the receiver position, height,
- * navigation mode, and velocity north/east.
- * *This sentence is intended for post-analysis
- * applications.*
- * 1 float UTC measurement time (seconds into week)
- * 2 float WGS-84 Lattitude (degrees, minutes)
- * 3 char N=North, S=South
- * 4 float WGS-84 Longitude (degrees, minutes)
- * 5 char E=East, W=West
- * 6 float Altitude (meters above mean sea level)
- * 7 float Geoidal height (meters)
- * 8 float East velocity (m/sec)
- * 9 float West Velocity (m/sec)
- * 10 int Navigation Mode
- * Mode if navigating:
- * 1 = Position from remote device
- * 2 = 2-D position
- * 3 = 3-D position
- * 4 = 2-D differential position
- * 5 = 3-D differential position
- * 6 = Static
- * 8 = Position known -- reference station
- * 9 = Position known -- Navigator
- * Mode if not navigating:
- * 51 = Too few satellites
- * 52 = DOPs too large
- * 53 = Position STD too large
- * 54 = Velocity STD too large
- * 55 = Too many iterations for velocity
- * 56 = Too many iterations for position
- * 57 = 3 sat startup failed
- * 58 = Command abort
- */
- static char *
- mx4200_parse_p(
- struct peer *peer
- )
- {
- struct refclockproc *pp;
- struct mx4200unit *up;
- int sentence_type, mode;
- double mtime, lat, lon, alt, geoid, vele, veln;
- char north_south, east_west;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /* Should never happen! */
- if (up->moving) return ("mobile platform - no pos!");
- sscanf ( pp->a_lastcode,
- "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
- &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
- &alt, &geoid, &vele, &veln, &mode);
- /* Sentence type */
- if (sentence_type != PMVXG_D_PHV)
- return ("wrong rec-type");
- /*
- * return if not navigating
- */
- if (mode > 10)
- return ("not navigating");
- if (mode != 3 && mode != 5)
- return ("not navigating in 3D");
- /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
- if (lat < 0.0) return ("negative latitude");
- if (lat > 9000.0) lat = 9000.0;
- lat *= 0.01;
- lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
- /* North/South */
- switch (north_south) {
- case 'N':
- break;
- case 'S':
- lat *= -1.0;
- break;
- default:
- return ("invalid north/south indicator");
- }
- /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
- if (lon < 0.0) return ("negative longitude");
- if (lon > 180.0) lon = 180.0;
- lon *= 0.01;
- lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
- /* East/West */
- switch (east_west) {
- case 'E':
- break;
- case 'W':
- lon *= -1.0;
- break;
- default:
- return ("invalid east/west indicator");
- }
- /*
- * Normalize longitude to near 0 degrees.
- * Assume all data are clustered around first reading.
- */
- if (up->central_meridian == NOT_INITIALIZED) {
- up->central_meridian = lon;
- mx4200_debug(peer,
- "mx4200_receive: central meridian = %.9f \n",
- up->central_meridian);
- }
- lon -= up->central_meridian;
- if (lon < -180.0) lon += 360.0;
- if (lon > 180.0) lon -= 360.0;
- /*
- * Calculate running averages
- */
- up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
- up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
- up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
- up->N_fixes += 1.0;
- up->avg_lon /= up->N_fixes;
- up->avg_lat /= up->N_fixes;
- up->avg_alt /= up->N_fixes;
- mx4200_debug(peer,
- "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
- up->N_fixes, lat, lon, alt, up->central_meridian);
- return (NULL);
- }
- /*
- * Parse a mx4200 Status sentence
- * Parse a mx4200 Mode Data sentence
- * Parse a mx4200 Software Configuration sentence
- * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
- * (used only for logging raw strings)
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,000,XXX,XX,X,HHMM,X
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 000=Status.
- * Returns status of the receiver to the controller.
- * 1 Current Receiver Status:
- * ACQ = Satellite re-acquisition
- * ALT = Constellation selection
- * COR = Providing corrections (for reference stations only)
- * IAC = Initial acquisition
- * IDL = Idle, no satellites
- * NAV = Navigation
- * STS = Search the Sky (no almanac available)
- * TRK = Tracking
- * 2 Number of satellites that should be visible
- * 3 Number of satellites being tracked
- * 4 Time since last navigation status if not currently navigating
- * (hours, minutes)
- * 5 Initialization status:
- * 0 = Waiting for initialization parameters
- * 1 = Initialization completed
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 004=Software Configuration.
- * Defines the navigation mode and criteria for
- * acceptable navigation for the receiver.
- * 1 Constrain Altitude Mode:
- * 0 = Auto. Constrain altitude (2-D solution) and use
- * manual altitude input when 3 sats avalable. Do
- * not constrain altitude (3-D solution) when 4 sats
- * available.
- * 1 = Always constrain altitude (2-D solution).
- * 2 = Never constrain altitude (3-D solution).
- * 3 = Coast. Constrain altitude (2-D solution) and use
- * last GPS altitude calculation when 3 sats avalable.
- * Do not constrain altitude (3-D solution) when 4 sats
- * available.
- * 2 Altitude Reference: (always 0 for MX4200)
- * 0 = Ellipsoid
- * 1 = Geoid (MSL)
- * 3 Differential Navigation Control:
- * 0 = Disabled
- * 1 = Enabled
- * 4 Horizontal Acceleration Constant (m/sec**2)
- * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
- * 6 Tracking Elevation Limit (degrees)
- * 7 HDOP Limit
- * 8 VDOP Limit
- * 9 Time Output Mode:
- * U = UTC
- * L = Local time
- * 10 Local Time Offset (minutes) (absent on MX4200)
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,030,NNNN,FFF
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 030=Software Configuration.
- * This sentence contains the navigation processor
- * and baseband firmware version numbers.
- * 1 Nav Processor Version Number
- * 2 Baseband Firmware Version Number
- *
- * A typical message looks like this. Checksum has already been stripped.
- *
- * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
- *
- * Field Field Contents
- * ----- --------------
- * Block Label: $PMVXG
- * Sentence Type: 523=Time Recovery Parameters Currently in Use.
- * This sentence contains the configuration of the
- * time recovery feature of the receiver.
- * 1 Time Recovery Mode:
- * D = Dynamic; solve for position and time while moving
- * S = Static; solve for position and time while stationary
- * K = Known position input, solve for time only
- * N = No time recovery
- * 2 Time Synchronization:
- * U = UTC time
- * G = GPS time
- * 3 Time Mark Mode:
- * A = Always output a time pulse
- * V = Only output time pulse if time is valid (as determined
- * by Maximum Time Error)
- * 4 Maximum Time Error - the maximum error (in nanoseconds) for
- * which a time mark will be considered valid.
- * 5 User Time Bias - external bias in nanoseconds
- * 6 Time Message Control:
- * 0 = Do not output the time recovery message
- * 1 = Output the time recovery message (record 830) to
- * Control port
- * 2 = Output the time recovery message (record 830) to
- * Equipment port
- * 7 Reserved
- * 8 Position Known PRN (absent on MX 4200)
- *
- */
- static char *
- mx4200_parse_s(
- struct peer *peer
- )
- {
- struct refclockproc *pp;
- struct mx4200unit *up;
- int sentence_type;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
- /* Sentence type */
- switch (sentence_type) {
- case PMVXG_D_STATUS:
- msyslog(LOG_DEBUG,
- "mx4200: status: %s", pp->a_lastcode);
- break;
- case PMVXG_D_MODEDATA:
- msyslog(LOG_DEBUG,
- "mx4200: mode data: %s", pp->a_lastcode);
- break;
- case PMVXG_D_SOFTCONF:
- msyslog(LOG_DEBUG,
- "mx4200: firmware configuration: %s", pp->a_lastcode);
- break;
- case PMVXG_D_TRECOVUSEAGE:
- msyslog(LOG_DEBUG,
- "mx4200: time recovery parms: %s", pp->a_lastcode);
- break;
- default:
- return ("wrong rec-type");
- }
- return (NULL);
- }
- /*
- * Process a PPS signal, placing a timestamp in pp->lastrec.
- */
- static int
- mx4200_pps(
- struct peer *peer
- )
- {
- int temp_serial;
- struct refclockproc *pp;
- struct mx4200unit *up;
- struct timespec timeout;
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /*
- * Grab the timestamp of the PPS signal.
- */
- temp_serial = up->pps_i.assert_sequence;
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
- if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
- &timeout) < 0) {
- mx4200_debug(peer,
- "mx4200_pps: time_pps_fetch: serial=%ul, %s\n",
- (unsigned long)up->pps_i.assert_sequence, strerror(errno));
- refclock_report(peer, CEVNT_FAULT);
- return(1);
- }
- if (temp_serial == up->pps_i.assert_sequence) {
- mx4200_debug(peer,
- "mx4200_pps: assert_sequence serial not incrementing: %ul\n",
- (unsigned long)up->pps_i.assert_sequence);
- refclock_report(peer, CEVNT_FAULT);
- return(1);
- }
- /*
- * Check pps serial number against last one
- */
- if (up->lastserial + 1 != up->pps_i.assert_sequence &&
- up->lastserial != 0) {
- if (up->pps_i.assert_sequence == up->lastserial) {
- mx4200_debug(peer, "mx4200_pps: no new pps event\n");
- } else {
- mx4200_debug(peer, "mx4200_pps: missed %ul pps events\n",
- up->pps_i.assert_sequence - up->lastserial - 1UL);
- }
- refclock_report(peer, CEVNT_FAULT);
- }
- up->lastserial = up->pps_i.assert_sequence;
- /*
- * Return the timestamp in pp->lastrec
- */
- pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
- (u_int32) JAN_1970;
- pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
- 4.2949672960) + 0.5;
- return(0);
- }
- /*
- * mx4200_debug - print debug messages
- */
- #if defined(__STDC__)
- static void
- mx4200_debug(struct peer *peer, char *fmt, ...)
- #else
- static void
- mx4200_debug(peer, fmt, va_alist)
- struct peer *peer;
- char *fmt;
- #endif /* __STDC__ */
- {
- #ifdef DEBUG
- va_list ap;
- struct refclockproc *pp;
- struct mx4200unit *up;
- if (debug) {
- #if defined(__STDC__)
- va_start(ap, fmt);
- #else
- va_start(ap);
- #endif /* __STDC__ */
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- /*
- * Print debug message to stdout
- * In the future, we may want to get get more creative...
- */
- vprintf(fmt, ap);
- va_end(ap);
- }
- #endif
- }
- /*
- * Send a character string to the receiver. Checksum is appended here.
- */
- #if defined(__STDC__)
- static void
- mx4200_send(struct peer *peer, char *fmt, ...)
- #else
- static void
- mx4200_send(peer, fmt, va_alist)
- struct peer *peer;
- char *fmt;
- va_dcl
- #endif /* __STDC__ */
- {
- struct refclockproc *pp;
- struct mx4200unit *up;
- register char *cp;
- register int n, m;
- va_list ap;
- char buf[1024];
- u_char ck;
- #if defined(__STDC__)
- va_start(ap, fmt);
- #else
- va_start(ap);
- #endif /* __STDC__ */
- pp = peer->procptr;
- up = (struct mx4200unit *)pp->unitptr;
- cp = buf;
- *cp++ = '$';
- n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
- ck = mx4200_cksum(cp, n);
- cp += n;
- ++n;
- n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
- m = write(pp->io.fd, buf, (unsigned)n);
- if (m < 0)
- msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
- mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
- va_end(ap);
- }
- #else
- int refclock_mx4200_bs;
- #endif /* REFCLOCK */