timebase.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*****************************************************************************
  2. All of gpsd's assumptions about time and GPS time reporting live in this file.
  3. This is a work in progress. Currently (3.11) GPSD requires that the host
  4. system clock be accurate to within one second. It would be nice to relax
  5. this to "accurate within one GPS rollover period" for receivers reporting
  6. GPS week+TOW, but this isn't possible in general.
  7. = Begin Sidebar: Why Leap Seconds =
  8. Read this carefully, and if there are errors, please correct. An
  9. understanding of the following terms is critical to make sense of the
  10. situation, which would be farcical if it were not serious.
  11. We discuss four timescales:
  12. 1. TAI, International Atomic Time, which ticks smoothly
  13. at the rate of the SI second. TAI has no concept of a day, year, etc.
  14. TAI does not define "days" or large units, and is hence difficult
  15. for humans to parse. Also, TAI is not broadcast or generally available.
  16. 2. GPS Time, which ticks at at the rate of TAI, but has a constant offset
  17. from it. For other GNSS systems, the offset is different. The
  18. offset is of purely historical interest, being chosen by each
  19. GNSS operator for convenience when the systems were inaugurated.
  20. In other words, only the "epoch" differs between GPS Time and TAI.
  21. 3. UT1, a smoothed earth rotation angle, which MUST return to zero
  22. once a day, (why? Because you want the sun to be overhead *each*
  23. day at the same time on your watch, no?), and ticks SI seconds
  24. (a non-integeral number of seconds will occur in a UT1 day,
  25. obviously). For those of you who still say "GMT", UT1 is the
  26. closest modern timescale.
  27. 4. UTC, Coordinated Universal Time, which ticks SI seconds. An attempt is
  28. made to keep UTC aligned with the rate of flow of seconds (TAI), and
  29. the rate of flow of days (UT1).
  30. The reason UTC has to struggle has little to do with the fact that the earth's
  31. rotation is slowing down. Although the length of the day, as measured by UT1,
  32. is lengthening in terms of the SI second, this is a very long term slowdown,
  33. and since 1980, the earth has actually speeded up.
  34. The issue simply is that the term "second" is defined in two incompatible ways:
  35. Def 1. As a fixed number (9,192,631,770) of cycles of an atomic standard.
  36. We believe this is a constant, and evidence to the contrary may
  37. involve GPSD code review, and Nobel Prizes. This is the SI second.
  38. Def 2. As 1/86400 of a "day". The number 86400 arises from
  39. 1 day == 24 * 60 * 60 secs. This is what we learn in school.
  40. Both of these have been defined separately, and the issue of leap seconds,
  41. rubber seconds, Smoothed Leap Seconds, etc, arises because we are
  42. unwilling to change the definition of either to be a derived unit of the
  43. other.
  44. At the time the SI second was defined, it was believed that Def 2 was correct,
  45. and the number in Def 1 was derived. Because of ease of measurement, Def 1 was
  46. codified, and the problem was ignored for some time. Prior to 1972,
  47. complicated formulae were used to scale the SI second, with the attendant
  48. confusion and fear when the formula would be revised.
  49. Since 1972, the start of UTC, the decision to have leap seconds means that UTC
  50. ticks SI seconds. Every 86400 SI seconds, we declare a new day, and we let the
  51. error (UT1 - UTC) build up. This is of the order of a few ms each midnight, not
  52. always the same way (think earthquakes that move the earth's crust).
  53. Once the error has built up substantially, every few years, we (and by
  54. "we", I mean M. Daniel Gambis at the IERS) declare that a future
  55. day will have 86401 secs. This is the Leap Second. Note that this
  56. often overcorrects, but if we wait a few months, the error will disappear.
  57. An animation of this process is available at:
  58. https://space-geodesy.nasa.gov/multimedia/videos/EarthOrientationAnimations/UT1/UT1.html
  59. Clear?
  60. Two last things:
  61. 1. Again, the earth slowing down is NOT the cause of leap seconds,
  62. except very indirectly. It is the conflict between the two
  63. definitions above that causes leap seconds
  64. 2. POSIX declares that there is no conflict, there are always 86400 SI
  65. secs in a day, and hence no leap seconds. The fact that ostriches
  66. survive in the wild indicates that this is not as mind-crushingly
  67. wrong as it may seem.
  68. = End Sidebar =
  69. Date and time in GPS is represented as number of weeks mod 1024 from
  70. 1980-01-06T00:00.00Z, and number of SI seconds into the week. GPS
  71. time is not leap-second corrected, and has a constant offset from TAI,
  72. but not from UTC.
  73. There are hence two issues with converting GPS Time to UTC:
  74. 1. We need to recover the epoch difference between TAI and GPS Time,
  75. which rolls over to 0 every 1024 weeks (approx 20 years). Think
  76. of this as analogous to the Y2K problem; we do not know if we are
  77. off by 1024 weeks. This is the "rollover" issue below.
  78. 2. Once we have the epoch right, we need to adjust for Leap Seconds
  79. that have been issued.
  80. (Complicating the issue is that most consumer devices may not apply
  81. the corrections when rollover occurs, as this may not be adequately
  82. tested. We hence have to accept the UTC time reported by the device,
  83. while checking it on the sly).
  84. Satellites also broadcast a current leap-second correction which is
  85. updated on (theoretically) three-month boundaries according to
  86. rotational bulletins issued by the International Earth Rotation and
  87. Reference Systems Service (IERS). Historically all corrections have
  88. been made on six-month boundaries.
  89. The leap-second correction is only included in the satellite subframe
  90. broadcast, roughly once ever 20 minutes. While the satellites do
  91. notify GPSes of upcoming leap-seconds, this notification is not
  92. necessarily processed correctly on consumer-grade devices, and will
  93. not be available at all when a GPS receiver has just
  94. cold-booted. Thus, the time reported from GPS devices, although
  95. supposed to be UTC, may be offset by an integer number of seconds
  96. between a cold boot or leap second and the following
  97. subframe broadcast.
  98. It might be best not to trust time for 20 minutes after GPSD startup
  99. if it is more than 500ms from current system time (that is long enough
  100. for an ephemeris to load) but this isn't actually implemented as the
  101. divergence will normally be only one second or less.
  102. GPS date and time are subject to a rollover problem in the 10-bit week
  103. number counter, which will re-zero every 1024 weeks (roughly every 20
  104. years). The first rollover was 1999-08-22T00:00:00; the most recent
  105. was 2019-04-07T00:00:00. Note that both these time stamps are in GPS
  106. Time, not UTC (the recent rollover occurred at 2019-04-06T23:59:42Z).
  107. Plans are afoot to upgrade the message format to 13 bits; this
  108. will delay the next rollover until 2173.
  109. For accurate time reporting, therefore, a GPS requires a supplemental
  110. time reference sufficient to identify the current rollover period,
  111. e.g. accurate to within 512 weeks. Many GPSes have a wired-in
  112. assumption about the UTC time of the last rollover and will thus report
  113. incorrect times outside the rollover period they were designed in.
  114. These conditions leave gpsd in a serious hole. Actually there are several
  115. interrelated problems:
  116. 1) Every device has some assumption about base epoch (date of
  117. last rollover) that we don't have access to. Thus, there's no way to
  118. check whether a rollover the device wasn't prepared for has occurred
  119. before gpsd startup time (making the reported UTC date invalid)
  120. without some other time source. (Some devices may keep a
  121. rollover count in NVRAM and avoid the problem; we can't tell when that's
  122. happening, either.)
  123. 2) Many NMEA devices - in fact, all that don't report ZDA - never tell
  124. us what century they think it is. Those that do report century are
  125. still subject to rollover problems. We need an external time reference
  126. for this, too.
  127. 3) Supposing we're looking at a binary protocol that returns week/tow,
  128. we can't know which rollover period we're in without an external time
  129. source.
  130. 4) Only one external time source, the host system clock, is reliably
  131. available, although it may not be accurate.
  132. 5) Another source *may* be available - the GPS leap second count, if we can
  133. get the device to report it. The latter is not a given; SiRFs before
  134. firmware rev 2.3.2 don't report it unless special subframe data reporting
  135. is enabled, which requires 38400bps. Evermore GPSes can't be made to
  136. report it at all. Furthermore, before the almanac load the GPS may report
  137. a fixed (and possibly out of date) offset.
  138. Conclusion: if the system clock isn't accurate enough that we can
  139. deduce what rollover period we're in, we're utterly
  140. hosed. Furthermore, if it's not accurate to within a second and only
  141. NMEA devices that don't emit ZDA are reporting, we don't even know
  142. what century it is!
  143. Therefore, we must assume the system clock is reliable to within a second.
  144. However, none of these caveats affect the usefulness of PPS, which
  145. tells us top of second to theoretical 50ns accuracy (actually about 1
  146. microsecond over RS232 and roughly one poll interval over USB) and can
  147. be made to condition a local NTP instance that does *not* rely on the
  148. system clock. The combination of PPS with NTP time should be reliable
  149. regardless of what the local system clock gets up to. That is, unless
  150. NTP clock skew goes over 1 second, but this is unlikely to ever happen
  151. - and if it does the reasons will have nothing to do with GPS
  152. idiosyncrasies.
  153. This file is Copyright 2010 by the GPSD project
  154. SPDX-License-Identifier: BSD-2-clause
  155. *****************************************************************************/
  156. #include "gpsd_config.h" /* must be before all includes */
  157. #include <ctype.h>
  158. #include <stdlib.h>
  159. #include <string.h>
  160. #include "gpsd.h"
  161. void gpsd_time_init(struct gps_context_t *context, time_t starttime)
  162. /* initialize the GPS context's time fields */
  163. {
  164. /*
  165. * gpsd can't work with 'right' timezones (leapseconds inserted in
  166. * the timezone offset). Avoid this and all manner of other local
  167. * time issues by telling the system we want times returned in UTC.
  168. */
  169. (void)putenv("TZ=UTC");
  170. /*
  171. * Provides a start time for getting the century. Do this, just
  172. * in case one of our embedded deployments is still in place in
  173. * the year 2.1K. Still likely to fail if we bring up the daemon
  174. * just before a century mark, but that case is probably doomed
  175. * anyhow because of 2-digit years.
  176. */
  177. context->leap_seconds = BUILD_LEAPSECONDS;
  178. context->century = BUILD_CENTURY;
  179. context->start_time = starttime;
  180. context->rollovers = (int)((context->start_time-GPS_EPOCH) / GPS_ROLLOVER);
  181. if (GPS_EPOCH > context->start_time) {
  182. GPSD_LOG(LOG_ERROR, &context->errout,
  183. "system time looks bogus, dates may not be reliable.\n");
  184. } else {
  185. /* we've forced the UTC timezone, so this is actually UTC */
  186. struct tm *now = localtime(&context->start_time);
  187. char scr[128];
  188. timespec_t ts_start_time;
  189. ts_start_time.tv_sec = context->start_time;
  190. ts_start_time.tv_nsec = 0;
  191. /*
  192. * This is going to break our regression-test suite once a century.
  193. * I think we can live with that consequence.
  194. */
  195. now->tm_year += 1900;
  196. context->century = now->tm_year - (now->tm_year % 100);
  197. GPSD_LOG(LOG_INF, &context->errout, "startup at %s (%ld)\n",
  198. timespec_to_iso8601(ts_start_time, scr, sizeof(scr)),
  199. (long)context->start_time);
  200. }
  201. }
  202. void gpsd_set_century(struct gps_device_t *session)
  203. /*
  204. * Interpret "Date: yyyy-mm-dd", setting the session context
  205. * century from the year. We do this so the behavior of the
  206. * regression tests won't depend on what century the daemon
  207. * started up in.
  208. */
  209. {
  210. char *end;
  211. if (strstr((char *)session->lexer.outbuffer, "Date:") != NULL) {
  212. int year;
  213. unsigned char *cp = session->lexer.outbuffer + 5;
  214. while (isspace(*cp))
  215. ++cp;
  216. year = (int)strtol((char *)cp, &end, 10);
  217. session->context->century = year - (year % 100);
  218. }
  219. }
  220. #ifdef NMEA0183_ENABLE
  221. /* resolve a UTC date, checking for rollovers */
  222. timespec_t gpsd_utc_resolve(struct gps_device_t *session)
  223. {
  224. /*
  225. * We'd like to *correct* for rollover the way we do for GPS week.
  226. * In theory, comparing extracted UTC against present time should
  227. * allow us to compute the device's epoch assumption. In practice,
  228. * this will be hairy and risky.
  229. */
  230. timespec_t t;
  231. char scr[128];
  232. t.tv_sec = (time_t)mkgmtime(&session->nmea.date);
  233. t.tv_nsec = session->nmea.subseconds.tv_nsec;
  234. session->context->valid &=~ GPS_TIME_VALID;
  235. /*
  236. * If the system clock is zero or has a small-integer value,
  237. * no further sanity-checking is possible.
  238. */
  239. if (session->context->start_time < GPS_EPOCH)
  240. return t;
  241. /* sanity check unix time against leap second.
  242. * Does not work well with regressions because the leap_sconds
  243. * could be from the receiver, or from BUILD_LEAPSECONDS.
  244. * Leap second 18 at 1 Jan 2017: 1483228800
  245. * (long long) for 32-bit systems */
  246. if (17 < session->context->leap_seconds &&
  247. 1483228800LL > t.tv_sec) {
  248. long long old_tv_sec = t.tv_sec;
  249. t.tv_sec += 619315200LL; // fast forward 1024 weeks
  250. (void)gmtime_r(&t.tv_sec, &session->nmea.date); // fix NMEA date
  251. (void)timespec_to_iso8601(t, scr, sizeof(scr));
  252. GPSD_LOG(LOG_WARN, &session->context->errout,
  253. "WARNING: WKRO bug: leap second %d inconsistent "
  254. "with %lld, corrected to %lld (%s)\n",
  255. session->context->leap_seconds,
  256. old_tv_sec, (long long)t.tv_sec, scr);
  257. }
  258. /*
  259. * If the GPS is reporting a time from before the daemon started, we've
  260. * had a rollover event while the daemon was running.
  261. */
  262. #ifdef __UNUSED__
  263. // 5 Dec 2019
  264. // This fails ALL regression tests as start time after regression added
  265. if (t.tv_sec < (time_t)session->context->start_time) {
  266. (void)timespec_to_iso8601(t, scr, sizeof(scr));
  267. GPSD_LOG(LOG_WARN, &session->context->errout,
  268. "GPS week rollover makes time %s (%lld) invalid\n",
  269. scr, (long long)t.tv_sec);
  270. }
  271. #endif // __UNUSED__
  272. return t;
  273. }
  274. void gpsd_century_update(struct gps_device_t *session, int century)
  275. {
  276. session->context->valid |= CENTURY_VALID;
  277. if (century > session->context->century) {
  278. /*
  279. * This mismatch is almost certainly not due to a GPS week
  280. * rollover, because that would throw the ZDA report backward
  281. * into the last rollover period instead of forward. Almost
  282. * certainly it means that a century mark has passed while
  283. * gpsd was running, and we should trust the new ZDA year.
  284. */
  285. GPSD_LOG(LOG_WARN, &session->context->errout,
  286. "century rollover detected.\n");
  287. session->context->century = century;
  288. } else if (session->context->start_time >=
  289. GPS_EPOCH && century < session->context->century) {
  290. /*
  291. * This looks like a GPS week-counter rollover.
  292. */
  293. GPSD_LOG(LOG_WARN, &session->context->errout,
  294. "ZDA year less than clock year, "
  295. "probable GPS week rollover lossage\n");
  296. session->context->valid &=~ CENTURY_VALID;
  297. }
  298. }
  299. #endif /* NMEA0183_ENABLE */
  300. /* gpsd_gpstime_resolv() convert week/tow to UTC as a timespec
  301. */
  302. timespec_t gpsd_gpstime_resolv(struct gps_device_t *session,
  303. unsigned week, timespec_t tow)
  304. {
  305. timespec_t t;
  306. /*
  307. * This code detects and compensates for week counter rollovers that
  308. * happen while gpsd is running. It will not save you if there was a
  309. * rollover that confused the receiver before gpsd booted up. It *will*
  310. * work even when Block IIF satellites increase the week counter width
  311. * to 13 bits.
  312. */
  313. if ((int)week < (session->context->gps_week & 0x3ff)) {
  314. GPSD_LOG(LOG_INF, &session->context->errout,
  315. "GPS week 10-bit rollover detected.\n");
  316. ++session->context->rollovers;
  317. }
  318. /*
  319. * This guard copes with both conventional GPS weeks and the "extended"
  320. * 15-or-16-bit version with no wraparound that appears in Zodiac
  321. * chips and is supposed to appear in the Geodetic Navigation
  322. * Information (0x29) packet of SiRF chips. Some SiRF firmware versions
  323. * (notably 231) actually ship the wrapped 10-bit week, despite what
  324. * the protocol reference claims.
  325. */
  326. if (week < 1024)
  327. week += session->context->rollovers * 1024;
  328. /* sanity check week number, GPS epoch, against leap seconds
  329. * Does not work well with regressions because the leap_sconds
  330. * could be from the receiver, or from BUILD_LEAPSECONDS. */
  331. if (0 < session->context->leap_seconds &&
  332. 19 > session->context->leap_seconds &&
  333. 2180 < week) {
  334. /* assume leap second = 19 by 31 Dec 2022
  335. * so week > 2180 is way in the future, do not allow it */
  336. week -= 1024;
  337. GPSD_LOG(LOG_WARN, &session->context->errout,
  338. "GPS week confusion. Adjusted week %u for leap %d\n",
  339. week, session->context->leap_seconds);
  340. }
  341. // gcc needs the (time_t)week to not overflow. clang got it right.
  342. // if time_t is 32-bits, then still 2038 issues
  343. t.tv_sec = GPS_EPOCH + ((time_t)week * SECS_PER_WEEK) + tow.tv_sec;
  344. t.tv_sec -= session->context->leap_seconds;
  345. t.tv_nsec = tow.tv_nsec;
  346. // 2038 rollover hack for unsigned 32-bit time, assuming today is < 2038
  347. if (0 > t.tv_sec) {
  348. // recompute for previous EPOCH
  349. week -= 1024;
  350. t.tv_sec = GPS_EPOCH + ((time_t)week * SECS_PER_WEEK) + tow.tv_sec;
  351. t.tv_sec -= session->context->leap_seconds;
  352. GPSD_LOG(LOG_WARN, &session->context->errout,
  353. "2038 rollover. Adjusting to %lld. week %u leap %d\n",
  354. (long long)t.tv_sec, week,
  355. session->context->leap_seconds);
  356. }
  357. session->context->gps_week = week;
  358. session->context->gps_tow = tow;
  359. session->context->valid |= GPS_TIME_VALID;
  360. return t;
  361. }
  362. /* end */
  363. // vim: set expandtab shiftwidth=4