From: Juergen Perlinger Last update:
- 04-Feb-2019 07:30
+ 4-Sep-2019 21:23
UTCGeneric NMEA GPS Receiver
Synopsis
@@ -333,6 +333,15 @@
Do not set the PPS flag in the clock status, so the
clock is not considered as PPS peer.
+
+
@@ -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.
-
+ 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!
+
Caveat: Using higher line speeds does not necessarily @@ -370,6 +379,29 @@ linespeed of 4800 bps or 9600 bps.
++ 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. +
+ +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;