]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 3611] NMEA time interpreted incorrectly
authorJuergen Perlinger <perlinger@ntp.org>
Wed, 4 Sep 2019 19:40:55 +0000 (21:40 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Wed, 4 Sep 2019 19:40:55 +0000 (21:40 +0200)
bk: 5d701347WbF5WzbvG93cb42sLqFzTA

ChangeLog
html/drivers/driver20.html
include/ntp_calgps.h
libntp/ntp_calgps.c
ntpd/refclock_nmea.c

index 3e7ad58c885e1f8c96410e62f1e4fff2923f9328..522eaf0667b05dd45504bda8bfb3df75012a638e 100644 (file)
--- 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 <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
index 27409c71ed8473bd46e1cc451ad4e87dbe2b39f7..8d7f69e1cd93bc1e936434732c918869960c3f11 100644 (file)
@@ -13,7 +13,7 @@
   <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
index a8620cc388f93dbd099b9ad10e7e1765b3763cf9..330a7d163843f4af7d4a4a5885cbbbf64f7896a9 100644 (file)
@@ -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);
index 773cfe909a834b662391a1c49a31bc8d7e7f76df..c478aa8af94c07efd37dd4c547d4d782fe22b858 100644 (file)
@@ -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(
index 91419ec4eaf04a1a119d1d85599a53910c57da63..2115373264d5b82ac43cdf6c6c14939e077d1c35 100644 (file)
 #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;