From: Juergen Perlinger Date: Wed, 4 Sep 2019 19:40:55 +0000 (+0200) Subject: [Bug 3611] NMEA time interpreted incorrectly X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfb130df1f250ddcea7327b3373dc7de11ec0c28;p=thirdparty%2Fntp.git [Bug 3611] NMEA time interpreted incorrectly bk: 5d701347WbF5WzbvG93cb42sLqFzTA --- diff --git a/ChangeLog b/ChangeLog index 3e7ad58c8..522eaf066 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ * [Sec 3610] process_control() should bail earlier on short packets. stenn@ - Reported by Philippe Antoine +* [Bug 3611] NMEA time interpreted incorrectly + - officially document new "trust date" mode bit for NMEA driver + - restore the (previously undocumented) "trust date" feature lost with [bug 3577] * [Bug 3608] libparse fails to compile on S11.4SRU13 and later - removed ffs() and fls() prototypes as per Brian Utterback * [Bug 3604] Wrong param byte order passing into record_raw_stats() in diff --git a/html/drivers/driver20.html b/html/drivers/driver20.html index 27409c71e..8d7f69e1c 100644 --- a/html/drivers/driver20.html +++ b/html/drivers/driver20.html @@ -13,7 +13,7 @@

Generic NMEA GPS Receiver

Last update: - 04-Feb-2019 07:30 + 4-Sep-2019 21:23 UTC


Synopsis

@@ -333,6 +333,15 @@ Do not set the PPS flag in the clock status, so the clock is not considered as PPS peer. + + 18 + 262144 + 0x40000 + Trust the date delivered via NMEA. Do this only if + you really trust the receiver! + See below. Caveat: + This (hitherto undocumented) bit has moved! + @@ -355,7 +364,7 @@ The driver uses 4800 bits per second by default, but faster bitrates can be selected using bits 4 to 6 of the mode field. -

+

Caveat: Using higher line speeds does not necessarily @@ -370,6 +379,29 @@ linespeed of 4800 bps or 9600 bps.

+

About distrusting NMEA date stamps

+

+ Trusting the calendar dates delivered via NMEA is a risky thing, and by + default these dates are handled with a huge dose of skepticism. Many + receivers deliver a correct calendar date for a period of just 1024 weeks, + with a starting point baked somewhere into their firmware. Beyond that, + they warp back to the begin of their era and simply provide wrong date + information. To battle this widely observed effect, the date delivered is + by default reduced to GPS time again and then (re-)mapped according to the + base date, either the implicit value or the value set via "tos basedate". + If the receiver can really be trusted to deliver the right date + (which is not impossible, just more expensive for the manufacturer), then + mode bit 18 can be used to bypass the era mapping. Setting this bit is + not needed under most circumstances, and setting it with an unreliable + receiver can have severe effects. Handle with care. +

+ Note: This functionality was available for some time as + undocumented feature, with a different bit value. It was moved in the + process of becoming officially acknowledged to avoid excessive scattering + of the mode bit mask. +

+ +

Monitor Data

The last GPS sentence that is accepted or rejected is written to the diff --git a/include/ntp_calgps.h b/include/ntp_calgps.h index a8620cc38..330a7d163 100644 --- a/include/ntp_calgps.h +++ b/include/ntp_calgps.h @@ -65,7 +65,12 @@ extern void gpscal_add_offset(TGpsDatum *datum, l_fp offset); extern TGpsDatum -gpscal_from_calendar(TcCivilDate*, l_fp fofs); +gpscal_from_calendar_ex(TcCivilDate*, l_fp fofs, int/*BOOL*/ warp); + +static inline TGpsDatum +gpscal_from_calendar(TcCivilDate *pCiv, l_fp fofs) { + return gpscal_from_calendar_ex(pCiv, fofs, TRUE); +} extern TGpsDatum /* see source for semantic of the 'fofs' value! */ gpscal_from_gpsweek(uint16_t w, int32_t s, l_fp fofs); @@ -98,7 +103,12 @@ extern void gpsntp_add_offset(TNtpDatum *datum, l_fp offset); extern TNtpDatum -gpsntp_from_calendar(TcCivilDate*, l_fp fofs); +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); +} extern TNtpDatum gpsntp_from_daytime1(TcCivilDate *dt, l_fp fofs, l_fp pivot); diff --git a/libntp/ntp_calgps.c b/libntp/ntp_calgps.c index 773cfe909..c478aa8af 100644 --- a/libntp/ntp_calgps.c +++ b/libntp/ntp_calgps.c @@ -263,13 +263,14 @@ gpsntp_from_daytime1( * that one into the NTP time scale. */ TNtpDatum -gpsntp_from_calendar( +gpsntp_from_calendar_ex( TcCivilDate * jd, - l_fp fofs + l_fp fofs, + int/*BOOL*/ warp ) { TGpsDatum gps; - gps = gpscal_from_calendar(jd, fofs); + gps = gpscal_from_calendar_ex(jd, fofs, warp); return gpsntp_from_gpscal(&gps); } @@ -368,7 +369,8 @@ gpscal_fix_gps_era( /* ----------------------------------------------------------------- * Given a calendar date, zap it into a GPS time format and the do a - * proper era mapping in the GPS time scale, based on the GPS base date. + * proper era mapping in the GPS time scale, based on the GPS base date, + * if so requested. * * This function also augments the century if just a 2-digit year * (0..99) is provided on input. @@ -382,11 +384,16 @@ gpscal_fix_gps_era( * with a configurable base date *inside* ntpd. */ TGpsDatum -gpscal_from_calendar( +gpscal_from_calendar_ex( TcCivilDate * jd, - l_fp fofs + l_fp fofs, + int/*BOOL*/ warp ) { + /* (-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; @@ -401,9 +408,9 @@ gpscal_from_calendar( cal.year += 1900; /* get RDN from date, possibly adjusting the century */ -again: if (cal.month && cal.monthday) { /* use y/m/d civil date */ +again: if (cal.month && cal.monthday) { /* use Y/M/D civil date */ days = ntpcal_date_to_rd(&cal); - } else { /* using y/doy date */ + } else { /* using Y/DoY date */ days = ntpcal_year_to_ystart(cal.year) + (int32_t)cal.yearday - 1; /* both RDN and yearday start with '1'. */ @@ -428,10 +435,16 @@ again: if (cal.month && cal.monthday) { /* use y/m/d civil date */ cal.year += 100; goto again; } else { - /* add the complement of DAY_GPS_STARTS (this is, use a - * congruential identity here) + /* A very bad date before the GPS epoch. There's not + * much we can do, except to add the complement of + * DAY_GPS_STARTS % (7 * 1024) here, that is, use a + * congruential identity: Add the complement instead of + * subtracting the value gives a value with the same + * modulus. But of course, now we MUST to go through a + * cycle fix... because the date was obviously wrong! */ - days += (7 * 1024) - DAY_GPS_STARTS % (7 * 1024); + warp = TRUE; + days += s_compl_shift; } /* Splitting to weeks is simple now: */ @@ -445,7 +458,7 @@ again: if (cal.month && cal.monthday) { /* use y/m/d civil date */ gps.wsecs = days * SECSPERDAY + ntpcal_date_to_daysec(&cal); gps.frac = 0; gpscal_add_offset(&gps, fofs); - return _gpscal_fix_gps_era(&gps); + return warp ? _gpscal_fix_gps_era(&gps) : gps; } /* ----------------------------------------------------------------- @@ -492,7 +505,7 @@ gpscal_from_gpsweek( } /* ----------------------------------------------------------------- - * internal work hores for time-of-week expansion + * internal work horse for time-of-week expansion */ static TGpsDatum _gpscal_from_weektime( diff --git a/ntpd/refclock_nmea.c b/ntpd/refclock_nmea.c index 91419ec4e..211537326 100644 --- a/ntpd/refclock_nmea.c +++ b/ntpd/refclock_nmea.c @@ -89,10 +89,10 @@ #define NMEA_BAUDRATE_MASK 0x00000070U #define NMEA_BAUDRATE_SHIFT 4 -#define NMEA_DELAYMEAS_MASK 0x80 +#define NMEA_DELAYMEAS_MASK 0x00000080U #define NMEA_EXTLOG_MASK 0x00010000U #define NMEA_QUIETPPS_MASK 0x00020000U -#define NMEA_DATETRUST_MASK 0x02000000U +#define NMEA_DATETRUST_MASK 0x00040000U #define NMEA_PROTO_IDLEN 5 /* tag name must be at least 5 chars */ #define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */ @@ -855,7 +855,8 @@ nmea_receive( /* now convert and possibly extend/expand the time stamp. */ if (rc_date == 1) { /* (truncated) date available */ - dntp = gpsntp_from_calendar(&date, tofs); + 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); @@ -1448,7 +1449,7 @@ static int _parse_date3(UCC *cp, UCC **ep, TCivilDate *into) && _parse_sep(cp, &cp) && _parse_u16(cp, &cp, &m) && (m - 1 < 12) && _parse_sep(cp, &cp) - && _parse_u16(cp, &cp, &y) + && _parse_u16(cp, &cp, &y) && (y > 1980) && _parse_eof(cp, ep); if (rc) { into->monthday = (uint8_t )d;