/src/ntp-dev/libntp/ntp_calgps.c
Line | Count | Source |
1 | | /* |
2 | | * ntp_calgps.c - calendar for GPS/GNSS based clocks |
3 | | * |
4 | | * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. |
5 | | * The contents of 'html/copyright.html' apply. |
6 | | * |
7 | | * -------------------------------------------------------------------- |
8 | | * |
9 | | * This module implements stuff often used with GPS/GNSS receivers |
10 | | */ |
11 | | |
12 | | #include <config.h> |
13 | | #include <sys/types.h> |
14 | | |
15 | | #include "ntp_types.h" |
16 | | #include "ntp_calendar.h" |
17 | | #include "ntp_calgps.h" |
18 | | #include "ntp_stdlib.h" |
19 | | #include "ntp_unixtime.h" |
20 | | |
21 | | #include "ntp_fp.h" |
22 | | #include "ntpd.h" |
23 | | #include "vint64ops.h" |
24 | | |
25 | | /* ==================================================================== |
26 | | * misc. helpers -- might go elsewhere sometime? |
27 | | * ==================================================================== |
28 | | */ |
29 | | |
30 | | l_fp |
31 | | ntpfp_with_fudge( |
32 | | l_fp lfp, |
33 | | double ofs |
34 | | ) |
35 | 0 | { |
36 | 0 | l_fp fpo; |
37 | | /* calculate 'lfp - ofs' as '(l_fp)(-ofs) + lfp': negating a |
38 | | * double is cheap, as it only flips one bit... |
39 | | */ |
40 | 0 | ofs = -ofs; |
41 | 0 | DTOLFP(ofs, &fpo); |
42 | 0 | L_ADD(&fpo, &lfp); |
43 | 0 | return fpo; |
44 | 0 | } |
45 | | |
46 | | |
47 | | /* ==================================================================== |
48 | | * GPS calendar functions |
49 | | * ==================================================================== |
50 | | */ |
51 | | |
52 | | /* -------------------------------------------------------------------- |
53 | | * normalization functions for day/time and week/time representations. |
54 | | * Since we only use moderate offsets (leap second corrections and |
55 | | * alike) it does not really pay off to do a floor-corrected division |
56 | | * here. We use compare/decrement/increment loops instead. |
57 | | * -------------------------------------------------------------------- |
58 | | */ |
59 | | static void |
60 | | _norm_ntp_datum( |
61 | | TNtpDatum * datum |
62 | | ) |
63 | 0 | { |
64 | 0 | static const int32_t limit = SECSPERDAY; |
65 | |
|
66 | 0 | if (datum->secs >= limit) { |
67 | 0 | do |
68 | 0 | ++datum->days; |
69 | 0 | while ((datum->secs -= limit) >= limit); |
70 | 0 | } else if (datum->secs < 0) { |
71 | 0 | do |
72 | 0 | --datum->days; |
73 | 0 | while ((datum->secs += limit) < 0); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | static void |
78 | | _norm_gps_datum( |
79 | | TGpsDatum * datum |
80 | | ) |
81 | 0 | { |
82 | 0 | static const int32_t limit = 7 * SECSPERDAY; |
83 | |
|
84 | 0 | if (datum->wsecs >= limit) { |
85 | 0 | do |
86 | 0 | ++datum->weeks; |
87 | 0 | while ((datum->wsecs -= limit) >= limit); |
88 | 0 | } else if (datum->wsecs < 0) { |
89 | 0 | do |
90 | 0 | --datum->weeks; |
91 | 0 | while ((datum->wsecs += limit) < 0); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | /* -------------------------------------------------------------------- |
96 | | * Add an offset to a day/time and week/time representation. |
97 | | * |
98 | | * !!Attention!! the offset should be small, compared to the time period |
99 | | * (either a day or a week). |
100 | | * -------------------------------------------------------------------- |
101 | | */ |
102 | | void |
103 | | gpsntp_add_offset( |
104 | | TNtpDatum * datum, |
105 | | l_fp offset |
106 | | ) |
107 | 0 | { |
108 | | /* fraction can be added easily */ |
109 | 0 | datum->frac += offset.l_uf; |
110 | 0 | datum->secs += (datum->frac < offset.l_uf); |
111 | | |
112 | | /* avoid integer overflow on the seconds */ |
113 | 0 | if (offset.l_ui >= INT32_MAX) |
114 | 0 | datum->secs -= (int32_t)~offset.l_ui + 1; |
115 | 0 | else |
116 | 0 | datum->secs += (int32_t)offset.l_ui; |
117 | 0 | _norm_ntp_datum(datum); |
118 | 0 | } |
119 | | |
120 | | void |
121 | | gpscal_add_offset( |
122 | | TGpsDatum * datum, |
123 | | l_fp offset |
124 | | ) |
125 | 0 | { |
126 | | /* fraction can be added easily */ |
127 | 0 | datum->frac += offset.l_uf; |
128 | 0 | datum->wsecs += (datum->frac < offset.l_uf); |
129 | | |
130 | | |
131 | | /* avoid integer overflow on the seconds */ |
132 | 0 | if (offset.l_ui >= INT32_MAX) |
133 | 0 | datum->wsecs -= (int32_t)~offset.l_ui + 1; |
134 | 0 | else |
135 | 0 | datum->wsecs += (int32_t)offset.l_ui; |
136 | 0 | _norm_gps_datum(datum); |
137 | 0 | } |
138 | | |
139 | | /* ------------------------------------------------------------------- |
140 | | * API functions civil calendar and NTP datum |
141 | | * ------------------------------------------------------------------- |
142 | | */ |
143 | | |
144 | | static TNtpDatum |
145 | | _gpsntp_fix_gps_era( |
146 | | TcNtpDatum * in |
147 | | ) |
148 | 0 | { |
149 | | /* force result in basedate era |
150 | | * |
151 | | * When calculating this directly in days, we have to execute a |
152 | | * real modulus calculation, since we're obviously not doing a |
153 | | * modulus by a power of 2. Executing this as true floor mod |
154 | | * needs some care and is done under explicit usage of one's |
155 | | * complement and masking to get mostly branchless code. |
156 | | */ |
157 | 0 | static uint32_t const clen = 7*1024; |
158 | |
|
159 | 0 | uint32_t base, days, sign; |
160 | 0 | TNtpDatum out = *in; |
161 | | |
162 | | /* Get base in NTP day scale. No overflows here. */ |
163 | 0 | base = (basedate_get_gpsweek() + GPSNTP_WSHIFT) * 7 |
164 | 0 | - GPSNTP_DSHIFT; |
165 | 0 | days = out.days; |
166 | |
|
167 | 0 | sign = (uint32_t)-(days < base); |
168 | 0 | days = sign ^ (days - base); |
169 | 0 | days %= clen; |
170 | 0 | days = base + (sign & clen) + (sign ^ days); |
171 | |
|
172 | 0 | out.days = days; |
173 | 0 | return out; |
174 | 0 | } |
175 | | |
176 | | TNtpDatum |
177 | | gpsntp_fix_gps_era( |
178 | | TcNtpDatum * in |
179 | | ) |
180 | 0 | { |
181 | 0 | TNtpDatum out = *in; |
182 | 0 | _norm_ntp_datum(&out); |
183 | 0 | return _gpsntp_fix_gps_era(&out); |
184 | 0 | } |
185 | | |
186 | | /* ----------------------------------------------------------------- */ |
187 | | static TNtpDatum |
188 | | _gpsntp_from_daytime( |
189 | | TcCivilDate * jd, |
190 | | l_fp fofs, |
191 | | TcNtpDatum * pivot, |
192 | | int warp |
193 | | ) |
194 | 0 | { |
195 | 0 | static const int32_t shift = SECSPERDAY / 2; |
196 | |
|
197 | 0 | TNtpDatum retv; |
198 | | |
199 | | /* set result based on pivot -- ops order is important here */ |
200 | 0 | ZERO(retv); |
201 | 0 | retv.secs = ntpcal_date_to_daysec(jd); |
202 | 0 | gpsntp_add_offset(&retv, fofs); /* result is normalized */ |
203 | 0 | retv.days = pivot->days; |
204 | | |
205 | | /* Manual periodic extension without division: */ |
206 | 0 | if (pivot->secs < shift) { |
207 | 0 | int32_t lim = pivot->secs + shift; |
208 | 0 | retv.days -= (retv.secs > lim || |
209 | 0 | (retv.secs == lim && retv.frac >= pivot->frac)); |
210 | 0 | } else { |
211 | 0 | int32_t lim = pivot->secs - shift; |
212 | 0 | retv.days += (retv.secs < lim || |
213 | 0 | (retv.secs == lim && retv.frac < pivot->frac)); |
214 | 0 | } |
215 | 0 | return warp ? _gpsntp_fix_gps_era(&retv) : retv; |
216 | 0 | } |
217 | | |
218 | | /* ----------------------------------------------------------------- |
219 | | * Given the time-of-day part of a civil datum and an additional |
220 | | * (fractional) offset, calculate a full time stamp around a given pivot |
221 | | * time so that the difference between the pivot and the resulting time |
222 | | * stamp is less or equal to 12 hours absolute. |
223 | | */ |
224 | | TNtpDatum |
225 | | gpsntp_from_daytime2_ex( |
226 | | TcCivilDate * jd, |
227 | | l_fp fofs, |
228 | | TcNtpDatum * pivot, |
229 | | int/*BOOL*/ warp |
230 | | ) |
231 | 0 | { |
232 | 0 | TNtpDatum dpiv = *pivot; |
233 | 0 | _norm_ntp_datum(&dpiv); |
234 | 0 | return _gpsntp_from_daytime(jd, fofs, &dpiv, warp); |
235 | 0 | } |
236 | | |
237 | | /* ----------------------------------------------------------------- |
238 | | * This works similar to 'gpsntp_from_daytime1()' and actually even uses |
239 | | * it, but the pivot is calculated from the pivot given as 'l_fp' in NTP |
240 | | * time scale. This is in turn expanded around the current system time, |
241 | | * and the resulting absolute pivot is then used to calculate the full |
242 | | * NTP time stamp. |
243 | | */ |
244 | | TNtpDatum |
245 | | gpsntp_from_daytime1_ex( |
246 | | TcCivilDate * jd, |
247 | | l_fp fofs, |
248 | | l_fp pivot, |
249 | | int/*BOOL*/ warp |
250 | | ) |
251 | 0 | { |
252 | 0 | vint64 pvi64; |
253 | 0 | TNtpDatum dpiv; |
254 | 0 | ntpcal_split split; |
255 | |
|
256 | 0 | pvi64 = ntpcal_ntp_to_ntp(pivot.l_ui, NULL); |
257 | 0 | split = ntpcal_daysplit(&pvi64); |
258 | 0 | dpiv.days = split.hi; |
259 | 0 | dpiv.secs = split.lo; |
260 | 0 | dpiv.frac = pivot.l_uf; |
261 | 0 | return _gpsntp_from_daytime(jd, fofs, &dpiv, warp); |
262 | 0 | } |
263 | | |
264 | | /* ----------------------------------------------------------------- |
265 | | * Given a calendar date, zap it into a GPS time format and then convert |
266 | | * that one into the NTP time scale. |
267 | | */ |
268 | | TNtpDatum |
269 | | gpsntp_from_calendar_ex( |
270 | | TcCivilDate * jd, |
271 | | l_fp fofs, |
272 | | int/*BOOL*/ warp |
273 | | ) |
274 | 0 | { |
275 | 0 | TGpsDatum gps; |
276 | 0 | gps = gpscal_from_calendar_ex(jd, fofs, warp); |
277 | 0 | return gpsntp_from_gpscal_ex(&gps, FALSE); |
278 | 0 | } |
279 | | |
280 | | /* ----------------------------------------------------------------- |
281 | | * create a civil calendar datum from a NTP date representation |
282 | | */ |
283 | | void |
284 | | gpsntp_to_calendar( |
285 | | TCivilDate * cd, |
286 | | TcNtpDatum * nd |
287 | | ) |
288 | 0 | { |
289 | 0 | memset(cd, 0, sizeof(*cd)); |
290 | 0 | ntpcal_rd_to_date( |
291 | 0 | cd, |
292 | 0 | nd->days + DAY_NTP_STARTS + ntpcal_daysec_to_date( |
293 | 0 | cd, nd->secs)); |
294 | 0 | } |
295 | | |
296 | | /* ----------------------------------------------------------------- |
297 | | * get day/tod representation from week/tow datum |
298 | | */ |
299 | | TNtpDatum |
300 | | gpsntp_from_gpscal_ex( |
301 | | TcGpsDatum * gd, |
302 | | int/*BOOL*/ warp |
303 | | ) |
304 | 0 | { |
305 | 0 | TNtpDatum retv; |
306 | 0 | vint64 ts64; |
307 | 0 | ntpcal_split split; |
308 | 0 | TGpsDatum date = *gd; |
309 | |
|
310 | 0 | if (warp) { |
311 | 0 | uint32_t base = basedate_get_gpsweek() + GPSNTP_WSHIFT; |
312 | 0 | _norm_gps_datum(&date); |
313 | 0 | date.weeks = ((date.weeks - base) & 1023u) + base; |
314 | 0 | } |
315 | |
|
316 | 0 | ts64 = ntpcal_weekjoin(date.weeks, date.wsecs); |
317 | 0 | ts64 = subv64u32(&ts64, (GPSNTP_DSHIFT * SECSPERDAY)); |
318 | 0 | split = ntpcal_daysplit(&ts64); |
319 | |
|
320 | 0 | retv.frac = gd->frac; |
321 | 0 | retv.secs = split.lo; |
322 | 0 | retv.days = split.hi; |
323 | 0 | return retv; |
324 | 0 | } |
325 | | |
326 | | /* ----------------------------------------------------------------- |
327 | | * get LFP from ntp datum |
328 | | */ |
329 | | l_fp |
330 | | ntpfp_from_ntpdatum( |
331 | | TcNtpDatum * nd |
332 | | ) |
333 | 0 | { |
334 | 0 | l_fp retv; |
335 | |
|
336 | 0 | retv.l_uf = nd->frac; |
337 | 0 | retv.l_ui = nd->days * (uint32_t)SECSPERDAY |
338 | 0 | + nd->secs; |
339 | 0 | return retv; |
340 | 0 | } |
341 | | |
342 | | /* ------------------------------------------------------------------- |
343 | | * API functions GPS week calendar |
344 | | * |
345 | | * Here we use a calendar base of 1899-12-31, so the NTP epoch has |
346 | | * { 0, 86400.0 } in this representation. |
347 | | * ------------------------------------------------------------------- |
348 | | */ |
349 | | |
350 | | static TGpsDatum |
351 | | _gpscal_fix_gps_era( |
352 | | TcGpsDatum * in |
353 | | ) |
354 | 0 | { |
355 | | /* force result in basedate era |
356 | | * |
357 | | * This is based on calculating the modulus to a power of two, |
358 | | * so signed integer overflow does not affect the result. Which |
359 | | * in turn makes for a very compact calculation... |
360 | | */ |
361 | 0 | uint32_t base, week; |
362 | 0 | TGpsDatum out = *in; |
363 | |
|
364 | 0 | week = out.weeks; |
365 | 0 | base = basedate_get_gpsweek() + GPSNTP_WSHIFT; |
366 | 0 | week = base + ((week - base) & (GPSWEEKS - 1)); |
367 | 0 | out.weeks = week; |
368 | 0 | return out; |
369 | 0 | } |
370 | | |
371 | | TGpsDatum |
372 | | gpscal_fix_gps_era( |
373 | | TcGpsDatum * in |
374 | | ) |
375 | 0 | { |
376 | 0 | TGpsDatum out = *in; |
377 | 0 | _norm_gps_datum(&out); |
378 | 0 | return _gpscal_fix_gps_era(&out); |
379 | 0 | } |
380 | | |
381 | | /* ----------------------------------------------------------------- |
382 | | * Given a calendar date, zap it into a GPS time format and the do a |
383 | | * proper era mapping in the GPS time scale, based on the GPS base date, |
384 | | * if so requested. |
385 | | * |
386 | | * This function also augments the century if just a 2-digit year |
387 | | * (0..99) is provided on input. |
388 | | * |
389 | | * This is a fail-safe against GPS receivers with an unknown starting |
390 | | * point for their internal calendar calculation and therefore |
391 | | * unpredictable (but reproducible!) rollover behavior. While there |
392 | | * *are* receivers that create a full date in the proper way, many |
393 | | * others just don't. The overall damage is minimized by simply not |
394 | | * trusting the era mapping of the receiver and doing the era assignment |
395 | | * with a configurable base date *inside* ntpd. |
396 | | */ |
397 | | TGpsDatum |
398 | | gpscal_from_calendar_ex( |
399 | | TcCivilDate * jd, |
400 | | l_fp fofs, |
401 | | int/*BOOL*/ warp |
402 | | ) |
403 | 0 | { |
404 | | /* (-DAY_GPS_STARTS) (mod 7*1024) -- complement of cycle shift */ |
405 | 0 | static const uint32_t s_compl_shift = |
406 | 0 | (7 * 1024) - DAY_GPS_STARTS % (7 * 1024); |
407 | |
|
408 | 0 | TGpsDatum gps; |
409 | 0 | TCivilDate cal; |
410 | 0 | int32_t days, week; |
411 | | |
412 | | /* if needed, convert from 2-digit year to full year |
413 | | * !!NOTE!! works only between 1980 and 2079! |
414 | | */ |
415 | 0 | cal = *jd; |
416 | 0 | if (cal.year < 80) |
417 | 0 | cal.year += 2000; |
418 | 0 | else if (cal.year < 100) |
419 | 0 | cal.year += 1900; |
420 | | |
421 | | /* get RDN from date, possibly adjusting the century */ |
422 | 0 | again: if (cal.month && cal.monthday) { /* use Y/M/D civil date */ |
423 | 0 | days = ntpcal_date_to_rd(&cal); |
424 | 0 | } else { /* using Y/DoY date */ |
425 | 0 | days = ntpcal_year_to_ystart(cal.year) |
426 | 0 | + (int32_t)cal.yearday |
427 | 0 | - 1; /* both RDN and yearday start with '1'. */ |
428 | 0 | } |
429 | | |
430 | | /* Rebase to days after the GPS epoch. 'days' is positive here, |
431 | | * but it might be less than the GPS epoch start. Depending on |
432 | | * the input, we have to do different things to get the desired |
433 | | * result. (Since we want to remap the era anyway, we only have |
434 | | * to retain congruential identities....) |
435 | | */ |
436 | |
|
437 | 0 | if (days >= DAY_GPS_STARTS) { |
438 | | /* simply shift to days since GPS epoch */ |
439 | 0 | days -= DAY_GPS_STARTS; |
440 | 0 | } else if (jd->year < 100) { |
441 | | /* Two-digit year on input: add another century and |
442 | | * retry. This can happen only if the century expansion |
443 | | * yielded a date between 1980-01-01 and 1980-01-05, |
444 | | * both inclusive. We have at most one retry here. |
445 | | */ |
446 | 0 | cal.year += 100; |
447 | 0 | goto again; |
448 | 0 | } else { |
449 | | /* A very bad date before the GPS epoch. There's not |
450 | | * much we can do, except to add the complement of |
451 | | * DAY_GPS_STARTS % (7 * 1024) here, that is, use a |
452 | | * congruential identity: Add the complement instead of |
453 | | * subtracting the value gives a value with the same |
454 | | * modulus. But of course, now we MUST to go through a |
455 | | * cycle fix... because the date was obviously wrong! |
456 | | */ |
457 | 0 | warp = TRUE; |
458 | 0 | days += s_compl_shift; |
459 | 0 | } |
460 | | |
461 | | /* Splitting to weeks is simple now: */ |
462 | 0 | week = days / 7; |
463 | 0 | days -= week * 7; |
464 | | |
465 | | /* re-base on start of NTP with weeks mapped to 1024 weeks |
466 | | * starting with the GPS base day set in the calendar. |
467 | | */ |
468 | 0 | gps.weeks = week + GPSNTP_WSHIFT; |
469 | 0 | gps.wsecs = days * SECSPERDAY + ntpcal_date_to_daysec(&cal); |
470 | 0 | gps.frac = 0; |
471 | 0 | gpscal_add_offset(&gps, fofs); |
472 | 0 | return warp ? _gpscal_fix_gps_era(&gps) : gps; |
473 | 0 | } |
474 | | |
475 | | /* ----------------------------------------------------------------- |
476 | | * get civil date from week/tow representation |
477 | | */ |
478 | | void |
479 | | gpscal_to_calendar( |
480 | | TCivilDate * cd, |
481 | | TcGpsDatum * wd |
482 | | ) |
483 | 0 | { |
484 | 0 | TNtpDatum nd; |
485 | |
|
486 | 0 | memset(cd, 0, sizeof(*cd)); |
487 | 0 | nd = gpsntp_from_gpscal_ex(wd, FALSE); |
488 | 0 | gpsntp_to_calendar(cd, &nd); |
489 | 0 | } |
490 | | |
491 | | /* ----------------------------------------------------------------- |
492 | | * Given the week and seconds in week, as well as the fraction/offset |
493 | | * (which should/could include the leap seconds offset), unfold the |
494 | | * weeks (which are assumed to have just 10 bits) into expanded weeks |
495 | | * based on the GPS base date derived from the build date (default) or |
496 | | * set by the configuration. |
497 | | * |
498 | | * !NOTE! This function takes RAW GPS weeks, aligned to the GPS start |
499 | | * (1980-01-06) on input. The output weeks will be aligned to NTPD's |
500 | | * week calendar start (1899-12-31)! |
501 | | */ |
502 | | TGpsDatum |
503 | | gpscal_from_gpsweek( |
504 | | uint16_t week, |
505 | | int32_t secs, |
506 | | l_fp fofs |
507 | | ) |
508 | 0 | { |
509 | 0 | TGpsDatum retv; |
510 | |
|
511 | 0 | retv.frac = 0; |
512 | 0 | retv.wsecs = secs; |
513 | 0 | retv.weeks = week + GPSNTP_WSHIFT; |
514 | 0 | gpscal_add_offset(&retv, fofs); |
515 | 0 | return _gpscal_fix_gps_era(&retv); |
516 | 0 | } |
517 | | |
518 | | /* ----------------------------------------------------------------- |
519 | | * internal work horse for time-of-week expansion |
520 | | */ |
521 | | static TGpsDatum |
522 | | _gpscal_from_weektime( |
523 | | int32_t wsecs, |
524 | | l_fp fofs, |
525 | | TcGpsDatum * pivot |
526 | | ) |
527 | 0 | { |
528 | 0 | static const int32_t shift = SECSPERWEEK / 2; |
529 | |
|
530 | 0 | TGpsDatum retv; |
531 | | |
532 | | /* set result based on pivot -- ops order is important here */ |
533 | 0 | ZERO(retv); |
534 | 0 | retv.wsecs = wsecs; |
535 | 0 | gpscal_add_offset(&retv, fofs); /* result is normalized */ |
536 | 0 | retv.weeks = pivot->weeks; |
537 | | |
538 | | /* Manual periodic extension without division: */ |
539 | 0 | if (pivot->wsecs < shift) { |
540 | 0 | int32_t lim = pivot->wsecs + shift; |
541 | 0 | retv.weeks -= (retv.wsecs > lim || |
542 | 0 | (retv.wsecs == lim && retv.frac >= pivot->frac)); |
543 | 0 | } else { |
544 | 0 | int32_t lim = pivot->wsecs - shift; |
545 | 0 | retv.weeks += (retv.wsecs < lim || |
546 | 0 | (retv.wsecs == lim && retv.frac < pivot->frac)); |
547 | 0 | } |
548 | 0 | return _gpscal_fix_gps_era(&retv); |
549 | 0 | } |
550 | | |
551 | | /* ----------------------------------------------------------------- |
552 | | * expand a time-of-week around a pivot given as week datum |
553 | | */ |
554 | | TGpsDatum |
555 | | gpscal_from_weektime2( |
556 | | int32_t wsecs, |
557 | | l_fp fofs, |
558 | | TcGpsDatum * pivot |
559 | | ) |
560 | 0 | { |
561 | 0 | TGpsDatum wpiv = * pivot; |
562 | 0 | _norm_gps_datum(&wpiv); |
563 | 0 | return _gpscal_from_weektime(wsecs, fofs, &wpiv); |
564 | 0 | } |
565 | | |
566 | | /* ----------------------------------------------------------------- |
567 | | * epand a time-of-week around an pivot given as LFP, which in turn |
568 | | * is expanded around the current system time and then converted |
569 | | * into a week datum. |
570 | | */ |
571 | | TGpsDatum |
572 | | gpscal_from_weektime1( |
573 | | int32_t wsecs, |
574 | | l_fp fofs, |
575 | | l_fp pivot |
576 | | ) |
577 | 0 | { |
578 | 0 | vint64 pvi64; |
579 | 0 | TGpsDatum wpiv; |
580 | 0 | ntpcal_split split; |
581 | | |
582 | | /* get 64-bit pivot in NTP epoch */ |
583 | 0 | pvi64 = ntpcal_ntp_to_ntp(pivot.l_ui, NULL); |
584 | | |
585 | | /* convert to weeks since 1899-12-31 and seconds in week */ |
586 | 0 | pvi64 = addv64u32(&pvi64, (GPSNTP_DSHIFT * SECSPERDAY)); |
587 | 0 | split = ntpcal_weeksplit(&pvi64); |
588 | |
|
589 | 0 | wpiv.weeks = split.hi; |
590 | 0 | wpiv.wsecs = split.lo; |
591 | 0 | wpiv.frac = pivot.l_uf; |
592 | 0 | return _gpscal_from_weektime(wsecs, fofs, &wpiv); |
593 | 0 | } |
594 | | |
595 | | /* ----------------------------------------------------------------- |
596 | | * get week/tow representation from day/tod datum |
597 | | */ |
598 | | TGpsDatum |
599 | | gpscal_from_gpsntp( |
600 | | TcNtpDatum * gd |
601 | | ) |
602 | 0 | { |
603 | 0 | TGpsDatum retv; |
604 | 0 | vint64 ts64; |
605 | 0 | ntpcal_split split; |
606 | |
|
607 | 0 | ts64 = ntpcal_dayjoin(gd->days, gd->secs); |
608 | 0 | ts64 = addv64u32(&ts64, (GPSNTP_DSHIFT * SECSPERDAY)); |
609 | 0 | split = ntpcal_weeksplit(&ts64); |
610 | |
|
611 | 0 | retv.frac = gd->frac; |
612 | 0 | retv.wsecs = split.lo; |
613 | 0 | retv.weeks = split.hi; |
614 | 0 | return retv; |
615 | 0 | } |
616 | | |
617 | | /* ----------------------------------------------------------------- |
618 | | * convert week/tow to LFP stamp |
619 | | */ |
620 | | l_fp |
621 | | ntpfp_from_gpsdatum( |
622 | | TcGpsDatum * gd |
623 | | ) |
624 | 0 | { |
625 | 0 | l_fp retv; |
626 | |
|
627 | 0 | retv.l_uf = gd->frac; |
628 | 0 | retv.l_ui = gd->weeks * (uint32_t)SECSPERWEEK |
629 | 0 | + (uint32_t)gd->wsecs |
630 | 0 | - (uint32_t)SECSPERDAY * GPSNTP_DSHIFT; |
631 | 0 | return retv; |
632 | 0 | } |
633 | | |
634 | | /* -*-EOF-*- */ |