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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/README.md +86 -0
- data/Rakefile +86 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -0
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +16019 -0
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -0
- data/ext/gps_pvt/extconf.rb +70 -0
- data/ext/ninja-scan-light/tool/navigation/EGM.h +2971 -0
- data/ext/ninja-scan-light/tool/navigation/GPS.h +2432 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +479 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +1081 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +199 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +210 -0
- data/ext/ninja-scan-light/tool/navigation/MagneticField.h +928 -0
- data/ext/ninja-scan-light/tool/navigation/NTCM.h +211 -0
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +1781 -0
- data/ext/ninja-scan-light/tool/navigation/WGS84.h +186 -0
- data/ext/ninja-scan-light/tool/navigation/coordinate.h +406 -0
- data/ext/ninja-scan-light/tool/param/bit_array.h +145 -0
- data/ext/ninja-scan-light/tool/param/complex.h +558 -0
- data/ext/ninja-scan-light/tool/param/matrix.h +4049 -0
- data/ext/ninja-scan-light/tool/param/matrix_fixed.h +665 -0
- data/ext/ninja-scan-light/tool/param/matrix_special.h +562 -0
- data/ext/ninja-scan-light/tool/param/quaternion.h +765 -0
- data/ext/ninja-scan-light/tool/param/vector3.h +651 -0
- data/ext/ninja-scan-light/tool/swig/Coordinate.i +177 -0
- data/ext/ninja-scan-light/tool/swig/GPS.i +1102 -0
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +1234 -0
- data/ext/ninja-scan-light/tool/swig/extconf.rb +5 -0
- data/ext/ninja-scan-light/tool/swig/makefile +53 -0
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +417 -0
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +489 -0
- data/gps_pvt.gemspec +57 -0
- data/lib/gps_pvt/receiver.rb +375 -0
- data/lib/gps_pvt/ubx.rb +148 -0
- data/lib/gps_pvt/version.rb +5 -0
- data/lib/gps_pvt.rb +9 -0
- data/sig/gps_pvt.rbs +4 -0
- 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 ¤t() 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 ¶ms,
|
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__ */
|