From: Juergen Perlinger
Date: Fri, 17 Jan 2020 05:59:50 +0000 (+0100)
Subject: [Bug 3636] NMEA: combine time/date from multiple sentences
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c250a4607dd38e441324fd29467b985aac4e199c;p=thirdparty%2Fntp.git
[Bug 3636] NMEA: combine time/date from multiple sentences
bk: 5e214d56Ar7oRt1p7Fd3OhvQzqjHCw
---
diff --git a/ChangeLog b/ChangeLog
index 0387b9a9b..7265ea04b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,7 @@
* [Sec 3610] process_control() should bail earlier on short packets. stenn@
- Reported by Philippe Antoine
+* [Bug 3636] NMEA: combine time/date from multiple sentences
* [Bug 3628] raw DCF decoding - improve robustness with Zeller's congruence
- implement Zeller's congruence in libparse and libntp
* [Bug 3620] memory leak in ntpq sysinfo
diff --git a/html/drivers/driver20.html b/html/drivers/driver20.html
index 8d7f69e1c..1c3ac782f 100644
--- a/html/drivers/driver20.html
+++ b/html/drivers/driver20.html
@@ -13,7 +13,7 @@
Generic NMEA GPS Receiver
Last update:
- 4-Sep-2019 21:23
+ 13-Jan-2020 07:12
UTC
Synopsis
@@ -85,8 +85,11 @@
Accord |
- | $PGRMF,GWEEK,WTIME,DATE,UTC,LEAPS,LAT,LAT_REF,LON,LON_REF,TYPE,MODE,SPD,HDOP,TDOP*CS<cr><lf> |
+ $PGRMF,gpsWk,gpsTow,DATE,UTC,LEAPS,LAT,LAT_REF,LON,LON_REF,TYPE,MODE,SPD,HDOP,TDOP*CS<cr><lf> |
Garmin |
+
+ | $PUBX,04,UTC,DATE,utcTow,utcWk,LEAPS,clkBias,clkDrift,tpGran,*CS<cr><lf> |
+ UBLOX |
@@ -125,8 +128,11 @@
GPSTIME |
Time of day on GPS timescale. Hours, minutes and seconds [fraction (opt.)] (hhmmss[.f]) |
- | GWEEK |
- Week number in the GPS time scale, modulo 1024 (0..1023) |
+ gpsTow |
+ GPS week time, seconds since start of GPS week (0..604799) |
+
+ | gpsWk |
+ Week number in the GPS time scale (may exceed 1024) |
| G_UNIT |
Geoid units (M/F) |
@@ -172,9 +178,6 @@
| UTC |
Time of day on UTC timescale. Hours, minutes and seconds [fraction (opt.)] (hhmmss[.fff]) |
-
- | WTIME |
- GPS week time, seconds since start of GPS week (0..604799) |
| YYYY |
Year |
@@ -314,9 +317,14 @@
0x100 |
process $PGRMF |
- | 9-15 |
+ 9 |
+ 512 |
+ 0x200 |
+ process $PUBX,04 |
+
+ | 10-15 |
|
- 0xFE00 |
+ 0xFC00 |
reserved - leave 0 |
| 16 |
diff --git a/include/ntp_calgps.h b/include/ntp_calgps.h
index 970c4d096..7b5d83d13 100644
--- a/include/ntp_calgps.h
+++ b/include/ntp_calgps.h
@@ -107,17 +107,32 @@ gpsntp_from_calendar_ex(TcCivilDate*, l_fp fofs, int/*BOOL*/ warp);
static inline TNtpDatum
gpsntp_from_calendar(TcCivilDate * pCiv, l_fp fofs) {
- return gpsntp_from_calendar_ex(pCiv, fofs, TRUE);
+ return gpsntp_from_calendar_ex(pCiv, fofs, TRUE);
}
extern TNtpDatum
-gpsntp_from_daytime1(TcCivilDate *dt, l_fp fofs, l_fp pivot);
+gpsntp_from_daytime1_ex(TcCivilDate *dt, l_fp fofs, l_fp pivot, int/*BOOL*/ warp);
+
+static inline TNtpDatum
+gpsntp_from_daytime1(TcCivilDate *dt, l_fp fofs, l_fp pivot) {
+ return gpsntp_from_daytime1_ex(dt, fofs, pivot, TRUE);
+}
extern TNtpDatum
-gpsntp_from_daytime2(TcCivilDate *dt, l_fp fofs, TcNtpDatum *pivot);
+gpsntp_from_daytime2_ex(TcCivilDate *dt, l_fp fofs, TcNtpDatum *pivot, int/*BOOL*/ warp);
+
+static inline TNtpDatum
+gpsntp_from_daytime2(TcCivilDate *dt, l_fp fofs, TcNtpDatum *pivot) {
+ return gpsntp_from_daytime2_ex(dt, fofs, pivot, TRUE);
+}
extern TNtpDatum
-gpsntp_from_gpscal(TcGpsDatum*);
+gpsntp_from_gpscal_ex(TcGpsDatum*, int/*BOOL*/ warp);
+
+static inline TNtpDatum
+gpsntp_from_gpscal(TcGpsDatum *wd) {
+ return gpsntp_from_gpscal_ex(wd, FALSE);
+}
extern void
gpsntp_to_calendar(TCivilDate*, TcNtpDatum*);
diff --git a/libntp/ntp_calgps.c b/libntp/ntp_calgps.c
index c478aa8af..3ce969a30 100644
--- a/libntp/ntp_calgps.c
+++ b/libntp/ntp_calgps.c
@@ -168,7 +168,7 @@ _gpsntp_fix_gps_era(
days = sign ^ (days - base);
days %= clen;
days = base + (sign & clen) + (sign ^ days);
-
+
out.days = days;
return out;
}
@@ -188,7 +188,8 @@ static TNtpDatum
_gpsntp_from_daytime(
TcCivilDate * jd,
l_fp fofs,
- TcNtpDatum * pivot
+ TcNtpDatum * pivot,
+ int warp
)
{
static const int32_t shift = SECSPERDAY / 2;
@@ -211,7 +212,7 @@ _gpsntp_from_daytime(
retv.days += (retv.secs < lim ||
(retv.secs == lim && retv.frac < pivot->frac));
}
- return _gpsntp_fix_gps_era(&retv);
+ return warp ? _gpsntp_fix_gps_era(&retv) : retv;
}
/* -----------------------------------------------------------------
@@ -221,15 +222,16 @@ _gpsntp_from_daytime(
* stamp is less or equal to 12 hours absolute.
*/
TNtpDatum
-gpsntp_from_daytime2(
+gpsntp_from_daytime2_ex(
TcCivilDate * jd,
l_fp fofs,
- TcNtpDatum * pivot
+ TcNtpDatum * pivot,
+ int/*BOOL*/ warp
)
{
TNtpDatum dpiv = *pivot;
_norm_ntp_datum(&dpiv);
- return _gpsntp_from_daytime(jd, fofs, &dpiv);
+ return _gpsntp_from_daytime(jd, fofs, &dpiv, warp);
}
/* -----------------------------------------------------------------
@@ -240,10 +242,11 @@ gpsntp_from_daytime2(
* NTP time stamp.
*/
TNtpDatum
-gpsntp_from_daytime1(
+gpsntp_from_daytime1_ex(
TcCivilDate * jd,
l_fp fofs,
- l_fp pivot
+ l_fp pivot,
+ int/*BOOL*/ warp
)
{
vint64 pvi64;
@@ -255,7 +258,7 @@ gpsntp_from_daytime1(
dpiv.days = split.hi;
dpiv.secs = split.lo;
dpiv.frac = pivot.l_uf;
- return _gpsntp_from_daytime(jd, fofs, &dpiv);
+ return _gpsntp_from_daytime(jd, fofs, &dpiv, warp);
}
/* -----------------------------------------------------------------
@@ -271,7 +274,7 @@ gpsntp_from_calendar_ex(
{
TGpsDatum gps;
gps = gpscal_from_calendar_ex(jd, fofs, warp);
- return gpsntp_from_gpscal(&gps);
+ return gpsntp_from_gpscal_ex(&gps, FALSE);
}
/* -----------------------------------------------------------------
@@ -294,15 +297,23 @@ gpsntp_to_calendar(
* get day/tod representation from week/tow datum
*/
TNtpDatum
-gpsntp_from_gpscal(
- TcGpsDatum * gd
+gpsntp_from_gpscal_ex(
+ TcGpsDatum * gd,
+ int/*BOOL*/ warp
)
{
TNtpDatum retv;
vint64 ts64;
ntpcal_split split;
-
- ts64 = ntpcal_weekjoin(gd->weeks, gd->wsecs);
+ TGpsDatum date = *gd;
+
+ if (warp) {
+ uint32_t base = basedate_get_gpsweek() + GPSNTP_WSHIFT;
+ _norm_gps_datum(&date);
+ date.weeks = ((date.weeks - base) & 1023u) + base;
+ }
+
+ ts64 = ntpcal_weekjoin(date.weeks, date.wsecs);
ts64 = subv64u32(&ts64, (GPSNTP_DSHIFT * SECSPERDAY));
split = ntpcal_daysplit(&ts64);
@@ -349,7 +360,7 @@ _gpscal_fix_gps_era(
*/
uint32_t base, week;
TGpsDatum out = *in;
-
+
week = out.weeks;
base = basedate_get_gpsweek() + GPSNTP_WSHIFT;
week = base + ((week - base) & (GPSWEEKS - 1));
@@ -393,7 +404,7 @@ gpscal_from_calendar_ex(
/* (-DAY_GPS_STARTS) (mod 7*1024) -- complement of cycle shift */
static const uint32_t s_compl_shift =
(7 * 1024) - DAY_GPS_STARTS % (7 * 1024);
-
+
TGpsDatum gps;
TCivilDate cal;
int32_t days, week;
@@ -473,7 +484,7 @@ gpscal_to_calendar(
TNtpDatum nd;
memset(cd, 0, sizeof(*cd));
- nd = gpsntp_from_gpscal(wd);
+ nd = gpsntp_from_gpscal_ex(wd, FALSE);
gpsntp_to_calendar(cd, &nd);
}
@@ -547,7 +558,7 @@ gpscal_from_weektime2(
TcGpsDatum * pivot
)
{
- TGpsDatum wpiv = * pivot;
+ TGpsDatum wpiv = * pivot;
_norm_gps_datum(&wpiv);
return _gpscal_from_weektime(wsecs, fofs, &wpiv);
}
@@ -555,7 +566,7 @@ gpscal_from_weektime2(
/* -----------------------------------------------------------------
* epand a time-of-week around an pivot given as LFP, which in turn
* is expanded around the current system time and then converted
- * into a week datum.
+ * into a week datum.
*/
TGpsDatum
gpscal_from_weektime1(
@@ -592,7 +603,7 @@ gpscal_from_gpsntp(
TGpsDatum retv;
vint64 ts64;
ntpcal_split split;
-
+
ts64 = ntpcal_dayjoin(gd->days, gd->secs);
ts64 = addv64u32(&ts64, (GPSNTP_DSHIFT * SECSPERDAY));
split = ntpcal_weeksplit(&ts64);
diff --git a/ntpd/refclock_nmea.c b/ntpd/refclock_nmea.c
index 93c82eee1..4fdadea61 100644
--- a/ntpd/refclock_nmea.c
+++ b/ntpd/refclock_nmea.c
@@ -94,7 +94,7 @@
#define NMEA_QUIETPPS_MASK 0x00020000U
#define NMEA_DATETRUST_MASK 0x00040000U
-#define NMEA_PROTO_IDLEN 5 /* tag name must be at least 5 chars */
+#define NMEA_PROTO_IDLEN 4 /* tag name must be at least 4 chars */
#define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */
#define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */
#define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */
@@ -152,6 +152,8 @@
#define SPEED232 B4800 /* uart speed (4800 bps) */
#define PRECISION (-9) /* precision assumed (about 2 ms) */
#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define DATE_HOLD 16 /* seconds to hold on provided GPS date */
+#define DATE_HLIM 4 /* when do we take ANY date format */
#define REFID "GPS\0" /* reference id */
#define DESCRIPTION "NMEA GPS Clock" /* who we are */
#ifndef O_NOCTTY
@@ -182,7 +184,8 @@
*/
#define NMEA_GPZDG 4
#define NMEA_PGRMF 5
-#define NMEA_ARRAY_SIZE (NMEA_PGRMF + 1)
+#define NMEA_PUBX04 6
+#define NMEA_ARRAY_SIZE (NMEA_PUBX04 + 1)
/*
* Sentence selection mode bits
@@ -192,6 +195,7 @@
#define USE_GPGLL 0x00000004u
#define USE_GPZDA 0x00000008u
#define USE_PGRMF 0x00000100u
+#define USE_PUBX04 0x00000200u
/* mapping from sentence index to controlling mode bit */
static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] =
@@ -201,7 +205,8 @@ static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] =
USE_GPGLL,
USE_GPZDA,
USE_GPZDA,
- USE_PGRMF
+ USE_PGRMF,
+ USE_PUBX04
};
/* date formats we support */
@@ -210,6 +215,15 @@ enum date_fmt {
DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */
};
+/* date type */
+enum date_type {
+ DTYP_NONE,
+ DTYP_Y2D, /* 2-digit year */
+ DTYP_W10B, /* 10-bit week in GPS epoch */
+ DTYP_Y4D, /* 4-digit (full) year */
+ DTYP_WEXT /* extended week in GPS epoch */
+};
+
/* results for 'field_init()'
*
* Note: If a checksum is present, the checksum test must pass OK or the
@@ -237,6 +251,8 @@ typedef struct {
u_char gps_time; /* use GPS time, not UTC */
l_fp last_reftime; /* last processed reference stamp */
TNtpDatum last_gpsdate; /* last processed split date/time */
+ u_short hold_gpsdate; /* validity ticker for above */
+ u_short type_gpsdate; /* date info type for above */
/* tally stats, reset each poll cycle */
struct
{
@@ -407,7 +423,7 @@ nmea_start(
up->ppsapi_fd = -1;
# endif /* HAVE_PPSAPI */
ZERO(up->tally);
-
+
/* Initialize miscellaneous variables */
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
@@ -598,6 +614,9 @@ nmea_timer(
up->lb_buf[0] = '\0';
up->lb_len = 0;
}
+
+ if (up->hold_gpsdate && (--up->hold_gpsdate < DATE_HLIM))
+ up->type_gpsdate = DTYP_NONE;
}
/*
@@ -641,8 +660,10 @@ nmea_procrec(
/* results of sentence/date/time parsing */
u_char sentence; /* sentence tag */
int checkres;
+ int warp; /* warp to GPS base date */
char * cp;
int rc_date, rc_time;
+ u_short rc_dtyp;
# ifdef HAVE_PPSAPI
int withpps = 0;
# endif /* HAVE_PPSAPI */
@@ -701,6 +722,8 @@ nmea_procrec(
sentence = NMEA_GPZDG;
else if (strncmp(cp, "PGRMF,", 6) == 0)
sentence = NMEA_PGRMF;
+ else if (strncmp(cp, "PUBX,04,", 8) == 0)
+ sentence = NMEA_PUBX04;
else
return; /* not something we know about */
@@ -751,9 +774,18 @@ nmea_procrec(
* Once have processed a $GPZDG, do not process any further UTC
* sentences (all but $GPZDG currently).
*/
- if (up->gps_time && NMEA_GPZDG != sentence) {
- up->tally.filtered++;
- return;
+ if (sentence == NMEA_GPZDG) {
+ if (!up->gps_time) {
+ msyslog(LOG_INFO,
+ "%s using GPS time as if it were UTC",
+ refnumtoa(&peer->srcadr));
+ up->gps_time = 1;
+ }
+ } else {
+ if (up->gps_time) {
+ up->tally.filtered++;
+ return;
+ }
}
DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n",
@@ -764,14 +796,18 @@ nmea_procrec(
* sensitive data from the last timecode.
*/
rc_date = -1; /* assume we have to do day-time mapping */
- switch (sentence) {
+ rc_dtyp = DTYP_NONE;
+ switch (sentence) {
case NMEA_GPRMC:
/* Check quality byte, fetch data & time */
rc_time = parse_time(&date, &tofs, &rdata, 1);
pp->leap = parse_qual(&rdata, 2, 'A', 0);
- rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY);
- if (CLK_FLAG4 & pp->sloppyclockflag)
+ if (up->type_gpsdate <= DTYP_Y2D) {
+ rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY);
+ rc_dtyp = DTYP_Y2D;
+ }
+ if (CLK_FLAG4 & pp->sloppyclockflag)
field_wipe(&rdata, 3, 4, 5, 6, -1);
break;
@@ -793,31 +829,47 @@ nmea_procrec(
case NMEA_GPZDA:
/* No quality. Assume best, fetch time & full date */
- pp->leap = LEAP_NOWARNING;
- rc_time = parse_time(&date, &tofs, &rdata, 1);
- rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
+ rc_time = parse_time(&date, &tofs, &rdata, 1);
+ if (up->type_gpsdate <= DTYP_Y4D) {
+ rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
+ rc_dtyp = DTYP_Y4D;
+ }
break;
case NMEA_GPZDG:
/* Check quality byte, fetch time & full date */
rc_time = parse_time(&date, &tofs, &rdata, 1);
- rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
pp->leap = parse_qual(&rdata, 4, '0', 1);
--tofs.l_ui; /* GPZDG gives *following* second */
+ if (up->type_gpsdate <= DTYP_Y4D) {
+ rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
+ rc_dtyp = DTYP_Y4D;
+ }
break;
case NMEA_PGRMF:
- /* get date, time, qualifier and GPS weektime. We need
- * date and time-of-day for the century fix, so we read
- * them first.
- */
- rc_time = parse_gpsw(&wgps, &rdata, 1, 2, 5);
- rc_date = -2;
+ /* get time, qualifier and GPS weektime. */
+ rc_time = parse_time(&date, &tofs, &rdata, 4);
+ if (up->type_gpsdate <= DTYP_W10B) {
+ rc_date = parse_gpsw(&wgps, &rdata, 1, 2, 5);
+ rc_dtyp = DTYP_W10B;
+ }
pp->leap = parse_qual(&rdata, 11, '0', 1);
if (CLK_FLAG4 & pp->sloppyclockflag)
field_wipe(&rdata, 6, 8, -1);
break;
+ case NMEA_PUBX04:
+ /* PUBX,04 is peculiar. The UTC time-of-week is the *internal*
+ * time base, which is not exactly on par with the fix time.
+ */
+ rc_time = parse_time(&date, &tofs, &rdata, 2);
+ if (up->type_gpsdate <= DTYP_WEXT) {
+ rc_date = parse_gpsw(&wgps, &rdata, 5, 4, -1);
+ rc_dtyp = DTYP_WEXT;
+ }
+ break;
+
default:
INVARIANT(0); /* Coverity 97123 */
return;
@@ -861,19 +913,35 @@ nmea_procrec(
rd_timestamp = ntpfp_with_fudge(
rd_timestamp, pp->fudgetime2);
+ /* set the GPS base date, if possible */
+ warp = !(peer->ttl & NMEA_DATETRUST_MASK);
+ if (rc_dtyp != DTYP_NONE) {
+ DPRINTF(1, ("%s saving date, type=%hu\n",
+ refnumtoa(&peer->srcadr), rc_dtyp));
+ switch (rc_dtyp) {
+ case DTYP_W10B:
+ up->last_gpsdate = gpsntp_from_gpscal_ex(
+ &wgps, (warp = TRUE));
+ break;
+ case DTYP_WEXT:
+ up->last_gpsdate = gpsntp_from_gpscal_ex(
+ &wgps, warp);
+ break;
+ default:
+ up->last_gpsdate = gpsntp_from_calendar_ex(
+ &date, tofs, warp);
+ break;
+ }
+ up->type_gpsdate = rc_dtyp;
+ up->hold_gpsdate = DATE_HOLD;
+ }
/* now convert and possibly extend/expand the time stamp. */
- if (rc_date == 1) { /* (truncated) date available */
- dntp = gpsntp_from_calendar_ex(
- &date, tofs, !(peer->ttl & NMEA_DATETRUST_MASK));
- up->last_gpsdate = dntp;
- } else if (rc_date == -2) { /* full GPS week time */
- dntp = gpsntp_from_gpscal(&wgps);
- up->last_gpsdate = dntp;
- } else if (up->last_gpsdate.days) { /* time of day, based */
- dntp = gpsntp_from_daytime2(&date, tofs, &up->last_gpsdate);
- up->last_gpsdate = dntp;
- } else { /* time of day, floating */
- dntp = gpsntp_from_daytime1(&date, tofs, rd_timestamp);
+ if (up->hold_gpsdate) { /* time of day, based */
+ dntp = gpsntp_from_daytime2_ex(
+ &date, tofs, &up->last_gpsdate, warp);
+ } else { /* time of day, floating */
+ dntp = gpsntp_from_daytime1_ex(
+ &date, tofs, rd_timestamp, warp);
}
if (debug) {
@@ -891,13 +959,6 @@ nmea_procrec(
# endif /* !HAVE_PPSAPI */
}
- /* Check if we must enter GPS time mode; log so if we do */
- if (!up->gps_time && (sentence == NMEA_GPZDG)) {
- msyslog(LOG_INFO, "%s using GPS time as if it were UTC",
- refnumtoa(&peer->srcadr));
- up->gps_time = 1;
- }
-
/* Get the reference time stamp from the calendar buffer.
* Process the new sample in the median filter and determine the
* timecode timestamp, but only if the PPS is not in control.
@@ -974,7 +1035,7 @@ nmea_procrec(
* timing is badly affected unless a PPS channel is also associated with
* the clock instance. TCP leaves us nothing to improve on here.
* -------------------------------------------------------------------
- */
+ */
static void
nmea_receive(
struct recvbuf * rbufp
@@ -991,7 +1052,7 @@ nmea_receive(
/* paranoia check: */
if (up->lb_len >= sizeof(up->lb_buf))
up->lb_len = 0;
-
+
/* pick up last assembly position; leave room for NUL */
dp = up->lb_buf + up->lb_len;
de = up->lb_buf + sizeof(up->lb_buf) - 1;
@@ -1013,7 +1074,7 @@ nmea_receive(
} else if (ch == '\n' || ch == '\r') {
*dp = '\0';
up->lb_len = (int)(dp - up->lb_buf);
- dp = up->lb_buf;
+ dp = up->lb_buf;
nmea_procrec(peer, rbufp->recv_time);
} else if (ch >= 0x20 && ch < 0x7f) {
*dp++ = ch;
@@ -1388,40 +1449,6 @@ static int _parse_sep(UCC *cp, UCC ** ep)
return rc;
}
-/* /(\.[[:digit:]]*)?/ --> l_fp{0, f}
- * read fractional seconds, convert to l_fp
- *
- * Only the first 9 decimal digits are evaluated; any excess is parsed
- * away but silently ignored. (--> truncation to 1 nanosecond)
- */
-static int _parse_frac(UCC *cp, UCC ** ep, l_fp *into)
-{
- unsigned int ndig = 9;
- struct timespec ts;
-
- ZERO(ts);
- if (*cp == '.') {
- while (ndig && isdigit(*++cp)) {
- ts.tv_nsec *= 10;
- ts.tv_nsec += (*cp - '0');
- --ndig;
- }
- if (ndig && ts.tv_nsec) {
- uint32_t mmul = 10;
- do {
- if (ndig & 1)
- ts.tv_nsec *= mmul;
- mmul *= mmul;
- } while (0 != (ndig >>= 1));
- }
- while (isdigit(*cp))
- ++cp;
- }
- *ep = cp;
- *into = tspec_intv_to_lfp(ts);
- return TRUE;
-}
-
/* /[[:digit:]]{2}/ --> uint16_t */
static int _parse_num2d(UCC *cp, UCC ** ep, uint16_t *into)
{
@@ -1437,15 +1464,15 @@ static int _parse_num2d(UCC *cp, UCC ** ep, uint16_t *into)
}
/* /[[:digit:]]+/ --> uint16_t */
-static int _parse_u16(UCC *cp, UCC ** ep, uint16_t *into)
+static int _parse_u16(UCC *cp, UCC **ep, uint16_t *into, unsigned int ndig)
{
uint16_t num = 0;
int rc = FALSE;
- if (isdigit(*cp)) {
+ if (isdigit(*cp) && ndig) {
rc = TRUE;
do
- num = num * 10 + *cp++ - '0';
- while (isdigit(*cp));
+ num = (num * 10) + (*cp - '0');
+ while (isdigit(*++cp) && --ndig);
*into = num;
}
*ep = cp;
@@ -1453,21 +1480,52 @@ static int _parse_u16(UCC *cp, UCC ** ep, uint16_t *into)
}
/* /[[:digit:]]+/ --> uint32_t */
-static int _parse_u32(UCC *cp, UCC ** ep, uint32_t *into)
+static int _parse_u32(UCC *cp, UCC **ep, uint32_t *into, unsigned int ndig)
{
uint32_t num = 0;
int rc = FALSE;
- if (isdigit(*cp)) {
+ if (isdigit(*cp) && ndig) {
rc = TRUE;
do
- num = num * 10 + *cp++ - '0';
- while (isdigit(*cp));
+ num = (num * 10) + (*cp - '0');
+ while (isdigit(*++cp) && --ndig);
*into = num;
}
*ep = cp;
return rc;
}
+/* /(\.[[:digit:]]*)?/ --> l_fp{0, f}
+ * read fractional seconds, convert to l_fp
+ *
+ * Only the first 9 decimal digits are evaluated; any excess is parsed
+ * away but silently ignored. (--> truncation to 1 nanosecond)
+ */
+static int _parse_frac(UCC *cp, UCC **ep, l_fp *into)
+{
+ static const uint32_t powtab[10] = {
+ 0,
+ 100000000, 10000000, 1000000,
+ 100000, 10000, 1000,
+ 100, 10, 1
+ };
+
+ struct timespec ts;
+ ZERO(ts);
+ if (*cp == '.') {
+ uint32_t fval = 0;
+ UCC * sp = cp + 1;
+ if (_parse_u32(sp, &cp, &fval, 9))
+ ts.tv_nsec = fval * powtab[(size_t)(cp - sp)];
+ while (isdigit(*cp))
+ ++cp;
+ }
+
+ *ep = cp;
+ *into = tspec_intv_to_lfp(ts);
+ return TRUE;
+}
+
/* /[[:digit:]]{6}/ --> time-of-day
* parses a number string representing 'HHMMSS'
*/
@@ -1529,11 +1587,11 @@ static int _parse_date3(UCC *cp, UCC **ep, TCivilDate *into)
int rc;
UCC * xp = cp;
- rc = _parse_u16(cp, &cp, &d) && (d - 1 < 31)
+ rc = _parse_u16(cp, &cp, &d, 2) && (d - 1 < 31)
&& _parse_sep(cp, &cp)
- && _parse_u16(cp, &cp, &m) && (m - 1 < 12)
+ && _parse_u16(cp, &cp, &m, 2) && (m - 1 < 12)
&& _parse_sep(cp, &cp)
- && _parse_u16(cp, &cp, &y) && (y > 1980)
+ && _parse_u16(cp, &cp, &y, 4) && (y > 1980)
&& _parse_eof(cp, ep);
if (rc) {
into->monthday = (uint8_t )d;
@@ -1647,31 +1705,34 @@ parse_gpsw(
)
{
uint32_t secs;
- uint16_t week, leap;
+ uint16_t week, leap = 0;
l_fp fofs;
int rc;
UCC * dpw = (UCC*)field_parse(rd, weekidx);
UCC * dps = (UCC*)field_parse(rd, timeidx);
- UCC * dpl = (UCC*)field_parse(rd, leapidx);
- rc = _parse_u16 (dpw, &dpw, &week)
- && _parse_eof (dpw, &dpw) && (week < 1024)
- && _parse_u32 (dps, &dps, &secs)
+ rc = _parse_u16 (dpw, &dpw, &week, 5)
+ && _parse_eof (dpw, &dpw)
+ && _parse_u32 (dps, &dps, &secs, 9)
&& _parse_frac(dps, &dps, &fofs)
- && _parse_eof (dps, &dps) && (secs < 7*SECSPERDAY)
- && _parse_u16 (dpl, &dpl, &leap)
- && _parse_eof (dpl, &dpl);
-
+ && _parse_eof (dps, &dps)
+ && (secs < 7*SECSPERDAY);
+ if (rc && leapidx > 0) {
+ UCC * dpl = (UCC*)field_parse(rd, leapidx);
+ rc = _parse_u16 (dpl, &dpl, &leap, 5)
+ && _parse_eof (dpl, &dpl);
+ }
if (rc) {
fofs.l_ui -= leap;
*wd = gpscal_from_gpsweek(week, secs, fofs);
} else {
- DPRINTF(1, ("nmea: parse_weekdata: invalid weektime spec\n"));
+ DPRINTF(1, ("nmea: parse_gpsw: invalid weektime spec\n"));
}
return rc;
}
+
#ifdef HAVE_PPSAPI
static double
tabsdiffd(