gps_pvt 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/exe/gps_pvt +3 -2
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +116 -1
- data/ext/ninja-scan-light/tool/navigation/GLONASS.h +4 -11
- data/ext/ninja-scan-light/tool/navigation/GLONASS_Solver.h +25 -13
- data/ext/ninja-scan-light/tool/navigation/GPS.h +21 -18
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +42 -25
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +38 -17
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +29 -1
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +2 -6
- data/ext/ninja-scan-light/tool/navigation/SBAS.h +31 -3
- data/ext/ninja-scan-light/tool/navigation/SBAS_Solver.h +26 -14
- data/ext/ninja-scan-light/tool/navigation/SP3.h +3 -2
- data/ext/ninja-scan-light/tool/param/bit_array.h +2 -2
- data/ext/ninja-scan-light/tool/swig/GPS.i +29 -1
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +134 -13
- data/gps_pvt.gemspec +1 -0
- data/lib/gps_pvt/receiver.rb +82 -55
- data/lib/gps_pvt/util.rb +33 -1
- data/lib/gps_pvt/version.rb +1 -1
- metadata +16 -2
@@ -60,6 +60,12 @@ struct GPS_PVT_RAIM_LSR : public PVT_BaseT {
|
|
60
60
|
} FDE_min, FDE_2nd; ///< Fault exclusion
|
61
61
|
};
|
62
62
|
|
63
|
+
template <class FloatT>
|
64
|
+
struct GPS_Solver_RAIM_LSR_Options {
|
65
|
+
bool skip_exclusion;
|
66
|
+
GPS_Solver_RAIM_LSR_Options() : skip_exclusion(false) {}
|
67
|
+
};
|
68
|
+
|
63
69
|
/*
|
64
70
|
* Comment on implementation of protection level (PL) calculation
|
65
71
|
*
|
@@ -96,6 +102,27 @@ struct GPS_Solver_RAIM_LSR : public SolverBaseT {
|
|
96
102
|
inheritate_type(measurement2_t);
|
97
103
|
#undef inheritate_type
|
98
104
|
|
105
|
+
typedef typename GPS_Solver_Base<float_t>::options_t::template merge_t<
|
106
|
+
GPS_Solver_RAIM_LSR_Options<float_t>, super_t> options_t;
|
107
|
+
|
108
|
+
protected:
|
109
|
+
GPS_Solver_RAIM_LSR_Options<float_t> _options;
|
110
|
+
|
111
|
+
public:
|
112
|
+
options_t available_options() const {
|
113
|
+
return options_t(super_t::available_options(), _options);
|
114
|
+
}
|
115
|
+
|
116
|
+
options_t available_options(const options_t &opt_wish) const {
|
117
|
+
GPS_Solver_RAIM_LSR_Options<float_t> opt(opt_wish);
|
118
|
+
return options_t(super_t::available_options(opt_wish), opt);
|
119
|
+
}
|
120
|
+
|
121
|
+
options_t update_options(const options_t &opt_wish){
|
122
|
+
_options = opt_wish;
|
123
|
+
return options_t(super_t::update_options(opt_wish), _options);
|
124
|
+
}
|
125
|
+
|
99
126
|
typedef GPS_PVT_RAIM_LSR<float_t, typename super_t::user_pvt_t> user_pvt_t;
|
100
127
|
|
101
128
|
typename super_t::template solver_interface_t<self_t> solve() const {
|
@@ -155,7 +182,8 @@ protected:
|
|
155
182
|
user_position_init, receiver_error_init,
|
156
183
|
opt);
|
157
184
|
|
158
|
-
if(
|
185
|
+
if(_options.skip_exclusion
|
186
|
+
|| !pvt.position_solved()
|
159
187
|
|| (!pvt.FD.valid)
|
160
188
|
|| (pvt.used_satellites < 6)){return;}
|
161
189
|
|
@@ -353,7 +353,6 @@ struct RINEX_NAV {
|
|
353
353
|
int t_oc_year4, t_oc_year2, t_oc_mon12;
|
354
354
|
FloatT t_oc_sec;
|
355
355
|
FloatT t_oe_WN;
|
356
|
-
FloatT ura_meter;
|
357
356
|
FloatT t_ot; ///< Transmitting time [s]
|
358
357
|
FloatT fit_interval_hr;
|
359
358
|
FloatT dummy;
|
@@ -367,7 +366,6 @@ struct RINEX_NAV {
|
|
367
366
|
t_oc_mon12(t_oc_tm.tm_mon + 1),
|
368
367
|
t_oc_sec(std::fmod(eph.t_oc, 60)),
|
369
368
|
t_oe_WN(eph.WN),
|
370
|
-
ura_meter(ephemeris_t::URA_meter(eph.URA)),
|
371
369
|
t_ot(0), // TODO
|
372
370
|
fit_interval_hr(eph.fit_interval / (60 * 60)),
|
373
371
|
dummy(0) {
|
@@ -378,8 +376,6 @@ struct RINEX_NAV {
|
|
378
376
|
eph.WN = t_oc.week;
|
379
377
|
eph.t_oc = t_oc.seconds;
|
380
378
|
|
381
|
-
eph.URA = ephemeris_t::URA_index(ura_meter); // meter to index
|
382
|
-
|
383
379
|
/* @see ftp://igs.org/pub/data/format/rinex210.txt
|
384
380
|
* 6.7 Satellite Health
|
385
381
|
* RINEX Value: 0 Health OK
|
@@ -1434,14 +1430,14 @@ const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>
|
|
1434
1430
|
|
1435
1431
|
template <class FloatT>
|
1436
1432
|
const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v2[] = {
|
1437
|
-
GEN_E ( 3, 19, 12, message_t,
|
1433
|
+
GEN_E ( 3, 19, 12, message_t, eph.URA),
|
1438
1434
|
GEN_E2(22, 19, 12, message_t, eph.SV_health, unsigned int),
|
1439
1435
|
GEN_E (41, 19, 12, message_t, eph.t_GD),
|
1440
1436
|
GEN_E2(60, 19, 12, message_t, eph.iodc, int),
|
1441
1437
|
};
|
1442
1438
|
template <class FloatT>
|
1443
1439
|
const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v3[] = {
|
1444
|
-
GEN_E ( 4, 19, 12, message_t,
|
1440
|
+
GEN_E ( 4, 19, 12, message_t, eph.URA),
|
1445
1441
|
GEN_E2(23, 19, 12, message_t, eph.SV_health, unsigned int),
|
1446
1442
|
GEN_E (42, 19, 12, message_t, eph.t_GD),
|
1447
1443
|
GEN_E2(61, 19, 12, message_t, eph.iodc, int),
|
@@ -1711,7 +1711,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
|
|
1711
1711
|
uint_t WN; ///< Week number
|
1712
1712
|
|
1713
1713
|
float_t t_0; ///< Time of applicability (s) <= time of a week
|
1714
|
-
|
1714
|
+
float_t URA; ///< User range accuracy (m)
|
1715
1715
|
float_t x, y, z; ///< ECEF position (m)
|
1716
1716
|
float_t dx, dy, dz; ///< ECEF velocity (m/s)
|
1717
1717
|
float_t ddx, ddy, ddz; ///< ECEF acceleration (m/s^2)
|
@@ -1804,6 +1804,15 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
|
|
1804
1804
|
return res;
|
1805
1805
|
}
|
1806
1806
|
|
1807
|
+
static const float_t URA_table[15];
|
1808
|
+
|
1809
|
+
inline static float_t URA_meter(const int_t &index){
|
1810
|
+
return gps_space_node_t::SatelliteProperties::Ephemeris::URA_meter(index, URA_table);
|
1811
|
+
}
|
1812
|
+
inline static int_t URA_index(const float_t &meter){
|
1813
|
+
return gps_space_node_t::SatelliteProperties::Ephemeris::URA_index(meter, URA_table);
|
1814
|
+
}
|
1815
|
+
|
1807
1816
|
struct raw_t {
|
1808
1817
|
u8_t svid; ///< Satellite number
|
1809
1818
|
|
@@ -1850,7 +1859,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
|
|
1850
1859
|
converted.svid = svid;
|
1851
1860
|
converted.WN = 0; // Week number (must be configured later) @see adjust_time
|
1852
1861
|
|
1853
|
-
converted.URA = URA;
|
1862
|
+
converted.URA = URA_meter(URA);
|
1854
1863
|
CONVERT(t_0); // Time of a day => time of a week (must be configured later) @see adjust_time
|
1855
1864
|
CONVERT2(x, xy); CONVERT2(y, xy); CONVERT(z);
|
1856
1865
|
CONVERT2(dx, dxy); CONVERT2(dy, dxy); CONVERT(dz);
|
@@ -1869,7 +1878,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
|
|
1869
1878
|
#define CONVERT(TARGET) CONVERT2(TARGET, TARGET)
|
1870
1879
|
svid = eph.svid;
|
1871
1880
|
|
1872
|
-
URA = eph.URA;
|
1881
|
+
URA = URA_index(eph.URA);
|
1873
1882
|
CONVERT3(t_0, std::fmod(t_0, gps_time_t::seconds_day), t_0);
|
1874
1883
|
CONVERT2(x, xy); CONVERT2(y, xy); CONVERT(z);
|
1875
1884
|
CONVERT2(dx, dxy); CONVERT2(dy, dxy); CONVERT(dz);
|
@@ -2350,4 +2359,23 @@ const typename SBAS_SpaceNode<FloatT>::float_t SBAS_SpaceNode<FloatT>::UTC_Param
|
|
2350
2359
|
|
2351
2360
|
#undef POWER_2
|
2352
2361
|
|
2362
|
+
template <class FloatT>
|
2363
|
+
const typename SBAS_SpaceNode<FloatT>::float_t SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::URA_table[] = {
|
2364
|
+
2,
|
2365
|
+
2.8,
|
2366
|
+
4,
|
2367
|
+
5.7,
|
2368
|
+
8,
|
2369
|
+
11.3,
|
2370
|
+
16,
|
2371
|
+
32,
|
2372
|
+
64,
|
2373
|
+
128,
|
2374
|
+
256,
|
2375
|
+
512,
|
2376
|
+
1024,
|
2377
|
+
2048,
|
2378
|
+
4096,
|
2379
|
+
};
|
2380
|
+
|
2353
2381
|
#endif /* __SBAS_H__ */
|
@@ -126,11 +126,15 @@ class SBAS_SinglePositioning : public SolverBaseT {
|
|
126
126
|
static float_t clock_error_dot(const void *ptr, const gps_time_t &t_tx) {
|
127
127
|
return sat(ptr).ephemeris().clock_error_dot(t_tx);
|
128
128
|
}
|
129
|
+
static float_t range_sigma(const void *ptr, const gps_time_t &t_tx) {
|
130
|
+
return sat(ptr).ephemeris().URA;
|
131
|
+
}
|
129
132
|
};
|
130
133
|
satellite_t res = {
|
131
|
-
&(it_sat->second), &(it_sat->second),
|
134
|
+
&(it_sat->second), &(it_sat->second), // position, time
|
132
135
|
impl_t::position, impl_t::velocity,
|
133
|
-
impl_t::clock_error, impl_t::clock_error_dot
|
136
|
+
impl_t::clock_error, impl_t::clock_error_dot,
|
137
|
+
&(it_sat->second), impl_t::range_sigma, NULL}; // error model
|
134
138
|
return res;
|
135
139
|
}
|
136
140
|
satellites_t(const space_node_t &sn)
|
@@ -245,7 +249,7 @@ class SBAS_SinglePositioning : public SolverBaseT {
|
|
245
249
|
float_t range;
|
246
250
|
range_error_t range_error;
|
247
251
|
if(!this->range(measurement, range, &range_error)){
|
248
|
-
return res; // If no range entry, return with
|
252
|
+
return res; // If no range entry, return with sigma = 0
|
249
253
|
}
|
250
254
|
|
251
255
|
satellite_t sat(select_satellite(prn, time_arrival));
|
@@ -297,21 +301,29 @@ class SBAS_SinglePositioning : public SolverBaseT {
|
|
297
301
|
|
298
302
|
// TODO Fast corrections (2.1.1.4.12)
|
299
303
|
|
300
|
-
//
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
304
|
+
// Setup weight
|
305
|
+
res.range_sigma = 1E+4; // sufficiently big value, 1E4 [m]
|
306
|
+
do{
|
307
|
+
// If residual is too big, gently exclude it.
|
308
|
+
if(std::abs(res.range_residual) > _options.residual_mask){break;}
|
305
309
|
|
306
310
|
float_t elv(relative_pos.elevation());
|
307
311
|
if(elv < _options.elevation_mask){
|
308
|
-
res.
|
309
|
-
|
310
|
-
// elevation weight based on "GPS���p�v���O���~���O" @see GPS_Solver.h
|
311
|
-
res.weight = std::pow(sin(elv)/0.8, 2);
|
312
|
-
if(res.weight < 1E-3){res.weight = 1E-3;}
|
312
|
+
res.range_sigma = 0; // exclude it when elevation is less than threshold
|
313
|
+
break;
|
313
314
|
}
|
314
|
-
|
315
|
+
|
316
|
+
res.range_sigma = sat.range_sigma(t_tx);
|
317
|
+
|
318
|
+
/* elevation weight based on "GPS���p�v���O���~���O"
|
319
|
+
* elevation[deg] : 90 53 45 30 15 10 5
|
320
|
+
* sf_sigma(k) : 0.80 1.00 1.13 1.60 3.09 4.61 9.18
|
321
|
+
* weight(k^-2) : 1.56 1.00 0.78 0.39 0.10 0.05 0.01
|
322
|
+
*/
|
323
|
+
static const float_t max_sf(10);
|
324
|
+
static const float_t elv_limit(std::asin(0.8/max_sf)); // limit
|
325
|
+
res.range_sigma *= (elv > elv_limit) ? (0.8 / sin(elv)) : max_sf;
|
326
|
+
}while(false);
|
315
327
|
|
316
328
|
res.range_corrected = range;
|
317
329
|
|
@@ -185,9 +185,10 @@ struct SP3_Product {
|
|
185
185
|
}
|
186
186
|
};
|
187
187
|
typename GPS_Solver_Base<FloatT>::satellite_t res = {
|
188
|
-
this, this,
|
188
|
+
this, this, // position, time
|
189
189
|
impl_t::position, impl_t::velocity,
|
190
|
-
impl_t::clock_error, impl_t::clock_error_dot
|
190
|
+
impl_t::clock_error, impl_t::clock_error_dot,
|
191
|
+
NULL, NULL, NULL, // TODO error model
|
191
192
|
};
|
192
193
|
return res;
|
193
194
|
}
|
@@ -125,9 +125,9 @@ struct BitArray {
|
|
125
125
|
return (unsigned int)res;
|
126
126
|
}
|
127
127
|
}
|
128
|
-
std::vector<int> indices_one() const {
|
128
|
+
std::vector<int> indices_one(const int &offset = 0) const {
|
129
129
|
std::vector<int> res;
|
130
|
-
int idx(
|
130
|
+
int idx(offset);
|
131
131
|
static const const_div_t<bits_per_addr> qr(MAX_SIZE);
|
132
132
|
int rem(qr.rem), i(0);
|
133
133
|
for(; i < qr.quot; ++i, idx += bits_per_addr){
|
@@ -1124,10 +1124,13 @@ struct GPS_RangeCorrector
|
|
1124
1124
|
static const int prop_items(sizeof(res.values) / sizeof(res.values[0]));
|
1125
1125
|
VALUE hook(rb_hash_lookup(hooks, key));
|
1126
1126
|
if(NIL_P(hook)){break;}
|
1127
|
+
FloatT weight((res.prop.range_sigma > 0)
|
1128
|
+
? (1. / std::pow(res.prop.range_sigma, 2)) // weight=1/(sigma^2)
|
1129
|
+
: res.prop.range_sigma);
|
1127
1130
|
VALUE values[] = {
|
1128
1131
|
SWIG_From(int)(prn), // prn
|
1129
1132
|
rb_ary_new_from_args(prop_items, // relative_property
|
1130
|
-
swig::from(
|
1133
|
+
swig::from(weight),
|
1131
1134
|
swig::from(res.prop.range_corrected),
|
1132
1135
|
swig::from(res.prop.range_residual),
|
1133
1136
|
swig::from(res.prop.rate_relative_neg),
|
@@ -1159,6 +1162,9 @@ struct GPS_RangeCorrector
|
|
1159
1162
|
.append(" @ [").append(std::to_string(i)).append("]"));
|
1160
1163
|
}
|
1161
1164
|
}
|
1165
|
+
if(res.values[0] > 0){
|
1166
|
+
res.values[0] = std::pow(1. / res.values[0], 0.5); // sigma=(1/weight)^0.5
|
1167
|
+
}
|
1162
1168
|
}while(false);
|
1163
1169
|
#endif
|
1164
1170
|
return res.prop;
|
@@ -1356,6 +1362,28 @@ struct GPS_RangeCorrector
|
|
1356
1362
|
return self->update_correction(true, hash);
|
1357
1363
|
}
|
1358
1364
|
#endif
|
1365
|
+
#ifdef SWIGRUBY
|
1366
|
+
%typemap(out) typename super_t::options_t {
|
1367
|
+
VALUE res(rb_hash_new());
|
1368
|
+
rb_hash_aset(res, ID2SYM(rb_intern("skip_exclusion")), SWIG_From(bool)($1.skip_exclusion));
|
1369
|
+
%set_output(res);
|
1370
|
+
}
|
1371
|
+
#endif
|
1372
|
+
%rename("options") get_options;
|
1373
|
+
typename super_t::options_t get_options() const {
|
1374
|
+
return self->available_options();
|
1375
|
+
}
|
1376
|
+
%rename("options=") set_options;
|
1377
|
+
typename super_t::options_t set_options(SWIG_Object obj) {
|
1378
|
+
GPS_Solver<FloatT>::super_t::options_t opt(self->available_options());
|
1379
|
+
#ifdef SWIGRUBY
|
1380
|
+
if(!RB_TYPE_P(obj, T_HASH)){SWIG_exception(SWIG_TypeError, "Hash is expected");}
|
1381
|
+
SWIG_AsVal(bool)(
|
1382
|
+
rb_hash_lookup(obj, ID2SYM(rb_intern("skip_exclusion"))),
|
1383
|
+
&opt.skip_exclusion);
|
1384
|
+
#endif
|
1385
|
+
return self->update_options(opt);
|
1386
|
+
}
|
1359
1387
|
}
|
1360
1388
|
%inline {
|
1361
1389
|
template <class FloatT>
|
@@ -658,6 +658,73 @@ __ANTEX_TEXT__
|
|
658
658
|
# https://files.igs.org/pub/station/general/igs14.atx
|
659
659
|
f.path
|
660
660
|
},
|
661
|
+
:rinex_clk => Tempfile::open{|f|
|
662
|
+
f.write(<<-__RINEX_CLK_TEXT__)
|
663
|
+
3.00 C RINEX VERSION / TYPE
|
664
|
+
CCLOCK IGSACC @ NOAA NGS PGM / RUN BY / DATE
|
665
|
+
GPS week: 1849 Day: 2 MJD: 57189 COMMENT
|
666
|
+
THE COMBINED CLOCKS ARE A WEIGHTED AVERAGE OF: COMMENT
|
667
|
+
cod emr esa gfz grg jpl COMMENT
|
668
|
+
THE FOLLOWING REFERENCE CLOCKS WERE USED BY ACs: COMMENT
|
669
|
+
WSRT AMC2 BRUX HRAO COMMENT
|
670
|
+
THE COMBINED CLOCKS ARE ALIGNED TO GPS TIME COMMENT
|
671
|
+
USING THE SATELLITE BROADCAST EPHEMERIDES COMMENT
|
672
|
+
All clocks have been re-aligned to the IGS time scale: IGST COMMENT
|
673
|
+
16 LEAP SECONDS
|
674
|
+
1 AS # / TYPES OF DATA
|
675
|
+
IGS IGSACC @ NOAA NGS ANALYSIS CENTER
|
676
|
+
6 # OF SOLN SATS
|
677
|
+
G12 G18 G24 G25 G29 G31 PRN LIST
|
678
|
+
G igs08_1848.atx SYS / PCVS APPLIED
|
679
|
+
END OF HEADER
|
680
|
+
AS G12 2015 06 15 23 45 0.000000 2 3.017937472687e-04 1.069824072610e-11
|
681
|
+
AS G18 2015 06 15 23 45 0.000000 2 4.096815344517e-04 9.225410060960e-12
|
682
|
+
AS G24 2015 06 15 23 45 0.000000 2 -4.998574751545e-05 2.372704308220e-11
|
683
|
+
AS G25 2015 06 15 23 45 0.000000 2 -2.290169594092e-06 1.683218228880e-11
|
684
|
+
AS G29 2015 06 15 23 45 0.000000 2 6.168866864097e-04 1.206217360840e-11
|
685
|
+
AS G31 2015 06 15 23 45 0.000000 2 3.128244102077e-04 2.173867579920e-11
|
686
|
+
AS G12 2015 06 15 23 50 0.000000 2 3.017948525918e-04 1.300769315700e-11
|
687
|
+
AS G18 2015 06 15 23 50 0.000000 2 4.096823340968e-04 8.242414325510e-12
|
688
|
+
AS G24 2015 06 15 23 50 0.000000 2 -4.998578937856e-05 3.095238106380e-11
|
689
|
+
AS G25 2015 06 15 23 50 0.000000 2 -2.291410963946e-06 2.242880773880e-11
|
690
|
+
AS G29 2015 06 15 23 50 0.000000 2 6.168873857128e-04 1.695910016600e-11
|
691
|
+
AS G31 2015 06 15 23 50 0.000000 2 3.128241216228e-04 1.904362802980e-11
|
692
|
+
AS G12 2015 06 15 23 55 0.000000 2 3.017958412577e-04 1.171495359430e-11
|
693
|
+
AS G18 2015 06 15 23 55 0.000000 2 4.096833491457e-04 1.008770038860e-11
|
694
|
+
AS G24 2015 06 15 23 55 0.000000 2 -4.998596967628e-05 2.600074192880e-11
|
695
|
+
AS G25 2015 06 15 23 55 0.000000 2 -2.292650290468e-06 2.671557682290e-11
|
696
|
+
AS G29 2015 06 15 23 55 0.000000 2 6.168880208367e-04 1.729095211710e-11
|
697
|
+
AS G31 2015 06 15 23 55 0.000000 2 3.128236493567e-04 1.807462018720e-11
|
698
|
+
AS G12 2015 06 16 00 00 0.000000 2 3.017967728708e-04 1.000801049580e-11
|
699
|
+
AS G18 2015 06 16 00 00 0.000000 2 4.096840539308e-04 1.041670195750e-11
|
700
|
+
AS G24 2015 06 16 00 00 0.000000 2 -4.998614775959e-05 1.699628132880e-11
|
701
|
+
AS G25 2015 06 16 00 00 0.000000 2 -2.293760646361e-06 1.805118466830e-11
|
702
|
+
AS G29 2015 06 16 00 00 0.000000 2 6.168886213338e-04 2.220055594770e-11
|
703
|
+
AS G31 2015 06 16 00 00 0.000000 2 3.128233311248e-04 1.215284180360e-11
|
704
|
+
AS G12 2015 06 16 00 05 0.000000 2 3.017980078016e-04 1.152529430320e-11
|
705
|
+
AS G18 2015 06 16 00 05 0.000000 2 4.096851745816e-04 1.517949585220e-11
|
706
|
+
AS G24 2015 06 16 00 05 0.000000 2 -4.998569129850e-05 1.987308774570e-11
|
707
|
+
AS G25 2015 06 16 00 05 0.000000 2 -2.295014987154e-06 1.568891027900e-11
|
708
|
+
AS G29 2015 06 16 00 05 0.000000 2 6.168894672506e-04 1.906237292480e-11
|
709
|
+
AS G31 2015 06 16 00 05 0.000000 2 3.128231387506e-04 1.007829428500e-11
|
710
|
+
AS G12 2015 06 16 00 10 0.000000 2 3.017987334831e-04 8.726251168980e-12
|
711
|
+
AS G18 2015 06 16 00 10 0.000000 2 4.096859333261e-04 1.403166740100e-11
|
712
|
+
AS G24 2015 06 16 00 10 0.000000 2 -4.998597422529e-05 1.727695888430e-11
|
713
|
+
AS G25 2015 06 16 00 10 0.000000 2 -2.296262879345e-06 1.460867818340e-11
|
714
|
+
AS G29 2015 06 16 00 10 0.000000 2 6.168900483001e-04 1.556536485070e-11
|
715
|
+
AS G31 2015 06 16 00 10 0.000000 2 3.128226535741e-04 1.258498073020e-11
|
716
|
+
AS G12 2015 06 16 00 15 0.000000 2 3.017999301583e-04 1.006627990480e-11
|
717
|
+
AS G18 2015 06 16 00 15 0.000000 2 4.096864636193e-04 1.460541676660e-11
|
718
|
+
AS G24 2015 06 16 00 15 0.000000 2 -4.998605241609e-05 1.970841363000e-11
|
719
|
+
AS G25 2015 06 16 00 15 0.000000 2 -2.297477664413e-06 1.598459869060e-11
|
720
|
+
AS G29 2015 06 16 00 15 0.000000 2 6.168906691983e-04 1.729396319140e-11
|
721
|
+
AS G31 2015 06 16 00 15 0.000000 2 3.128222195893e-04 1.358710059900e-11
|
722
|
+
__RINEX_CLK_TEXT__
|
723
|
+
# modified version. original data are
|
724
|
+
# https://cddis.nasa.gov/archive/gnss/products/1849/igs1849[12].clk.Z
|
725
|
+
# mirrored: ftp://garner.ucsd.edu/pub/products/1849/igs1849[12].clk.Z
|
726
|
+
f.path
|
727
|
+
},
|
661
728
|
}}
|
662
729
|
let(:solver){
|
663
730
|
res = GPS::Solver::new
|
@@ -733,20 +800,20 @@ __ANTEX_TEXT__
|
|
733
800
|
expect(pvt.position_solved?).to be(true)
|
734
801
|
expect(pvt.receiver_time.to_a).to eq([1849, 172413])
|
735
802
|
expect(pvt.llh.to_a).to eq([:lat, :lng, :alt].collect{|k| pvt.llh.send(k)})
|
736
|
-
expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-
|
737
|
-
expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-
|
738
|
-
expect(pvt.llh.alt) .to be_within(1E-
|
739
|
-
expect(pvt.receiver_error).to be_within(1E-
|
740
|
-
expect(pvt.gdop).to be_within(1E-
|
741
|
-
expect(pvt.pdop).to be_within(1E-
|
742
|
-
expect(pvt.hdop).to be_within(1E-
|
743
|
-
expect(pvt.vdop).to be_within(1E-
|
744
|
-
expect(pvt.tdop).to be_within(1E-
|
803
|
+
expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-3).of(35.7) # latitude
|
804
|
+
expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-3).of(139.542) # longitude
|
805
|
+
expect(pvt.llh.alt) .to be_within(1E-1).of(104.3) # altitude
|
806
|
+
expect(pvt.receiver_error).to be_within(1E-1).of(1259087.8)
|
807
|
+
expect(pvt.gdop).to be_within(1E-2).of(2.42)
|
808
|
+
expect(pvt.pdop).to be_within(1E-2).of(2.17)
|
809
|
+
expect(pvt.hdop).to be_within(1E-2).of(1.11)
|
810
|
+
expect(pvt.vdop).to be_within(1E-2).of(1.87)
|
811
|
+
expect(pvt.tdop).to be_within(1E-2).of(1.08)
|
745
812
|
expect(pvt.velocity.to_a).to eq([:e, :n, :u].collect{|k| pvt.velocity.send(k)})
|
746
|
-
expect(pvt.velocity.north).to be_within(1E-
|
747
|
-
expect(pvt.velocity.east) .to be_within(1E-
|
748
|
-
expect(pvt.velocity.down) .to be_within(1E-
|
749
|
-
expect(pvt.receiver_error_rate).to be_within(1E-
|
813
|
+
expect(pvt.velocity.north).to be_within(1E-2).of(-0.86) # north
|
814
|
+
expect(pvt.velocity.east) .to be_within(1E-2).of(-1.10) # east
|
815
|
+
expect(pvt.velocity.down) .to be_within(1E-2).of(-0.22) # down
|
816
|
+
expect(pvt.receiver_error_rate).to be_within(1E-2).of(-1061.86)
|
750
817
|
expect(pvt.G.rows).to eq(6)
|
751
818
|
expect(pvt.W.rows).to eq(6)
|
752
819
|
expect(pvt.delta_r.rows).to eq(6)
|
@@ -778,6 +845,10 @@ __ANTEX_TEXT__
|
|
778
845
|
|
779
846
|
it 'can be modified through hooks' do
|
780
847
|
sn = solver.gps_space_node
|
848
|
+
expect(solver.options).to be_a_kind_of(Hash)
|
849
|
+
expect(solver.options.keys).to include(:skip_exclusion)
|
850
|
+
expect{solver.options = {:skip_exclusion => true}}.not_to raise_error
|
851
|
+
expect(solver.options[:skip_exclusion]).to eq(true)
|
781
852
|
expect(solver.correction[:gps_ionospheric]).to include(:klobuchar)
|
782
853
|
expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
|
783
854
|
expect{solver.correction = nil}.to raise_error(RuntimeError)
|
@@ -937,5 +1008,55 @@ __ANTEX_TEXT__
|
|
937
1008
|
puts ([:lat, :lng].collect{|f| pvt.llh.send(f) / Math::PI * 180} + [pvt.llh.alt]).inspect
|
938
1009
|
}
|
939
1010
|
end
|
1011
|
+
it 'calculates satellite clock error based on RINEX clock' do
|
1012
|
+
clk, sn = [GPS::RINEX_Clock::new, solver.gps_space_node]
|
1013
|
+
expect(clk.read(input[:rinex_clk])).to eq(6 * 7)
|
1014
|
+
proc{|sats|
|
1015
|
+
expect(sats.kind_of?(Array)).to eq(true)
|
1016
|
+
expect(sats[clk.class::SYS_GPS]).to eq(6)
|
1017
|
+
}.call(clk.satellites)
|
1018
|
+
sn.read(input[:rinex_nav])
|
1019
|
+
t0 = GPS::Time::new(1849, 172800)
|
1020
|
+
sn.update_all_ephemeris(t0)
|
1021
|
+
(-5..5).step(1){|dt_min|
|
1022
|
+
t = t0 + (dt_min * 60)
|
1023
|
+
[12, 18, 24, 25, 29, 31].each{|sat_id|
|
1024
|
+
eph = sn.ephemeris(sat_id).constellation(t)
|
1025
|
+
expect(clk.clock_error(sat_id, t)).to be_within(1E-7).of(eph[2]) # 100 ns
|
1026
|
+
expect(clk.clock_error_dot(sat_id, t)).to be_within(1E-10).of(eph[3]) # 100 ps
|
1027
|
+
}
|
1028
|
+
}
|
1029
|
+
end
|
1030
|
+
it 'calculates position without any error with RINEX NAV and CLK' do
|
1031
|
+
sn = solver.gps_space_node
|
1032
|
+
sn.read(input[:rinex_nav])
|
1033
|
+
clk = GPS::RINEX_Clock::new
|
1034
|
+
clk.read(input[:rinex_clk])
|
1035
|
+
expect(clk.push(solver, clk.class::SYS_GPS)).to eq(true)
|
1036
|
+
GPS::RINEX_Observation::read(input[:rinex_obs]){|item|
|
1037
|
+
t_meas = item[:time]
|
1038
|
+
sn.update_all_ephemeris(t_meas)
|
1039
|
+
meas = GPS::Measurement::new
|
1040
|
+
types = (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
|
1041
|
+
type_ = {
|
1042
|
+
"C1" => :L1_PSEUDORANGE,
|
1043
|
+
"D1" => :L1_RANGE_RATE,
|
1044
|
+
}[type_]
|
1045
|
+
type_ && [i, GPS::Measurement::const_get(type_)]
|
1046
|
+
}.compact
|
1047
|
+
item[:meas].each{|k, v|
|
1048
|
+
sys, prn = k
|
1049
|
+
next unless sys == 'G' # GPS only
|
1050
|
+
types.each{|i, type_|
|
1051
|
+
meas.add(prn, type_, v[i][0]) if v[i]
|
1052
|
+
}
|
1053
|
+
}
|
1054
|
+
pvt = solver.solve(meas, t_meas)
|
1055
|
+
expect(pvt.position_solved?).to eq(true)
|
1056
|
+
[-3952590.4754, 3360273.8926, 3697987.2632].zip(pvt.xyz.to_a).each{|a, b|
|
1057
|
+
expect(a).to be_within(1E+2).of(b) # 10 m
|
1058
|
+
}
|
1059
|
+
}
|
1060
|
+
end
|
940
1061
|
end
|
941
1062
|
end
|
data/gps_pvt.gemspec
CHANGED
@@ -58,6 +58,7 @@ Gem::Specification.new do |spec|
|
|
58
58
|
|
59
59
|
# Uncomment to register a new dependency of your gem
|
60
60
|
# spec.add_dependency "example-gem", "~> 1.0"
|
61
|
+
spec.add_dependency "rubyserial"
|
61
62
|
spec.add_development_dependency "rake"
|
62
63
|
spec.add_development_dependency "rake-compiler"
|
63
64
|
|