gps_pvt 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|