gps_pvt 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +10 -0
  6. data/README.md +86 -0
  7. data/Rakefile +86 -0
  8. data/bin/console +15 -0
  9. data/bin/setup +8 -0
  10. data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -0
  11. data/ext/gps_pvt/GPS/GPS_wrap.cxx +16019 -0
  12. data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -0
  13. data/ext/gps_pvt/extconf.rb +70 -0
  14. data/ext/ninja-scan-light/tool/navigation/EGM.h +2971 -0
  15. data/ext/ninja-scan-light/tool/navigation/GPS.h +2432 -0
  16. data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +479 -0
  17. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +1081 -0
  18. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +199 -0
  19. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +210 -0
  20. data/ext/ninja-scan-light/tool/navigation/MagneticField.h +928 -0
  21. data/ext/ninja-scan-light/tool/navigation/NTCM.h +211 -0
  22. data/ext/ninja-scan-light/tool/navigation/RINEX.h +1781 -0
  23. data/ext/ninja-scan-light/tool/navigation/WGS84.h +186 -0
  24. data/ext/ninja-scan-light/tool/navigation/coordinate.h +406 -0
  25. data/ext/ninja-scan-light/tool/param/bit_array.h +145 -0
  26. data/ext/ninja-scan-light/tool/param/complex.h +558 -0
  27. data/ext/ninja-scan-light/tool/param/matrix.h +4049 -0
  28. data/ext/ninja-scan-light/tool/param/matrix_fixed.h +665 -0
  29. data/ext/ninja-scan-light/tool/param/matrix_special.h +562 -0
  30. data/ext/ninja-scan-light/tool/param/quaternion.h +765 -0
  31. data/ext/ninja-scan-light/tool/param/vector3.h +651 -0
  32. data/ext/ninja-scan-light/tool/swig/Coordinate.i +177 -0
  33. data/ext/ninja-scan-light/tool/swig/GPS.i +1102 -0
  34. data/ext/ninja-scan-light/tool/swig/SylphideMath.i +1234 -0
  35. data/ext/ninja-scan-light/tool/swig/extconf.rb +5 -0
  36. data/ext/ninja-scan-light/tool/swig/makefile +53 -0
  37. data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +417 -0
  38. data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +489 -0
  39. data/gps_pvt.gemspec +57 -0
  40. data/lib/gps_pvt/receiver.rb +375 -0
  41. data/lib/gps_pvt/ubx.rb +148 -0
  42. data/lib/gps_pvt/version.rb +5 -0
  43. data/lib/gps_pvt.rb +9 -0
  44. data/sig/gps_pvt.rbs +4 -0
  45. metadata +117 -0
@@ -0,0 +1,2432 @@
1
+ /*
2
+ * Copyright (c) 2016, M.Naruoka (fenrir)
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without modification,
6
+ * are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice,
9
+ * this list of conditions and the following disclaimer.
10
+ * - Redistributions in binary form must reproduce the above copyright notice,
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ * - Neither the name of the naruoka.org nor the names of its contributors
14
+ * may be used to endorse or promote products derived from this software
15
+ * without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
21
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ */
31
+
32
+ #ifndef __GPS_H__
33
+ #define __GPS_H__
34
+
35
+ /** @file
36
+ * @brief GPS ICD definitions including C/A code, time, ephemeris, ...
37
+ */
38
+
39
+ #include <vector>
40
+ #include <iterator>
41
+ #include <map>
42
+ #include <bitset>
43
+
44
+ #include <ctime>
45
+ #define _USE_MATH_DEFINES
46
+ #include <cmath>
47
+ #include <climits>
48
+ #include <cstdlib>
49
+
50
+ #include "WGS84.h"
51
+
52
+ #include "coordinate.h"
53
+
54
+ #ifdef pow2
55
+ #define POW2_ALREADY_DEFINED
56
+ #else
57
+ #define pow2(x) ((x)*(x))
58
+ #endif
59
+ #ifdef pow3
60
+ #define POW3_ALREADY_DEFINED
61
+ #else
62
+ #define pow3(x) ((x)*(x)*(x))
63
+ #endif
64
+
65
+ template <class FloatT = double>
66
+ class GPS_Signal {
67
+ public:
68
+ typedef FloatT float_t;
69
+
70
+ class PRN {
71
+ public:
72
+ typedef std::bitset<10> content_t;
73
+ protected:
74
+ content_t content;
75
+ public:
76
+ void reset(){content.set();}
77
+ PRN() : content() {reset();}
78
+ PRN(const unsigned long &init) : content(init) {}
79
+ ~PRN(){}
80
+ friend std::ostream &operator<<(std::ostream &out, const PRN &prn){
81
+ out << const_cast<PRN &>(prn).content;
82
+ return out;
83
+ }
84
+ };
85
+
86
+ class G1 : public PRN {
87
+ public:
88
+ G1() : PRN() {}
89
+ ~G1(){}
90
+ bool get() const {return PRN::content[9];}
91
+ void next(){
92
+ bool tmp(PRN::content[2] ^ PRN::content[9]);
93
+ PRN::content <<= 1;
94
+ PRN::content[0] = tmp;
95
+ }
96
+ };
97
+
98
+ class G2 : public PRN {
99
+ protected:
100
+ const int _selector1, _selector2;
101
+ public:
102
+ G2(int selector1, int selector2) : PRN(), _selector1(selector1), _selector2(selector2) {}
103
+ ~G2(){}
104
+ bool get() const {return PRN::content[_selector1] ^ PRN::content[_selector2];}
105
+ void next(){
106
+ bool tmp(PRN::content[1]
107
+ ^ PRN::content[2]
108
+ ^ PRN::content[5]
109
+ ^ PRN::content[7]
110
+ ^ PRN::content[8]
111
+ ^ PRN::content[9]);
112
+ PRN::content <<= 1;
113
+ PRN::content[0] = tmp;
114
+ }
115
+ static G2 get_G2(const int &prn){
116
+ switch(prn){
117
+ case 1: return G2(1, 5);
118
+ case 2: return G2(2, 6);
119
+ case 3: return G2(3, 7);
120
+ case 4: return G2(4, 8);
121
+ case 5: return G2(0, 8);
122
+ case 6: return G2(1, 9);
123
+ case 7: return G2(0, 7);
124
+ case 8: return G2(1, 8);
125
+ case 9: return G2(2, 9);
126
+ case 10: return G2(1, 2);
127
+ case 11: return G2(2, 3);
128
+ case 12: return G2(4, 5);
129
+ case 13: return G2(5, 6);
130
+ case 14: return G2(6, 7);
131
+ case 15: return G2(7, 8);
132
+ case 16: return G2(8, 9);
133
+ case 17: return G2(0, 3);
134
+ case 18: return G2(1, 4);
135
+ case 19: return G2(2, 5);
136
+ case 20: return G2(3, 6);
137
+ case 21: return G2(4, 7);
138
+ case 22: return G2(5, 8);
139
+ case 23: return G2(0, 2);
140
+ case 24: return G2(3, 5);
141
+ case 25: return G2(4, 6);
142
+ case 26: return G2(5, 7);
143
+ case 27: return G2(6, 8);
144
+ case 28: return G2(7, 9);
145
+ case 29: return G2(0, 5);
146
+ case 30: return G2(1, 6);
147
+ case 31: return G2(2, 7);
148
+ case 32: return G2(3, 8);
149
+ case 33: return G2(4, 9);
150
+ case 34: return G2(3, 9);
151
+ case 35: return G2(0, 6);
152
+ case 36: return G2(1, 7);
153
+ default: return G2(3, 9);
154
+ }
155
+ }
156
+ };
157
+
158
+ class CA_Code {
159
+ public:
160
+ typedef FloatT float_t;
161
+ static const float_t FREQENCY;
162
+ static const float_t length_1chip() {
163
+ static const float_t res(1. / FREQENCY);
164
+ return res;
165
+ }
166
+ protected:
167
+ G1 g1;
168
+ G2 g2;
169
+ public:
170
+ CA_Code(const int &prn) : g1(), g2(G2::get_G2(prn)){}
171
+ ~CA_Code(){}
172
+ bool get() const {return g1.get() ^ g2.get();}
173
+ int get_multi() const {return get() ? 1 : -1;}
174
+ void next(){
175
+ g1.next();
176
+ g2.next();
177
+ }
178
+ };
179
+ };
180
+
181
+ template <class FloatT>
182
+ const typename GPS_Signal<FloatT>::float_t GPS_Signal<FloatT>::CA_Code::FREQENCY = 1.023E6;
183
+
184
+ template <class FloatT = double>
185
+ struct GPS_Time {
186
+ typedef FloatT float_t;
187
+ static const unsigned int seconds_day = 60U * 60 * 24;
188
+ static const unsigned int seconds_week = (60U * 60 * 24) * 7;
189
+
190
+ static const int days_of_month[];
191
+
192
+ /**
193
+ * Check whether leap year
194
+ *
195
+ * @param year
196
+ * @return true when leap year, otherwise false
197
+ */
198
+ static inline bool is_leap_year(const int &year) {
199
+ return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
200
+ }
201
+
202
+ struct leap_year_prop_res_t {
203
+ int extra_days; ///< extra leap years since 1980
204
+ bool is_leap_year; ///< true when leap year
205
+ };
206
+
207
+ /**
208
+ * Check leap year property
209
+ * The return values are;
210
+ * 1) extra_days equals to years which can be divided by 4, but are not leap years
211
+ * since 1980 (the first GPS year), and before (and except for) this_year.
212
+ * 2) is_leap_year equals to whether this_year is leap year or not.
213
+ *
214
+ * @param this_year check target
215
+ * @param skip_init_leap_year_check whether skip initial check whether this_year is leap year or not.
216
+ * @return leap year property
217
+ */
218
+ static leap_year_prop_res_t leap_year_prop(const int &this_year, const bool &skip_init_leap_year_check = false){
219
+ leap_year_prop_res_t res = {0, skip_init_leap_year_check || (this_year % 4 == 0)};
220
+ do{ // check leap year
221
+ std::div_t y_400(std::div(this_year, 400));
222
+ if((y_400.quot -= 5) < 0){break;} // year < 2000
223
+ res.extra_days += (y_400.quot * 3); // no leap year; [2100, 2200, 2300], [2500, ...
224
+ if(y_400.rem == 0){break;}
225
+ std::div_t y_100(std::div(y_400.rem, 100));
226
+ res.extra_days += y_100.quot;
227
+ if(y_100.rem == 0){ // when this_year is just 2100, 2200, 2300, or 2500, ...
228
+ res.extra_days--;
229
+ res.is_leap_year = false;
230
+ }
231
+ }while(false);
232
+ return res;
233
+ }
234
+
235
+ int week;
236
+ float_t seconds;
237
+
238
+ GPS_Time() {}
239
+ GPS_Time(const GPS_Time &t)
240
+ : week(t.week), seconds(t.seconds) {}
241
+ GPS_Time(const int &_week, const float_t &_seconds)
242
+ : week(_week), seconds(_seconds) {}
243
+ GPS_Time &canonicalize(){
244
+ int quot(std::floor(seconds / seconds_week));
245
+ week += quot;
246
+ seconds -= (seconds_week * quot);
247
+ return *this;
248
+ }
249
+ GPS_Time(const std::tm &t, const float_t &leap_seconds = 0) {
250
+ int days(-6);
251
+ int y(t.tm_year + 1900); // tm_year is year minus 1900
252
+ bool leap_year;
253
+ {
254
+ leap_year_prop_res_t prop(leap_year_prop(y));
255
+ days -= prop.extra_days;
256
+ leap_year = prop.is_leap_year;
257
+ }
258
+
259
+ y -= 1980; // base is 1980/1/6
260
+ days += y * 365 + ((y + 3) / 4);
261
+ for(int i(0); i < t.tm_mon; i++){
262
+ days += days_of_month[i];
263
+ if((i == 1) && leap_year){days++;}
264
+ }
265
+ days += t.tm_mday;
266
+
267
+ std::div_t week_day(std::div(days, 7));
268
+ week = week_day.quot;
269
+ seconds = leap_seconds + week_day.rem * seconds_day
270
+ + t.tm_hour * 60 * 60
271
+ + t.tm_min * 60
272
+ + t.tm_sec;
273
+ canonicalize();
274
+ }
275
+ static GPS_Time now(const float_t &leap_seconds = 0) {
276
+ time_t timer;
277
+ std::tm t;
278
+
279
+ time(&timer); // Current serial time, then convert the time to struct
280
+ #if defined(_MSC_VER)
281
+ gmtime_s(&t, &timer);
282
+ #elif defined(__GNUC__)
283
+ gmtime_r(&timer, &t);
284
+ #else
285
+ t = *gmtime(&timer);
286
+ #endif
287
+
288
+ return GPS_Time(t, leap_seconds);
289
+ }
290
+ float_t serialize() const {
291
+ return seconds + (float_t)seconds_week * week;
292
+ }
293
+
294
+ GPS_Time &operator+=(const float_t &sec){
295
+ seconds += sec;
296
+ canonicalize();
297
+ return *this;
298
+ }
299
+ GPS_Time &operator-=(const float_t &sec){
300
+ return operator+=(-sec);
301
+ }
302
+ GPS_Time operator+(const float_t &sec) const {
303
+ GPS_Time t(*this);
304
+ return (t += sec);
305
+ }
306
+ GPS_Time operator-(const float_t &sec) const {
307
+ return operator+(-sec);
308
+ }
309
+ /**
310
+ * Get interval in unit of second
311
+ */
312
+ float_t operator-(const GPS_Time &t) const {
313
+ float_t res(seconds - t.seconds);
314
+ res += ((float_t)week - t.week) * seconds_week;
315
+ return res;
316
+ }
317
+ friend float_t operator+(float_t v, const GPS_Time &t){
318
+ return v + (((float_t)t.week * seconds_week) + t.seconds);
319
+ }
320
+ friend float_t operator-(float_t v, const GPS_Time &t){
321
+ return v - (((float_t)t.week * seconds_week) + t.seconds);
322
+ }
323
+ bool operator<(const GPS_Time &t) const {
324
+ return ((week < t.week) ? true : ((week > t.week) ? false : (seconds < t.seconds)));
325
+ }
326
+ bool operator>(const GPS_Time &t) const {
327
+ return t.operator<(*this);
328
+ }
329
+ bool operator==(const GPS_Time &t) const {
330
+ return (week == t.week) && (seconds == t.seconds);
331
+ }
332
+ bool operator!=(const GPS_Time &t) const {
333
+ return !(operator==(t));
334
+ }
335
+ bool operator<=(const GPS_Time &t) const {
336
+ return ((week < t.week) ? true : ((week > t.week) ? false : (seconds <= t.seconds)));
337
+ }
338
+ bool operator>=(const GPS_Time &t) const {
339
+ return t.operator<=(*this);
340
+ }
341
+
342
+ std::tm c_tm(const float_t &leap_seconds = 0) const {
343
+ std::tm t;
344
+
345
+ GPS_Time mod_t((*this) + leap_seconds);
346
+
347
+ std::div_t min_sec(std::div((int)mod_t.seconds, 60));
348
+ t.tm_sec = min_sec.rem;
349
+ std::div_t hr_min(std::div(min_sec.quot, 60));
350
+ t.tm_min = hr_min.rem;
351
+ std::div_t day_hr(std::div(hr_min.quot, 24));
352
+ t.tm_hour = day_hr.rem;
353
+ t.tm_wday = t.tm_mday = day_hr.quot;
354
+
355
+ t.tm_mday += 6 + (mod_t.week * 7);
356
+ std::div_t days_4year(std::div(t.tm_mday, 366 + 365 * 3)); // standard day of years
357
+ t.tm_mday = days_4year.rem;
358
+ int y(days_4year.quot * 4 + 1980);
359
+ bool leap_year;
360
+ {
361
+ leap_year_prop_res_t prop(leap_year_prop(y, true));
362
+ t.tm_mday += prop.extra_days;
363
+ leap_year = prop.is_leap_year;
364
+ }
365
+
366
+ // process remaining 4 years
367
+ int doy[] = {
368
+ leap_year ? 366 : 365,
369
+ 365, 365, 365
370
+ };
371
+ for(unsigned i(0); i < sizeof(doy) / sizeof(doy[0]); ++i){
372
+ if(t.tm_mday <= doy[i]){break;}
373
+ t.tm_mday -= doy[i];
374
+ y++;
375
+ }
376
+
377
+ // process current year
378
+ leap_year = is_leap_year(y);
379
+ t.tm_yday = t.tm_mday;
380
+ t.tm_year = y - 1900; // tm_year is year minus 1900.
381
+ for(t.tm_mon = 0;
382
+ t.tm_mday > days_of_month[t.tm_mon];
383
+ (t.tm_mon)++){
384
+ if((t.tm_mon == 1) && leap_year){
385
+ if(t.tm_mday == 29){break;}
386
+ else{t.tm_mday--;}
387
+ }
388
+ t.tm_mday -= days_of_month[t.tm_mon];
389
+ }
390
+ t.tm_isdst = 0;
391
+
392
+ return t;
393
+ }
394
+
395
+ float_t year(const float_t &leap_seconds = 0) const {
396
+ float_t days((seconds + leap_seconds) / seconds_day + (week * 7) + (6 - 1)); // days from 1980/1/1, whose 00:00:00 is just 0
397
+ float_t year4;
398
+ days = std::modf(days / (366 + 365 * 3), &year4) * (366 + 365 * 3);
399
+ int year(1980 + (int)year4 * 4);
400
+ bool leap_year;
401
+ {
402
+ leap_year_prop_res_t prop(leap_year_prop(year, true));
403
+ days += prop.extra_days;
404
+ leap_year = prop.is_leap_year;
405
+ }
406
+
407
+ // process remaining 4 years
408
+ int doy_i(0), doy[] = {
409
+ leap_year ? 366 : 365,
410
+ 365, 365, 365,
411
+ is_leap_year(year + 4) ? 366 : 365,
412
+ };
413
+ for(; doy_i < sizeof(doy) / sizeof(doy[0]); ++doy_i){
414
+ if(days <= doy[doy_i]){break;}
415
+ days -= doy[doy_i];
416
+ year++;
417
+ }
418
+
419
+ return days / doy[doy_i] + year;
420
+ }
421
+
422
+ /**
423
+ * When t >= self positive value will be returned,
424
+ * otherwise(t < self), negative.
425
+ */
426
+ float_t interval(const unsigned int &t_week,
427
+ const float_t &t_seconds) const {
428
+ return t_seconds - seconds
429
+ + ((float_t)t_week - week) * seconds_week;
430
+ }
431
+ float_t interval(const GPS_Time &t) const {
432
+ return interval(t.week, t.seconds);
433
+ }
434
+
435
+ friend std::ostream &operator<<(std::ostream &out, const GPS_Time &t){
436
+ out << t.week << " week " << t.seconds << " sec.";
437
+ return out;
438
+ }
439
+
440
+ struct leap_second_event_t {
441
+ int tm_year; // year - 1900
442
+ int tm_mon; // [0, 11]
443
+ int tm_mday; // [1, 31]
444
+ int leap_seconds;
445
+ struct {
446
+ int week;
447
+ float_t seconds;
448
+ } uncorrected; // to work around of "incomplete type" error within g++
449
+ leap_second_event_t(
450
+ const int &year, const int &month, const int &day,
451
+ const int &leap)
452
+ : tm_year(year - 1900), tm_mon(month - 1), tm_mday(day),
453
+ leap_seconds(leap) {
454
+ std::tm t = {0};
455
+ t.tm_year = tm_year;
456
+ t.tm_mon = tm_mon;
457
+ t.tm_mday = tm_mday;
458
+ GPS_Time t_gps(t);
459
+ uncorrected.week = t_gps.week;
460
+ uncorrected.seconds = t_gps.seconds;
461
+ }
462
+ };
463
+ static const leap_second_event_t leap_second_events[];
464
+ static int guess_leap_seconds(const std::tm &t) {
465
+ for(const leap_second_event_t *i(&leap_second_events[0]); i->leap_seconds > 0; ++i){
466
+ if(t.tm_year > i->tm_year){return i->leap_seconds;}
467
+ if(t.tm_year < i->tm_year){continue;}
468
+ if(t.tm_mon > i->tm_mon){return i->leap_seconds;}
469
+ if(t.tm_mon < i->tm_mon){continue;}
470
+ if(t.tm_mday >= i->tm_mday){return i->leap_seconds;}
471
+ }
472
+ return 0;
473
+ }
474
+ static int guess_leap_seconds(const GPS_Time<float_t> &uncorrected) {
475
+ for(const leap_second_event_t *i(&leap_second_events[0]); i->leap_seconds > 0; ++i){
476
+ if(uncorrected >= GPS_Time(i->uncorrected.week, i->uncorrected.seconds)){return i->leap_seconds;}
477
+ }
478
+ return 0;
479
+ }
480
+ };
481
+
482
+ template <class FloatT>
483
+ const int GPS_Time<FloatT>::days_of_month[] = {
484
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
485
+ };
486
+
487
+ template <class FloatT>
488
+ const typename GPS_Time<FloatT>::leap_second_event_t GPS_Time<FloatT>::leap_second_events[] = {
489
+ leap_second_event_t(2017, 1, 1, 18),
490
+ leap_second_event_t(2015, 7, 1, 17),
491
+ leap_second_event_t(2012, 7, 1, 16),
492
+ leap_second_event_t(2009, 1, 1, 15),
493
+ leap_second_event_t(2006, 1, 1, 14),
494
+ leap_second_event_t(1999, 1, 1, 13),
495
+ leap_second_event_t(1997, 7, 1, 12),
496
+ leap_second_event_t(1996, 1, 1, 11),
497
+ leap_second_event_t(1994, 7, 1, 10),
498
+ leap_second_event_t(1993, 7, 1, 9),
499
+ leap_second_event_t(1992, 7, 1, 8),
500
+ leap_second_event_t(1991, 1, 1, 7),
501
+ leap_second_event_t(1990, 1, 1, 6),
502
+ leap_second_event_t(1988, 1, 1, 5),
503
+ leap_second_event_t(1985, 7, 1, 4),
504
+ leap_second_event_t(1983, 7, 1, 3),
505
+ leap_second_event_t(1982, 7, 1, 2),
506
+ leap_second_event_t(1981, 7, 1, 1),
507
+ leap_second_event_t(1980, 1, 6, 0), // anchor
508
+ };
509
+
510
+ template <class FloatT = double>
511
+ class GPS_SpaceNode {
512
+ public:
513
+ typedef FloatT float_t;
514
+ static const float_t light_speed;
515
+ static const float_t L1_Frequency;
516
+ static const float_t &L1_WaveLength() {
517
+ static const float_t res(light_speed / L1_Frequency);
518
+ return res;
519
+ }
520
+ static const float_t SC2RAD;
521
+
522
+ static const float_t L2_Frequency;
523
+ static const float_t &L2_WaveLength() {
524
+ static const float_t res(light_speed / L2_Frequency);
525
+ return res;
526
+ }
527
+ static const float_t gamma_L1_L2;
528
+
529
+ protected:
530
+ static float_t rad2sc(const float_t &rad) {return rad / M_PI;}
531
+ static float_t sc2rad(const float_t &sc) {return sc * M_PI;}
532
+
533
+ public:
534
+ typedef GPS_SpaceNode<float_t> self_t;
535
+ typedef GPS_Time<float_t> gps_time_t;
536
+ typedef System_XYZ<float_t, WGS84> xyz_t;
537
+ typedef System_LLH<float_t, WGS84> llh_t;
538
+ typedef System_ENU<float_t, WGS84> enu_t;
539
+
540
+ typedef unsigned char u8_t;
541
+ typedef signed char s8_t;
542
+ typedef unsigned short u16_t;
543
+ typedef signed short s16_t;
544
+ typedef unsigned int u32_t;
545
+ typedef signed int s32_t;
546
+
547
+ typedef int int_t;
548
+ typedef unsigned int uint_t;
549
+
550
+ struct DataParser {
551
+ template <
552
+ class OutputT, class InputT,
553
+ int EffectiveBits_in_InputT = sizeof(InputT) * CHAR_BIT,
554
+ int PaddingBits_in_InputT_MSB = (int)sizeof(InputT) * CHAR_BIT - EffectiveBits_in_InputT,
555
+ bool output_is_smaller_than_input
556
+ = (EffectiveBits_in_InputT >= ((int)sizeof(OutputT) * CHAR_BIT))>
557
+ struct bits2num_t {
558
+ static OutputT run(const InputT *buf, const uint_t &index){
559
+ // ex.1) I_8 0 1 2 3 4 5 6 7 | 8 9 0 1 2 3 4 5
560
+ // O_8 0*1*2*3* *4*5*6*7
561
+ // ex.2) I_16 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 | 6 7 8 9 0 1 2 3
562
+ // O_8 0*1*2*3*4*5*6*7
563
+ // ex.3) I_16 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 | 6 7 8 9 0 1 2 3
564
+ // O_8 0*1*2*3* *4*5*6*7
565
+ static const int
566
+ output_bits(sizeof(OutputT) * CHAR_BIT),
567
+ input_bits(sizeof(InputT) * CHAR_BIT);
568
+ static const int padding_bits_MSB_abs(
569
+ PaddingBits_in_InputT_MSB * (PaddingBits_in_InputT_MSB >= 0 ? 1 : -1));
570
+ std::div_t aligned(std::div(index, EffectiveBits_in_InputT));
571
+ if(PaddingBits_in_InputT_MSB >= 0){
572
+ OutputT res(
573
+ (buf[aligned.quot] << (aligned.rem + padding_bits_MSB_abs))
574
+ >> (input_bits - output_bits));
575
+ if(aligned.rem > (EffectiveBits_in_InputT - output_bits)){
576
+ // in case of overrun; ex.1 and ex.3
577
+ res |= (OutputT)(
578
+ (buf[++aligned.quot] << padding_bits_MSB_abs)
579
+ // left shift to remove padding
580
+ >> (EffectiveBits_in_InputT
581
+ + (input_bits - output_bits) - aligned.rem));
582
+ // right shift to fill remaining;
583
+ // shift = input - [require_bits = output - (effective - rem)]
584
+ }
585
+ return res;
586
+ }else{
587
+ // rare case: negative MSB padding
588
+ int left_shift(aligned.rem + PaddingBits_in_InputT_MSB);
589
+ OutputT res(
590
+ (buf[aligned.quot] << (left_shift >= 0 ? left_shift : 0))
591
+ >> (input_bits - output_bits + (left_shift >= 0 ? 0 : -left_shift)));
592
+ if(aligned.rem > (EffectiveBits_in_InputT - output_bits)){
593
+ res |= (OutputT)(
594
+ buf[++aligned.quot]
595
+ >> (EffectiveBits_in_InputT + padding_bits_MSB_abs
596
+ + (input_bits - output_bits) - aligned.rem));
597
+ }
598
+ return res;
599
+ }
600
+ }
601
+ };
602
+ template <class OutputT, class InputT,
603
+ int EffectiveBits_in_InputT, int PaddingBits_in_InputT_MSB>
604
+ struct bits2num_t<OutputT, InputT,
605
+ EffectiveBits_in_InputT, PaddingBits_in_InputT_MSB, false> {
606
+ static OutputT run(const InputT *buf, const uint_t &index){
607
+ // When sizeof(OutputT) > sizeof(InputT)
608
+ // ex.4) I_8 0 1 2 3 4 5 6 7 | 8 9 0 1 2 3 4 5 | 6 7 8 9 0 1 2 3
609
+ // O_16 0*1*2*3* *4*5*6*7*8*9*0*1* *2*3*4*5
610
+ static const int
611
+ output_bits(sizeof(OutputT) * CHAR_BIT),
612
+ input_bits(sizeof(InputT) * CHAR_BIT);
613
+ static const int padding_bits_LSB(
614
+ input_bits - EffectiveBits_in_InputT - PaddingBits_in_InputT_MSB);
615
+ static const int padding_bits_LSB_abs(
616
+ padding_bits_LSB >= 0 ? padding_bits_LSB : -padding_bits_LSB);
617
+ static const int effective_mask_shift(
618
+ (PaddingBits_in_InputT_MSB <= 0) ? 0 : (PaddingBits_in_InputT_MSB - 1));
619
+ static const InputT effective_mask((PaddingBits_in_InputT_MSB <= 0)
620
+ ? (~(InputT)0)
621
+ : (((((InputT)1) << (input_bits - 1)) - 1) >> effective_mask_shift));
622
+ static const int shift_after_mask(
623
+ (PaddingBits_in_InputT_MSB < 0) ? -PaddingBits_in_InputT_MSB : 0);
624
+ std::div_t aligned(std::div(index, EffectiveBits_in_InputT));
625
+ OutputT res(0);
626
+ for(int i(output_bits / EffectiveBits_in_InputT); i > 0; --i, ++aligned.quot){
627
+ res <<= EffectiveBits_in_InputT;
628
+ res |= ((padding_bits_LSB >= 0)
629
+ ? (((buf[aligned.quot] & effective_mask) >> shift_after_mask) >> padding_bits_LSB_abs)
630
+ : (((buf[aligned.quot] & effective_mask) >> shift_after_mask) << padding_bits_LSB_abs));
631
+ }
632
+ int last_shift(aligned.rem + (output_bits % EffectiveBits_in_InputT));
633
+ if(last_shift > 0){
634
+ res <<= last_shift;
635
+ res |= (((buf[aligned.quot] & effective_mask) >> shift_after_mask)
636
+ >> (EffectiveBits_in_InputT + padding_bits_LSB - last_shift));
637
+ }
638
+ return res;
639
+ }
640
+ };
641
+
642
+ template <class OutputT, class InputT>
643
+ static OutputT bits2num(const InputT *buf, const uint_t &index){
644
+ return bits2num_t<OutputT, InputT>::run(buf, index);
645
+ }
646
+ template <class OutputT, class InputT>
647
+ static OutputT bits2num(const InputT *buf, const uint_t &index, const uint_t &length){
648
+ return (bits2num<OutputT, InputT>(buf, index) >> ((sizeof(OutputT) * CHAR_BIT) - length));
649
+ }
650
+
651
+ template <class OutputT,
652
+ int EffectiveBits_in_InputT, int PaddingBits_in_InputT_MSB,
653
+ class InputT>
654
+ static OutputT bits2num(const InputT *buf, const uint_t &index){
655
+ return bits2num_t<OutputT, InputT,
656
+ EffectiveBits_in_InputT, PaddingBits_in_InputT_MSB>::run(buf, index);
657
+ }
658
+ template <class OutputT,
659
+ int EffectiveBits_in_InputT, int PaddingBits_in_InputT_MSB,
660
+ class InputT>
661
+ static OutputT bits2num(const InputT *buf, const uint_t &index, const uint_t &length){
662
+ return (bits2num<OutputT,
663
+ EffectiveBits_in_InputT, PaddingBits_in_InputT_MSB,
664
+ InputT>(buf, index)
665
+ >> ((sizeof(OutputT) * CHAR_BIT) - length));
666
+ }
667
+ };
668
+
669
+ template <class InputT,
670
+ int EffectiveBits = sizeof(InputT) * CHAR_BIT,
671
+ int PaddingBits_MSB = (int)sizeof(InputT) * CHAR_BIT - EffectiveBits>
672
+ struct BroadcastedMessage : public DataParser {
673
+ #define convert_u(bits, offset_bits, length, name) \
674
+ static u ## bits ## _t name(const InputT *buf){ \
675
+ return \
676
+ DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
677
+ buf, offset_bits, length); \
678
+ }
679
+ #define convert_s(bits, offset_bits, length, name) \
680
+ static s ## bits ## _t name(const InputT *buf){ \
681
+ return ((s ## bits ## _t) \
682
+ DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
683
+ buf, offset_bits)) \
684
+ >> (bits - length); \
685
+ }
686
+ #define convert_u_2(bits, offset_bits1, length1, offset_bits2, length2, name) \
687
+ static u ## bits ## _t name(const InputT *buf){ \
688
+ return \
689
+ (DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
690
+ buf, offset_bits1, length1) << length2) \
691
+ | DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
692
+ buf, offset_bits2, length2); \
693
+ }
694
+ #define convert_s_2(bits, offset_bits1, length1, offset_bits2, length2, name) \
695
+ static s ## bits ## _t name(const InputT *buf){ \
696
+ return ((s ## bits ## _t) \
697
+ ((DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
698
+ buf, offset_bits1, length1) << (bits - length1)) \
699
+ | (DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
700
+ buf, offset_bits2, length2) << (bits - length1 - length2)))) \
701
+ >> (bits - length1 - length2); \
702
+ }
703
+ convert_u( 8, 0, 8, preamble);
704
+ convert_u(32, 30, 24, how);
705
+ convert_u( 8, 49, 3, subframe_id);
706
+
707
+ struct SubFrame1 {
708
+ convert_u(16, 60, 10, WN);
709
+ convert_u( 8, 72, 4, URA);
710
+ convert_u( 8, 76, 6, SV_health);
711
+ convert_u_2(16, 82, 2, 210, 8, iodc);
712
+ convert_s( 8, 196, 8, t_GD);
713
+ convert_u(16, 218, 16, t_oc);
714
+ convert_s( 8, 240, 8, a_f2);
715
+ convert_s(16, 248, 16, a_f1);
716
+ convert_s(32, 270, 22, a_f0);
717
+ };
718
+
719
+ struct SubFrame2 {
720
+ convert_u( 8, 60, 8, iode);
721
+ convert_s(16, 68, 16, c_rs);
722
+ convert_s(16, 90, 16, delta_n);
723
+ convert_s_2(32, 106, 8, 120, 24, M0);
724
+ convert_s(16, 150, 16, c_uc);
725
+ convert_u_2(32, 166, 8, 180, 24, e);
726
+ convert_s(16, 210, 16, c_us);
727
+ convert_u_2(32, 226, 8, 240, 24, sqrt_A);
728
+ convert_u(16, 270, 16, t_oe);
729
+ convert_u( 8, 286, 1, fit);
730
+ };
731
+
732
+ struct SubFrame3 {
733
+ convert_s(16, 60, 16, c_ic);
734
+ convert_s_2(32, 76, 8, 90, 24, Omega0);
735
+ convert_s(16, 120, 16, c_is);
736
+ convert_s_2(32, 136, 8, 150, 24, i0);
737
+ convert_s(16, 180, 16, c_rc);
738
+ convert_s_2(32, 196, 8, 210, 24, omega);
739
+ convert_s(32, 240, 24, dot_Omega0);
740
+ convert_u( 8, 270, 8, iode);
741
+ convert_s(16, 278, 14, dot_i0);
742
+ };
743
+
744
+ convert_u( 8, 62, 6, sv_page_id);
745
+
746
+ struct SubFrame4_5_Alnamac {
747
+ convert_u(16, 68, 16, e);
748
+ convert_u( 8, 90, 8, t_oa);
749
+ convert_s(16, 98, 16, delta_i);
750
+ convert_s(16, 120, 16, dot_Omega0);
751
+ convert_u( 8, 128, 8, SV_health);
752
+ convert_u(32, 150, 24, sqrt_A);
753
+ convert_s(32, 180, 24, Omega0);
754
+ convert_s(32, 210, 24, omega);
755
+ convert_s(32, 240, 24, M0);
756
+ convert_s_2(16, 270, 8, 289, 3, a_f0);
757
+ convert_s(16, 278, 11, a_f1);
758
+ };
759
+
760
+ struct SubFrame4_Page18 {
761
+ convert_s( 8, 68, 8, alpha0);
762
+ convert_s( 8, 76, 8, alpha1);
763
+ convert_s( 8, 90, 8, alpha2);
764
+ convert_s( 8, 98, 8, alpha3);
765
+ convert_s( 8, 106, 8, beta0);
766
+ convert_s( 8, 120, 8, beta1);
767
+ convert_s( 8, 128, 8, beta2);
768
+ convert_s( 8, 136, 8, beta3);
769
+ convert_s(32, 150, 24, A1);
770
+ convert_s_2(32, 180, 24, 210, 8, A0);
771
+ convert_u( 8, 218, 8, t_ot);
772
+ convert_s( 8, 240, 8, delta_t_LS);
773
+ convert_u( 8, 226, 8, WN_t);
774
+ convert_u( 8, 248, 8, WN_LSF);
775
+ convert_u( 8, 256, 8, DN);
776
+ convert_s( 8, 270, 8, delta_t_LSF);
777
+ };
778
+ #undef convert_s_2
779
+ #undef convert_u_2
780
+ #undef convert_s
781
+ #undef convert_u
782
+ };
783
+
784
+ /**
785
+ * GPS Ionospheric correction and UTC parameters
786
+ *
787
+ */
788
+ struct Ionospheric_UTC_Parameters {
789
+ float_t alpha[4]; ///< Ionospheric parameters[0-3] (s, s/sc, s/sc^2, s/sc^3)
790
+ float_t beta[4]; ///< Ionospheric parameters[0-3] (s, s/sc, s/sc^2, s/sc^3)
791
+ float_t A1; ///< UTC parameter (s/s)
792
+ float_t A0; ///< UTC parameter (s)
793
+ uint_t t_ot; ///< Epoch time (UTC) (s)
794
+ uint_t WN_t; ///< Epoch time (UTC) (weeks)
795
+ int_t delta_t_LS; ///< Current leap seconds (s)
796
+ uint_t WN_LSF; ///< Last leap second update week (weeks)
797
+ uint_t DN; ///< Last leap second update day (days)
798
+ int_t delta_t_LSF; ///< Updated leap seconds (s)
799
+
800
+ struct raw_t {
801
+ s8_t alpha0; ///< Ionospheric parameter (-30, s)
802
+ s8_t alpha1; ///< Ionospheric parameter (-27, s/sc)
803
+ s8_t alpha2; ///< Ionospheric parameter (-24, s/sc^2)
804
+ s8_t alpha3; ///< Ionospheric parameter (-24, s/sc^3)
805
+ s8_t beta0; ///< Ionospheric parameter (11, s)
806
+ s8_t beta1; ///< Ionospheric parameter (14, s/sc)
807
+ s8_t beta2; ///< Ionospheric parameter (16, s/sc^2)
808
+ s8_t beta3; ///< Ionospheric parameter (16, s/sc^3)
809
+ s32_t A1; ///< UTC parameter (-50, s/s)
810
+ s32_t A0; ///< UTC parameter (-30, s)
811
+ u8_t t_ot; ///< Epoch time (UTC) (12, s)
812
+ u8_t WN_t; ///< Epoch time (UTC) (weeks, truncated)
813
+ s8_t delta_t_LS; ///< Current leap seconds (s)
814
+ u8_t WN_LSF; ///< Last leap second update week (weeks, truncated)
815
+ u8_t DN; ///< Last leap second update day (days)
816
+ s8_t delta_t_LSF; ///< Updated leap seconds (s)
817
+
818
+ #define fetch_item(name) name = BroadcastedMessage< \
819
+ InputT, (int)sizeof(InputT) * CHAR_BIT - PaddingBits_MSB - PaddingBits_LSB, PaddingBits_MSB> \
820
+ :: SubFrame4_Page18 :: name (src)
821
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
822
+ void update(const InputT *src){
823
+ fetch_item(alpha0); fetch_item(alpha1); fetch_item(alpha2); fetch_item(alpha3);
824
+ fetch_item(beta0); fetch_item(beta1); fetch_item(beta2); fetch_item(beta3);
825
+ fetch_item(A1); fetch_item(A0);
826
+ fetch_item(WN_t); fetch_item(WN_LSF);
827
+ fetch_item(t_ot); fetch_item(delta_t_LS); fetch_item(delta_t_LSF);
828
+ fetch_item(DN);
829
+ }
830
+ #undef fetch_item
831
+
832
+ enum {
833
+ SF_alpha0,
834
+ SF_alpha1,
835
+ SF_alpha2,
836
+ SF_alpha3,
837
+ SF_beta0,
838
+ SF_beta1,
839
+ SF_beta2,
840
+ SF_beta3,
841
+ SF_A1,
842
+ SF_A0,
843
+
844
+ SF_NUM,
845
+ };
846
+ static const float_t sf[SF_NUM];
847
+
848
+ operator Ionospheric_UTC_Parameters() const {
849
+ Ionospheric_UTC_Parameters converted;
850
+ #define CONVERT(TARGET) \
851
+ {converted.TARGET = sf[SF_ ## TARGET] * TARGET;}
852
+ #define CONVERT2(dst, src) \
853
+ {converted.dst = sf[SF_ ## src] * src;}
854
+ CONVERT2(alpha[0], alpha0);
855
+ CONVERT2(alpha[1], alpha1);
856
+ CONVERT2(alpha[2], alpha2);
857
+ CONVERT2(alpha[3], alpha3);
858
+ CONVERT2(beta[0], beta0);
859
+ CONVERT2(beta[1], beta1);
860
+ CONVERT2(beta[2], beta2);
861
+ CONVERT2(beta[3], beta3);
862
+ CONVERT(A1);
863
+ CONVERT(A0);
864
+ converted.t_ot = ((uint_t)t_ot) << 12;
865
+ converted.WN_t = WN_t;
866
+ converted.delta_t_LS = delta_t_LS;
867
+ converted.WN_LSF = WN_LSF;
868
+ converted.DN = DN;
869
+ converted.delta_t_LSF = delta_t_LSF;
870
+ #undef CONVERT
871
+ #undef CONVERT2
872
+ return converted;
873
+ };
874
+ };
875
+ };
876
+ public:
877
+
878
+ class SatelliteProperties {
879
+ public:
880
+ struct constellation_t {
881
+ xyz_t position;
882
+ xyz_t velocity;
883
+ };
884
+
885
+ /**
886
+ * GPS ephemeris
887
+ * (Subframe 1,2,3)
888
+ *
889
+ */
890
+ struct Ephemeris {
891
+ uint_t svid; ///< Satellite number
892
+
893
+ // Subframe.1
894
+ uint_t WN; ///< Week number
895
+ int_t URA; ///< User range accuracy (index)
896
+ uint_t SV_health; ///< Health status
897
+ int_t iodc; ///< Issue of clock data
898
+ float_t t_GD; ///< Group delay (s)
899
+ float_t t_oc; ///< Clock data reference time
900
+ float_t a_f2; ///< Clock correction parameter (s/s^2)
901
+ float_t a_f1; ///< Clock correction parameter (s/s)
902
+ float_t a_f0; ///< Clock correction parameter (s)
903
+
904
+ // Subframe.2
905
+ int_t iode; ///< Issue of ephemeris data
906
+ float_t c_rs; ///< Sine correction, orbit (m)
907
+ float_t delta_n; ///< Mean motion difference (rad/s)
908
+ float_t M0; ///< Mean anomaly (rad)
909
+ float_t c_uc; ///< Cosine correction, latitude (rad)
910
+ float_t e; ///< Eccentricity
911
+ float_t c_us; ///< Sine correction, latitude (rad)
912
+ float_t sqrt_A; ///< Square root of semi-major axis (sqrt(m))
913
+ float_t t_oe; ///< Reference time ephemeris (s)
914
+ float_t fit_interval; ///< Fit interval; if negative, it indicates invalid ephemeris.
915
+
916
+ // Subframe.3
917
+ float_t c_ic; ///< Cosine correction, inclination (rad)
918
+ float_t Omega0; ///< Longitude of ascending node (rad)
919
+ float_t c_is; ///< Sine correction, inclination (rad)
920
+ float_t i0; ///< Inclination angle (rad)
921
+ float_t c_rc; ///< Cosine correction, orbit (m)
922
+ float_t omega; ///< Argument of perigee (rad)
923
+ float_t dot_Omega0; ///< Rate of right ascension (rad/s)
924
+ float_t dot_i0; ///< Rate of inclination angle (rad/s)
925
+
926
+ inline float_t period_from_time_of_clock(const gps_time_t &t) const {
927
+ return -t.interval(WN, t_oc);
928
+ }
929
+
930
+ inline float_t period_from_time_of_ephemeris(const gps_time_t &t) const {
931
+ return -t.interval(WN, t_oe);
932
+ }
933
+
934
+ /**
935
+ * @return (float_t) if valid ephemeris, the return value is always positive,
936
+ * because (t - t_oc), and (t - t_oe) in equations are normally negative.
937
+ * @see 20.3.4.5, Table 20-XIII
938
+ */
939
+ inline float_t period_from_first_valid_transmission(const gps_time_t &t) const {
940
+ return period_from_time_of_clock(t) + (fit_interval / 2);
941
+ }
942
+
943
+ /**
944
+ * Return true when valid
945
+ *
946
+ * @param t GPS time
947
+ */
948
+ bool is_valid(const gps_time_t &t) const {
949
+ return std::abs(period_from_time_of_clock(t)) <= (fit_interval / 2);
950
+ }
951
+
952
+ /**
953
+ * Return true when newer ephemeris may be available
954
+ *
955
+ * @param t GPS time
956
+ */
957
+ bool maybe_better_one_avilable(const gps_time_t &t) const {
958
+ float_t delta_t(period_from_first_valid_transmission(t));
959
+ float_t transmission_interval( // @see IDC 20.3.4.5 Reference Times, Table 20-XIII
960
+ (fit_interval > (4 * 60 * 60))
961
+ ? fit_interval / 2 // fit_interval is more than 4 hour, fit_interval / 2
962
+ : (1 * 60 * 60)); // fit_interval equals to 4 hour, some SVs transmits every one hour.
963
+ return !((delta_t >= 0) && (delta_t < transmission_interval));
964
+ }
965
+
966
+ static const float_t URA_limits[];
967
+ static const int URA_MAX_INDEX;
968
+
969
+ static float_t URA_meter(const int_t &index){
970
+ if(index < 0){return -1;}
971
+ return (index < URA_MAX_INDEX)
972
+ ? URA_limits[index]
973
+ : URA_limits[URA_MAX_INDEX - 1] * 2;
974
+ }
975
+
976
+ static int_t URA_index(const float_t &meter){
977
+ if(meter < 0){return -1;}
978
+ for(int i(0); i < URA_MAX_INDEX; ++i){
979
+ if(meter <= URA_limits[i]){return i;}
980
+ }
981
+ return URA_MAX_INDEX;
982
+ }
983
+
984
+ float_t eccentric_anomaly(const float_t &period_from_toe) const {
985
+
986
+ // Kepler's Equation for Eccentric Anomaly M(Mk)
987
+ float_t n0(std::sqrt(WGS84::mu_Earth) / pow3(sqrt_A));
988
+ float_t Mk(M0
989
+ + (n0 + delta_n) * period_from_toe);
990
+
991
+ // Eccentric Anomaly E(Ek)
992
+ float_t Ek(Mk);
993
+ #ifndef KEPLER_DELTA_LIMIT
994
+ #define KEPLER_DELTA_LIMIT 1E-12
995
+ #endif
996
+ for(int loop(0); loop < 10; loop++){
997
+ float_t Ek2(Mk + e * sin(Ek));
998
+ if(std::abs(Ek2 - Ek) < KEPLER_DELTA_LIMIT){break;}
999
+ Ek = Ek2;
1000
+ }
1001
+
1002
+ return Ek;
1003
+ }
1004
+
1005
+ float_t eccentric_anomaly(const gps_time_t &t) const {
1006
+ return eccentric_anomaly(period_from_time_of_ephemeris(t));
1007
+ }
1008
+
1009
+ float_t eccentric_anomaly_dot(const float_t &eccentric_anomaly) const {
1010
+ float_t n((std::sqrt(WGS84::mu_Earth) / pow3(sqrt_A)) + delta_n);
1011
+ return n / (1.0 - e * cos(eccentric_anomaly));
1012
+ }
1013
+
1014
+ /**
1015
+ * Calculate correction value in accordance with clock error model
1016
+ *
1017
+ * @param t current time
1018
+ * @param pseudo_range pseudo range in meters
1019
+ * @param gamma factor for compensation of group delay
1020
+ * L1 = 1, L2 = (77/60)^2, see ICD 20.3.3.3.3.2 L1 - L2 Correction
1021
+ * @return in seconds
1022
+ */
1023
+ float_t clock_error(const gps_time_t &t, const float_t &pseudo_range = 0,
1024
+ const float_t &gamma = 1) const{
1025
+
1026
+ float_t transit_time(pseudo_range / light_speed);
1027
+ float_t tk(period_from_time_of_clock(t) - transit_time);
1028
+ float_t Ek(eccentric_anomaly(tk));
1029
+
1030
+ // Relativistic correction term
1031
+ static const float_t F(-2.0 * std::sqrt(WGS84::mu_Earth) / pow2(light_speed));
1032
+ float_t dt_r(F * e * sqrt_A * sin(Ek));
1033
+
1034
+ float_t dt_sv(a_f0 + a_f1 * tk + a_f2 * pow2(tk) + dt_r); // ICD 20.3.3.3.1 Eq.(2)
1035
+
1036
+ return dt_sv - (gamma * t_GD);
1037
+ }
1038
+
1039
+ float_t clock_error_dot(const gps_time_t &t, const float_t &pseudo_range = 0) const {
1040
+
1041
+ float_t transit_time(pseudo_range / light_speed);
1042
+ float_t tk(period_from_time_of_clock(t) - transit_time);
1043
+ float_t Ek(eccentric_anomaly(tk));
1044
+ float_t Ek_dot(eccentric_anomaly_dot(Ek));
1045
+
1046
+ // Derivative of Relativistic correction term
1047
+ static const float_t F(-2.0 * std::sqrt(WGS84::mu_Earth) / pow2(light_speed));
1048
+ float_t dt_r_dot(F * e * sqrt_A * Ek_dot * cos(Ek));
1049
+
1050
+ float_t dt_sv_dot(a_f1 + a_f2 * 2 * tk + dt_r_dot);
1051
+
1052
+ return dt_sv_dot;
1053
+ }
1054
+
1055
+ constellation_t constellation(
1056
+ const gps_time_t &t, const float_t &pseudo_range = 0,
1057
+ const bool &with_velocity = true) const {
1058
+
1059
+ constellation_t res;
1060
+
1061
+ // Time from ephemeris reference epoch (tk)
1062
+ float_t tk0(period_from_time_of_ephemeris(t));
1063
+
1064
+ // Remove transit time
1065
+ float_t tk(tk0 - pseudo_range / light_speed);
1066
+
1067
+ // Eccentric Anomaly (Ek)
1068
+ float_t Ek(eccentric_anomaly(tk));
1069
+
1070
+ // Corrected Radius (rk)
1071
+ float_t rk(pow2(sqrt_A)
1072
+ * (1.0 - e * cos(Ek)));
1073
+
1074
+ // True Anomaly (vk)
1075
+ float_t vk(atan2(
1076
+ sqrt(1.0 - pow2(e)) * sin(Ek),
1077
+ cos(Ek) - e));
1078
+
1079
+ // (Corrected) Argument of Latitude (pk) [rad]
1080
+ float_t pk(vk + omega);
1081
+
1082
+ // (Corrected) Inclination (ik)
1083
+ float_t ik(i0);
1084
+
1085
+ { // Correction
1086
+ float_t pk2_sin(sin(pk * 2)),
1087
+ pk2_cos(cos(pk * 2));
1088
+ float_t d_uk(
1089
+ c_us * pk2_sin
1090
+ + c_uc * pk2_cos);
1091
+ float_t d_rk(
1092
+ c_rs * pk2_sin
1093
+ + c_rc * pk2_cos);
1094
+ float_t d_ik(
1095
+ c_is * pk2_sin
1096
+ + c_ic * pk2_cos);
1097
+
1098
+ pk += d_uk;
1099
+ rk += d_rk;
1100
+ ik += d_ik
1101
+ + dot_i0 * tk;
1102
+ }
1103
+
1104
+ // Position in orbital plane (xk, yk)
1105
+ float_t xk(rk * cos(pk)),
1106
+ yk(rk * sin(pk));
1107
+
1108
+ // Corrected longitude of ascending node (Omegak) [rad]
1109
+ float_t Omegak(Omega0);
1110
+ if(false){ // __MISUNDERSTANDING_ABOUT_OMEGA0_CORRECTION__
1111
+ Omegak += (
1112
+ (dot_Omega0 - WGS84::Omega_Earth_IAU) * tk0
1113
+ - WGS84::Omega_Earth_IAU * t_oe);
1114
+ }else{
1115
+ Omegak += (
1116
+ dot_Omega0 * tk // corrected with the time when the wave is transmitted
1117
+ - WGS84::Omega_Earth_IAU * (t_oe + tk0)); // corrected with the time when the wave is received
1118
+ }
1119
+
1120
+ float_t Omegak_sin(sin(Omegak)),
1121
+ Omegak_cos(cos(Omegak));
1122
+ float_t ik_sin(sin(ik)),
1123
+ ik_cos(cos(ik));
1124
+
1125
+ res.position.x() = xk * Omegak_cos - yk * Omegak_sin * ik_cos;
1126
+ res.position.y() = xk * Omegak_sin + yk * Omegak_cos * ik_cos;
1127
+ res.position.z() = yk * ik_sin;
1128
+
1129
+ // Velocity calculation => GPS solution vol.8 (3) http://www.ngs.noaa.gov/gps-toolbox/bc_velo.htm
1130
+ if(with_velocity){
1131
+ float_t Ek_dot(eccentric_anomaly_dot(Ek));
1132
+ float_t vk_dot(sin(Ek) * Ek_dot * (1.0 + e * cos(vk))
1133
+ / (sin(vk) * (1.0 - e * cos(Ek))));
1134
+
1135
+ float_t pk2_sin(sin(pk * 2)), pk2_cos(cos(pk * 2));
1136
+ float_t pk_dot(((c_us * pk2_cos - c_uc * pk2_sin) * 2 + 1.0) * vk_dot);
1137
+ float_t rk_dot(pow2(sqrt_A) * e * sin(Ek) * Ek_dot
1138
+ + (c_rs * pk2_cos - c_rc * pk2_sin) * 2 * vk_dot);
1139
+ float_t ik_dot(dot_i0 + (c_is * pk2_cos - c_ic * pk2_sin) * 2 * vk_dot);
1140
+
1141
+ // Velocity in orbital plane (xk_dot, yk_dot)
1142
+ float_t xk_dot(rk_dot * cos(pk) - yk * pk_dot),
1143
+ yk_dot(rk_dot * sin(pk) + xk * pk_dot);
1144
+
1145
+ float_t Omegak_dot(dot_Omega0 - WGS84::Omega_Earth_IAU);
1146
+
1147
+ res.velocity.x() = (xk_dot - yk * ik_cos * Omegak_dot) * Omegak_cos
1148
+ - (xk * Omegak_dot + yk_dot * ik_cos - yk * ik_sin * ik_dot) * Omegak_sin;
1149
+ res.velocity.y() = (xk_dot - yk * ik_cos * Omegak_dot) * Omegak_sin
1150
+ + (xk * Omegak_dot + yk_dot * ik_cos - yk * ik_sin * ik_dot) * Omegak_cos;
1151
+ res.velocity.z() = yk_dot * ik_sin + yk * ik_cos * ik_dot;
1152
+ }
1153
+
1154
+ return res;
1155
+ }
1156
+
1157
+ struct raw_t {
1158
+ u8_t svid; ///< Satellite number
1159
+
1160
+ u16_t WN; ///< Week number
1161
+ u8_t URA; ///< User range accuracy
1162
+ u8_t SV_health; ///< Health status
1163
+ u16_t iodc; ///< Issue of clock data
1164
+ s8_t t_GD; ///< Group delay (-31, s)
1165
+ u16_t t_oc; ///< Clock data reference time
1166
+ s8_t a_f2; ///< Clock correction parameter (-55, s/s^2)
1167
+ s16_t a_f1; ///< Clock correction parameter (-43, s/s)
1168
+ s32_t a_f0; ///< Clock correction parameter (-31, s)
1169
+
1170
+ u8_t iode; ///< Issue of eph. data
1171
+ s16_t c_rs; ///< Sin. correction, orbit ( -5, m)
1172
+ s16_t delta_n; ///< Mean motion difference (-43, sc/s)
1173
+ s32_t M0; ///< Mean anomaly (-31, sc)
1174
+ s16_t c_uc; ///< Cos. correction, lat. (-29, rad)
1175
+ u32_t e; ///< Eccentricity (-33)
1176
+ s16_t c_us; ///< Sin. correction, lat. (-29, rad)
1177
+ u32_t sqrt_A; ///< Root semi-Major Axis (-19, sqrt(m))
1178
+ u16_t t_oe; ///< Reference time eph. ( 4, s)
1179
+ bool fit_interval_flag; ///< Fit interval flag (ICD:20.3.4.4)
1180
+
1181
+ s16_t c_ic; ///< Cos. correction, incl. (-29, rad)
1182
+ s32_t Omega0; ///< Ascending node long. (-31, sc)
1183
+ s16_t c_is; ///< Sin. correction, incl. (-29, rad)
1184
+ s32_t i0; ///< Inclination angle (-31, sc)
1185
+ s16_t c_rc; ///< Cos. correction, orbit ( -5, m)
1186
+ s32_t omega; ///< Argument of perigee (-31, sc)
1187
+ s32_t dot_Omega0; ///< Right ascension rate (-43, sc/s)
1188
+ s16_t dot_i0; ///< Inclination angle rate (-43, sc/s)
1189
+
1190
+ #define fetch_item(num, name) name = BroadcastedMessage< \
1191
+ InputT, (int)sizeof(InputT) * CHAR_BIT - PaddingBits_MSB - PaddingBits_LSB, PaddingBits_MSB> \
1192
+ :: SubFrame ## num :: name (src)
1193
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
1194
+ u16_t update_subframe1(const InputT *src){
1195
+ fetch_item(1, WN);
1196
+ fetch_item(1, URA);
1197
+ fetch_item(1, SV_health);
1198
+ fetch_item(1, iodc);
1199
+ fetch_item(1, t_GD);
1200
+ fetch_item(1, t_oc);
1201
+ fetch_item(1, a_f2);
1202
+ fetch_item(1, a_f1);
1203
+ fetch_item(1, a_f0);
1204
+ return iodc;
1205
+ }
1206
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
1207
+ u8_t update_subframe2(const InputT *src){
1208
+ fetch_item(2, iode);
1209
+ fetch_item(2, c_rs);
1210
+ fetch_item(2, delta_n);
1211
+ fetch_item(2, M0);
1212
+ fetch_item(2, c_uc);
1213
+ fetch_item(2, e);
1214
+ fetch_item(2, c_us);
1215
+ fetch_item(2, sqrt_A);
1216
+ fetch_item(2, t_oe);
1217
+ u8_t fetch_item(2, fit); fit_interval_flag = (fit == 1);
1218
+ return iode;
1219
+ }
1220
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
1221
+ u8_t update_subframe3(const InputT *src){
1222
+ fetch_item(3, c_ic);
1223
+ fetch_item(3, Omega0);
1224
+ fetch_item(3, c_is);
1225
+ fetch_item(3, i0);
1226
+ fetch_item(3, c_rc);
1227
+ fetch_item(3, omega);
1228
+ fetch_item(3, dot_Omega0);
1229
+ fetch_item(3, dot_i0);
1230
+ fetch_item(3, iode);
1231
+ return iode;
1232
+ }
1233
+ #undef fetch_item
1234
+
1235
+ static float_t fit_interval(const bool &_flag, const u16_t &_iodc){
1236
+ // Fit interval (ICD:20.3.4.4)
1237
+ if(_flag == false){
1238
+ // normal operation
1239
+ return 4 * 60 * 60;
1240
+ }else{
1241
+ // short/long-term extended operation (Table 20-XI, XII)
1242
+ if(_iodc >= 240 && _iodc <= 247){
1243
+ return 8 * 60 * 60;
1244
+ }else if((_iodc >= 248 && _iodc <= 255) || (_iodc == 496)){
1245
+ return 14 * 60 * 60;
1246
+ }else if(_iodc >= 497 && _iodc <= 503){
1247
+ return 26 * 60 * 60;
1248
+ }else if(_iodc >= 504 && _iodc <= 510){
1249
+ return 50 * 60 * 60;
1250
+ }else if((_iodc == 511) || (_iodc >= 752 && _iodc <= 756)){
1251
+ return 74 * 60 * 60;
1252
+ }else if(_iodc >= 757 && _iodc <= 763){
1253
+ return 98 * 60 * 60;
1254
+ }else if((_iodc >= 764 && _iodc <= 767) || (_iodc >= 1008 && _iodc <= 1010)){
1255
+ return 122 * 60 * 60;
1256
+ }else if(_iodc >= 1011 && _iodc <= 1020){
1257
+ return 146 * 60 * 60;
1258
+ }else{
1259
+ return 6 * 60 * 60;
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ enum {
1265
+ SF_t_GD,
1266
+ SF_t_oc,
1267
+ SF_a_f0,
1268
+ SF_a_f1,
1269
+ SF_a_f2,
1270
+
1271
+ SF_c_rs,
1272
+ SF_delta_n,
1273
+ SF_M0,
1274
+ SF_c_uc,
1275
+ SF_e,
1276
+ SF_c_us,
1277
+ SF_sqrt_A,
1278
+ SF_t_oe,
1279
+
1280
+ SF_c_ic,
1281
+ SF_Omega0,
1282
+ SF_c_is,
1283
+ SF_i0,
1284
+ SF_c_rc,
1285
+ SF_omega,
1286
+ SF_dot_Omega0,
1287
+ SF_dot_i0,
1288
+
1289
+ SF_NUM,
1290
+ };
1291
+ static const float_t sf[SF_NUM];
1292
+
1293
+ operator Ephemeris() const {
1294
+ Ephemeris converted;
1295
+ #define CONVERT(TARGET) \
1296
+ {converted.TARGET = sf[SF_ ## TARGET] * TARGET;}
1297
+ converted.svid = svid;
1298
+
1299
+ converted.WN = WN;
1300
+ converted.URA = URA;
1301
+ converted.SV_health = SV_health;
1302
+ converted.iodc = iodc;
1303
+ CONVERT(t_GD);
1304
+ CONVERT(t_oc);
1305
+ CONVERT(a_f0);
1306
+ CONVERT(a_f1);
1307
+ CONVERT(a_f2);
1308
+
1309
+ converted.iode = iode;
1310
+ CONVERT(c_rs);
1311
+ CONVERT(delta_n);
1312
+ CONVERT(M0);
1313
+ CONVERT(c_uc);
1314
+ CONVERT(e);
1315
+ CONVERT(c_us);
1316
+ CONVERT(sqrt_A);
1317
+ CONVERT(t_oe);
1318
+
1319
+ CONVERT(c_ic);
1320
+ CONVERT(Omega0);
1321
+ CONVERT(c_is);
1322
+ CONVERT(i0);
1323
+ CONVERT(c_rc);
1324
+ CONVERT(omega);
1325
+ CONVERT(dot_Omega0);
1326
+ CONVERT(dot_i0);
1327
+ #undef CONVERT
1328
+ converted.fit_interval = fit_interval(fit_interval_flag, iodc);
1329
+
1330
+ return converted;
1331
+ }
1332
+
1333
+ raw_t &operator=(const Ephemeris &eph){
1334
+ #define CONVERT(TARGET) \
1335
+ {TARGET = (s32_t)((eph.TARGET + 0.5 * sf[SF_ ## TARGET]) / sf[SF_ ## TARGET]);}
1336
+ svid = eph.svid;
1337
+
1338
+ WN = eph.WN;
1339
+ URA = eph.URA;
1340
+ SV_health = eph.SV_health;
1341
+ iodc = eph.iodc;
1342
+ CONVERT(t_GD);
1343
+ CONVERT(t_oc);
1344
+ CONVERT(a_f0);
1345
+ CONVERT(a_f1);
1346
+ CONVERT(a_f2);
1347
+
1348
+ iode = eph.iode;
1349
+ CONVERT(c_rs);
1350
+ CONVERT(delta_n);
1351
+ CONVERT(M0);
1352
+ CONVERT(c_uc);
1353
+ CONVERT(e);
1354
+ CONVERT(c_us);
1355
+ CONVERT(sqrt_A);
1356
+ CONVERT(t_oe);
1357
+
1358
+ CONVERT(c_ic);
1359
+ CONVERT(Omega0);
1360
+ CONVERT(c_is);
1361
+ CONVERT(i0);
1362
+ CONVERT(c_rc);
1363
+ CONVERT(omega);
1364
+ CONVERT(dot_Omega0);
1365
+ CONVERT(dot_i0);
1366
+ #undef CONVERT
1367
+ fit_interval_flag = (eph.fit_interval > 5 * 60 * 60);
1368
+
1369
+ return *this;
1370
+ }
1371
+ };
1372
+
1373
+ bool is_equivalent(const Ephemeris &eph) const {
1374
+ do{
1375
+ if(WN != eph.WN){break;}
1376
+ if(URA != eph.URA){break;}
1377
+ if(SV_health != eph.SV_health){break;}
1378
+
1379
+ #define CHECK(TARGET) \
1380
+ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1381
+ CHECK(t_GD);
1382
+ CHECK(t_oc);
1383
+ CHECK(a_f2);
1384
+ CHECK(a_f1);
1385
+ CHECK(a_f0);
1386
+
1387
+ CHECK(c_rs);
1388
+ CHECK(delta_n);
1389
+ CHECK(M0);
1390
+ CHECK(c_uc);
1391
+ CHECK(e);
1392
+ CHECK(c_us);
1393
+ CHECK(sqrt_A);
1394
+ CHECK(t_oe);
1395
+
1396
+ CHECK(c_ic);
1397
+ CHECK(Omega0);
1398
+ CHECK(c_is);
1399
+ CHECK(i0);
1400
+ CHECK(c_rc);
1401
+ CHECK(omega);
1402
+ CHECK(dot_Omega0);
1403
+ CHECK(dot_i0);
1404
+ #undef CHECK
1405
+ return true;
1406
+ }while(false);
1407
+ return false;
1408
+ }
1409
+
1410
+ gps_time_t base_time() const {
1411
+ return gps_time_t(WN, t_oc);
1412
+ }
1413
+ };
1414
+
1415
+ /**
1416
+ * GPS almanac
1417
+ * (Subframe 4,5)
1418
+ *
1419
+ */
1420
+ struct Almanac {
1421
+ uint_t svid; ///< Satellite number
1422
+
1423
+ float_t e; ///< Eccentricity
1424
+ float_t t_oa; ///< Almanac reference time (s)
1425
+ float_t delta_i; ///< Correction to inclination (rad)
1426
+ float_t dot_Omega0; ///< Omega0 rate (rad/s)
1427
+ uint_t SV_health; ///< Health status
1428
+ float_t sqrt_A; ///< Square root of semi-major axis (sqrt(m))
1429
+ float_t Omega0; ///< Longitude of ascending node (rad)
1430
+ float_t omega; ///< Argument of perigee (rad)
1431
+ float_t M0; ///< Mean anomaly (rad)
1432
+ float_t a_f0; ///< Clock correction parameter (s/s)
1433
+ float_t a_f1; ///< Clock correction parameter (s)
1434
+
1435
+ /**
1436
+ * Up-cast to ephemeris
1437
+ *
1438
+ */
1439
+ operator Ephemeris() const {
1440
+ Ephemeris converted;
1441
+
1442
+ converted.svid = svid;
1443
+
1444
+ // Subframe.1
1445
+ converted.WN = 0; // Week number (must be configured later)
1446
+ converted.URA = -1; // User range accuracy
1447
+ converted.SV_health = SV_health; // Health status
1448
+ converted.iodc = -1; // Issue of clock data
1449
+ converted.t_GD = 0; // Group delay (s)
1450
+ converted.t_oc = t_oa; // Clock data reference time
1451
+ converted.a_f2 = 0; // Clock correction parameter (s/s^2)
1452
+ converted.a_f1 = a_f1; // Clock correction parameter (s/s)
1453
+ converted.a_f0 = a_f0; // Clock correction parameter (s)
1454
+
1455
+ // Subframe.2
1456
+ converted.iode = -1; // Issue of ephemeris data
1457
+ converted.c_rs = 0; // Sine correction, orbit (m)
1458
+ converted.delta_n = 0; // Mean motion difference (rad/s)
1459
+ converted.M0 = M0; // Mean anomaly (rad)
1460
+ converted.c_uc = 0; // Cosine correction, latitude (rad)
1461
+ converted.e = e; // Eccentricity
1462
+ converted.c_us = 0; // Sine correction, latitude (rad)
1463
+ converted.sqrt_A = sqrt_A; // Square root of semi-major axis (sqrt(m))
1464
+ converted.t_oe = t_oa; // Reference time ephemeris (s)
1465
+ converted.fit_interval = 4 * 60 * 60;// Fit interval
1466
+
1467
+ // Subframe.3
1468
+ converted.c_ic = 0; // Cosine correction, inclination (rad)
1469
+ converted.Omega0 = Omega0; // Longitude of ascending node (rad)
1470
+ converted.c_is = 0; // Sine correction, inclination (rad)
1471
+ converted.i0 = delta_i; // Inclination angle (rad)
1472
+ converted.c_rc = 0; // Cosine correction, orbit (m)
1473
+ converted.omega = omega; // Argument of perigee (rad)
1474
+ converted.dot_Omega0 = dot_Omega0; // Rate of right ascension (rad/s)
1475
+ converted.dot_i0 = 0; // Rate of inclination angle (rad/s)
1476
+
1477
+ return converted;
1478
+ }
1479
+
1480
+ struct raw_t {
1481
+ u8_t svid; ///< Satellite number
1482
+
1483
+ u16_t e; ///< Eccentricity (-21)
1484
+ u8_t t_oa; ///< Almanac ref. time ( 12, s)
1485
+ s16_t delta_i; ///< Correction to inc. (-19, sc)
1486
+ s16_t dot_Omega0; ///< Omega0 rate (-38, sc/s)
1487
+ u8_t SV_health; ///< Health status
1488
+ u32_t sqrt_A; ///< Semi-major axis (-11, sqrt(m))
1489
+ s32_t Omega0; ///< Long. of asc. node (-23, sc)
1490
+ s32_t omega; ///< Arg. of perigee (-23, sc)
1491
+ s32_t M0; ///< Mean anomaly (-23, sc)
1492
+ s16_t a_f0; ///< Clock corr. param. (-20, s)
1493
+ s16_t a_f1; ///< Clock corr. param. (-38, s)
1494
+
1495
+ #define fetch_item(name) name = BroadcastedMessage< \
1496
+ InputT, (int)sizeof(InputT) * CHAR_BIT - PaddingBits_MSB - PaddingBits_LSB, PaddingBits_MSB> \
1497
+ :: SubFrame4_5_Alnamac :: name (src)
1498
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
1499
+ void update(const InputT *src){
1500
+ fetch_item(e);
1501
+ fetch_item(t_oa);
1502
+ fetch_item(delta_i);
1503
+ fetch_item(dot_Omega0);
1504
+ fetch_item(SV_health);
1505
+ fetch_item(sqrt_A);
1506
+ fetch_item(Omega0);
1507
+ fetch_item(omega);
1508
+ fetch_item(M0);
1509
+ fetch_item(a_f0);
1510
+ fetch_item(a_f1);
1511
+ }
1512
+ #undef fetch_item
1513
+
1514
+ enum {
1515
+ SF_e,
1516
+ SF_t_oa,
1517
+ SF_delta_i,
1518
+ SF_dot_Omega0,
1519
+ SF_sqrt_A,
1520
+ SF_Omega0,
1521
+ SF_omega,
1522
+ SF_M0,
1523
+ SF_a_f0,
1524
+ SF_a_f1,
1525
+
1526
+ SF_NUM,
1527
+ };
1528
+ static const float_t sf[SF_NUM];
1529
+
1530
+ operator Almanac() const {
1531
+ Almanac converted;
1532
+ #define CONVERT(TARGET) \
1533
+ {converted.TARGET = sf[SF_ ## TARGET] * TARGET;}
1534
+ converted.svid = svid;
1535
+ CONVERT(e);
1536
+ CONVERT(t_oa);
1537
+ CONVERT(delta_i);
1538
+ CONVERT(dot_Omega0);
1539
+ converted.SV_health = SV_health;
1540
+ CONVERT(sqrt_A);
1541
+ CONVERT(Omega0);
1542
+ CONVERT(omega);
1543
+ CONVERT(M0);
1544
+ CONVERT(a_f0);
1545
+ CONVERT(a_f1);
1546
+ #undef CONVERT
1547
+ return converted;
1548
+ }
1549
+ };
1550
+ };
1551
+ };
1552
+
1553
+ template <
1554
+ class PropertyT = typename SatelliteProperties::Ephemeris,
1555
+ int TimeQuantization = 10>
1556
+ class PropertyHistory {
1557
+ protected:
1558
+ struct item_t : public PropertyT {
1559
+ int priority;
1560
+ int_t t_tag; ///< time tag calculated with base_time()
1561
+
1562
+ static int calc_t_tag(const float_t &t){
1563
+ float_t res(std::floor((t + (0.5 * TimeQuantization)) / TimeQuantization));
1564
+ if(res >= INT_MAX){return INT_MAX;}
1565
+ if(res <= INT_MIN){return INT_MIN;}
1566
+ return (int)res;
1567
+ }
1568
+ static int calc_t_tag(const gps_time_t &t){
1569
+ return calc_t_tag(t.serialize());
1570
+ }
1571
+ static int calc_t_tag(const PropertyT &prop){
1572
+ return calc_t_tag(prop.base_time());
1573
+ }
1574
+
1575
+ item_t() : priority(0), t_tag(0) {}
1576
+ item_t(const PropertyT &prop)
1577
+ : PropertyT(prop), priority(0), t_tag(calc_t_tag(prop)) {}
1578
+ item_t(const PropertyT &prop, const int &priority_init)
1579
+ : PropertyT(prop), priority(priority_init), t_tag(calc_t_tag(prop)) {}
1580
+ item_t &operator=(const PropertyT &prop){
1581
+ PropertyT::operator=(prop);
1582
+ t_tag = calc_t_tag(prop);
1583
+ return *this;
1584
+ }
1585
+
1586
+ bool operator==(const PropertyT &prop){
1587
+ return PropertyT::is_equivalent(prop);
1588
+ }
1589
+ };
1590
+ typedef std::vector<item_t> history_t;
1591
+ history_t history; // listed in chronological and higher priority order
1592
+ typename history_t::size_type selected_index;
1593
+
1594
+ typename history_t::iterator selected_iterator() {
1595
+ typename history_t::iterator it(history.begin());
1596
+ std::advance(it, selected_index);
1597
+ return it;
1598
+ }
1599
+
1600
+ public:
1601
+ PropertyHistory() : history(1), selected_index(0) {}
1602
+
1603
+ enum each_mode_t {
1604
+ EACH_ALL,
1605
+ EACH_ALL_INVERTED,
1606
+ EACH_NO_REDUNDANT,
1607
+ };
1608
+
1609
+ /**
1610
+ * Iterate each item.
1611
+ *
1612
+ * @param mode If EACH_ALL, all items will be passed, and when multiple items have same t_tag,
1613
+ * their order will be unchanged (highest to lowest priorities).
1614
+ * If EACH_ALL_INVERTED, all items will be passed, and when multiple items have same t_tag,
1615
+ * their order will be inverted (lowest to highest priorities).
1616
+ * If EACH_NO_REDUNDANT, when multiple items have same t_tag,
1617
+ * then, only item which has highest priority will be passed.
1618
+ */
1619
+ template <class Functor>
1620
+ void each(
1621
+ Functor &functor,
1622
+ const each_mode_t &mode = EACH_ALL) const {
1623
+ switch(mode){
1624
+ case EACH_ALL_INVERTED: {
1625
+ typename history_t::const_iterator it(history.begin() + 1);
1626
+ while(it != history.end()){
1627
+ typename history_t::const_iterator it2(it + 1);
1628
+ while((it2 != history.end()) && (it->t_tag == it2->t_tag)){
1629
+ ++it2;
1630
+ }
1631
+ typename history_t::const_iterator it_next(it2);
1632
+ do{
1633
+ functor(*(--it2));
1634
+ }while(it2 != it);
1635
+ it = it_next;
1636
+ }
1637
+ break;
1638
+ }
1639
+ case EACH_NO_REDUNDANT: {
1640
+ int_t t_tag(0);
1641
+ for(typename history_t::const_iterator it(history.begin() + 1), it_end(history.end());
1642
+ it != it_end;
1643
+ ++it){
1644
+ if(t_tag == it->t_tag){continue;}
1645
+ functor(*it);
1646
+ t_tag = it->t_tag;
1647
+ }
1648
+ break;
1649
+ }
1650
+ case EACH_ALL:
1651
+ default:
1652
+ for(typename history_t::const_iterator it(history.begin() + 1), it_end(history.end());
1653
+ it != it_end;
1654
+ ++it){
1655
+ functor(*it);
1656
+ }
1657
+ break;
1658
+ }
1659
+ }
1660
+
1661
+ /**
1662
+ * Add new item
1663
+ *
1664
+ * @param item new item, assuming the latest one
1665
+ * @param priority_delta If the new item is already registered,
1666
+ * its priority will be increased by priority_delta.
1667
+ * If priority_delta == 0, the previous item is replaced to the new item.
1668
+ */
1669
+ void add(const PropertyT &item, const int &priority_delta = 1){
1670
+ int t_tag_new(item_t::calc_t_tag(item));
1671
+ typename history_t::iterator it_insert(history.begin());
1672
+
1673
+ for(typename history_t::reverse_iterator it(history.rbegin()), it_end(history.rend());
1674
+ it != it_end;
1675
+ ++it){
1676
+
1677
+ int delta_t_tag(t_tag_new - it->t_tag);
1678
+ if(delta_t_tag < 0){continue;} // adding item is older.
1679
+
1680
+ it_insert = it.base();
1681
+ if(delta_t_tag > 0){break;} // adding item is newer.
1682
+
1683
+ while(true){ // Loop for items having the same time stamp; delta_t_tag == 0
1684
+ if(!(it->is_equivalent(item))){
1685
+ if(it->priority <= priority_delta){
1686
+ it_insert = it.base() - 1;
1687
+ }
1688
+ if(((++it) == history.rend()) || (it->t_tag < t_tag_new)){break;}
1689
+ continue;
1690
+ }
1691
+
1692
+ // When the content is equivalent
1693
+ if(priority_delta == 0){ // replace to newer one
1694
+ *it = item;
1695
+ return;
1696
+ }
1697
+
1698
+ int rel_pos(selected_index - (std::distance(history.begin(), it.base()) - 1));
1699
+ int shift(0);
1700
+ (it->priority) += priority_delta;
1701
+ item_t copy(*it);
1702
+
1703
+ if(priority_delta > 0){ // priority increased, thus move backward
1704
+ for(typename history_t::reverse_iterator it_previous(it + 1), it_previous_end(history.rend());
1705
+ it_previous != it_previous_end
1706
+ && (it_previous->t_tag == t_tag_new)
1707
+ && (it_previous->priority <= copy.priority); // if priority is same or higher, then swap.
1708
+ ++it, ++it_previous, --shift){
1709
+ *it = *it_previous;
1710
+ }
1711
+ if(shift != 0){*it = copy;} // moved
1712
+ }else{ // priority decreased, thus move forward
1713
+ typename history_t::iterator it2(it.base() - 1);
1714
+ for(typename history_t::iterator it2_next(it2 + 1), it2_next_end(history.end());
1715
+ it2_next != it2_next_end
1716
+ && (it2_next->t_tag == t_tag_new)
1717
+ && (it2_next->priority > copy.priority); // if priority is lower, then swap.
1718
+ ++it2, ++it2_next, ++shift){
1719
+ *it2 = *it2_next;
1720
+ }
1721
+ if(shift != 0){*it2 = copy;} // moved
1722
+ }
1723
+
1724
+ if(rel_pos == 0){ // When the added item is selected
1725
+ selected_index += shift;
1726
+ }else if((rel_pos < 0) && (shift <= rel_pos)){
1727
+ // When selected item has same time stamp and different content, and its priority is higher.
1728
+ selected_index++;
1729
+ }else if((rel_pos > 0) && (shift >= rel_pos)){
1730
+ // When selected item has same time stamp and different content, and its priority is higher.
1731
+ selected_index--;
1732
+ }
1733
+
1734
+ return;
1735
+ }
1736
+
1737
+ break; // Reach only when having the same time stamp, but different content
1738
+ }
1739
+
1740
+ // insert new one.
1741
+ if(std::distance(history.begin(), it_insert) < (int)selected_index){
1742
+ selected_index++;
1743
+ }
1744
+ history.insert(it_insert, item_t(item, priority_delta));
1745
+ }
1746
+
1747
+ /**
1748
+ * Select best valid item among registered ones.
1749
+ *
1750
+ * @param target_time time at measurement
1751
+ * @param is_valid function to check validity subject to specific time
1752
+ * @param get_delta_t function to get time difference (delta_t).
1753
+ * When NULL (default), time tag is used to calculate delta_t.
1754
+ * @return if true, a better valid item is newly selected; otherwise false.
1755
+ */
1756
+ bool select(
1757
+ const gps_time_t &target_time,
1758
+ bool (PropertyT::*is_valid)(const gps_time_t &) const,
1759
+ float_t (PropertyT::*get_delta_t)(const gps_time_t &) const
1760
+ #if !defined(SWIG) // work around for SWIG parser error
1761
+ = NULL
1762
+ #endif
1763
+ ){
1764
+ typename history_t::iterator it_selected(selected_iterator());
1765
+
1766
+ bool changed(false);
1767
+
1768
+ int t_tag(it_selected->t_tag);
1769
+ int t_tag_target(item_t::calc_t_tag(target_time));
1770
+ float_t delta_t(get_delta_t
1771
+ ? ((*it_selected).*get_delta_t)(target_time)
1772
+ : (t_tag_target - t_tag));
1773
+
1774
+ typename history_t::iterator it, it_last;
1775
+ if(delta_t >= 0){
1776
+ // find newer
1777
+ it = it_selected + 1;
1778
+ it_last = history.end();
1779
+ }else{
1780
+ // find older (rare case, slow)
1781
+ it = history.begin();
1782
+ it_last = it_selected;
1783
+ delta_t *= -1;
1784
+ }
1785
+
1786
+ /* Selection priority:
1787
+ * Valid item having higher priority and less abs(delta_t),
1788
+ * which means when an item has been selected,
1789
+ * the others having same time tag will be skipped.
1790
+ */
1791
+ for( ; it != it_last; ++it){
1792
+ if(changed && (t_tag == it->t_tag)){continue;} // skip one having same time tag, because highest priority one is selected.
1793
+ if(!(((*it).*is_valid)(target_time))){continue;}
1794
+ float_t delta_t2(get_delta_t
1795
+ ? ((*it).*get_delta_t)(target_time)
1796
+ : (t_tag_target - it->t_tag));
1797
+ if(delta_t2 < 0){delta_t2 *= -1;}
1798
+ if(delta_t > delta_t2){ // update
1799
+ changed = true;
1800
+ t_tag = it->t_tag;
1801
+ delta_t = delta_t2;
1802
+ selected_index = std::distance(history.begin(), it);
1803
+ }
1804
+ }
1805
+
1806
+ return changed;
1807
+ }
1808
+
1809
+ /**
1810
+ * Merge information
1811
+ */
1812
+ void merge(const PropertyHistory &another, const bool &keep_original = true){
1813
+ history_t list_new;
1814
+ list_new.push_back(history[0]);
1815
+ typename history_t::const_iterator
1816
+ it1(history.begin() + 1), it2(another.history.begin() + 1);
1817
+ typename history_t::size_type current_index_new(selected_index);
1818
+ int shift_count(selected_index - 1);
1819
+ while(true){
1820
+ if(it1 == history.end()){
1821
+ while(it2 != another.history.end()){
1822
+ list_new.push_back(*it2);
1823
+ }
1824
+ break;
1825
+ }else if(it2 == another.history.end()){
1826
+ while(it1 != history.end()){
1827
+ list_new.push_back(*it1);
1828
+ }
1829
+ break;
1830
+ }
1831
+ int delta_t(it1->t_tag - it2->t_tag);
1832
+ bool use_it1(true);
1833
+ if(delta_t == 0){
1834
+ if(it1->is_equivalent(*it2)){
1835
+ list_new.push_back(keep_original ? *it1 : *it2);
1836
+ ++it1;
1837
+ ++it2;
1838
+ --shift_count;
1839
+ continue;
1840
+ }else if(it1->priority < it2->priority){
1841
+ use_it1 = false;
1842
+ }
1843
+ }else if(delta_t > 0){
1844
+ use_it1 = false;
1845
+ }
1846
+
1847
+ if(use_it1){
1848
+ list_new.push_back(*it1);
1849
+ ++it1;
1850
+ --shift_count;
1851
+ }else{
1852
+ list_new.push_back(*it2);
1853
+ ++it2;
1854
+ if(shift_count >= 0){++current_index_new;}
1855
+ }
1856
+ }
1857
+ history = list_new;
1858
+ selected_index = current_index_new;
1859
+ }
1860
+
1861
+ const PropertyT &current() const {
1862
+ return history[selected_index];
1863
+ }
1864
+ };
1865
+
1866
+ class Satellite : public SatelliteProperties {
1867
+ public:
1868
+ typedef typename SatelliteProperties::Ephemeris eph_t;
1869
+ typedef PropertyHistory<eph_t> eph_list_t;
1870
+ protected:
1871
+ eph_list_t eph_history;
1872
+ public:
1873
+ Satellite() : eph_history() {
1874
+ // setup first ephemeris as invalid one
1875
+ eph_t &eph_current(const_cast<eph_t &>(eph_history.current()));
1876
+ eph_current.WN = 0;
1877
+ eph_current.t_oc = 0;
1878
+ eph_current.t_oe = 0;
1879
+ eph_current.fit_interval = -1;
1880
+ }
1881
+
1882
+ template <class Functor>
1883
+ void each_ephemeris(
1884
+ Functor &functor,
1885
+ const typename eph_list_t::each_mode_t &mode = eph_list_t::EACH_ALL) const {
1886
+ eph_history.each(functor, mode);
1887
+ }
1888
+
1889
+ void register_ephemeris(const eph_t &eph, const int &priority_delta = 1){
1890
+ eph_history.add(eph, priority_delta);
1891
+ }
1892
+
1893
+ void merge(const Satellite &another, const bool &keep_original = true){
1894
+ eph_history.merge(another.eph_history, keep_original);
1895
+ }
1896
+
1897
+ const eph_t &ephemeris() const {
1898
+ return eph_history.current();
1899
+ }
1900
+
1901
+ /**
1902
+ * Select appropriate ephemeris within registered ones.
1903
+ *
1904
+ * @param target_time time at measurement
1905
+ * @return if true, appropriate ephemeris is selected, otherwise, not selected.
1906
+ */
1907
+ bool select_ephemeris(const gps_time_t &target_time){
1908
+ bool is_valid(ephemeris().is_valid(target_time));
1909
+ if(is_valid && (!ephemeris().maybe_better_one_avilable(target_time))){
1910
+ return true; // conservative
1911
+ }
1912
+ return eph_history.select(
1913
+ target_time,
1914
+ &eph_t::is_valid,
1915
+ &eph_t::period_from_time_of_clock) || is_valid;
1916
+ }
1917
+
1918
+ float_t clock_error(const gps_time_t &t, const float_t &pseudo_range = 0) const{
1919
+ return ephemeris().clock_error(t, pseudo_range);
1920
+ }
1921
+
1922
+ float_t clock_error_dot(const gps_time_t &t, const float_t &pseudo_range = 0) const {
1923
+ return ephemeris().clock_error_dot(t, pseudo_range);
1924
+ }
1925
+
1926
+ typename SatelliteProperties::constellation_t constellation(
1927
+ const gps_time_t &t, const float_t &pseudo_range = 0,
1928
+ const bool &with_velocity = true) const {
1929
+ return ephemeris().constellation(t, pseudo_range, with_velocity);
1930
+ }
1931
+
1932
+ xyz_t position(const gps_time_t &t, const float_t &pseudo_range = 0) const {
1933
+ return constellation(t, pseudo_range, false).position;
1934
+ }
1935
+
1936
+ xyz_t velocity(const gps_time_t &t, const float_t &pseudo_range = 0) const {
1937
+ return constellation(t, pseudo_range, true).velocity;
1938
+ }
1939
+ };
1940
+ public:
1941
+ typedef std::map<int, Satellite> satellites_t;
1942
+ protected:
1943
+ Ionospheric_UTC_Parameters _iono_utc;
1944
+ bool _iono_initialized, _utc_initialized;
1945
+ satellites_t _satellites;
1946
+ public:
1947
+ GPS_SpaceNode()
1948
+ : _iono_initialized(false), _utc_initialized(false),
1949
+ _satellites() {
1950
+ }
1951
+ ~GPS_SpaceNode(){
1952
+ _satellites.clear();
1953
+ }
1954
+ const Ionospheric_UTC_Parameters &iono_utc() const {
1955
+ return _iono_utc;
1956
+ }
1957
+ bool is_valid_iono() const {
1958
+ return _iono_initialized;
1959
+ }
1960
+ bool is_valid_utc() const {
1961
+ return _utc_initialized;
1962
+ }
1963
+ bool is_valid_iono_utc() const {
1964
+ return is_valid_iono() && is_valid_utc();
1965
+ }
1966
+ const Ionospheric_UTC_Parameters &update_iono_utc(
1967
+ const Ionospheric_UTC_Parameters &params,
1968
+ const bool &iono_valid = true,
1969
+ const bool &utc_valid = true) {
1970
+ _iono_initialized = iono_valid;
1971
+ _utc_initialized = utc_valid;
1972
+ return (_iono_utc = params);
1973
+ }
1974
+
1975
+ const satellites_t &satellites() const {
1976
+ return _satellites;
1977
+ }
1978
+ Satellite &satellite(const int &prn) {
1979
+ return _satellites[prn];
1980
+ }
1981
+ bool has_satellite(const int &prn) const {
1982
+ return _satellites.find(prn) != _satellites.end();
1983
+ }
1984
+ void update_all_ephemeris(const gps_time_t &target_time) {
1985
+ for(typename satellites_t::iterator it(_satellites.begin()), it_end(_satellites.end());
1986
+ it != it_end; ++it){
1987
+ it->second.select_ephemeris(target_time);
1988
+ }
1989
+ }
1990
+ void merge(const self_t &another, const bool &keep_original = true){
1991
+ for(typename satellites_t::const_iterator it(another._satellites.begin()), it_end(another._satellites.end());
1992
+ it != it_end;
1993
+ ++it){
1994
+ satellite(it->first).merge(it->second, keep_original);
1995
+ }
1996
+ if((!is_valid_iono_utc()) || (!keep_original)){
1997
+ _iono_utc = another._iono_utc;
1998
+ _iono_initialized = another._iono_initialized;
1999
+ _utc_initialized = another._utc_initialized;
2000
+ }
2001
+ }
2002
+
2003
+ /**
2004
+ * Calculate pierce point position
2005
+ *
2006
+ * @param relative_pos satellite position (relative position, NEU)
2007
+ * @param usrllh user position (absolute position, LLH)
2008
+ * @param height_over_ellipsoid
2009
+ * @see DO-229D A4.4.10.1 Pierce Point Location Determination
2010
+ */
2011
+ struct pierce_point_res_t {
2012
+ float_t latitude, longitude;
2013
+ };
2014
+ static pierce_point_res_t pierce_point(
2015
+ const enu_t &relative_pos,
2016
+ const llh_t &usrllh,
2017
+ const float_t &height_over_ellipsoid = 350E3) {
2018
+ float_t el(relative_pos.elevation()),
2019
+ az(relative_pos.azimuth());
2020
+ // Earth's central angle between use pos. and projection of PP.
2021
+ float_t psi_pp(M_PI / 2 - el
2022
+ - std::asin(WGS84::R_e / (WGS84::R_e + height_over_ellipsoid) * std::cos(el)));
2023
+ float_t phi_pp(
2024
+ std::asin(std::sin(usrllh.latitude() * std::cos(psi_pp)
2025
+ + std::cos(usrllh.latitude()) * std::sin(psi_pp) * std::cos(az)))); // latitude
2026
+ float_t lambda_pp_last(
2027
+ std::asin(std::sin(psi_pp) * std::sin(az) / std::cos(phi_pp)));
2028
+
2029
+ pierce_point_res_t res;
2030
+ res.latitude = phi_pp;
2031
+ {
2032
+ const float_t phi_limit(std::asin(WGS84::R_e / (WGS84::R_e + height_over_ellipsoid)));
2033
+ // Check whether longitude is opposite side.
2034
+ // This is possible when pierce point is located on the horizontal plane.
2035
+ // If pierce point height is 350km, the limit latitude yields asin(Re / (350E3 + Re)) = 71.4 [deg].
2036
+ float_t lhs(std::tan(psi_pp) * std::cos(az)), rhs(std::tan(M_PI / 2 - usrllh.latitude()));
2037
+ if(((usrllh.latitude() > phi_limit) && (lhs > rhs))
2038
+ || ((usrllh.latitude() < -phi_limit) & (lhs < rhs))){
2039
+ res.longitude = usrllh.longitude() + M_PI - lambda_pp_last;
2040
+ }else{
2041
+ res.longitude = usrllh.longitude() + lambda_pp_last;
2042
+ }
2043
+ }
2044
+ return res;
2045
+ }
2046
+
2047
+ /**
2048
+ * Calculate ratio of slant versus vertical
2049
+ *
2050
+ * @relative_pos satellite position (relative position, NEU)
2051
+ * @param height_over_ellipsoid
2052
+ * @see spherically single layer approach, for example,
2053
+ * Eq.(3) of "Ionospheric Range Error Correction Models" by N. Jakowski
2054
+ */
2055
+ static float_t slant_factor(
2056
+ const enu_t &relative_pos,
2057
+ const float_t &height_over_ellipsoid = 350E3) {
2058
+ return std::sqrt(
2059
+ -std::pow(std::cos(relative_pos.elevation()) / ((height_over_ellipsoid / WGS84::R_e) + 1), 2)
2060
+ + 1);
2061
+ }
2062
+
2063
+ /**
2064
+ * Calculate ionospheric delay by using TEC
2065
+ *
2066
+ * @param tec TEC (total electron count)
2067
+ * @param freq frequency (default is L1 frequency)
2068
+ * @return delay (if delayed, positive number will be returned)
2069
+ * @see http://www.navipedia.net/index.php/Ionospheric_Delay Eq.(13)
2070
+ */
2071
+ static float_t tec2delay(const float_t &tec, const float_t &freq = L1_Frequency) {
2072
+ const float_t a_f(40.3E16 / std::pow(freq, 2));
2073
+ return a_f * tec;
2074
+ }
2075
+
2076
+ /**
2077
+ * Calculate correction value in accordance with ionospheric model
2078
+ *
2079
+ * @param relative_pos satellite position (relative position, NEU)
2080
+ * @param usrllh user position (absolute position, LLH)
2081
+ * @return correction in meters
2082
+ */
2083
+ float_t iono_correction(
2084
+ const enu_t &relative_pos,
2085
+ const llh_t &usrllh,
2086
+ const gps_time_t &t) const {
2087
+
2088
+
2089
+ // Elevation and azimuth
2090
+ float_t el(relative_pos.elevation()),
2091
+ az(relative_pos.azimuth());
2092
+ float_t sc_el(rad2sc(el)),
2093
+ sc_az(rad2sc(az));
2094
+
2095
+ // Pierce point (PP stands for the earth projection of the Pierce point)
2096
+ // (the following equation is based on GPS ICD)
2097
+ float_t psi(0.0137 / (sc_el + 0.11) - 0.022); // central angle between user pos and PP.
2098
+ float_t phi_i(rad2sc(usrllh.latitude())
2099
+ + psi * cos(az)); // geodetic latitude of PP [sc]
2100
+ if(phi_i > 0.416){phi_i = 0.416;}
2101
+ else if(phi_i < -0.416){phi_i = -0.416;}
2102
+ float_t lambda_i(rad2sc(usrllh.longitude())
2103
+ + psi * sin(az) / cos(sc2rad(phi_i))); // geodetic longitude of PP [sc]
2104
+ float_t phi_m(phi_i
2105
+ + 0.064 * cos(sc2rad(lambda_i - 1.617))); // geomagnetic latitude of PP [sc]
2106
+
2107
+ // Local time [sec]
2108
+ float_t lt(4.32E4 * lambda_i + t.seconds);
2109
+ lt -= std::floor(lt / gps_time_t::seconds_day) * gps_time_t::seconds_day; // lt = [0,86400)
2110
+
2111
+ // Period and amplitude of cosine function
2112
+ float_t amp(0), per(0);
2113
+ float_t phi_mn(1.);
2114
+ for(int i(0); i < 4; i++){
2115
+ amp += _iono_utc.alpha[i] * phi_mn;
2116
+ per += _iono_utc.beta[i] * phi_mn;
2117
+ phi_mn *= phi_m;
2118
+ }
2119
+ if(amp < 0){amp = 0;}
2120
+ if(per < 72000){per = 72000;}
2121
+
2122
+ // Obliquity factor
2123
+ float_t F(1.0 + 16.0 * pow((0.53 - sc_el), 3));
2124
+
2125
+ float_t x(M_PI * 2 * (lt - 50400) / per); // phase [rad] => (-1.4 pi) < x < (0.42 pi) because min(per) = 72000
2126
+ //x -= M_PI * 2 * std::floor(x / (M_PI * 2) + 0.5); // x = [-pi,pi)
2127
+
2128
+ float_t T_iono(5E-9);
2129
+ if(std::abs(x) < 1.57){
2130
+ T_iono += amp * (1. - pow2(x) * (1.0 / 2 - pow2(x) / 24)); // ICD p.148
2131
+ }
2132
+ T_iono *= F;
2133
+
2134
+ return -T_iono * light_speed;
2135
+ }
2136
+
2137
+ /**
2138
+ * Calculate correction value in accordance with ionospheric model
2139
+ *
2140
+ * @param sat satellite position (absolute position, XYZ)
2141
+ * @param usr user position (absolute position, XYZ)
2142
+ * @return correction in meters
2143
+ */
2144
+ float_t iono_correction(
2145
+ const xyz_t &sat,
2146
+ const xyz_t &usr,
2147
+ const gps_time_t &t) const {
2148
+ return iono_correction(
2149
+ enu_t::relative(sat, usr),
2150
+ usr.llh(),
2151
+ t);
2152
+ }
2153
+
2154
+ struct IonospericCorrection {
2155
+ const GPS_SpaceNode<float_t> &space_node;
2156
+ float_t operator()(
2157
+ const enu_t &relative_pos,
2158
+ const llh_t &usrllh,
2159
+ const gps_time_t &t) const {
2160
+ return space_node.iono_correction(relative_pos, usrllh, t);
2161
+ }
2162
+ float_t operator()(
2163
+ const xyz_t &sat,
2164
+ const xyz_t &usr,
2165
+ const gps_time_t &t) const {
2166
+ return space_node.iono_correction(sat, usr, t);
2167
+ }
2168
+ };
2169
+ IonospericCorrection iono_correction() const {
2170
+ IonospericCorrection res = {*this};
2171
+ return res;
2172
+ }
2173
+
2174
+ /**
2175
+ * Calculate correction value in accordance with tropospheric model
2176
+ *
2177
+ * @param relative_pos satellite position (relative position, NEU)
2178
+ * @param usrllh user position (absolute position, LLH)
2179
+ * @return correction in meters
2180
+ */
2181
+ float_t tropo_correction(
2182
+ const enu_t &relative_pos,
2183
+ const llh_t &usrllh) const {
2184
+
2185
+ // Elevation (rad)
2186
+ float_t el(relative_pos.elevation());
2187
+
2188
+ // Altitude (m)
2189
+ const float_t &h(usrllh.height());
2190
+ float_t f(1.0);
2191
+ if(h > (1.0 / 2.3E-5)){
2192
+ f = 0;
2193
+ }else if(h > 0){
2194
+ f -= h * 2.3E-5;
2195
+ }
2196
+
2197
+ return -2.47 * pow(f, 5) / (sin(el) + 0.0121);
2198
+ }
2199
+
2200
+ struct NiellMappingFunction {
2201
+ float_t hydrostatic, wet;
2202
+ static float_t marini1972_2(const float_t &v, const float_t (&coef)[3]) {
2203
+ return (coef[0] / ((coef[1] / (coef[2] + v)) + v) + v);
2204
+ }
2205
+ static float_t marini1972(const float_t &sin_elv, const float_t (&coef)[3]) {
2206
+ return marini1972_2(1, coef) / marini1972_2(sin_elv, coef);
2207
+ }
2208
+ static NiellMappingFunction get(
2209
+ const float_t &year,
2210
+ const float_t &latitude, const float_t &elevation, const float_t &height_km){
2211
+ static const struct coef_t {
2212
+ float_t coef[3];
2213
+ } tbl_hyd_avg[] = {
2214
+ {1.2769934e-3, 2.9153695e-3, 62.610505e-3}, // 15
2215
+ {1.2683230e-3, 2.9152299e-3, 62.837393e-3}, // 30
2216
+ {1.2465397e-3, 2.9288445e-3, 63.721774e-3}, // 45
2217
+ {1.2196049e-3, 2.9022565e-3, 63.824265e-3}, // 60
2218
+ {1.2045996e-3, 2.9024912e-3, 64.258455e-3}, // 75
2219
+ }, tbl_hyd_amp[] = {
2220
+ {0.0, 0.0, 0.0 }, // 15
2221
+ {1.2709626e-5, 2.1414979e-5, 9.0128400e-5}, // 30
2222
+ {2.6523662e-5, 3.0160779e-5, 4.3497037e-5}, // 45
2223
+ {3.4000452e-5, 7.2562722e-5, 84.795348e-5}, // 60
2224
+ {4.1202191e-5, 11.723375e-5, 170.37206e-5}, // 75
2225
+ }, tbl_wet[] = {
2226
+ {5.8021897e-4, 1.4275268e-3, 4.3472961e-2}, // 15
2227
+ {5.6794847e-4, 1.5138625e-3, 4.6729510e-2}, // 30
2228
+ {5.8118019e-4, 1.4572752e-3, 4.3908931e-2}, // 45
2229
+ {5.9727542e-4, 1.5007428e-3, 4.4626982e-2}, // 60
2230
+ {6.1641693e-4, 1.7599082e-3, 5.4736038e-2}, // 75
2231
+ };
2232
+ static const int tbl_length(sizeof(tbl_hyd_avg) / sizeof(tbl_hyd_avg[0]));
2233
+ static const float_t delta(M_PI / 180 * 15);
2234
+ float_t idx_f(latitude / delta);
2235
+ int idx(idx_f);
2236
+
2237
+ coef_t abc_avg, abc_amp, abc_wet;
2238
+ if(idx < 1){
2239
+ abc_avg = tbl_hyd_avg[0];
2240
+ abc_amp = tbl_hyd_amp[0];
2241
+ abc_wet = tbl_wet[0];
2242
+ }else if(idx >= (tbl_length - 1)){
2243
+ abc_avg = tbl_hyd_avg[tbl_length - 1];
2244
+ abc_amp = tbl_hyd_amp[tbl_length - 1];
2245
+ abc_wet = tbl_wet[tbl_length - 1];
2246
+ }else{
2247
+ // interpolation
2248
+ float_t weight_b((idx_f - idx) / delta), weight_a(1. - weight_b);
2249
+ for(int i(0); i < 3; ++i){
2250
+ abc_avg = tbl_hyd_avg[i] * weight_a + tbl_hyd_avg[i + 1] * weight_b;
2251
+ abc_amp = tbl_hyd_amp[i] * weight_a + tbl_hyd_amp[i + 1] * weight_b;
2252
+ abc_wet = tbl_wet[i] * weight_a + tbl_wet[i + 1] * weight_b;;
2253
+ }
2254
+ }
2255
+
2256
+ NiellMappingFunction res;
2257
+ float_t sin_elv(sin(elevation));
2258
+ {
2259
+ float_t xi[3];
2260
+ float_t k_amp(cos(M_PI * 2 * (year - (28. / 365.25))));
2261
+ for(int i(0); i < 3; ++i){
2262
+ xi[i] = abc_avg.coef[i] - abc_amp.coef[i] * k_amp;
2263
+ }
2264
+
2265
+ static const float_t abc_ht[] = {2.53e-5, 5.49e-3, 1.14e-3};
2266
+ res.hydrostatic = marini1972(sin_elv, xi)
2267
+ + ((1. / sin_elv) - marini1972(sin_elv, abc_ht)) * height_km;
2268
+ }
2269
+ res.wet = marini1972(sin_elv, abc_wet);
2270
+ return res;
2271
+ }
2272
+ NiellMappingFunction(
2273
+ const enu_t &relative_pos,
2274
+ const llh_t &usrllh,
2275
+ const gps_time_t &t){
2276
+ (*this) = get(t.year(), usrllh.latitude(), relative_pos.elevation(), usrllh.height() / 1E3);
2277
+ }
2278
+ };
2279
+
2280
+ static float_t tropo_correction_zenith_hydrostatic_Saastamoinen(
2281
+ const float_t &latitude, const float_t &p_hpa, const float_t &height_km){
2282
+ return (0.0022767 * p_hpa)
2283
+ / (1. - 0.00266 * cos(latitude * 2) - 0.00028 * height_km);
2284
+ }
2285
+
2286
+ /**
2287
+ * Calculate correction value in accordance with tropospheric model
2288
+ *
2289
+ * @param sat satellite position (absolute position, XYZ)
2290
+ * @param usr user position (absolute position, XYZ)
2291
+ * @return correction in meters
2292
+ */
2293
+ float_t tropo_correction(
2294
+ const xyz_t &sat,
2295
+ const xyz_t &usr) const {
2296
+ return tropo_correction(
2297
+ enu_t::relative(sat, usr),
2298
+ usr.llh());
2299
+ }
2300
+
2301
+ struct TropospericCorrection {
2302
+ const GPS_SpaceNode<float_t> &space_node;
2303
+ float_t operator()(
2304
+ const enu_t &relative_pos,
2305
+ const llh_t &usrllh) const {
2306
+ return space_node.tropo_correction(relative_pos, usrllh);
2307
+ }
2308
+ float_t operator()(
2309
+ const xyz_t &sat,
2310
+ const xyz_t &usr) const {
2311
+ return space_node.tropo_correction(sat, usr);
2312
+ }
2313
+ };
2314
+ TropospericCorrection tropo_correction() const {
2315
+ TropospericCorrection res = {*this};
2316
+ return res;
2317
+ }
2318
+ };
2319
+
2320
+ template <class FloatT>
2321
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::light_speed = 2.99792458E8;
2322
+
2323
+ template <class FloatT>
2324
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::L1_Frequency = 1575.42E6;
2325
+
2326
+ #define GPS_SC2RAD 3.1415926535898
2327
+ template <class FloatT>
2328
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::SC2RAD = GPS_SC2RAD;
2329
+
2330
+ template <class FloatT>
2331
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::L2_Frequency = 1227.6E6;
2332
+
2333
+ template <class FloatT>
2334
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::gamma_L1_L2 = (FloatT)(77 * 77) / (60 * 60);
2335
+
2336
+ #define POWER_2(n) \
2337
+ (((n) >= 0) \
2338
+ ? (float_t)(1 << (n >= 0 ? n : 0)) \
2339
+ : (((float_t)1) / (1 << (-(n) >= 30 ? 30 : -(n > 0 ? 0 : n))) \
2340
+ / (1 << (-(n) >= 30 ? (-(n) - 30) : 0))))
2341
+ template <class FloatT>
2342
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::Ionospheric_UTC_Parameters::raw_t::sf[] = {
2343
+ POWER_2(-30), // alpha0
2344
+ POWER_2(-27), // alpha1
2345
+ POWER_2(-24), // alpha2
2346
+ POWER_2(-24), // alpha3
2347
+ POWER_2( 11), // beta0
2348
+ POWER_2( 14), // beta1
2349
+ POWER_2( 16), // beta2
2350
+ POWER_2( 16), // beta3
2351
+ POWER_2(-50), // A1
2352
+ POWER_2(-30), // A0
2353
+ };
2354
+
2355
+ template <class FloatT>
2356
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::raw_t::sf[] = {
2357
+ POWER_2(-31), // t_GD
2358
+ POWER_2(4), // t_oc
2359
+ POWER_2(-31), // a_f0
2360
+ POWER_2(-43), // a_f1
2361
+ POWER_2(-55), // a_f2
2362
+
2363
+ POWER_2(-5), // c_rs
2364
+ GPS_SC2RAD * POWER_2(-43), // delta_n
2365
+ GPS_SC2RAD * POWER_2(-31), // M0
2366
+ POWER_2(-29), // c_uc
2367
+ POWER_2(-33), // e
2368
+ POWER_2(-29), // c_us
2369
+ POWER_2(-19), // sqrt_A
2370
+ POWER_2(4), // t_oe
2371
+
2372
+ POWER_2(-29), // c_ic
2373
+ GPS_SC2RAD * POWER_2(-31), // Omega0
2374
+ POWER_2(-29), // c_is
2375
+ GPS_SC2RAD * POWER_2(-31), // i0
2376
+ POWER_2(-5), // c_rc
2377
+ GPS_SC2RAD * POWER_2(-31), // omega
2378
+ GPS_SC2RAD * POWER_2(-43), // dot_Omega0
2379
+ GPS_SC2RAD * POWER_2(-43), // dot_i0
2380
+ };
2381
+
2382
+ template <class FloatT>
2383
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::SatelliteProperties::Almanac::raw_t::sf[] = {
2384
+ POWER_2(-21), // e
2385
+ POWER_2(12), // t_oa
2386
+ GPS_SC2RAD * POWER_2(-19), // delta_i
2387
+ GPS_SC2RAD * POWER_2(-38), // dot_Omega0
2388
+ POWER_2(-11), // sqrt_A
2389
+ GPS_SC2RAD * POWER_2(-23), // Omega0
2390
+ GPS_SC2RAD * POWER_2(-23), // omega
2391
+ GPS_SC2RAD * POWER_2(-23), // M0
2392
+ POWER_2(-20), // a_f0
2393
+ POWER_2(-38), // a_f1
2394
+ };
2395
+ #undef POWER_2
2396
+ #undef GPS_SC2RAD
2397
+
2398
+ template <class FloatT>
2399
+ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::URA_limits[] = {
2400
+ 2.40,
2401
+ 3.40,
2402
+ 4.85,
2403
+ 6.85,
2404
+ 9.65,
2405
+ 13.65,
2406
+ 24.00,
2407
+ 48.00,
2408
+ 96.00,
2409
+ 192.00,
2410
+ 384.00,
2411
+ 768.00,
2412
+ 1536.00,
2413
+ 3072.00,
2414
+ 6144.00,
2415
+ };
2416
+
2417
+ template <class FloatT>
2418
+ const int GPS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::URA_MAX_INDEX
2419
+ = sizeof(URA_limits) / sizeof(URA_limits[0]);
2420
+
2421
+ #ifdef POW2_ALREADY_DEFINED
2422
+ #undef POW2_ALREADY_DEFINED
2423
+ #else
2424
+ #undef pow2
2425
+ #endif
2426
+ #ifdef POW3_ALREADY_DEFINED
2427
+ #undef POW3_ALREADY_DEFINED
2428
+ #else
2429
+ #undef pow3
2430
+ #endif
2431
+
2432
+ #endif /* __GPS_H__ */