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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bccc645ba148e24c0d8b43c1663ed245a6306eedfd376bdccf0f817977d5964
|
4
|
+
data.tar.gz: 13b461cf2ce59b7064f4c69f13969f3e758974ae6dd331bf3628a901f76b3762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cebbb365561e4a2a56cadde92d69f03490313d56baa33ed5e14749220385a85813ee91cbdb93cb3980f66d69d95e1a63fb2852fb8441992767b64e7a80558ec5
|
7
|
+
data.tar.gz: 251feae90231587a9e1c720ab20675084573b6ef1fc50429ad04223a1ad269eccc16d0e79cb7f4ffa547be95c1129a83e12e3edb5dc1495d20245cb1afc39693
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ An attached executable is useful. After installation, type
|
|
32
32
|
|
33
33
|
$ gps_pvt file_or_URI(s)
|
34
34
|
|
35
|
-
The format of file is automatically determined with its extension, such as .ubx will be treated as UBX format. A compressed file of .gz or .Z can be specified directly (decompression is internally performed)
|
35
|
+
The format of file is automatically determined with its extension, such as .ubx will be treated as UBX format. A compressed file of .gz or .Z can be specified directly (decompression is internally performed). URI such as http(s)://... and ftp://, and serial port (COMn for Windows and /dev/tty* for *NIX, version >= 0.8.0) are also acceptable. If you want to specify the file format, instead of file_or_URI(s), use the following arguments:
|
36
36
|
|
37
37
|
| specification | recoginized as |
|
38
38
|
----|----
|
@@ -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,10 +11,10 @@ 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
|
-
Major URL such as http(s)://... or ftp
|
17
|
+
Major URL such as http(s)://... or ftp://..., and serial port (COMn for Windows, /dev/tty* for *NIX) is acceptable as an input file name.
|
18
18
|
__STRING__
|
19
19
|
|
20
20
|
options = []
|
@@ -80,6 +80,7 @@ files.collect!{|fname, ftype|
|
|
80
80
|
end
|
81
81
|
fname = proc{
|
82
82
|
next fname if File::exist?(fname)
|
83
|
+
next fname if ((fname =~ Serial::SPEC) rescue false)
|
83
84
|
if uri = URI::parse(fname) and !uri.instance_of?(URI::Generic) then
|
84
85
|
next uri
|
85
86
|
end
|
@@ -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;
|
@@ -4171,6 +4177,19 @@ SWIGINTERN VALUE GPS_Solver_Sl_double_Sg__get_correction(GPS_Solver< double > co
|
|
4171
4177
|
SWIGINTERN VALUE GPS_Solver_Sl_double_Sg__set_correction(GPS_Solver< double > *self,VALUE hash){
|
4172
4178
|
return self->update_correction(true, hash);
|
4173
4179
|
}
|
4180
|
+
SWIGINTERN GPS_Solver< double >::super_t::options_t GPS_Solver_Sl_double_Sg__get_options(GPS_Solver< double > const *self){
|
4181
|
+
return self->available_options();
|
4182
|
+
}
|
4183
|
+
SWIGINTERN GPS_Solver< double >::super_t::options_t GPS_Solver_Sl_double_Sg__set_options(GPS_Solver< double > *self,VALUE obj){
|
4184
|
+
GPS_Solver<double>::super_t::options_t opt(self->available_options());
|
4185
|
+
|
4186
|
+
if(!RB_TYPE_P(obj, T_HASH)){SWIG_exception(SWIG_TypeError, "Hash is expected");}
|
4187
|
+
SWIG_AsVal_bool (
|
4188
|
+
rb_hash_lookup(obj, ID2SYM(rb_intern("skip_exclusion"))),
|
4189
|
+
&opt.skip_exclusion);
|
4190
|
+
|
4191
|
+
return self->update_options(opt);
|
4192
|
+
}
|
4174
4193
|
SWIGINTERN unsigned int SBAS_Ephemeris_Sl_double_Sg__set_svid(SBAS_Ephemeris< double > *self,unsigned int const &v){
|
4175
4194
|
return self->svid = v;
|
4176
4195
|
}
|
@@ -17225,6 +17244,100 @@ fail:
|
|
17225
17244
|
}
|
17226
17245
|
|
17227
17246
|
|
17247
|
+
/*
|
17248
|
+
Document-method: GPS_PVT::GPS::Solver.options
|
17249
|
+
|
17250
|
+
call-seq:
|
17251
|
+
options -> GPS_Solver< double >::super_t::options_t
|
17252
|
+
|
17253
|
+
An instance method.
|
17254
|
+
|
17255
|
+
*/
|
17256
|
+
SWIGINTERN VALUE
|
17257
|
+
_wrap_Solver_options(int argc, VALUE *argv, VALUE self) {
|
17258
|
+
GPS_Solver< double > *arg1 = (GPS_Solver< double > *) 0 ;
|
17259
|
+
void *argp1 = 0 ;
|
17260
|
+
int res1 = 0 ;
|
17261
|
+
GPS_Solver< double >::super_t::options_t result;
|
17262
|
+
VALUE vresult = Qnil;
|
17263
|
+
|
17264
|
+
if ((argc < 0) || (argc > 0)) {
|
17265
|
+
rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)",argc); SWIG_fail;
|
17266
|
+
}
|
17267
|
+
res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_GPS_SolverT_double_t, 0 | 0 );
|
17268
|
+
if (!SWIG_IsOK(res1)) {
|
17269
|
+
SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "GPS_Solver< double > const *","get_options", 1, self ));
|
17270
|
+
}
|
17271
|
+
arg1 = reinterpret_cast< GPS_Solver< double > * >(argp1);
|
17272
|
+
{
|
17273
|
+
try {
|
17274
|
+
result = GPS_Solver_Sl_double_Sg__get_options((GPS_Solver< double > const *)arg1);
|
17275
|
+
} catch (const native_exception &e) {
|
17276
|
+
e.regenerate();
|
17277
|
+
SWIG_fail;
|
17278
|
+
} catch (const std::exception& e) {
|
17279
|
+
SWIG_exception_fail(SWIG_RuntimeError, e.what());
|
17280
|
+
}
|
17281
|
+
}
|
17282
|
+
{
|
17283
|
+
VALUE res(rb_hash_new());
|
17284
|
+
rb_hash_aset(res, ID2SYM(rb_intern("skip_exclusion")), SWIG_From_bool ((&result)->skip_exclusion));
|
17285
|
+
vresult = res;
|
17286
|
+
}
|
17287
|
+
return vresult;
|
17288
|
+
fail:
|
17289
|
+
return Qnil;
|
17290
|
+
}
|
17291
|
+
|
17292
|
+
|
17293
|
+
/*
|
17294
|
+
Document-method: GPS_PVT::GPS::Solver.options=
|
17295
|
+
|
17296
|
+
call-seq:
|
17297
|
+
options=(VALUE obj) -> GPS_Solver< double >::super_t::options_t
|
17298
|
+
|
17299
|
+
An instance method.
|
17300
|
+
|
17301
|
+
*/
|
17302
|
+
SWIGINTERN VALUE
|
17303
|
+
_wrap_Solver_optionse___(int argc, VALUE *argv, VALUE self) {
|
17304
|
+
GPS_Solver< double > *arg1 = (GPS_Solver< double > *) 0 ;
|
17305
|
+
VALUE arg2 = (VALUE) 0 ;
|
17306
|
+
void *argp1 = 0 ;
|
17307
|
+
int res1 = 0 ;
|
17308
|
+
GPS_Solver< double >::super_t::options_t result;
|
17309
|
+
VALUE vresult = Qnil;
|
17310
|
+
|
17311
|
+
if ((argc < 1) || (argc > 1)) {
|
17312
|
+
rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)",argc); SWIG_fail;
|
17313
|
+
}
|
17314
|
+
res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_GPS_SolverT_double_t, 0 | 0 );
|
17315
|
+
if (!SWIG_IsOK(res1)) {
|
17316
|
+
SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "GPS_Solver< double > *","set_options", 1, self ));
|
17317
|
+
}
|
17318
|
+
arg1 = reinterpret_cast< GPS_Solver< double > * >(argp1);
|
17319
|
+
arg2 = argv[0];
|
17320
|
+
{
|
17321
|
+
try {
|
17322
|
+
result = GPS_Solver_Sl_double_Sg__set_options(arg1,arg2);
|
17323
|
+
} catch (const native_exception &e) {
|
17324
|
+
e.regenerate();
|
17325
|
+
SWIG_fail;
|
17326
|
+
} catch (const std::exception& e) {
|
17327
|
+
SWIG_exception_fail(SWIG_RuntimeError, e.what());
|
17328
|
+
}
|
17329
|
+
}
|
17330
|
+
{
|
17331
|
+
VALUE res(rb_hash_new());
|
17332
|
+
rb_hash_aset(res, ID2SYM(rb_intern("skip_exclusion")), SWIG_From_bool ((&result)->skip_exclusion));
|
17333
|
+
vresult = res;
|
17334
|
+
}
|
17335
|
+
return vresult;
|
17336
|
+
fail:
|
17337
|
+
return Qnil;
|
17338
|
+
}
|
17339
|
+
|
17340
|
+
|
17228
17341
|
SWIGINTERN void
|
17229
17342
|
free_GPS_Solver_Sl_double_Sg_(void *self) {
|
17230
17343
|
GPS_Solver< double > *arg1 = (GPS_Solver< double > *)self;
|
@@ -26933,6 +27046,8 @@ SWIGEXPORT void Init_GPS(void) {
|
|
26933
27046
|
rb_define_method(SwigClassSolver.klass, "solve", VALUEFUNC(_wrap_Solver_solve), -1);
|
26934
27047
|
rb_define_method(SwigClassSolver.klass, "correction", VALUEFUNC(_wrap_Solver_correction), -1);
|
26935
27048
|
rb_define_method(SwigClassSolver.klass, "correction=", VALUEFUNC(_wrap_Solver_correctione___), -1);
|
27049
|
+
rb_define_method(SwigClassSolver.klass, "options", VALUEFUNC(_wrap_Solver_options), -1);
|
27050
|
+
rb_define_method(SwigClassSolver.klass, "options=", VALUEFUNC(_wrap_Solver_optionse___), -1);
|
26936
27051
|
SwigClassSolver.mark = (void (*)(void *)) GPS_Solver<double>::mark;
|
26937
27052
|
SwigClassSolver.destroy = (void (*)(void *)) free_GPS_Solver_Sl_double_Sg_;
|
26938
27053
|
SwigClassSolver.trackObjects = 0;
|
@@ -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
|
@@ -474,12 +499,7 @@ struct GPS_Solver_Base {
|
|
474
499
|
using super_t::reset;
|
475
500
|
void reset(const int &prn) {set(prn, false);}
|
476
501
|
std::vector<int> excluded() const {
|
477
|
-
|
478
|
-
for(std::vector<int>::iterator it(res.begin()), it_end(res.end());
|
479
|
-
it != it_end; ++it){
|
480
|
-
*it += prn_begin;
|
481
|
-
}
|
482
|
-
return res;
|
502
|
+
return super_t::indices_one(prn_begin);
|
483
503
|
}
|
484
504
|
};
|
485
505
|
};
|
@@ -519,6 +539,9 @@ protected:
|
|
519
539
|
* @param G_ original design matrix
|
520
540
|
* @param rotation_matrix 3 by 3 rotation matrix
|
521
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).
|
522
545
|
*/
|
523
546
|
static matrix_t rotate_G(const MatrixT &G_, const matrix_t &rotation_matrix){
|
524
547
|
unsigned int r(G_.rows()), c(G_.columns()); // Normally c=4
|
@@ -534,17 +557,15 @@ protected:
|
|
534
557
|
}
|
535
558
|
|
536
559
|
/**
|
537
|
-
* Calculate C matrix,
|
538
|
-
* C = G^t * W * G
|
539
|
-
*
|
560
|
+
* Calculate C matrix, where C = G^t * G to be used for DOP calculation
|
540
561
|
* @return C matrix
|
541
562
|
*/
|
542
563
|
matrix_t C() const {
|
543
|
-
return (G.transpose() *
|
564
|
+
return (G.transpose() * G).inverse();
|
544
565
|
}
|
545
566
|
/**
|
546
|
-
* Transform coordinate of matrix C
|
547
|
-
* 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,
|
548
569
|
* where R is a rotation matrix, for example, ECEF to ENU.
|
549
570
|
*
|
550
571
|
* @param rotation_matrix 3 by 3 rotation matrix
|
@@ -809,14 +830,14 @@ protected:
|
|
809
830
|
res.receiver_error, time_arrival,
|
810
831
|
res.user_position, zero));
|
811
832
|
|
812
|
-
if(prop.
|
833
|
+
if(prop.range_sigma <= 0){
|
813
834
|
continue; // intentionally excluded satellite
|
814
835
|
}else{
|
815
836
|
res.used_satellite_mask.set(it->prn);
|
816
837
|
}
|
817
838
|
|
818
839
|
if(coarse_estimation){
|
819
|
-
prop.
|
840
|
+
prop.range_sigma = 1;
|
820
841
|
}else{
|
821
842
|
idx_rate_rel.push_back(std::make_pair(i_measurement, prop.rate_relative_neg));
|
822
843
|
}
|
@@ -825,7 +846,7 @@ protected:
|
|
825
846
|
geomat.G(i_row, 0) = prop.los_neg[0];
|
826
847
|
geomat.G(i_row, 1) = prop.los_neg[1];
|
827
848
|
geomat.G(i_row, 2) = prop.los_neg[2];
|
828
|
-
geomat.W(i_row, i_row) = prop.
|
849
|
+
geomat.W(i_row, i_row) = 1. / std::pow(prop.range_sigma, 2);
|
829
850
|
|
830
851
|
++i_row;
|
831
852
|
}
|