+---
+* [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>
#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.
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
*/
#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
* 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 */
/*
}
/*
- * 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
*/
static int32_t s_baseday = NTP_TO_UNIX_DAYS;
+static int32_t s_gpsweek = 0;
int32_t
basedate_eval_buildstamp(void)
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",
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;
}
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-*- */
clock_time->flags = PARSEB_POWERUP;
return CVT_OK;
}
- if (week < GPSWRAP) {
- week += GPSWEEKS;
- }
+ week = basedate_expand_gpsweek(week);
/* time OK */
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)
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;
}
break;
}
}
+
+ if (basedate_get_day() <= NTP_TO_UNIX_DAYS)
+ basedate_set_day(basedate_eval_buildstamp() - 11);
+
return ret;
}
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
*/
}
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
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) */
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