* [Sec 3610] process_control() should bail earlier on short packets. stenn@
- Reported by Philippe Antoine
+* [Bug 3611] NMEA time interpreted incorrectly <perlinger@ntp.org>
+ - 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 <perlinger@ntp.org>
- removed ffs() and fls() prototypes as per Brian Utterback
* [Bug 3604] Wrong param byte order passing into record_raw_stats() in
<body>
<h3>Generic NMEA GPS Receiver</h3>
<p>Last update:
- <!-- #BeginDate format:En2m -->04-Feb-2019 07:30<!-- #EndDate -->
+ <!-- #BeginDate format:En2m -->4-Sep-2019 21:23<!-- #EndDate -->
UTC</p>
<hr>
<h4>Synopsis</h4>
Do <em>not</em> set the PPS flag in the clock status, so the
clock is not considered as PPS peer.
</td>
+ </tr><tr>
+ <td align="center">18</td>
+ <td align="center">262144</td>
+ <td align="center">0x40000</td>
+ <td>Trust the date delivered via NMEA. Do this only if
+ you <em>really</em> trust the receiver!
+ See <a href="#datetrust">below</a>. <strong>Caveat:</strong>
+ This (hitherto undocumented) bit has moved!
+ </td>
</tr>
</tbody></table>
</li></ul>
The driver uses 4800 bits per second by default, but faster bitrates can
be selected using bits 4 to 6 of the mode field.
- <p></p>
+ </p>
<p>
<strong>Caveat:</strong> Using higher line speeds does not necessarily
linespeed of 4800 bps or 9600 bps.
</p>
+ <h4><a name="datetrust"/>About distrusting NMEA date stamps</h4>
+ <p>
+ 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 <em>really</em> 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.
+ </p><p>
+ <strong>Note:</strong> 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.
+ </p>
+
+
<h4>Monitor Data</h4>
<p>The last GPS sentence that is accepted or rejected is written to the
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);
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);
* 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);
}
/* -----------------------------------------------------------------
* 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.
* 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;
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'. */
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: */
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;
}
/* -----------------------------------------------------------------
}
/* -----------------------------------------------------------------
- * internal work hores for time-of-week expansion
+ * internal work horse for time-of-week expansion
*/
static TGpsDatum
_gpscal_from_weektime(
#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 */
/* 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);
&& _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;