summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/refclock_nmea.c
diff options
context:
space:
mode:
authorroberto <roberto@FreeBSD.org>2001-08-29 14:35:15 +0000
committerroberto <roberto@FreeBSD.org>2001-08-29 14:35:15 +0000
commit40b8e415eb0f835a9dd7a473ddf134ec67877fd7 (patch)
tree3cfb63f1a112ee17469b17fc1593a88d004ddda6 /contrib/ntp/ntpd/refclock_nmea.c
parenta5a8dc6136fcee95f261a31609a25669038c3861 (diff)
downloadFreeBSD-src-40b8e415eb0f835a9dd7a473ddf134ec67877fd7.zip
FreeBSD-src-40b8e415eb0f835a9dd7a473ddf134ec67877fd7.tar.gz
Virgin import of ntpd 4.1.0
Diffstat (limited to 'contrib/ntp/ntpd/refclock_nmea.c')
-rw-r--r--contrib/ntp/ntpd/refclock_nmea.c427
1 files changed, 364 insertions, 63 deletions
diff --git a/contrib/ntp/ntpd/refclock_nmea.c b/contrib/ntp/ntpd/refclock_nmea.c
index d90f71c..e9304ee 100644
--- a/contrib/ntp/ntpd/refclock_nmea.c
+++ b/contrib/ntp/ntpd/refclock_nmea.c
@@ -9,16 +9,25 @@
#if defined(REFCLOCK) && defined(CLOCK_NMEA)
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/time.h>
-#include <time.h>
-
#include "ntpd.h"
#include "ntp_io.h"
+#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif /* HAVE_PPSAPI */
+
/*
* This driver supports the NMEA GPS Receiver with
*
@@ -27,6 +36,23 @@
* The receiver used spits out the NMEA sentences for boat navigation.
* And you thought it was an information superhighway. Try a raging river
* filled with rapids and whirlpools that rip away your data and warp time.
+ *
+ * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
+ * On startup if initialization of the PPSAPI fails, it will fall back
+ * to the "normal" timestamps.
+ *
+ * The PPSAPI part of the driver understands fudge flag2 and flag3. If
+ * flag2 is set, it will use the clear edge of the pulse. If flag3 is
+ * set, kernel hardpps is enabled.
+ *
+ * GPS sentences other than RMC (the default) may be enabled by setting
+ * the relevent bits of 'mode' in the server configuration line
+ * server 127.127.20.x mode X
+ *
+ * bit 0 - enables RMC (1)
+ * bit 1 - enables GGA (2)
+ * bit 2 - enables GLL (4)
+ * multiple sentences may be selected
*/
/*
@@ -39,9 +65,11 @@
#endif
#define SPEED232 B4800 /* uart speed (4800 bps) */
#define PRECISION (-9) /* precision assumed (about 2 ms) */
-#define DCD_PRECISION (-20) /* precision assumed (about 1 us) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
#define REFID "GPS\0" /* reference id */
#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+#define NANOSECOND 1000000000 /* one second (ns) */
+#define RANGEGATE 500000 /* range gate (ns) */
#define LENNMEA 75 /* min timecode length */
@@ -59,6 +87,12 @@ struct nmeaunit {
int pollcnt; /* poll message counter */
int polled; /* Hand in a sample? */
l_fp tstamp; /* timestamp of last poll */
+#ifdef HAVE_PPSAPI
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+#endif /* HAVE_PPSAPI */
};
/*
@@ -66,6 +100,12 @@ struct nmeaunit {
*/
static int nmea_start P((int, struct peer *));
static void nmea_shutdown P((int, struct peer *));
+#ifdef HAVE_PPSAPI
+static void nmea_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int nmea_ppsapi P((struct peer *, int, int));
+static int nmea_pps P((struct nmeaunit *, l_fp *));
+#endif /* HAVE_PPSAPI */
static void nmea_receive P((struct recvbuf *));
static void nmea_poll P((int, struct peer *));
static void gps_send P((int, const char *, struct peer *));
@@ -78,7 +118,11 @@ struct refclock refclock_nmea = {
nmea_start, /* start up driver */
nmea_shutdown, /* shut down driver */
nmea_poll, /* transmit poll message */
- noentry, /* handle control */
+#ifdef HAVE_PPSAPI
+ nmea_control, /* fudge control */
+#else
+ noentry, /* fudge control */
+#endif /* HAVE_PPSAPI */
noentry, /* initialize driver */
noentry, /* buginfo */
NOFLAGS /* not used */
@@ -130,13 +174,27 @@ nmea_start(
/*
* Initialize miscellaneous variables
*/
- peer->precision = DCD_PRECISION;
+ peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
memcpy((char *)&pp->refid, REFID, 4);
up->pollcnt = 2;
gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
+#ifdef HAVE_PPSAPI
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_create failed: %m");
+ return (1);
+ }
+ return(nmea_ppsapi(peer, 0, 0));
+#else
return (1);
+#endif /* HAVE_PPSAPI */
}
/*
@@ -153,10 +211,149 @@ nmea_shutdown(
pp = peer->procptr;
up = (struct nmeaunit *)pp->unitptr;
+#ifdef HAVE_PPSAPI
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+#endif /* HAVE_PPSAPI */
io_closeclock(&pp->io);
free(up);
}
+#ifdef HAVE_PPSAPI
+/*
+ * nmea_control - fudge control
+ */
+static void
+nmea_control(
+ int unit, /* unit (not used */
+ struct refclockstat *in, /* input parameters (not uded) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+nmea_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct nmeaunit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+nmea_pps(
+ struct nmeaunit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ *tsptr = tstmp;
+ return (1);
+}
+#endif /* HAVE_PPSAPI */
+
/*
* nmea_receive - receive data from the serial interface
*/
@@ -168,11 +365,14 @@ nmea_receive(
register struct nmeaunit *up;
struct refclockproc *pp;
struct peer *peer;
- l_fp trtmp;
int month, day;
int i;
char *cp, *dp;
int cmdtype;
+ /* Use these variables to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
/*
* Initialize pointers and read the timecode and timestamp
@@ -180,23 +380,18 @@ nmea_receive(
peer = (struct peer *)rbufp->recv_srcclock;
pp = peer->procptr;
up = (struct nmeaunit *)pp->unitptr;
- pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
/*
* There is a case that a <CR><LF> gives back a "blank" line
*/
- if (pp->lencode == 0)
+ if (rd_lencode == 0)
return;
- /*
- * We get a buffer and timestamp for each <cr>.
- */
- pp->lastrec = up->tstamp = trtmp;
- up->pollcnt = 2;
#ifdef DEBUG
if (debug)
- printf("nmea: timecode %d %s\n", pp->lencode,
- pp->a_lastcode);
+ printf("nmea: gpsread %d %s\n", rd_lencode,
+ rd_lastcode);
#endif
/*
@@ -204,18 +399,31 @@ nmea_receive(
* we only care about a few of them. The most important being
* the $GPRMC format
* $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
- * $GPGGA,162617.0,4548.339,N,00837.719,E,1,07,0.97,00262,M,048,M,,*5D
+ * For Magellan (ColorTrak) GLL probably datum (order of sentences)
+ * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
+ * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
+ * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
+ * $GPRMB,...
+ * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
+ * $GPAPB,...
+ * $GPGSA,...
+ * $GPGSV,...
+ * $GPGSV,...
*/
-#define GPRMC 0
-#define GPXXX 1
-#define GPGCA 2
- cp = pp->a_lastcode;
+#define GPXXX 0
+#define GPRMC 1
+#define GPGGA 2
+#define GPGLL 4
+ cp = rd_lastcode;
cmdtype=0;
if(strncmp(cp,"$GPRMC",6)==0) {
cmdtype=GPRMC;
}
else if(strncmp(cp,"$GPGGA",6)==0) {
- cmdtype=GPGCA;
+ cmdtype=GPGGA;
+ }
+ else if(strncmp(cp,"$GPGLL",6)==0) {
+ cmdtype=GPGLL;
}
else if(strncmp(cp,"$GPXXX",6)==0) {
cmdtype=GPXXX;
@@ -223,14 +431,84 @@ nmea_receive(
else
return;
+
+ /* See if I want to process this message type */
+ if ( ((peer->ttlmax == 0) && (cmdtype != GPRMC))
+ || ((peer->ttlmax != 0) && !(cmdtype & peer->ttlmax)) )
+ return;
+
+ pp->lencode = rd_lencode;
+ strcpy(pp->a_lastcode,rd_lastcode);
+ cp = pp->a_lastcode;
+
+ pp->lastrec = up->tstamp = rd_tmp;
+ up->pollcnt = 2;
+
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+
+ /* Grab field depending on clock string type */
switch( cmdtype ) {
case GPRMC:
- case GPGCA:
/*
- * Check time code format of NMEA
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,2);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,1);
+ break;
+
+
+ case GPGGA:
+ /*
+ * Test for synchronization. Check for quality byte.
*/
+ dp = field_parse(cp,6);
+ if( dp[0] == '0')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ /* Now point at the time field */
dp = field_parse(cp,1);
+ break;
+
+
+ case GPGLL:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,6);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,5);
+ break;
+
+
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ /*
+ * Check time code format of NMEA
+ */
+
if( !isdigit((int)dp[0]) ||
!isdigit((int)dp[1]) ||
!isdigit((int)dp[2]) ||
@@ -242,40 +520,55 @@ nmea_receive(
return;
}
- /*
- * Test for synchronization. Check for quality byte.
- */
- dp = field_parse(cp,2);
- if( dp[0] != 'A') {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
+
+ /*
+ * Convert time and check values.
+ */
+ pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
+ pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
+ pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
+ /* Default to 0 milliseconds, if decimal convert milliseconds in
+ one, two or three digits
+ */
+ pp->msec = 0;
+ if (dp[6] == '.') {
+ if (isdigit((int)dp[7])) {
+ pp->msec = (dp[7] - '0') * 100;
+ if (isdigit((int)dp[8])) {
+ pp->msec += (dp[8] - '0') * 10;
+ if (isdigit((int)dp[9])) {
+ pp->msec += (dp[9] - '0');
+ }
+ }
}
- break;
- case GPXXX:
- return;
- default:
- return;
+ }
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59
+ || pp->msec > 1000) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
}
- if (cmdtype ==GPGCA) {
- /* only time */
- time_t tt = time(NULL);
- struct tm * t = gmtime(&tt);
- day = t->tm_mday;
- month = t->tm_mon + 1;
- pp->year= t->tm_year;
- } else {
- dp = field_parse(cp,9);
+
/*
* Convert date and check values.
*/
- day = dp[0] - '0';
- day = (day * 10) + dp[1] - '0';
- month = dp[2] - '0';
- month = (month * 10) + dp[3] - '0';
- pp->year = dp[4] - '0';
- pp->year = (pp->year * 10) + dp[5] - '0';
+ if (cmdtype==GPRMC) {
+ dp = field_parse(cp,9);
+ day = dp[0] - '0';
+ day = (day * 10) + dp[1] - '0';
+ month = dp[2] - '0';
+ month = (month * 10) + dp[3] - '0';
+ pp->year = dp[4] - '0';
+ pp->year = (pp->year * 10) + dp[5] - '0';
+ }
+ else {
+ /* only time */
+ time_t tt = time(NULL);
+ struct tm * t = gmtime(&tt);
+ day = t->tm_mday;
+ month = t->tm_mon + 1;
+ pp->year= t->tm_year;
}
if (month < 1 || month > 12 || day < 1) {
@@ -283,6 +576,9 @@ nmea_receive(
return;
}
+ /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
+ /* good thing that 2000 is a leap year */
+ /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
if (pp->year % 4) {
if (day > day1tab[month - 1]) {
refclock_report(peer, CEVNT_BADTIME);
@@ -300,19 +596,17 @@ nmea_receive(
}
pp->day = day;
- dp = field_parse(cp,1);
+
+#ifdef HAVE_PPSAPI
/*
- * Convert time and check values.
+ * If the PPSAPI is working, rather use its timestamps.
+ * assume that the PPS occurs on the second so blow any msec
*/
- pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
- pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
- pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
- pp->msec = 0;
-
- if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) {
- refclock_report(peer, CEVNT_BADTIME);
- return;
+ if (nmea_pps(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->msec = 0;
}
+#endif /* HAVE_PPSAPI */
/*
* Process the new sample in the median filter and determine the
@@ -322,11 +616,14 @@ nmea_receive(
* time, which may cause a paranoid protocol module to chuck out
* the data.
*/
+
if (!refclock_process(pp)) {
refclock_report(peer, CEVNT_BADTIME);
return;
}
+
+
/*
* Only go on if we had been polled.
*/
@@ -336,7 +633,11 @@ nmea_receive(
refclock_receive(peer);
+ /* If we get here - what we got from the clock is OK, so say so */
+ refclock_report(peer, CEVNT_NOMINAL);
+
record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
}
/*
OpenPOWER on IntegriCloud