]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 3535] libparse won't handle GPS week rollover
authorJuergen Perlinger <perlinger@ntp.org>
Sat, 13 Oct 2018 06:03:48 +0000 (08:03 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Sat, 13 Oct 2018 06:03:48 +0000 (08:03 +0200)
bk: 5bc18ac4JFajcugcOSU67J1N4SV1gQ

ChangeLog
include/ntp_calendar.h
include/parse.h
libntp/calyearstart.c
libntp/ntp_calendar.c
libparse/clk_trimtsip.c
libparse/gpstolfp.c
ntpd/ntp_config.c
ntpd/refclock_jupiter.c
ntpd/refclock_parse.c

index f381a093cf948c1202162000eaf2d8abbf1a1dcf..23d0f2ddb082ef9bd6c9e6a439b5a9940cebf3ec 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+---
+* [Bug 3535] libparse won't handle GPS week rollover <perlinger@ntp.org>
+  - refactored handling of GPS era based on 'tos basedate' for
+    parse (TSIP) and JUPITER clocks
+
 ---
 (4.2.8p12) 2018/08/14 Released by Harlan Stenn <stenn@ntp.org>
 
index 41c58797b2c785b350965cbca628d6993ba8596c..0b1f20d6bd7b702ed44290c17c49622abc53bdd2 100644 (file)
@@ -93,6 +93,7 @@ extern systime_func_ptr ntpcal_set_timefunc(systime_func_ptr);
 #define        SECSPERLEAPYEAR (366 * SECSPERDAY)      /* leap year */
 #define        SECSPERAVGYEAR  31556952                /* mean year length over 400yrs */
 
+#define GPSWEEKS       1024                    /* GPS week cycle */
 /*
  * Gross hacks.         I have illicit knowlege that there won't be overflows
  * here, the compiler often can't tell this.
@@ -404,14 +405,21 @@ basedate_get_eracenter(void);
 extern time_t
 basedate_get_erabase(void);
 
+extern uint32_t
+basedate_get_gpsweek(void);
+
+extern uint32_t
+basedate_expand_gpsweek(unsigned short weekno);
 
 /*
  * Additional support stuff for Ed Rheingold's calendrical calculations
  */
 
 /*
- * Start day of NTP time as days past the imaginary date 12/1/1 BC.
- * (This is the beginning of the Christian Era, or BCE.)
+ * Start day of NTP time as days past 0000-12-31 in the proleptic
+ * Gregorian calendar. (So 0001-01-01 is day number 1; this is the Rata
+ * Die counting scheme used by Ed Rheingold in his book "Calendrical
+ * Calculations".)
  */
 #define        DAY_NTP_STARTS 693596
 
@@ -420,15 +428,25 @@ basedate_get_erabase(void);
  */
 #define DAY_UNIX_STARTS 719163
 
+/*
+ * Start day of the GPS epoch. This is the Rata Die of 1980-01-06
+ */
+#define DAY_GPS_STARTS 722819
+
 /*
  * Difference between UN*X and NTP epoch (25567).
  */
 #define NTP_TO_UNIX_DAYS (DAY_UNIX_STARTS - DAY_NTP_STARTS)
 
+/*
+ * Difference between GPS and NTP epoch (29224)
+ */
+#define NTP_TO_GPS_DAYS (DAY_GPS_STARTS - DAY_NTP_STARTS)
+
 /*
  * Days in a normal 4 year leap year calendar cycle (1461).
  */
-#define        GREGORIAN_NORMAL_LEAP_CYCLE_DAYS        (3 * 365 + 366)
+#define        GREGORIAN_NORMAL_LEAP_CYCLE_DAYS        (4 * 365 + 1)
 
 /*
  * Days in a normal 100 year leap year calendar (36524).  We lose a
index 02dbb3021904c11e885478db694b6b2630738ad7..e2caa52ea28fb46d50b7f3664f2a41fd6f037c3b 100644 (file)
@@ -108,7 +108,6 @@ extern unsigned int splclock (void);
  * some constants useful for GPS time conversion
  */
 #define GPSORIGIN       2524953600UL         /* NTP origin - GPS origin in seconds */
-#define GPSWRAP         990                  /* assume week count less than this in the previous epoch */
 #define GPSWEEKS        1024                 /* number of weeks until the GPS epch rolls over */
 
 /*
index 9e3f58fb393d83963d65d21035a7aaa519a9e31f..5616c8195aa9bfeecf6ac74a146c9e5a923fafa2 100644 (file)
@@ -54,7 +54,7 @@ calmonthstart(u_int32 ntptime, const time_t *pivot)
 }
 
 /*
- * calweekstart - get NTP time at midnight of the last monday on or
+ * calweekstart - get NTP time at midnight of the last Monday on or
  * before the current date.
  */
 u_int32
index f8b7db4ea501f62b06daad4b968774a10c7a3b61..79742688a2bde779a065685a46fcd9eeae1f96d4 100644 (file)
@@ -1832,6 +1832,7 @@ isocal_date_to_ntp(
  */
 
 static int32_t s_baseday = NTP_TO_UNIX_DAYS;
+static int32_t s_gpsweek = 0;
 
 int32_t
 basedate_eval_buildstamp(void)
@@ -1901,6 +1902,7 @@ basedate_set_day(
        struct calendar jd;
        int32_t         retv;
 
+       /* set NTP base date for NTP era unfolding */
        if (day < NTP_TO_UNIX_DAYS) {
                msyslog(LOG_WARNING,
                        "baseday_set_day: invalid day (%lu), UNIX epoch substituted",
@@ -1912,6 +1914,17 @@ basedate_set_day(
        ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS);
        msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu",
                jd.year, (u_short)jd.month, (u_short)jd.monthday);
+
+       /* set GPS base week for GPS week unfolding */
+       day = ntpcal_weekday_ge(day + DAY_NTP_STARTS, CAL_SUNDAY)
+           - DAY_NTP_STARTS;
+       if (day < NTP_TO_GPS_DAYS)
+           day = NTP_TO_GPS_DAYS;
+       s_gpsweek = (day - NTP_TO_GPS_DAYS) / DAYSPERWEEK;
+       ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS);
+       msyslog(LOG_INFO, "gps base set to %04hu-%02hu-%02hu (week %d)",
+               jd.year, (u_short)jd.month, (u_short)jd.monthday, s_gpsweek);
+       
        return retv;
 }
 
@@ -1934,4 +1947,29 @@ basedate_get_erabase(void)
        return retv;
 }
 
+uint32_t
+basedate_get_gpsweek(void)
+{
+    return s_gpsweek;
+}
+
+uint32_t
+basedate_expand_gpsweek(
+    unsigned short weekno
+    )
+{
+    /* We do a fast modulus expansion here. Since all quantities are
+     * unsigned and we cannot go before the start of the GPS epoch
+     * anyway, and since the truncated GPS week number is 10 bit, the
+     * expansion becomes a simple sub/and/add sequence.
+     */
+    #if GPSWEEKS != 1024
+    # error GPSWEEKS defined wrong -- should be 1024!
+    #endif
+    
+    uint32_t diff;
+    diff = ((uint32_t)weekno - s_gpsweek) & (GPSWEEKS - 1);
+    return s_gpsweek + diff;
+}
+
 /* -*-EOF-*- */
index 64359efa0b329630323bc46e9d3365208fc7924b..8cf5660657cc37a9bdefbebce6a2e657460c80dd 100644 (file)
@@ -265,9 +265,7 @@ cvt_trimtsip(
                                            clock_time->flags = PARSEB_POWERUP;
                                            return CVT_OK;
                                    }
-                                   if (week < GPSWRAP) {
-                                           week += GPSWEEKS;
-                                   }
+                                   week = basedate_expand_gpsweek(week);
 
                                    /* time OK */
 
@@ -351,14 +349,12 @@ cvt_trimtsip(
                                    int tls   = t->t_gpsutc     = (u_short) getshort((unsigned char *)&mb(12)); /* current leap correction (GPS-UTC) */
                                    int tlsf  = t->t_gpsutcleap = (u_short) getshort((unsigned char *)&mb(24)); /* new leap correction */
 
-                                   t->t_weekleap   = (u_short) getshort((unsigned char *)&mb(20)); /* week no of leap correction */
-                                   if (t->t_weekleap < GPSWRAP)
-                                     t->t_weekleap = (u_short)(t->t_weekleap + GPSWEEKS);
+                                   t->t_weekleap   = basedate_expand_gpsweek(
+                                       (u_short) getshort((unsigned char *)&mb(20))); /* week no of leap correction */
 
                                    t->t_dayleap    = (u_short) getshort((unsigned char *)&mb(22)); /* day in week of leap correction */
-                                   t->t_week = (u_short) getshort((unsigned char *)&mb(18)); /* current week no */
-                                   if (t->t_week < GPSWRAP)
-                                     t->t_week = (u_short)(t->t_weekleap + GPSWEEKS);
+                                   t->t_week = basedate_expand_gpsweek(
+                                       (u_short) getshort((unsigned char *)&mb(18))); /* current week no */
 
                                    lbp = (unsigned char *)&mb(14); /* last update time */
                                    if (fetch_ieee754(&lbp, IEEE_SINGLE, &t0t, trim_offsets) != IEEE_OK)
index 8a3607514985dbc4f75183ae59ee9fe9f83fa16b..6a99e8851fd7aa3e3db88275e3d7ab832cdb636a 100644 (file)
@@ -45,11 +45,6 @@ gpstolfp(
         l_fp * lfp
         )
 {
-  if (weeks < GPSWRAP)
-    {
-      weeks += GPSWEEKS;
-    }
-
   lfp->l_ui = (uint32_t)(weeks * SECSPERWEEK + days * SECSPERDAY + seconds + GPSORIGIN); /* convert to NTP time */
   lfp->l_uf = 0;
 }
index 89c920c1ecb6a86da4439bfb6cf0a6cd123711bb..d7a6b197ff8583019915d5291794a205835ba39f 100644 (file)
@@ -2112,6 +2112,10 @@ config_tos_clock(
                        break;
                }
        }
+
+       if (basedate_get_day() <= NTP_TO_UNIX_DAYS)
+               basedate_set_day(basedate_eval_buildstamp() - 11);
+           
        return ret;
 }
 
index dbed272467ed8d51d768b7a58d993f1f831528fc..8d6cde2481a7b5291669fd1c10f844dd5362a207 100644 (file)
@@ -158,10 +158,6 @@ static     char *  jupiter_send    (struct instance *, struct jheader *);
 static void    jupiter_shutdown(int, struct peer *);
 static int     jupiter_start   (int, struct peer *);
 
-static u_int   get_full_week(u_int base_week, u_int gpos_week);
-static u_int   get_base_week(void);
-
-
 /*
  * Transfer vector
  */
@@ -855,8 +851,7 @@ jupiter_parse_gpos(struct instance *instance, u_short *sp)
        }
 
        instance->gpos_sweek = DS2UI(jg->sweek);
-       instance->gpos_gweek = get_full_week(get_base_week(),
-                                            getshort(jg->gweek));
+       instance->gpos_gweek = basedate_expand_gpsweek(getshort(jg->gweek));
 
        /* according to the protocol spec, the seconds-in-week cannot
         * exceed the nominal value: Is it really necessary to normalise
@@ -1129,56 +1124,6 @@ jupiter_recv(struct instance *instance)
        return (cc);
 }
 
-static u_int
-get_base_week(void)
-{
-       static int      init_done /* = 0 */;
-       static u_int    base_week;
-
-       /* Get the build date, convert to days since GPS epoch and
-        * finally weeks since GPS epoch.  Note that the build stamp is
-        * trusted once it is fetched -- only dates before the GPS epoch
-        * are not permitted. This will permit proper synchronisation
-        * for a time range of 1024 weeks starting with 00:00:00 of the
-        * last Sunday on or before the build time.
-        *
-        * If the impossible happens and fetching the build date fails,
-        * a 1024-week cycle starting with 2016-01-03 is assumed to
-        * avoid catastropic errors. This will work until 2035-08-19.
-        */
-       if (!init_done) {
-               struct calendar bd;
-               if (ntpcal_get_build_date(&bd)) {
-                       int32_t days = ntpcal_date_to_rd(&bd);
-                       if (days > RDN_GPS_EPOCH)
-                               days -= RDN_GPS_EPOCH;
-                       else
-                               days = 0;
-                       base_week = days / 7; 
-               } else {
-                       base_week = 1878; /* 2016-01-03, Sunday */
-                       msyslog(LOG_ERR,
-                               "refclock_jupiter: ntpcal_get_build_date() failed: %s",
-                               "using 2016-01-03 as GPS base!");
-               }
-               init_done = 1;
-       }
-       return base_week;
-}
-
-static u_int
-get_full_week(
-       u_int base_week,
-       u_int gpos_week
-       )
-{
-       /* Periodic extension on base week. Since the period is 1024
-        * weeks and we do unsigned arithmetic here, we can do wonderful
-        * things with masks and the well-defined overflow behaviour.
-        */
-       return base_week + ((gpos_week - base_week) & 1023);
-}
-
 #else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
 int refclock_jupiter_bs;
 #endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
index cfe2a8968886674126938e3f953a34278f27dabb..9f5b0d7129f8c5bda3124b21b09f520af1a8ebef 100644 (file)
@@ -4256,8 +4256,7 @@ mk_utcinfo(
                struct tm *tm;
                int nc;
 
-               if (wnlsf < GPSWRAP)
-                       wnlsf += GPSWEEKS;
+               wnlsf = basedate_expand_gpsweek(wnlsf);
                /* 'wnt' not used here: would need the same treatment as 'wnlsf */
 
                t_ls = (time_t) wnlsf * SECSPERWEEK