gps_pvt 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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__ */