gps_pvt 0.2.3 → 0.4.0

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.
@@ -0,0 +1,1270 @@
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 __GLONASS_H__
33
+ #define __GLONASS_H__
34
+
35
+ /** @file
36
+ * @brief GLONASS ICD definitions
37
+ */
38
+
39
+
40
+ #include <ctime>
41
+ #define _USE_MATH_DEFINES
42
+ #include <cmath>
43
+ #include <climits>
44
+ #include <cstdlib>
45
+ #include <ctime>
46
+
47
+ #include "GPS.h"
48
+ #include "algorithm/integral.h"
49
+
50
+ template <class FloatT = double>
51
+ class GLONASS_SpaceNode {
52
+ public:
53
+ typedef FloatT float_t;
54
+
55
+ static const float_t light_speed;
56
+ static const float_t L1_frequency_base;
57
+ static const float_t L1_frequency_gap;
58
+ static const float_t L2_frequency_base;
59
+ static const float_t L2_frequency_gap;
60
+
61
+ public:
62
+ typedef GLONASS_SpaceNode<float_t> self_t;
63
+
64
+ typedef unsigned char u8_t;
65
+ typedef signed char s8_t;
66
+ typedef unsigned short u16_t;
67
+ typedef signed short s16_t;
68
+ typedef unsigned int u32_t;
69
+ typedef signed int s32_t;
70
+
71
+ typedef int int_t;
72
+ typedef unsigned int uint_t;
73
+
74
+ static float_t L1_frequency(const int_t &freq_ch) {
75
+ return L1_frequency_base + L1_frequency_gap * freq_ch;
76
+ }
77
+ static float_t L2_frequency(const int_t &freq_ch) {
78
+ return L2_frequency_base + L2_frequency_gap * freq_ch;
79
+ }
80
+
81
+ typedef typename GPS_SpaceNode<float_t>::DataParser DataParser;
82
+
83
+ template <class InputT,
84
+ int EffectiveBits = sizeof(InputT) * CHAR_BIT,
85
+ int PaddingBits_MSB = (int)sizeof(InputT) * CHAR_BIT - EffectiveBits>
86
+ struct BroadcastedMessage : public DataParser {
87
+ #define convert_u(bits, offset_bits, length, name) \
88
+ static u ## bits ## _t name(const InputT *buf){ \
89
+ return \
90
+ DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
91
+ buf, offset_bits, length); \
92
+ }
93
+ #define convert_s(bits, offset_bits, length, name) \
94
+ static s ## bits ## _t name(const InputT *buf){ \
95
+ u ## bits ## _t temp( \
96
+ DataParser::template bits2num<u ## bits ## _t, EffectiveBits, PaddingBits_MSB>( \
97
+ buf, offset_bits, length)); \
98
+ static const u ## bits ## _t mask((u ## bits ## _t)1 << (length - 1)); /* MSB is sign bit */ \
99
+ return (temp & mask) ? ((s ## bits ## _t)-1 * (temp & (mask - 1))) : temp; \
100
+ }
101
+ convert_u( 8, 1, 4, m);
102
+ convert_u( 8, 77, 8, KX);
103
+
104
+ struct String1 {
105
+ convert_u( 8, 7, 2, P1);
106
+ convert_u(16, 9, 12, t_k);
107
+ convert_s(32, 21, 24, xn_dot);
108
+ convert_s( 8, 45, 5, xn_ddot);
109
+ convert_s(32, 50, 27, xn);
110
+ };
111
+ struct String2 {
112
+ convert_u( 8, 5, 3, B_n);
113
+ convert_u( 8, 8, 1, P2);
114
+ convert_u( 8, 9, 7, t_b);
115
+ convert_s(32, 21, 24, yn_dot);
116
+ convert_s( 8, 45, 5, yn_ddot);
117
+ convert_s(32, 50, 27, yn);
118
+ };
119
+ struct String3 {
120
+ convert_u( 8, 5, 1, P3);
121
+ convert_s(16, 6, 11, gamma_n);
122
+ convert_u( 8, 18, 2, p);
123
+ convert_u( 8, 20, 1, l_n);
124
+ convert_s(32, 21, 24, zn_dot);
125
+ convert_s( 8, 45, 5, zn_ddot);
126
+ convert_s(32, 50, 27, zn);
127
+ };
128
+ struct String4 {
129
+ convert_s(32, 5, 22, tau_n);
130
+ convert_u( 8, 27, 5, delta_tau_n);
131
+ convert_u( 8, 32, 5, E_n);
132
+ convert_u( 8, 51, 1, P4);
133
+ convert_u( 8, 52, 4, F_T);
134
+ convert_u(16, 59, 11, N_T);
135
+ convert_u( 8, 70, 5, n);
136
+ convert_u( 8, 75, 2, M);
137
+ };
138
+ struct String5_Alnamac {
139
+ convert_u(16, 5, 11, NA);
140
+ convert_s(32, 16, 32, tau_c);
141
+ convert_u( 8, 49, 5, N_4);
142
+ convert_s(32, 54, 22, tau_GPS);
143
+ convert_u( 8, 76, 1, l_n);
144
+ };
145
+
146
+ struct String6_Alnamac { // String 6; same as 8, 10, 12, 14
147
+ convert_u( 8, 5, 1, C_n);
148
+ convert_u( 8, 6, 2, M_n);
149
+ convert_u( 8, 8, 5, nA);
150
+ convert_s(16, 13, 10, tauA_n);
151
+ convert_s(32, 23, 21, lambdaA_n);
152
+ convert_s(32, 44, 18, delta_iA_n);
153
+ convert_u(16, 62, 15, epsilonA_n);
154
+ };
155
+ struct String7_Alnamac { // String 7; same as 9, 11, 13, 15
156
+ convert_s(16, 5, 16, omegaA_n);
157
+ convert_u(32, 21, 21, tA_lambda_n);
158
+ convert_s(32, 42, 22, delta_TA_n);
159
+ convert_s( 8, 64, 7, delta_TA_dot_n);
160
+ convert_u( 8, 71, 5, HA_n);
161
+ convert_u( 8, 76, 1, l_n);
162
+ };
163
+
164
+ struct Frame5_String14 {
165
+ convert_s(16, 5, 11, B1);
166
+ convert_s(16, 16, 10, B2);
167
+ convert_u( 8, 26, 2, KP);
168
+ };
169
+ #undef convert_s
170
+ #undef convert_u
171
+ };
172
+
173
+ struct TimeProperties {
174
+ float_t tau_c; ///< GLONASS time scale correction to UTC(SU) time [s]
175
+ float_t tau_GPS; ///< fractional part of time difference from GLONASS time to GPS time [s], integer part should be derived from another source
176
+ struct date_t {
177
+ int_t year; // 20XX
178
+ int_t day_of_year; // Jan. 1st = 0
179
+ static const int_t days_m[12];
180
+ /**
181
+ * @return (std::tm)
182
+ */
183
+ std::tm c_tm() const {
184
+ std::tm res = {0};
185
+ res.tm_year = year - 1900;
186
+ res.tm_yday = res.tm_mday = day_of_year; // tm_yday ranges [0, 365]
187
+ do{
188
+ if(res.tm_mday < days_m[0]){break;} // tm_mon ranges [0, 11], Check January
189
+ res.tm_mday -= days_m[0];
190
+ ++res.tm_mon;
191
+ if((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))){ // is leap year?
192
+ if(res.tm_mday < (days_m[1] + 1)){break;} // Check February in leap year
193
+ res.tm_mday -= (days_m[1] + 1);
194
+ ++res.tm_mon;
195
+ }
196
+ for(; res.tm_mon < (int)(sizeof(days_m) / sizeof(days_m[0])); ++res.tm_mon){
197
+ if(res.tm_mday < days_m[res.tm_mon]){break;}
198
+ res.tm_mday -= days_m[res.tm_mon];
199
+ }
200
+ }while(false);
201
+ ++res.tm_mday; // tm_mday ranges [1, 31]
202
+ {
203
+ /* day of week according to (modified) Sakamoto's methods
204
+ * @see https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Sakamoto's_methods
205
+ */
206
+ int_t y(year - 1); // year/1/1
207
+ res.tm_wday = (y + y/4 - y/100 + y/400 + day_of_year + 1) % 7;
208
+ }
209
+ return res;
210
+ }
211
+ static date_t from_c_tm(const std::tm &date){
212
+ static const struct days_t {
213
+ int_t sum_m[sizeof(days_m) / sizeof(days_m[0])];
214
+ days_t(){
215
+ sum_m[0] = 0;
216
+ for(unsigned int i(1); i < sizeof(days_m) / sizeof(days_m[0]); ++i){
217
+ sum_m[i] = sum_m[i - 1] + days_m[i - 1];
218
+ }
219
+ }
220
+ } days;
221
+ date_t res = {date.tm_year + 1900, days.sum_m[date.tm_mon] + date.tm_mday - 1};
222
+ if((date.tm_mon >= 2)
223
+ && ((res.year % 400 == 0) || ((res.year % 4 == 0) && (res.year % 100 != 0)))){
224
+ ++res.day_of_year; // leap year
225
+ }
226
+ return res;
227
+ }
228
+ static float_t julian_day(const std::tm &date){
229
+ // (5.48) of "Preliminary Orbit Determination" by Curtis, Howard D
230
+ // @see original Boulet(1991)
231
+ int y(date.tm_year + 1900), m(date.tm_mon + 1);
232
+ return 1721013.5 // expect all argument of (int) to be >= 0
233
+ + 367 * y
234
+ - (int)(7 * (y + (int)((m + 9) / 12)) / 4)
235
+ + (int)(275 * m / 9)
236
+ + date.tm_mday;
237
+ }
238
+ static float_t Greenwich_sidereal_time_deg(
239
+ const std::tm &date, const float_t &ut_hr = 0){
240
+ // @see "Preliminary Orbit Determination" by Curtis, Howard D
241
+ float_t T0((julian_day(date) - 2451545) / 36525); // (5.49)
242
+ float_t theta_G0_deg( // (5.50)
243
+ 100.4606184
244
+ + 36000.77004 * T0
245
+ + 0.000387933 * std::pow(T0, 2)
246
+ - 2.583E-8 * std::pow(T0, 3));
247
+ return theta_G0_deg + 360.98564724 * ut_hr / 24; // (5.51)
248
+ }
249
+ } date;
250
+ bool l_n; // health flag; 0 - healthy, 1 - malfunction
251
+
252
+ struct raw_t {
253
+ s32_t tau_c;
254
+ s32_t tau_GPS;
255
+ u8_t N_4;
256
+ u16_t NA;
257
+ bool l_n;
258
+ #define fetch_item(name) name = BroadcastedMessage< \
259
+ InputT, (int)sizeof(InputT) * CHAR_BIT - PaddingBits_MSB - PaddingBits_LSB, PaddingBits_MSB> \
260
+ :: String5_Alnamac :: name (src)
261
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
262
+ void update_string5(const InputT *src){
263
+ fetch_item(tau_c);
264
+ fetch_item(tau_GPS);
265
+ fetch_item(N_4);
266
+ fetch_item(NA);
267
+ fetch_item(l_n);
268
+ }
269
+ #undef fetch_item
270
+
271
+ enum {
272
+ SF_tau_c, SF_tau_GPS,
273
+ SF_NUM,
274
+ };
275
+ static const float_t sf[SF_NUM];
276
+
277
+ static date_t raw2date(const u8_t &N_4_, const u16_t &NA_) {
278
+ // @see A.3.1.3 (ver.5.1)
279
+ date_t res;
280
+ res.year = 1996 + 4 * (N_4_ - 1);
281
+ if(NA_ <= 366){
282
+ res.day_of_year = NA_ - 1;
283
+ }else if(NA_ <= 731){
284
+ res.year += 1;
285
+ res.day_of_year = NA_ - 367;
286
+ }else if(NA_ <= 1096){
287
+ res.year += 2;
288
+ res.day_of_year = NA_ - 732;
289
+ }else if(NA_ <= 1461){
290
+ res.year += 3;
291
+ res.day_of_year = NA_ - 1097;
292
+ }
293
+ return res;
294
+ }
295
+
296
+ operator TimeProperties() const {
297
+ TimeProperties res;
298
+ #define CONVERT(TARGET) \
299
+ {res.TARGET = sf[SF_ ## TARGET] * TARGET;}
300
+ CONVERT(tau_c);
301
+ CONVERT(tau_GPS);
302
+ res.date = raw2date(N_4, NA);
303
+ res.l_n = (l_n > 0);
304
+ #undef CONVERT
305
+ return res;
306
+ }
307
+
308
+ raw_t &operator=(const TimeProperties &t){
309
+ #define CONVERT(TARGET) \
310
+ {TARGET = (s32_t)((t.TARGET + 0.5 * sf[SF_ ## TARGET]) / sf[SF_ ## TARGET]);}
311
+ CONVERT(tau_c);
312
+ CONVERT(tau_GPS);
313
+ std::div_t divmod(std::div(t.date.year - 1996, 4));
314
+ N_4 = divmod.quot + 1;
315
+ NA = t.date.day_of_year + 1;
316
+ switch(divmod.rem){
317
+ case 1: NA += 366; break;
318
+ case 2: NA += 731; break;
319
+ case 3: NA += 1096; break;
320
+ }
321
+ l_n = (t.l_n ? 1 : 0);
322
+ #undef CONVERT
323
+ return *this;
324
+ }
325
+ };
326
+
327
+ bool is_equivalent(const TimeProperties &t) const {
328
+ do{
329
+ #define CHECK(TARGET) if(TARGET != t.TARGET){break;}
330
+ CHECK(l_n);
331
+ CHECK(date.year);
332
+ CHECK(date.day_of_year);
333
+ #undef CHECK
334
+ #define CHECK(TARGET) \
335
+ if(std::abs(TARGET - t.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
336
+ CHECK(tau_c);
337
+ CHECK(tau_GPS);
338
+ #undef CHECK
339
+ return true;
340
+ }while(false);
341
+ return false;
342
+ }
343
+ };
344
+
345
+ class SatelliteProperties {
346
+ public:
347
+ struct Ephemeris {
348
+ uint_t svid;
349
+ int_t freq_ch;
350
+ uint_t t_k; ///< time referenced to the beginning of the frame from current day [s]
351
+ uint_t t_b; ///< base time of ephemeris parameters in UTC(SU) + 3hr [s]
352
+ uint_t M; ///< satellite type; 0 - GLONASS, 1 - GLONASS-M
353
+ float_t gamma_n; ///< gamma_n(t_c = t_b) = d/dt (t_c - t_n) [dimensionless]
354
+ float_t tau_n; ///< tau_n(t_c = t_b) = t_c (GLONASS time) - t_n(n-th satellite time) [s]
355
+ float_t xn, yn, zn; // [m]
356
+ float_t xn_dot, yn_dot, zn_dot; // [m/s]
357
+ float_t xn_ddot, yn_ddot, zn_ddot; // [m/s^2]
358
+ uint_t B_n; // health flag
359
+ uint_t p; // satellite operation mode
360
+ uint_t N_T; // current date counting up from leap year Jan. 1st [days]
361
+ float_t F_T; // user range accuracy at t_b [m]
362
+ uint_t n; // time difference between L1 and L2
363
+ float_t delta_tau_n;
364
+ uint_t E_n; // [days]
365
+ uint_t P1; // [s] time interval of two adjacent t_b; 0 - 0, 1 - 30, 2 - 45, 3 - 60 mins
366
+ bool P2;
367
+ //bool P3; // flag for alnamac; 1 - five, 0 - four
368
+ bool P4; // flag for ephemeris; 1 - uploaded by the control segment
369
+ bool l_n; // health flag; 0 - healthy, 1 - malfunction
370
+
371
+ float_t L1_frequency() const {
372
+ return GLONASS_SpaceNode::L1_frequency(freq_ch);
373
+ }
374
+
375
+ float_t L2_frequency() const {
376
+ return GLONASS_SpaceNode::L2_frequency(freq_ch);
377
+ }
378
+
379
+ struct constellation_t { // TODO make it to be a subclass of System_XYZ<float_t, PZ90>
380
+ float_t position[3];
381
+ float_t velocity[3];
382
+ float_t pos_abs2() const {
383
+ float_t res(0);
384
+ for(int i(0); i < 3; ++i){
385
+ res += (position[i] * position[i]);
386
+ }
387
+ return res;
388
+ }
389
+ constellation_t operator+(const constellation_t &another) const { // for RK4
390
+ constellation_t res(*this);
391
+ for(int i(0); i < 3; ++i){
392
+ res.position[i] += another.position[i];
393
+ res.velocity[i] += another.velocity[i];
394
+ }
395
+ return res;
396
+ }
397
+ constellation_t operator*(const float_t &sf) const { // for RK4
398
+ constellation_t res(*this);
399
+ for(int i(0); i < 3; ++i){
400
+ res.position[i] *= sf;
401
+ res.velocity[i] *= sf;
402
+ }
403
+ return res;
404
+ }
405
+ constellation_t operator/(const float_t &sf) const { // for RK4
406
+ return operator*(((float_t)1)/sf);
407
+ }
408
+ // TODO constant definitions should be moved to PZ-90(.02, .11)
409
+ static const float_t omega_E;
410
+ constellation_t abs_corrdinate(const float_t &sidereal_time_in_rad){
411
+ // @see Appendix.A PZ-90 to O-X0Y0Z0
412
+ float_t crad(std::cos(sidereal_time_in_rad)), srad(std::sin(sidereal_time_in_rad));
413
+ float_t
414
+ x0(position[0] * crad - position[1] * srad),
415
+ y0(position[0] * srad + position[1] * crad);
416
+ constellation_t res = {
417
+ {x0, y0, position[2]},
418
+ {
419
+ velocity[0] * crad - velocity[1] * srad - omega_E * y0,
420
+ velocity[0] * srad + velocity[1] * crad + omega_E * x0,
421
+ velocity[2]
422
+ }
423
+ };
424
+ return res;
425
+ }
426
+ constellation_t rel_corrdinate(const float_t &sidereal_time_in_rad){
427
+ // @see Appendix.A O-X0Y0Z0 to PZ-90
428
+ float_t crad(std::cos(sidereal_time_in_rad)), srad(std::sin(sidereal_time_in_rad));
429
+ float_t
430
+ x( position[0] * crad + position[1] * srad),
431
+ y(-position[0] * srad + position[1] * crad);
432
+ constellation_t res = {
433
+ {x, y, position[2]},
434
+ {
435
+ velocity[0] * crad + velocity[1] * srad + omega_E * y,
436
+ -velocity[0] * srad + velocity[1] * crad - omega_E * x,
437
+ velocity[2]
438
+ }
439
+ };
440
+ return res;
441
+ }
442
+ operator typename GPS_SpaceNode<float_t>::SatelliteProperties
443
+ ::constellation_t() const {
444
+ #if 1
445
+ // @see https://gssc.esa.int/navipedia/index.php/Reference_Frames_in_GNSS
446
+ // PZ90.11 -> WGS84 (starting from 3:00 pm on December 31, 2013)
447
+ typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t res = {
448
+ typename GPS_SpaceNode<float_t>::xyz_t(
449
+ position[0] + 0.003, position[1] + 0.001, position[2] + 0.001),
450
+ typename GPS_SpaceNode<float_t>::xyz_t(
451
+ velocity[0], velocity[1], velocity[2]),
452
+ };
453
+ return res;
454
+ #else
455
+ // @see https://www.gsi.go.jp/common/000070971.pdf
456
+ // @see (originally) Federal Air Navigation Authority (FANA), Aeronautical Information Circular
457
+ // of the Russian Federation, 12 February 2009, Russia.
458
+ // PZ90.02 -> WGS84
459
+ typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t res = {
460
+ typename GPS_SpaceNode<float_t>::xyz_t(
461
+ position[0] - 0.36, position[1] + 0.08, position[2] + 0.18),
462
+ typename GPS_SpaceNode<float_t>::xyz_t(
463
+ velocity[0], velocity[1], velocity[2]),
464
+ };
465
+ return res;
466
+ #endif
467
+ }
468
+ };
469
+
470
+ struct eccentric_anomaly_t {
471
+ float_t E_k;
472
+ float_t snu_k, cnu_k;
473
+ eccentric_anomaly_t(const float_t &g_k, const float_t &e_k){
474
+ static const float_t delta_limit(1E-12);
475
+ for(int loop(0); loop < 10; loop++){
476
+ float_t E_k2(g_k + e_k * std::sin(E_k));
477
+ if(std::abs(E_k2 - E_k) < delta_limit){break;}
478
+ E_k = E_k2;
479
+ }
480
+ float_t sE_k(std::sin(E_k)), cE_k(std::cos(E_k)), denom(-e_k * cE_k + 1);
481
+ snu_k = std::sqrt(-std::pow(e_k, 2) + 1) * sE_k / denom;
482
+ cnu_k = (cE_k - e_k) / denom;
483
+ }
484
+ };
485
+
486
+ struct lunar_solar_perturbations_t {
487
+ struct arg_t {
488
+ float_t xi, eta, zeta, r;
489
+ } m, s;
490
+ struct constatnt_t {
491
+ float_t
492
+ a_m, a_s, // [m]
493
+ e_m, e_s,
494
+ i_m;
495
+ };
496
+ struct var_t {
497
+ float_t
498
+ epsilon,
499
+ g_m, Omega_m, Gamma_m,
500
+ g_s, omega_s;
501
+ };
502
+
503
+ static lunar_solar_perturbations_t setup(
504
+ const constatnt_t &C, const var_t &var){
505
+
506
+ lunar_solar_perturbations_t res;
507
+
508
+ static const float_t
509
+ sf_ci_m(-std::cos(C.i_m) + 1), si_m(std::sin(C.i_m)),
510
+ cepsilon(std::cos(var.epsilon)), sepsilon(std::sin(var.epsilon));
511
+
512
+ { // ICD ver.4 and 5.1 Eq.(3) lunar (m) corresponding to Appendix.J.1 in CDMA ICD
513
+ // (ICD4) float_t Omega_m(Omega_om + Omega_1m * T);
514
+ float_t sOmega_m(std::sin(var.Omega_m)), cOmega_m(std::cos(var.Omega_m));
515
+
516
+ float_t xi_ast(-std::pow(cOmega_m, 2) * sf_ci_m + 1);
517
+ float_t eta_ast(sOmega_m * si_m);
518
+ float_t zeta_ast(cOmega_m * si_m);
519
+
520
+ float_t xi_11(sOmega_m * cOmega_m * sf_ci_m);
521
+ float_t xi_12(-std::pow(sOmega_m, 2) * sf_ci_m + 1);
522
+
523
+ float_t eta_11(xi_ast * cepsilon - zeta_ast * sepsilon);
524
+ float_t eta_12(xi_11 * cepsilon + eta_ast * sepsilon);
525
+
526
+ float_t zeta_11(xi_ast * sepsilon + zeta_ast * cepsilon);
527
+ float_t zeta_12(zeta_11 * sepsilon - eta_ast * cepsilon);
528
+
529
+ // (ICD4) float_t Gamma_m(Gamma_o + Gamma_1 * T);
530
+ float_t sGamma(std::sin(var.Gamma_m)), cGamma(std::cos(var.Gamma_m));
531
+
532
+ // (ICD4) eccentric_anomaly_t E_m(g_om + g_1m * T, C.e_m);
533
+ eccentric_anomaly_t E_m(var.g_m, C.e_m);
534
+
535
+ float_t
536
+ srad(E_m.snu_k * cGamma + E_m.cnu_k * sGamma),
537
+ crad(E_m.cnu_k * cGamma - E_m.snu_k * sGamma);
538
+ res.m.xi = srad * xi_11 + crad * xi_12;
539
+ res.m.eta = srad * eta_11 + crad * eta_12;
540
+ res.m.zeta = srad * zeta_11 + crad * zeta_12;
541
+ res.m.r = C.a_m * (-C.e_m * std::cos(E_m.E_k) + 1);
542
+ }
543
+
544
+ { // ICD ver.4 and 5.1 Eq.(3) solar (s) corresponding to Appendix.S in CDMA ICD
545
+ // (ICD4) float_t omega_s(dms2rad(281, 13, (15.00 + (6189.03 * T))));
546
+ float_t co(std::cos(var.omega_s)), so(std::sin(var.omega_s));
547
+
548
+ // (ICD4) eccentric_anomaly_t E_s(g_os + g_1s * T, C.e_s);
549
+ eccentric_anomaly_t E_s(var.g_s, C.e_s);
550
+
551
+ float_t
552
+ srad(E_s.snu_k * co + E_s.cnu_k * so), // = sin(nu_s + omega_s)
553
+ crad(E_s.cnu_k * co - E_s.snu_k * so); // = cos(nu_s + omega_s)
554
+
555
+ res.s.xi = crad;
556
+ res.s.eta = srad * cepsilon;
557
+ res.s.zeta = srad * sepsilon;
558
+ res.s.r = C.a_s * (-C.e_s * std::cos(E_s.E_k) + 1);
559
+ }
560
+ return res;
561
+ }
562
+ /**
563
+ * @param days Sum of days from the epoch at 00 hours Moscow Time (MT) on 1st January 1975
564
+ * to the epoch at 00 hours MT of current date within which the instant t_e is.
565
+ * @param t_e reference time of ephemeris parameters (in Julian centuries of 36525 ephemeris days);
566
+ */
567
+ static lunar_solar_perturbations_t base1975(
568
+ const float_t &days, const float_t &t_e) {
569
+ #define dms2rad(deg, min, sec) \
570
+ (M_PI / 180 * ((float_t)sec / 3600 + (float_t)min / 60 + deg))
571
+ static const constatnt_t C = {
572
+ 3.84385243E8, // a_m // [m]
573
+ 1.49598E11, // a_s // [m]
574
+ 0.054900489, // e_m
575
+ 0.016719, // e_s
576
+ dms2rad(5, 8, 43.4), // i_m // 0.08980398 rad
577
+ };
578
+ static const float_t
579
+ epsilon(dms2rad(23, 26, 33)),
580
+ g_om(dms2rad(-63, 53, 43.41)),
581
+ g_1m(dms2rad(477198, 50, 56.79)),
582
+ Omega_om(dms2rad(259, 10, 59.79)),
583
+ Omega_1m(dms2rad(-1934, 8, 31.23)),
584
+ Gamma_o(dms2rad(-334, 19, 46.40)),
585
+ Gamma_1(dms2rad(4069, 2, 2.52)),
586
+ g_os(dms2rad(358, 28, 33.04)),
587
+ g_1s(dms2rad(0, 0, 129596579.10));
588
+
589
+ float_t T((27392.375 + days + t_e / 86400) / 36525);
590
+ // 27392.375 equals to interval days between 1900/1/1 0:0:0(GMT) to 1975/1/1 0:0:0(MT)
591
+ var_t var = {
592
+ epsilon, // epsilon
593
+ g_om + g_1m * T, // g_m
594
+ Omega_om + Omega_1m * T, // Omega_m
595
+ Gamma_o + Gamma_1 * T, // Gamma_m
596
+ g_os + g_1s * T, // g_s
597
+ dms2rad(281, 13, (15.00 + (6189.03 * T))), // omega_s
598
+ };
599
+ return setup(C, var);
600
+ #undef dms2rad
601
+ }
602
+ static lunar_solar_perturbations_t base2000(
603
+ const float_t &jd0, const float_t &t_b) {
604
+ static const constatnt_t C = {
605
+ 3.84385243E8, // a_m // [m]
606
+ 1.49598E11, // a_s // [m]
607
+ 0.054900489, // e_m
608
+ 0.016719, // e_s
609
+ 0.0898041080, // i_m
610
+ };
611
+
612
+ float_t T((jd0 + ((t_b - 10800) / 86400) - 2451545.0) / 36525);
613
+ // 2451545.0 equals to Julian date for 2000/1/1 0:0:0(UTC)
614
+ float_t T2(std::pow(T, 2));
615
+
616
+ var_t var = {
617
+ 0.4090926006 - 0.0002270711 * T, // epsilon
618
+ 2.3555557435 + 8328.6914257190 * T + 0.0001545547 * T2, // g_m (q_m)
619
+ 2.1824391966 - 33.7570459536 * T + 0.0000362262 * T2, // Omega_m
620
+ 1.4547885346 + 71.0176852437 * T - 0.0001801481 * T2, // Gamma_m
621
+ 6.2400601269 + 628.3019551714 * T - 0.0000026820 * T2, // g_s (q_s)
622
+ -7.6281824375 + 0.0300101976 * T + 0.0000079741 * T2, // omega_s
623
+ };
624
+ return setup(C, var);
625
+ }
626
+
627
+ };
628
+
629
+ struct differential_t {
630
+ float_t J_x_a, J_y_a, J_z_a;
631
+ differential_t() : J_x_a(0), J_y_a(0), J_z_a(0) {}
632
+ differential_t(const float_t &J_x, const float_t &J_y, const float_t &J_z)
633
+ : J_x_a(J_x), J_y_a(J_y), J_z_a(J_z) {}
634
+ /**
635
+ * @param sidereal_time_in_rad TODO calculate from t_b and UTC
636
+ */
637
+ differential_t(const Ephemeris &eph, const float_t &sidereal_time_in_rad){
638
+ float_t crad(std::cos(sidereal_time_in_rad)), srad(std::sin(sidereal_time_in_rad));
639
+ J_x_a = eph.xn_ddot * crad - eph.yn_ddot * srad;
640
+ J_y_a = eph.xn_ddot * srad + eph.yn_ddot * crad;
641
+ J_z_a = eph.zn_ddot;
642
+ }
643
+ constellation_t operator()(const float_t &t, const constellation_t &x) const {
644
+ // @see APPENDIX 3 EXAMPLES OF ALGORITHMS FOR CALCULATION OF COORDINATES AND VELOCITY
645
+ float_t r2(x.pos_abs2()), r(std::sqrt(r2));
646
+ static const float_t
647
+ a_e(6378136), mu(398600.44E9), C_20(1082625.75E-9); // values are obtained from ver.5.1; TODO move to PZ-90
648
+ float_t mu_bar(mu / r2),
649
+ x_a_bar(x.position[0] / r), y_a_bar(x.position[1] / r), z_a_bar(x.position[2] / r),
650
+ sf_z_a_bar(z_a_bar * z_a_bar * -5 + 1),
651
+ rho2(a_e * a_e / r2);
652
+ constellation_t res = {
653
+ {x.velocity[0], x.velocity[1], x.velocity[2]},
654
+ { // @see ver.5.1 Eq.(1)
655
+ ((-C_20 * rho2 * sf_z_a_bar * 3 / 2) - 1) * mu_bar * x_a_bar + J_x_a,
656
+ ((-C_20 * rho2 * sf_z_a_bar * 3 / 2) - 1) * mu_bar * y_a_bar + J_y_a,
657
+ ((-C_20 * rho2 * (sf_z_a_bar + 2) * 3 / 2) - 1) * mu_bar * z_a_bar + J_z_a,
658
+ }
659
+ };
660
+ #if 0
661
+ // If integration is performed in PZ-90, then these additional term is required,
662
+ // This is derived form simplified algorithm in CDMA ICD, (neither ver 5.1 nor ver.4 ICDs use it)
663
+ static const float_t omega_E(7.2921151467E-5), omega_E2(omega_E * omega_E);
664
+ res.velocity[0] += omega_E2 * x.position[0] + omega_E * 2 * x.velocity[1];
665
+ res.velocity[1] += omega_E2 * x.position[1] - omega_E * 2 * x.velocity[0];
666
+ #endif
667
+ return res;
668
+ }
669
+ };
670
+
671
+ struct differential2_t {
672
+ lunar_solar_perturbations_t J_src;
673
+ static void equation2(
674
+ const float_t &mu, const typename lunar_solar_perturbations_t::arg_t &arg,
675
+ const float_t (&position)[3],
676
+ float_t (&J)[3]){
677
+ // Eq.(2)
678
+ float_t
679
+ mu_bar(mu / std::pow(arg.r, 2)),
680
+ x_a_bar(position[0] / arg.r),
681
+ y_a_bar(position[1] / arg.r),
682
+ z_a_bar(position[2] / arg.r),
683
+ delta_x(arg.xi - x_a_bar),
684
+ delta_y(arg.eta - y_a_bar),
685
+ delta_z(arg.zeta - z_a_bar),
686
+ Delta3(std::pow(
687
+ std::pow(delta_x, 2) + std::pow(delta_y, 2) + std::pow(delta_z, 2), 1.5));
688
+ J[0] = mu_bar * (delta_x / Delta3 - arg.xi);
689
+ J[1] = mu_bar * (delta_y / Delta3 - arg.eta);
690
+ J[2] = mu_bar * (delta_z / Delta3 - arg.zeta);
691
+ }
692
+ void calculate_Jm(const float_t (&position)[3], float_t (&J)[3]) const {
693
+ equation2(4902.835E9, J_src.m, position, J); // for lunar (m); mu in [m/s]
694
+ }
695
+ void calculate_Js(const float_t (&position)[3], float_t (&J)[3]) const {
696
+ equation2(0.1325263E21, J_src.s, position, J); // for solar (s); mu in [m/s]
697
+ }
698
+ constellation_t operator()(const float_t &t, const constellation_t &x) const {
699
+ // @see APPENDIX 3 EXAMPLES OF ALGORITHMS FOR CALCULATION OF COORDINATES AND VELOCITY
700
+ float_t J_am[3], J_as[3];
701
+ calculate_Jm(x.position, J_am);
702
+ calculate_Js(x.position, J_as);
703
+ return differential_t(J_am[0] + J_as[0], J_am[1] + J_as[1], J_am[2] + J_as[2])(t, x);
704
+ }
705
+ };
706
+
707
+ u8_t F_T_index() const;
708
+
709
+ u8_t P1_index() const {
710
+ if(P1 > 45 * 60){
711
+ return 3;
712
+ }else if(P1 > 30 * 60){
713
+ return 2;
714
+ }else if(P1 > 0){
715
+ return 1;
716
+ }else{
717
+ return 0;
718
+ }
719
+ }
720
+
721
+ struct raw_t {
722
+ u8_t svid;
723
+
724
+ // String1
725
+ u8_t P1;
726
+ u16_t t_k;
727
+ s32_t xn_dot; ///< SF: 2^-20 [km/s]
728
+ s8_t xn_ddot; ///< SF: 2^-30 [km/s^2]
729
+ s32_t xn; ///< SF: 2^-11 [km]
730
+
731
+ // String2
732
+ u8_t B_n;
733
+ u8_t P2;
734
+ u8_t t_b; ///< SF: 15
735
+ s32_t yn_dot;
736
+ s8_t yn_ddot;
737
+ s32_t yn;
738
+
739
+ // String3
740
+ u8_t P3;
741
+ s16_t gamma_n; ///< SF: 2^-40
742
+ u8_t p;
743
+ u8_t l_n;
744
+ s32_t zn_dot;
745
+ s8_t zn_ddot;
746
+ s32_t zn;
747
+
748
+ // String4
749
+ s32_t tau_n; ///< SF: 2^-30
750
+ u8_t delta_tau_n; ///< SF: 2^-30
751
+ u8_t E_n;
752
+ u8_t P4;
753
+ u8_t F_T;
754
+ u16_t N_T; ///< [days]
755
+ u8_t n;
756
+ u8_t M;
757
+
758
+ #define fetch_item(num, name) name = BroadcastedMessage< \
759
+ InputT, (int)sizeof(InputT) * CHAR_BIT - PaddingBits_MSB - PaddingBits_LSB, PaddingBits_MSB> \
760
+ :: String ## num :: name (src)
761
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
762
+ void update_string1(const InputT *src){
763
+ fetch_item(1, P1);
764
+ fetch_item(1, t_k);
765
+ fetch_item(1, xn_dot);
766
+ fetch_item(1, xn_ddot);
767
+ fetch_item(1, xn);
768
+ }
769
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
770
+ void update_string2(const InputT *src){
771
+ // String2
772
+ fetch_item(2, B_n);
773
+ fetch_item(2, P2);
774
+ fetch_item(2, t_b);
775
+ fetch_item(2, yn_dot);
776
+ fetch_item(2, yn_ddot);
777
+ fetch_item(2, yn);
778
+ }
779
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
780
+ void update_string3(const InputT *src){
781
+ fetch_item(3, P3);
782
+ fetch_item(3, gamma_n);
783
+ fetch_item(3, p);
784
+ fetch_item(3, l_n);
785
+ fetch_item(3, zn_dot);
786
+ fetch_item(3, zn_ddot);
787
+ fetch_item(3, zn);
788
+ }
789
+ template <int PaddingBits_MSB, int PaddingBits_LSB, class InputT>
790
+ void update_string4(const InputT *src){
791
+ fetch_item(4, tau_n);
792
+ fetch_item(4, delta_tau_n);
793
+ fetch_item(4, E_n);
794
+ fetch_item(4, P4);
795
+ fetch_item(4, F_T);
796
+ fetch_item(4, N_T);
797
+ fetch_item(4, n);
798
+ fetch_item(4, M);
799
+ }
800
+ #undef fetch_item
801
+
802
+ enum {
803
+ SF_xn, SF_yn = SF_xn, SF_zn = SF_xn,
804
+ SF_xn_dot, SF_yn_dot = SF_xn_dot, SF_zn_dot = SF_xn_dot,
805
+ SF_xn_ddot, SF_yn_ddot = SF_xn_ddot, SF_zn_ddot = SF_xn_ddot,
806
+ SF_t_b,
807
+ SF_gamma_n,
808
+ SF_tau_n,
809
+ SF_delta_tau_n,
810
+ SF_NUM,
811
+ };
812
+ static const float_t sf[SF_NUM];
813
+
814
+ static const float_t F_T_table[15]; ///< @see Table 4.4
815
+
816
+ static const float_t F_T_value(const u8_t &F_T_){
817
+ if(F_T_ >= (sizeof(F_T_table) / sizeof(F_T_table[0]))){
818
+ return -1; // not used
819
+ }else{
820
+ return F_T_table[F_T_];
821
+ }
822
+ }
823
+
824
+ static const uint_t P1_value(const u8_t &P1_){
825
+ switch(P1_){
826
+ case 1: return 30 * 60;
827
+ case 2: return 45 * 60;
828
+ case 3: return 60 * 60;
829
+ case 0: default: return 0;
830
+ }
831
+ }
832
+
833
+ operator Ephemeris() const {
834
+ Ephemeris res;
835
+ #define CONVERT(TARGET) \
836
+ {res.TARGET = sf[SF_ ## TARGET] * TARGET;}
837
+ res.svid = svid;
838
+ res.t_k = (((t_k >> 7) & 0x1F) * 3600) // hour
839
+ + (((t_k >> 1) & 0x3F) * 60) // min
840
+ + ((t_k & 0x1) ? 30 : 0); // sec
841
+ CONVERT(t_b);
842
+ res.M = M;
843
+ CONVERT(gamma_n);
844
+ CONVERT(tau_n);
845
+ CONVERT(xn); CONVERT(xn_dot); CONVERT(xn_ddot);
846
+ CONVERT(yn); CONVERT(yn_dot); CONVERT(yn_ddot);
847
+ CONVERT(zn); CONVERT(zn_dot); CONVERT(zn_ddot);
848
+ res.B_n = B_n;
849
+ res.p = p;
850
+ res.N_T = N_T;
851
+
852
+ res.F_T = F_T_value(F_T);
853
+ res.n = n;
854
+ CONVERT(delta_tau_n);
855
+ res.E_n = E_n;
856
+ res.P1 = P1_value(P1);
857
+ res.P2 = (P2 > 0);
858
+ //res.P3 = (P3 > 0);
859
+ res.P4 = (P4 > 0);
860
+ res.l_n = (l_n > 0);
861
+ #undef CONVERT
862
+ return res;
863
+ }
864
+ raw_t &operator=(const Ephemeris &eph){
865
+ // TODO: m?
866
+ #define CONVERT(TARGET) \
867
+ {TARGET = (s32_t)((eph.TARGET + 0.5 * sf[SF_ ## TARGET]) / sf[SF_ ## TARGET]);}
868
+ svid = eph.svid;
869
+ { // t_k
870
+ std::div_t minutes(div(eph.t_k, 60));
871
+ std::div_t hrs(div(minutes.quot, 60));
872
+ t_k = (((hrs.quot << 6) + minutes.quot) << 1) + (minutes.rem > 0 ? 1 : 0);
873
+ }
874
+ CONVERT(t_b);
875
+ M = eph.M;
876
+ CONVERT(gamma_n);
877
+ CONVERT(tau_n);
878
+ CONVERT(xn); CONVERT(xn_dot); CONVERT(xn_ddot);
879
+ CONVERT(yn); CONVERT(yn_dot); CONVERT(yn_ddot);
880
+ CONVERT(zn); CONVERT(zn_dot); CONVERT(zn_ddot);
881
+ B_n = eph.B_n;
882
+ p = eph.p;
883
+ N_T = eph.N_T;
884
+ F_T = eph.F_T_index();
885
+ n = eph.n;
886
+ CONVERT(delta_tau_n);
887
+ E_n = eph.E_n;
888
+ if(eph.P1 > 45 * 60){
889
+ P1 = 3;
890
+ }else if(eph.P1 > 30 * 60){
891
+ P1 = 2;
892
+ }else if(eph.P1 > 0){
893
+ P1 = 1;
894
+ }else{
895
+ P1 = 0;
896
+ }
897
+ P2 = (eph.P2 ? 1 : 0);
898
+ //P3 = (eph.P3 ? 1 : 0);
899
+ P4 = (eph.P4 ? 1 : 0);
900
+ l_n = (eph.l_n ? 1 : 0);
901
+ #undef CONVERT
902
+ return *this;
903
+ }
904
+ };
905
+
906
+ bool is_equivalent(const Ephemeris &eph) const {
907
+ do{
908
+ #define CHECK(TARGET) if(TARGET != eph.TARGET){break;}
909
+ //CHECK(t_k); // t_k is just a reference time
910
+ CHECK(t_b);
911
+ CHECK(M);
912
+ CHECK(B_n);
913
+ CHECK(p);
914
+ CHECK(N_T);
915
+ CHECK(F_T_index());
916
+ CHECK(n);
917
+ CHECK(E_n);
918
+ CHECK(P1);
919
+ CHECK(P2);
920
+ //CHECK(P3);
921
+ CHECK(P4);
922
+ CHECK(l_n);
923
+ #undef CHECK
924
+ #define CHECK(TARGET) \
925
+ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
926
+ CHECK(gamma_n);
927
+ CHECK(tau_n);
928
+ CHECK(xn); CHECK(xn_dot); CHECK(xn_ddot);
929
+ CHECK(yn); CHECK(yn_dot); CHECK(yn_ddot);
930
+ CHECK(zn); CHECK(zn_dot); CHECK(zn_ddot);
931
+ CHECK(delta_tau_n);
932
+ #undef CHECK
933
+ return true;
934
+ }while(false);
935
+ return false;
936
+ }
937
+ };
938
+
939
+ /**
940
+ *
941
+ * Relationship of time systems
942
+ * 1) t_GL = t_UTC + 3hr (defined in ICD 3.3.3 GLONASS Time)
943
+ * 2) t_UTC + 3hr = t_rcv + tau_c + tau_n - gamma_n * (t_rcv - t_b) (defined in ICD 3.3.3 GLONASS Time, also in RINEX spec.)
944
+ * where tau_c: system wise, (-tau_c = GLUT in RINEX)
945
+ * ta_n, gamm_n: per satellite
946
+ * t_b along to UTC + 3hr (a.k.a, t_GL)
947
+ * 3) t_GPS - t_GL = delta_T + tau_GPS (defined in ICD 4.5 Non-immediate info.)
948
+ */
949
+ struct Ephemeris_with_Time : public Ephemeris, TimeProperties {
950
+ typedef typename Ephemeris::constellation_t constellation_t;
951
+ constellation_t xa_t_b;
952
+ float_t sidereal_t_b_rad;
953
+ typename Ephemeris::differential_t eq_of_motion;
954
+
955
+ void calculate_additional() {
956
+ sidereal_t_b_rad = TimeProperties::date_t::Greenwich_sidereal_time_deg(
957
+ TimeProperties::date.c_tm(),
958
+ (float_t)(this->t_b) / (60 * 60) - 3) / 180 * M_PI;
959
+ constellation_t x_t_b = {
960
+ {this->xn, this->yn, this->zn},
961
+ {this->xn_dot, this->yn_dot, this->zn_dot},
962
+ };
963
+ xa_t_b = x_t_b.abs_corrdinate(sidereal_t_b_rad); // PZ-90 => O-XYZ
964
+ eq_of_motion = typename Ephemeris::differential_t((*this), sidereal_t_b_rad);
965
+ }
966
+ Ephemeris_with_Time(const Ephemeris &eph, const TimeProperties &t_prop)
967
+ : Ephemeris(eph), TimeProperties(t_prop) {
968
+ calculate_additional();
969
+ }
970
+ Ephemeris_with_Time(const Ephemeris &eph, const std::tm &t_utc)
971
+ : Ephemeris(eph) {
972
+ this->tau_c = this->tau_GPS = 0;
973
+ std::tm t_mt(t_utc); // calculate Moscow time
974
+ t_mt.tm_hour += 3;
975
+ std::mktime(&t_mt); // renormalization
976
+ this->date = TimeProperties::date_t::from_c_tm(t_mt);
977
+ this->t_b = (t_mt.tm_hour * 60 + t_mt.tm_min) * 60 + t_mt.tm_sec;
978
+ calculate_additional();
979
+ }
980
+ std::tm c_tm_utc() const {
981
+ std::tm t(TimeProperties::date.c_tm()); // set date on Moscow time
982
+ (t.tm_sec = (int)(this->t_b)) -= 3 * 60 * 60; // add second on UTC
983
+ std::mktime(&t); // renormalization
984
+ return t;
985
+ }
986
+ struct raw_t : public Ephemeris::raw_t, TimeProperties::raw_t {
987
+ operator Ephemeris_with_Time() const {
988
+ return Ephemeris_with_Time((Ephemeris)(*this), (TimeProperties)(*this));
989
+ }
990
+ raw_t &operator=(const Ephemeris_with_Time &eph){
991
+ (typename Ephemeris::raw_t &)(*this) = eph;
992
+ (typename TimeProperties::raw_t &)(*this) = eph;
993
+ return *this;
994
+ }
995
+ };
996
+ bool is_equivalent(const Ephemeris_with_Time &eph) const {
997
+ return Ephemeris::is_equivalent(eph) && TimeProperties::is_equivalent(eph);
998
+ }
999
+ float_t calculate_clock_error(float_t delta_t, const float_t &pseudo_range = 0) const {
1000
+ delta_t -= pseudo_range / light_speed;
1001
+ return -TimeProperties::tau_c - Ephemeris::tau_n + Ephemeris::gamma_n * delta_t;
1002
+ }
1003
+ /**
1004
+ * @param t_arrival_onboard signal arrival time in onboard time scale,
1005
+ * which is along to glonass time scale (UTC + 3 hr) but is shifted in gradually changing length.
1006
+ */
1007
+ float_t clock_error(
1008
+ const float_t &t_arrival_onboard, const float_t &pseudo_range = 0) const {
1009
+ return calculate_clock_error(t_arrival_onboard - Ephemeris::t_b, pseudo_range);
1010
+ }
1011
+ /**
1012
+ * Calculate constellation(t) based on constellation(t_0).
1013
+ * t_0 is a time around t_b, and is used to calculate
1014
+ * an intermediate result specified with the 3rd argument.
1015
+ * This method is useful to calculate constellation effectively
1016
+ * by using a cache.
1017
+ * @param delta_t time interval from t_0 to t
1018
+ * @param pseudo_range measured pusedo_range to correct delta_t
1019
+ * @param xa_t_0 constellation(t_0)
1020
+ * @param t_0_from_t_b time interval from t_b to t_0
1021
+ */
1022
+ constellation_t calculate_constellation(
1023
+ float_t delta_t, const float_t &pseudo_range,
1024
+ const constellation_t &xa_t_0, const float_t &t_0_from_t_b) const {
1025
+
1026
+ constellation_t res(xa_t_0);
1027
+ { // time integration from t_b to t_transmit
1028
+ float_t delta_t_to_transmit(delta_t - pseudo_range / light_speed);
1029
+ float_t t_step_max(delta_t_to_transmit >= 0 ? 60 : -60);
1030
+ int i(std::floor(delta_t_to_transmit / t_step_max));
1031
+ float_t t_step_remain(delta_t_to_transmit - t_step_max * i);
1032
+ float_t delta_t_itg(0); // accumulative time of integration
1033
+ for(; i > 0; --i, delta_t_itg += t_step_max){
1034
+ res = nextByRK4(eq_of_motion, delta_t_itg, res, t_step_max);
1035
+ }
1036
+ res = nextByRK4(eq_of_motion, delta_t_itg, res, t_step_remain);
1037
+ }
1038
+
1039
+ static const float_t omega_E(0.7292115E-4); // Earth's rotation rate, TODO move to PZ-90.02
1040
+ return res.rel_corrdinate(
1041
+ sidereal_t_b_rad + (omega_E * (delta_t + t_0_from_t_b))); // transform from abs to PZ-90.02
1042
+ }
1043
+ /**
1044
+ * Calculate constellation(t) based on constellation(t_b).
1045
+ * @param delta_t time interval from t_0 to t
1046
+ * @param pseudo_range measured pusedo_range to correct delta_t, default is 0.
1047
+ */
1048
+ constellation_t calculate_constellation(
1049
+ float_t delta_t, const float_t &pseudo_range = 0) const {
1050
+ return calculate_constellation(delta_t, pseudo_range, xa_t_b, float_t(0));
1051
+ }
1052
+ /**
1053
+ * @param t_arrival_glonass signal arrival time in glonass time scale,
1054
+ * which is the corrected onboard time by removing clock error.
1055
+ */
1056
+ constellation_t constellation(
1057
+ const float_t &t_arrival_glonass, const float_t &pseudo_range = 0) const {
1058
+ return calculate_constellation(
1059
+ t_arrival_glonass - Ephemeris::t_b, pseudo_range); // measure in UTC + 3hr scale
1060
+ }
1061
+ };
1062
+
1063
+ struct Ephemeris_with_GPS_Time : public Ephemeris_with_Time {
1064
+ GPS_Time<float_t> t_b_gps;
1065
+ Ephemeris_with_GPS_Time()
1066
+ : Ephemeris_with_Time(Ephemeris(), GPS_Time<float_t>(0, 0).c_tm()),
1067
+ t_b_gps(0, 0) {}
1068
+ /**
1069
+ *
1070
+ * @param deltaT integer part of difference of GPS and GLONASS time scales.
1071
+ * This is often identical to the leap seconds because GLONASS time base is UTC
1072
+ */
1073
+ Ephemeris_with_GPS_Time(const Ephemeris_with_Time &eph, const int_t &deltaT = 0)
1074
+ : Ephemeris_with_Time(eph),
1075
+ t_b_gps(GPS_Time<float_t>(eph.c_tm_utc()) // in UTC scale
1076
+ + Ephemeris_with_Time::tau_GPS // in (GPS - delta_T) scale (delta_T is integer), -tau_GPS = GLGP in RINEX
1077
+ + deltaT) {
1078
+ }
1079
+ GPS_Time<float_t> base_time() const {
1080
+ return t_b_gps;
1081
+ }
1082
+ bool is_valid(const GPS_Time<float_t> &t) const {
1083
+ return std::abs(t_b_gps.interval(t)) <= 60 * 60; // 1 hour
1084
+ }
1085
+ using Ephemeris_with_Time::clock_error;
1086
+ float_t clock_error(
1087
+ const GPS_Time<float_t> &t_arrival_onboard, const float_t &pseudo_range = 0) const {
1088
+ return Ephemeris_with_Time::calculate_clock_error(t_arrival_onboard - t_b_gps, pseudo_range);
1089
+ }
1090
+ using Ephemeris_with_Time::constellation;
1091
+ typename Ephemeris_with_Time::constellation_t constellation(
1092
+ const GPS_Time<float_t> &t_arrival_gps, const float_t &pseudo_range = 0) const {
1093
+ return this->calculate_constellation(t_arrival_gps - t_b_gps, pseudo_range);
1094
+ }
1095
+ };
1096
+ };
1097
+
1098
+ struct Satellite : public SatelliteProperties {
1099
+ public:
1100
+ typedef typename SatelliteProperties::Ephemeris_with_GPS_Time eph_t;
1101
+ typedef typename GPS_SpaceNode<float_t>::template PropertyHistory<eph_t> eph_list_t;
1102
+ protected:
1103
+ eph_list_t eph_history;
1104
+ public:
1105
+ Satellite() : eph_history() {
1106
+ // TODO setup first ephemeris as invalid one
1107
+ // eph_t &eph_current(const_cast<eph_t &>(eph_history.current()));
1108
+ }
1109
+
1110
+ template <class Functor>
1111
+ void each_ephemeris(
1112
+ Functor &functor,
1113
+ const typename eph_list_t::each_mode_t &mode = eph_list_t::EACH_ALL) const {
1114
+ eph_history.each(functor, mode);
1115
+ }
1116
+
1117
+ void register_ephemeris(const eph_t &eph, const int &priority_delta = 1){
1118
+ eph_history.add(eph, priority_delta);
1119
+ }
1120
+
1121
+ void merge(const Satellite &another, const bool &keep_original = true){
1122
+ eph_history.merge(another.eph_history, keep_original);
1123
+ }
1124
+
1125
+ const eph_t &ephemeris() const {
1126
+ return eph_history.current();
1127
+ }
1128
+
1129
+ /**
1130
+ * Select appropriate ephemeris within registered ones.
1131
+ *
1132
+ * @param target_time time at measurement
1133
+ * @return if true, appropriate ephemeris is selected, otherwise, not selected.
1134
+ */
1135
+ bool select_ephemeris(const GPS_Time<float_t> &target_time){
1136
+ return eph_history.select(target_time, &eph_t::is_valid);
1137
+ }
1138
+
1139
+ float_t clock_error(const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const{
1140
+ return ephemeris().clock_error(t, pseudo_range);
1141
+ }
1142
+
1143
+ typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t constellation(
1144
+ const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const {
1145
+ return (typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t)(
1146
+ ephemeris().constellation(t, pseudo_range));
1147
+ }
1148
+
1149
+ typename GPS_SpaceNode<float_t>::xyz_t position(const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const {
1150
+ return constellation(t, pseudo_range).position;
1151
+ }
1152
+
1153
+ typename GPS_SpaceNode<float_t>::xyz_t velocity(const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const {
1154
+ return constellation(t, pseudo_range).velocity;
1155
+ }
1156
+ };
1157
+ public:
1158
+ typedef std::map<int, Satellite> satellites_t;
1159
+ protected:
1160
+ satellites_t _satellites;
1161
+ public:
1162
+ GLONASS_SpaceNode()
1163
+ : _satellites() {}
1164
+ ~GLONASS_SpaceNode(){
1165
+ _satellites.clear();
1166
+ }
1167
+ const satellites_t &satellites() const {
1168
+ return _satellites;
1169
+ }
1170
+ Satellite &satellite(const int &prn) {
1171
+ return _satellites[prn];
1172
+ }
1173
+ bool has_satellite(const int &prn) const {
1174
+ return _satellites.find(prn) != _satellites.end();
1175
+ }
1176
+ void update_all_ephemeris(const GPS_Time<float_t> &target_time) {
1177
+ for(typename satellites_t::iterator it(_satellites.begin()), it_end(_satellites.end());
1178
+ it != it_end; ++it){
1179
+ it->second.select_ephemeris(target_time);
1180
+ }
1181
+ }
1182
+ typename Satellite::eph_t latest_ephemeris() const {
1183
+ struct {
1184
+ typename Satellite::eph_t res;
1185
+ void operator()(const typename Satellite::eph_t &eph){
1186
+ if(res.t_b_gps < eph.t_b_gps){res = eph;}
1187
+ }
1188
+ } functor;
1189
+ for(typename satellites_t::const_iterator
1190
+ it(_satellites.begin()), it_end(_satellites.end());
1191
+ it != it_end; ++it){
1192
+ it->second.each_ephemeris(functor, Satellite::eph_list_t::EACH_NO_REDUNDANT);
1193
+ }
1194
+ return functor.res;
1195
+ }
1196
+ };
1197
+
1198
+ template <class FloatT>
1199
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::light_speed
1200
+ = 299792458; // [m/s]
1201
+
1202
+ template <class FloatT>
1203
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::L1_frequency_base
1204
+ = 1602E6; // [Hz]
1205
+
1206
+ template <class FloatT>
1207
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::L1_frequency_gap
1208
+ = 562.5E3; // [Hz]
1209
+
1210
+ template <class FloatT>
1211
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::L2_frequency_base
1212
+ = 1246E6; // [Hz]
1213
+
1214
+ template <class FloatT>
1215
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::L2_frequency_gap
1216
+ = 437.5E3; // [Hz]
1217
+
1218
+ template <class FloatT>
1219
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::constellation_t::omega_E
1220
+ = 0.7292115E-4; // [s^-1]
1221
+
1222
+ template <class FloatT>
1223
+ const typename GLONASS_SpaceNode<FloatT>::int_t GLONASS_SpaceNode<FloatT>::TimeProperties::date_t::days_m[] = {
1224
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
1225
+ };
1226
+
1227
+ #define POWER_2(n) \
1228
+ (((n) >= 0) \
1229
+ ? (float_t)(1 << (n >= 0 ? n : 0)) \
1230
+ : (((float_t)1) / (1 << (-(n) >= 30 ? 30 : -(n > 0 ? 0 : n))) \
1231
+ / (1 << (-(n) >= 30 ? (-(n) - 30) : 0))))
1232
+
1233
+ template <class FloatT>
1234
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::TimeProperties::raw_t::sf[] = {
1235
+ POWER_2(-31), // tau_c [s]
1236
+ POWER_2(-30) /* * 60 * 60 * 24*/, // tau_GPS [s], unit is described as [day] in ICD, however, it may be wrong.
1237
+ };
1238
+
1239
+ template <class FloatT>
1240
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::raw_t::sf[] = {
1241
+ POWER_2(-11) * 1E3, // x
1242
+ POWER_2(-20) * 1E3, // x_dot
1243
+ POWER_2(-30) * 1E3, // x_ddot
1244
+ 15 * 60, // t_b
1245
+ POWER_2(-40), // gamma_n
1246
+ POWER_2(-30), // tau_n
1247
+ POWER_2(-30), // delta_tau_n
1248
+ };
1249
+
1250
+ #undef POWER_2
1251
+
1252
+ template <class FloatT>
1253
+ const typename GLONASS_SpaceNode<FloatT>::float_t GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::raw_t::F_T_table[] = {
1254
+ 1, 2, 2.5, 4, 5, 7, 10, 12, 14, 16, 32, 64, 128, 256, 512,
1255
+ };
1256
+
1257
+ template <class FloatT>
1258
+ typename GLONASS_SpaceNode<FloatT>::u8_t GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::F_T_index() const {
1259
+ if(F_T <= 0){ // invalid value
1260
+ return sizeof(raw_t::F_T_table) / sizeof(raw_t::F_T_table[0]);
1261
+ }
1262
+ u8_t res(0);
1263
+ while(res < (sizeof(raw_t::F_T_table) / sizeof(raw_t::F_T_table[0]))){
1264
+ if(F_T <= raw_t::F_T_table[res]){break;}
1265
+ ++res;
1266
+ }
1267
+ return res;
1268
+ }
1269
+
1270
+ #endif /* __GLONASS_H__ */