gps_pvt 0.7.1 → 0.7.2
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 +4 -4
- data/README.md +1 -1
- data/exe/gps_pvt +1 -1
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +7 -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 +37 -11
- 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/swig/GPS.i +7 -1
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +13 -13
- data/lib/gps_pvt/receiver.rb +6 -6
- data/lib/gps_pvt/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2eb20de6303a24b0e5b134c7c78cd1edb043ff38d823181e596b4626b6bc536
|
4
|
+
data.tar.gz: 183ae6e8cfd2577132113bb3684e6e225989ce444d6647820496b13ddd3a4903
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a35228704032066f682e15f5abf6f8a2238d90424e6b50182e07b8fc342a1f4150ba6747ca88a9fcaef163e0ce8149a68edf087e6c0f5525a2c7a09553beb504
|
7
|
+
data.tar.gz: 10ace25d753123375bf8ad46cbd4d7490b13219522b39ee5774a1df6d3ba1fd3fd9f318364a19253981f24a3b83998e5df9bb82700b37d6d7f17bd1ad27ad14d
|
data/README.md
CHANGED
@@ -113,7 +113,7 @@ receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_
|
|
113
113
|
# rcv_e, t_arv, usr_pos, usr_vel are temporary solution of
|
114
114
|
# receiver clock error [m], time of arrival [s], user position and velocity in ECEF, respectively.
|
115
115
|
|
116
|
-
weight = 1 #
|
116
|
+
weight = 1 # quick example: identical weight for each visible satellite
|
117
117
|
# or weight based on elevation, for example:
|
118
118
|
# elv = GPS_PVT::Coordinate::ENU::relative_rel(GPS_PVT::Coordinate::XYZ::new(*los_neg), usr_pos).elevation
|
119
119
|
# weight = (Math::sin(elv)/0.8)**2
|
data/exe/gps_pvt
CHANGED
@@ -11,7 +11,7 @@ As GPS_file, rinex_nav(*.YYn, *.YYh, *.YYq, *.YYg), rinex_obs(*.YYo), ubx(*.ubx)
|
|
11
11
|
(YY = last two digit of year)
|
12
12
|
File format is automatically determined based on its extention described in above parentheses.
|
13
13
|
If you want to specify its format manually, command options like --rinex_nav=file_name are available.
|
14
|
-
Other than --rinex_nav, --rinex_obs, --ubx, --sp3 or --antex are supported.
|
14
|
+
Other than --rinex_nav, --rinex_obs, -rinex_clk, --ubx, --sp3 or --antex are supported.
|
15
15
|
Supported RINEX versions are 2 and 3.
|
16
16
|
A file having additional ".gz" or ".Z" extension is recognized as a compressed file.
|
17
17
|
Major URL such as http(s)://... or ftp://... is acceptable as an input file name.
|
@@ -3948,10 +3948,13 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
|
|
3948
3948
|
static const int prop_items(sizeof(res.values) / sizeof(res.values[0]));
|
3949
3949
|
VALUE hook(rb_hash_lookup(hooks, key));
|
3950
3950
|
if(NIL_P(hook)){break;}
|
3951
|
+
double weight((res.prop.range_sigma > 0)
|
3952
|
+
? (1. / std::pow(res.prop.range_sigma, 2)) // weight=1/(sigma^2)
|
3953
|
+
: res.prop.range_sigma);
|
3951
3954
|
VALUE values[] = {
|
3952
3955
|
SWIG_From_int (prn), // prn
|
3953
3956
|
rb_ary_new_from_args(prop_items, // relative_property
|
3954
|
-
swig::from(
|
3957
|
+
swig::from(weight),
|
3955
3958
|
swig::from(res.prop.range_corrected),
|
3956
3959
|
swig::from(res.prop.range_residual),
|
3957
3960
|
swig::from(res.prop.rate_relative_neg),
|
@@ -3983,6 +3986,9 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
|
|
3983
3986
|
.append(" @ [").append(std::to_string(i)).append("]"));
|
3984
3987
|
}
|
3985
3988
|
}
|
3989
|
+
if(res.values[0] > 0){
|
3990
|
+
res.values[0] = std::pow(1. / res.values[0], 0.5); // sigma=(1/weight)^0.5
|
3991
|
+
}
|
3986
3992
|
}while(false);
|
3987
3993
|
|
3988
3994
|
return res.prop;
|
@@ -814,11 +814,8 @@ if(std::abs(TARGET - t.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
|
|
814
814
|
static const float_t F_T_table[15]; ///< @see Table 4.4
|
815
815
|
|
816
816
|
static const float_t F_T_value(const u8_t &F_T_){
|
817
|
-
|
818
|
-
|
819
|
-
}else{
|
820
|
-
return F_T_table[F_T_];
|
821
|
-
}
|
817
|
+
return GPS_SpaceNode<float_t>::SatelliteProperties::Ephemeris::URA_meter(
|
818
|
+
F_T_, F_T_table);
|
822
819
|
}
|
823
820
|
|
824
821
|
static const uint_t P1_value(const u8_t &P1_){
|
@@ -1315,12 +1312,8 @@ typename GLONASS_SpaceNode<FloatT>::u8_t GLONASS_SpaceNode<FloatT>::SatellitePro
|
|
1315
1312
|
if(F_T <= 0){ // invalid value
|
1316
1313
|
return sizeof(raw_t::F_T_table) / sizeof(raw_t::F_T_table[0]);
|
1317
1314
|
}
|
1318
|
-
|
1319
|
-
|
1320
|
-
if(F_T <= raw_t::F_T_table[res]){break;}
|
1321
|
-
++res;
|
1322
|
-
}
|
1323
|
-
return res;
|
1315
|
+
return (u8_t)GPS_SpaceNode<float_t>::SatelliteProperties::Ephemeris::URA_index(
|
1316
|
+
F_T, raw_t::F_T_table);
|
1324
1317
|
}
|
1325
1318
|
|
1326
1319
|
template <class FloatT>
|
@@ -131,11 +131,15 @@ class GLONASS_SinglePositioning : public SolverBaseT {
|
|
131
131
|
static float_t clock_error_dot(const void *ptr, const gps_time_t &t_tx) {
|
132
132
|
return sat(ptr).clock_error_dot(t_tx);
|
133
133
|
}
|
134
|
+
static float_t range_sigma(const void *ptr, const gps_time_t &t_tx) {
|
135
|
+
return sat(ptr).ephemeris().F_T;
|
136
|
+
}
|
134
137
|
};
|
135
138
|
satellite_t res = {
|
136
|
-
&(it_sat->second), &(it_sat->second),
|
139
|
+
&(it_sat->second), &(it_sat->second), // position, time
|
137
140
|
impl_t::position, impl_t::velocity,
|
138
|
-
impl_t::clock_error, impl_t::clock_error_dot
|
141
|
+
impl_t::clock_error, impl_t::clock_error_dot,
|
142
|
+
&(it_sat->second), impl_t::range_sigma, NULL}; // error model
|
139
143
|
return res;
|
140
144
|
}
|
141
145
|
satellites_t(const space_node_t &sn)
|
@@ -292,21 +296,29 @@ class GLONASS_SinglePositioning : public SolverBaseT {
|
|
292
296
|
res.range_residual += range_error.value[range_error_t::IONOSPHERIC];
|
293
297
|
}
|
294
298
|
|
295
|
-
// Setup weight
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
299
|
+
// Setup range standard deviation, whose reciprocal square is used as weight
|
300
|
+
res.range_sigma = 1E+4; // sufficiently big value, 1E4 [m]
|
301
|
+
do{
|
302
|
+
// If residual is too big, gently exclude it.
|
303
|
+
if(std::abs(res.range_residual) > _options.residual_mask){break;}
|
300
304
|
|
301
305
|
float_t elv(relative_pos.elevation());
|
302
306
|
if(elv < _options.elevation_mask){
|
303
|
-
res.
|
304
|
-
|
305
|
-
// elevation weight based on "GPS���p�v���O���~���O" @see GPS_Solver.h
|
306
|
-
res.weight = std::pow(sin(elv)/0.8, 2);
|
307
|
-
if(res.weight < 1E-3){res.weight = 1E-3;}
|
307
|
+
res.range_sigma = 0; // exclude it when elevation is less than threshold
|
308
|
+
break;
|
308
309
|
}
|
309
|
-
|
310
|
+
|
311
|
+
res.range_sigma = sat.range_sigma(t_tx);
|
312
|
+
|
313
|
+
/* elevation weight based on "GPS���p�v���O���~���O"
|
314
|
+
* elevation[deg] : 90 53 45 30 15 10 5
|
315
|
+
* sf_sigma(k) : 0.80 1.00 1.13 1.60 3.09 4.61 9.18
|
316
|
+
* weight(k^-2) : 1.56 1.00 0.78 0.39 0.10 0.05 0.01
|
317
|
+
*/
|
318
|
+
static const float_t max_sf(10);
|
319
|
+
static const float_t elv_limit(std::asin(0.8/max_sf)); // limit
|
320
|
+
res.range_sigma *= (elv > elv_limit) ? (0.8 / sin(elv)) : max_sf;
|
321
|
+
}while(false);
|
310
322
|
|
311
323
|
res.range_corrected = range;
|
312
324
|
|
@@ -990,7 +990,7 @@ static s ## bits ## _t name(const InputT *buf){ \
|
|
990
990
|
|
991
991
|
// Subframe.1
|
992
992
|
uint_t WN; ///< Week number
|
993
|
-
|
993
|
+
float_t URA; ///< User range accuracy (m)
|
994
994
|
uint_t SV_health; ///< Health status
|
995
995
|
int_t iodc; ///< Issue of clock data
|
996
996
|
float_t t_GD; ///< Group delay (s)
|
@@ -1061,22 +1061,29 @@ static s ## bits ## _t name(const InputT *buf){ \
|
|
1061
1061
|
return !((delta_t >= 0) && (delta_t < transmission_interval));
|
1062
1062
|
}
|
1063
1063
|
|
1064
|
-
static const float_t URA_limits[];
|
1065
|
-
static const int URA_MAX_INDEX;
|
1064
|
+
static const float_t URA_limits[15];
|
1066
1065
|
|
1067
|
-
|
1066
|
+
template <std::size_t N>
|
1067
|
+
static float_t URA_meter(const int_t &index, const float_t (&table)[N]){
|
1068
1068
|
if(index < 0){return -1;}
|
1069
|
-
return (index <
|
1070
|
-
?
|
1071
|
-
:
|
1069
|
+
return (index < N)
|
1070
|
+
? table[index]
|
1071
|
+
: table[N - 1] * 2;
|
1072
|
+
}
|
1073
|
+
inline static float_t URA_meter(const int_t &index){
|
1074
|
+
return URA_meter(index, URA_limits);
|
1072
1075
|
}
|
1073
1076
|
|
1074
|
-
|
1077
|
+
template <std::size_t N>
|
1078
|
+
static int_t URA_index(const float_t &meter, const float_t (&table)[N]){
|
1075
1079
|
if(meter < 0){return -1;}
|
1076
|
-
for(
|
1077
|
-
if(meter <=
|
1080
|
+
for(std::size_t i(0); i < N; ++i){
|
1081
|
+
if(meter <= table[i]){return i;}
|
1078
1082
|
}
|
1079
|
-
return
|
1083
|
+
return N;
|
1084
|
+
}
|
1085
|
+
inline static int_t URA_index(const float_t &meter){
|
1086
|
+
return URA_index(meter, URA_limits);
|
1080
1087
|
}
|
1081
1088
|
|
1082
1089
|
float_t eccentric_anomaly(const float_t &period_from_toe) const {
|
@@ -1399,7 +1406,7 @@ static s ## bits ## _t name(const InputT *buf){ \
|
|
1399
1406
|
converted.svid = svid;
|
1400
1407
|
|
1401
1408
|
converted.WN = WN;
|
1402
|
-
converted.URA = URA;
|
1409
|
+
converted.URA = URA_meter(URA);
|
1403
1410
|
converted.SV_health = SV_health;
|
1404
1411
|
converted.iodc = iodc;
|
1405
1412
|
CONVERT(t_GD);
|
@@ -1438,7 +1445,7 @@ static s ## bits ## _t name(const InputT *buf){ \
|
|
1438
1445
|
svid = eph.svid;
|
1439
1446
|
|
1440
1447
|
WN = eph.WN;
|
1441
|
-
URA = eph.URA;
|
1448
|
+
URA = URA_index(eph.URA);
|
1442
1449
|
SV_health = eph.SV_health;
|
1443
1450
|
iodc = eph.iodc;
|
1444
1451
|
CONVERT(t_GD);
|
@@ -1475,7 +1482,7 @@ static s ## bits ## _t name(const InputT *buf){ \
|
|
1475
1482
|
bool is_equivalent(const Ephemeris &eph) const {
|
1476
1483
|
do{
|
1477
1484
|
if(WN != eph.WN){break;}
|
1478
|
-
if(URA != eph.URA){break;}
|
1485
|
+
if(URA_index(URA) != URA_index(eph.URA)){break;}
|
1479
1486
|
if(SV_health != eph.SV_health){break;}
|
1480
1487
|
|
1481
1488
|
#define CHECK(TARGET) \
|
@@ -2478,10 +2485,6 @@ const typename GPS_SpaceNode<FloatT>::float_t GPS_SpaceNode<FloatT>::SatellitePr
|
|
2478
2485
|
6144.00,
|
2479
2486
|
};
|
2480
2487
|
|
2481
|
-
template <class FloatT>
|
2482
|
-
const int GPS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::URA_MAX_INDEX
|
2483
|
-
= sizeof(URA_limits) / sizeof(URA_limits[0]);
|
2484
|
-
|
2485
2488
|
#ifdef POW2_ALREADY_DEFINED
|
2486
2489
|
#undef POW2_ALREADY_DEFINED
|
2487
2490
|
#else
|
@@ -155,11 +155,15 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
155
155
|
static float_t clock_error_dot(const void *ptr, const gps_time_t &t_tx) {
|
156
156
|
return sat(ptr).clock_error_dot(t_tx);
|
157
157
|
}
|
158
|
+
static float_t range_sigma(const void *ptr, const gps_time_t &t_tx) {
|
159
|
+
return sat(ptr).ephemeris().URA;
|
160
|
+
}
|
158
161
|
};
|
159
162
|
satellite_t res = {
|
160
|
-
&(it_sat->second), &(it_sat->second),
|
163
|
+
&(it_sat->second), &(it_sat->second), // position, clock
|
161
164
|
impl_t::position, impl_t::velocity,
|
162
|
-
impl_t::clock_error, impl_t::clock_error_dot
|
165
|
+
impl_t::clock_error, impl_t::clock_error_dot,
|
166
|
+
&(it_sat->second), impl_t::range_sigma, NULL}; // error model
|
163
167
|
return res;
|
164
168
|
}
|
165
169
|
satellites_t(const space_node_t &sn)
|
@@ -248,7 +252,7 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
248
252
|
float_t &los_neg_x;
|
249
253
|
float_t &los_neg_y;
|
250
254
|
float_t &los_neg_z;
|
251
|
-
float_t &
|
255
|
+
float_t &range_sigma;
|
252
256
|
};
|
253
257
|
|
254
258
|
/**
|
@@ -258,8 +262,8 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
258
262
|
* @param range "corrected" pseudo range subtracted by (temporal solution of) receiver clock error in meter
|
259
263
|
* @param time_arrival time when signal arrive at receiver
|
260
264
|
* @param usr_pos (temporal solution of) user position
|
261
|
-
* @param residual calculated residual with line of site vector, and
|
262
|
-
* When
|
265
|
+
* @param residual calculated residual with line of site vector, and pseudorange standard deviation (sigma);
|
266
|
+
* When sigma is equal to or less than zero, the calculated results should not be used.
|
263
267
|
* @param error Some correction can be overwritten. If its unknown_flag is zero,
|
264
268
|
* corrections will be skipped as possible. @see range_errors_t
|
265
269
|
* @return (float_t) corrected range just including delay, and excluding receiver/satellite error.
|
@@ -281,7 +285,8 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
281
285
|
|
282
286
|
// Calculate satellite position
|
283
287
|
float_t dt_transit(range / c);
|
284
|
-
|
288
|
+
gps_time_t time_depature(time_arrival - dt_transit);
|
289
|
+
xyz_t sat_pos(sat.position(time_depature, dt_transit));
|
285
290
|
float_t geometric_range(usr_pos.xyz.dist(sat_pos));
|
286
291
|
|
287
292
|
// Calculate residual
|
@@ -305,25 +310,30 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
305
310
|
? tropospheric_correction(time_arrival, usr_pos, relative_pos)
|
306
311
|
: error.value[range_error_t::TROPOSPHERIC];
|
307
312
|
|
308
|
-
// Setup weight
|
309
|
-
|
310
|
-
|
311
|
-
residual
|
312
|
-
|
313
|
+
// Setup range standard deviation, whose reciprocal square is used as weight
|
314
|
+
residual.range_sigma = 1E+4; // sufficiently big value, 1E4 [m]
|
315
|
+
do{
|
316
|
+
// If residual is too big, gently exclude it.
|
317
|
+
if(std::abs(residual.residual) > _options.residual_mask){break;}
|
313
318
|
|
314
319
|
float_t elv(relative_pos.elevation());
|
315
320
|
if(elv < _options.elevation_mask){
|
316
|
-
residual.
|
317
|
-
|
318
|
-
/* elevation weight based on "GPS���p�v���O���~���O"
|
319
|
-
* elevation[deg] : 90 53 45 30 15 10 5
|
320
|
-
* sigma(s) : 0.80 1.00 1.13 1.60 3.09 4.61 9.18
|
321
|
-
* weight(s^-2) : 1.56 1.00 0.78 0.39 0.10 0.05 0.01
|
322
|
-
*/
|
323
|
-
residual.weight = std::pow(sin(elv)/0.8, 2); // weight=1 @ elv=53[deg]
|
324
|
-
if(residual.weight < 1E-3){residual.weight = 1E-3;}
|
321
|
+
residual.range_sigma = 0; // exclude it when elevation is less than threshold
|
322
|
+
break;
|
325
323
|
}
|
326
|
-
|
324
|
+
|
325
|
+
residual.range_sigma = sat.range_sigma(time_depature);
|
326
|
+
|
327
|
+
/* elevation weight based on "GPS���p�v���O���~���O"
|
328
|
+
* elevation[deg] : 90 53 45 30 15 10 5
|
329
|
+
* sf_sigma(k) : 0.80 1.00 1.13 1.60 3.09 4.61 9.18
|
330
|
+
* weight(k^-2) : 1.56 1.00 0.78 0.39 0.10 0.05 0.01
|
331
|
+
*/
|
332
|
+
static const float_t max_sf(10);
|
333
|
+
static const float_t elv_limit(std::asin(0.8/max_sf)); // limit
|
334
|
+
residual.range_sigma *= (elv > elv_limit) ? (0.8 / sin(elv)) : max_sf;
|
335
|
+
}while(false);
|
336
|
+
|
327
337
|
|
328
338
|
return range;
|
329
339
|
}
|
@@ -396,16 +406,16 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
396
406
|
float_t range;
|
397
407
|
range_error_t range_error;
|
398
408
|
if(!this->range(measurement, range, &range_error)){
|
399
|
-
return res; // If no range entry, return with
|
409
|
+
return res; // If no range entry, return with range_sigma = 0
|
400
410
|
}
|
401
411
|
|
402
412
|
satellite_t sat(select_satellite(prn, time_arrival));
|
403
|
-
if(!sat.is_available()){return res;} // If satellite is unavailable, return with
|
413
|
+
if(!sat.is_available()){return res;} // If satellite is unavailable, return with range_sigma = 0
|
404
414
|
|
405
415
|
residual_t residual = {
|
406
416
|
res.range_residual,
|
407
417
|
res.los_neg[0], res.los_neg[1], res.los_neg[2],
|
408
|
-
res.
|
418
|
+
res.range_sigma,
|
409
419
|
};
|
410
420
|
|
411
421
|
res.range_corrected = range_corrected(
|
@@ -414,8 +424,15 @@ class GPS_SinglePositioning : public SolverBaseT {
|
|
414
424
|
res.rate_relative_neg = rate_relative_neg(sat, res.range_corrected, time_arrival, usr_vel,
|
415
425
|
res.los_neg[0], res.los_neg[1], res.los_neg[2]);
|
416
426
|
|
417
|
-
|
427
|
+
#if 0
|
428
|
+
// TODO consider case when standard deviation of pseudorange measurement is provided by receiver
|
429
|
+
if(!this->range_sigma(measurement, res.range_sigma)){
|
430
|
+
// If receiver's range variance is not provided
|
431
|
+
res.range_sigma = 1E0; // TODO range error variance [m]
|
432
|
+
}
|
433
|
+
#endif
|
418
434
|
|
435
|
+
return res;
|
419
436
|
}
|
420
437
|
|
421
438
|
/**
|
@@ -232,6 +232,9 @@ struct GPS_Solver_Base {
|
|
232
232
|
xyz_t (*impl_velocity)(const void *, const gps_time_t &, const float_t &);
|
233
233
|
float_t (*impl_clock_error)(const void *, const gps_time_t &);
|
234
234
|
float_t (*impl_clock_error_dot)(const void *, const gps_time_t &);
|
235
|
+
const void *impl_error; ///< pointer to actual implementation of error model
|
236
|
+
float_t (*impl_range_sigma)(const void *, const gps_time_t &);
|
237
|
+
float_t (*impl_rate_sigma)(const void *, const gps_time_t &);
|
235
238
|
inline bool is_available() const {
|
236
239
|
return (impl_xyz != NULL) && (impl_t != NULL);
|
237
240
|
}
|
@@ -267,6 +270,26 @@ struct GPS_Solver_Base {
|
|
267
270
|
inline float_t clock_error_dot(const gps_time_t &t_tx) const {
|
268
271
|
return impl_clock_error_dot(impl_t, t_tx);
|
269
272
|
}
|
273
|
+
/**
|
274
|
+
* Return expected user range accuracy (URA) in standard deviation (1-sigma)
|
275
|
+
* at the transmission time.
|
276
|
+
* @param t_tx transmission time
|
277
|
+
*/
|
278
|
+
inline float_t range_sigma(const gps_time_t &t_tx) const {
|
279
|
+
return impl_range_sigma
|
280
|
+
? impl_range_sigma(impl_error, t_tx)
|
281
|
+
: 3.5; // 7.0 [m] of 95% (2-sigma) URE in Sec. 3.4.1 of April 2020 GPS SPS PS;
|
282
|
+
}
|
283
|
+
/**
|
284
|
+
* Return expected user range rate accuracy (URRA) in standard deviation (1-sigma)
|
285
|
+
* at the transmission time.
|
286
|
+
* @param t_tx transmission time
|
287
|
+
*/
|
288
|
+
inline float_t rate_sigma(const gps_time_t &t_tx) const {
|
289
|
+
return impl_rate_sigma
|
290
|
+
? impl_rate_sigma(impl_error, t_tx)
|
291
|
+
: 0.003; // 0.006 [m/s] of 95% (2-sigma) URRE in Sec. 3.4.2 of April 2020 GPS SPS PS
|
292
|
+
}
|
270
293
|
static const satellite_t &unavailable() {
|
271
294
|
struct impl_t {
|
272
295
|
static xyz_t v3(const void *, const gps_time_t &, const float_t &){
|
@@ -276,7 +299,9 @@ struct GPS_Solver_Base {
|
|
276
299
|
return float_t(0);
|
277
300
|
}
|
278
301
|
};
|
279
|
-
static const satellite_t res = {
|
302
|
+
static const satellite_t res = {
|
303
|
+
NULL, NULL, impl_t::v3, impl_t::v3, impl_t::v, impl_t::v,
|
304
|
+
NULL, NULL, NULL};
|
280
305
|
return res;
|
281
306
|
}
|
282
307
|
};
|
@@ -359,7 +384,7 @@ struct GPS_Solver_Base {
|
|
359
384
|
}
|
360
385
|
|
361
386
|
struct relative_property_t {
|
362
|
-
float_t
|
387
|
+
float_t range_sigma; ///< Standard deviation of pseudorange; If zero or negative, other values are invalid.
|
363
388
|
float_t range_corrected; ///< corrected range just including delay, and excluding receiver/satellite error
|
364
389
|
float_t range_residual; ///< residual range
|
365
390
|
float_t rate_relative_neg; /// relative rate
|
@@ -514,6 +539,9 @@ protected:
|
|
514
539
|
* @param G_ original design matrix
|
515
540
|
* @param rotation_matrix 3 by 3 rotation matrix
|
516
541
|
* @return transformed design matrix G'
|
542
|
+
* @see Eq.(3) and (10) in https://gssc.esa.int/navipedia/index.php/Positioning_Error,
|
543
|
+
* however, be careful that their R is used as both range error covariance in Eq.(1)
|
544
|
+
* and rotation matrix in Eq.(3).
|
517
545
|
*/
|
518
546
|
static matrix_t rotate_G(const MatrixT &G_, const matrix_t &rotation_matrix){
|
519
547
|
unsigned int r(G_.rows()), c(G_.columns()); // Normally c=4
|
@@ -529,17 +557,15 @@ protected:
|
|
529
557
|
}
|
530
558
|
|
531
559
|
/**
|
532
|
-
* Calculate C matrix,
|
533
|
-
* C = G^t * W * G
|
534
|
-
*
|
560
|
+
* Calculate C matrix, where C = G^t * G to be used for DOP calculation
|
535
561
|
* @return C matrix
|
536
562
|
*/
|
537
563
|
matrix_t C() const {
|
538
|
-
return (G.transpose() *
|
564
|
+
return (G.transpose() * G).inverse();
|
539
565
|
}
|
540
566
|
/**
|
541
|
-
* Transform coordinate of matrix C
|
542
|
-
* C' = (G * R^t)^t
|
567
|
+
* Transform coordinate of matrix C
|
568
|
+
* C' = (G * R^t)^t * (G * R^t) = R * G^t * G * R^t = R * C * R^t,
|
543
569
|
* where R is a rotation matrix, for example, ECEF to ENU.
|
544
570
|
*
|
545
571
|
* @param rotation_matrix 3 by 3 rotation matrix
|
@@ -804,14 +830,14 @@ protected:
|
|
804
830
|
res.receiver_error, time_arrival,
|
805
831
|
res.user_position, zero));
|
806
832
|
|
807
|
-
if(prop.
|
833
|
+
if(prop.range_sigma <= 0){
|
808
834
|
continue; // intentionally excluded satellite
|
809
835
|
}else{
|
810
836
|
res.used_satellite_mask.set(it->prn);
|
811
837
|
}
|
812
838
|
|
813
839
|
if(coarse_estimation){
|
814
|
-
prop.
|
840
|
+
prop.range_sigma = 1;
|
815
841
|
}else{
|
816
842
|
idx_rate_rel.push_back(std::make_pair(i_measurement, prop.rate_relative_neg));
|
817
843
|
}
|
@@ -820,7 +846,7 @@ protected:
|
|
820
846
|
geomat.G(i_row, 0) = prop.los_neg[0];
|
821
847
|
geomat.G(i_row, 1) = prop.los_neg[1];
|
822
848
|
geomat.G(i_row, 2) = prop.los_neg[2];
|
823
|
-
geomat.W(i_row, i_row) = prop.
|
849
|
+
geomat.W(i_row, i_row) = 1. / std::pow(prop.range_sigma, 2);
|
824
850
|
|
825
851
|
++i_row;
|
826
852
|
}
|
@@ -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
|
}
|
@@ -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;
|
@@ -800,20 +800,20 @@ __RINEX_CLK_TEXT__
|
|
800
800
|
expect(pvt.position_solved?).to be(true)
|
801
801
|
expect(pvt.receiver_time.to_a).to eq([1849, 172413])
|
802
802
|
expect(pvt.llh.to_a).to eq([:lat, :lng, :alt].collect{|k| pvt.llh.send(k)})
|
803
|
-
expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-
|
804
|
-
expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-
|
805
|
-
expect(pvt.llh.alt) .to be_within(1E-
|
806
|
-
expect(pvt.receiver_error).to be_within(1E-
|
807
|
-
expect(pvt.gdop).to be_within(1E-
|
808
|
-
expect(pvt.pdop).to be_within(1E-
|
809
|
-
expect(pvt.hdop).to be_within(1E-
|
810
|
-
expect(pvt.vdop).to be_within(1E-
|
811
|
-
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)
|
812
812
|
expect(pvt.velocity.to_a).to eq([:e, :n, :u].collect{|k| pvt.velocity.send(k)})
|
813
|
-
expect(pvt.velocity.north).to be_within(1E-
|
814
|
-
expect(pvt.velocity.east) .to be_within(1E-
|
815
|
-
expect(pvt.velocity.down) .to be_within(1E-
|
816
|
-
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)
|
817
817
|
expect(pvt.G.rows).to eq(6)
|
818
818
|
expect(pvt.W.rows).to eq(6)
|
819
819
|
expect(pvt.delta_r.rows).to eq(6)
|
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -150,10 +150,6 @@ class Receiver
|
|
150
150
|
|
151
151
|
def initialize(options = {})
|
152
152
|
@solver = GPS::Solver::new
|
153
|
-
@solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
|
154
|
-
rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
|
155
|
-
rel_prop
|
156
|
-
}
|
157
153
|
@solver.options = {
|
158
154
|
:skip_exclusion => true, # default is to skip fault exclusion calculation
|
159
155
|
}
|
@@ -180,7 +176,7 @@ class Receiver
|
|
180
176
|
next true
|
181
177
|
when :weight
|
182
178
|
case v.to_sym
|
183
|
-
when :elevation # (same as underneath C++ library)
|
179
|
+
when :elevation # (same as underneath C++ library except for ignoring broadcasted/calculated URA)
|
184
180
|
@solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
|
185
181
|
if rel_prop[0] > 0 then
|
186
182
|
elv = Coordinate::ENU::relative_rel(
|
@@ -190,7 +186,11 @@ class Receiver
|
|
190
186
|
rel_prop
|
191
187
|
}
|
192
188
|
next true
|
193
|
-
when :identical # same
|
189
|
+
when :identical # treat each satellite range having same accuracy
|
190
|
+
@solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
|
191
|
+
rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
|
192
|
+
rel_prop
|
193
|
+
}
|
194
194
|
next true
|
195
195
|
end
|
196
196
|
when :elevation_mask_deg
|
data/lib/gps_pvt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gps_pvt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fenrir(M.Naruoka)
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|