gps_pvt 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6820c87f08dcf6c64e70d304c634b32c1bea265d6f360fc79c8c9405822569a
4
- data.tar.gz: 75b9db05a1c23827fc9e1163b476f4e3293a7d09efbf4a9a1cd9636dda7387bd
3
+ metadata.gz: 3ceeb392e9d4a3df9a3b6af1ed6b19a92fec4fae110c9c89118602f3213d6905
4
+ data.tar.gz: c91c94043d2e39dde8ea0909dbd862f0194b15a4f2baa3030b48d760f933a147
5
5
  SHA512:
6
- metadata.gz: f22eb60b17dc0bbd6a8c249deedd39e0fa5ec179bf28abb5c36cdf4191d79219af0dd456af93e8432f93173d07e42debdef11cdcdf6c9327395c38535c7d8de5
7
- data.tar.gz: a7643df74fd9947d07383353d4cdf860b45494b13e2c332ccb2176a771112acd3887a9a8dc95e62ad8f97841506cceaccb79d8ba990dc6148aeb66efc07f51a3
6
+ metadata.gz: bcf702f83fd0df488470b385df370c3639e953b064a6acd9cdc4418a98b03ffab9c15dc9af0ad79185177bc3feb03e74b0ee0c9dba667bf73af1e1afabdca7be
7
+ data.tar.gz: eac046233bfd52db17a3fb94d2c74fcf01c8cd3200d0d5dce2546a669bb9313cccda4bdb74e2b991f1397d27325ec87d79d8c0e87be36b05fa6856daf0342ad0
data/README.md CHANGED
@@ -41,7 +41,7 @@ From version 0.2.0, SBAS and QZSS are supported in addition to GPS. QZSS ranging
41
41
 
42
42
  $ gps_pvt --with=137 RINEX_or_UBX_file(s)
43
43
 
44
- For developer, this library will be used in the following:
44
+ For developer, this library will be used like:
45
45
 
46
46
  ```ruby
47
47
  require 'gps_pvt'
@@ -81,25 +81,41 @@ receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
81
81
  }
82
82
  }
83
83
 
84
- # Customize solution
84
+ ## Further customization
85
+ # General options
85
86
  receiver.solver.gps_options.exclude(prn) # Exclude satellite; the default is to use every satellite if visible
86
87
  receiver.solver.gps_options.include(prn) # Discard previous setting of exclusion
87
88
  receiver.solver.gps_options.elevation_mask = Math::PI / 180 * 10 # example 10 [deg] elevation mask
89
+ # receiver.solver.sbas_options is for SBAS.
90
+
91
+ # Precise control of properties for each satellite and for each iteration
88
92
  receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
89
- # control weight per satellite per iteration
90
93
  weight, range_c, range_r, rate_rel_neg, *los_neg = rel_prop # relative property
91
94
  # meas is measurement represented by pseudo range of the selected satellite.
92
95
  # rcv_e, t_arv, usr_pos, usr_vel are temporary solution of
93
96
  # receiver clock error [m], time of arrival [s], user position and velocity in ECEF, respectively.
94
97
 
95
98
  weight = 1 # same as default; identical weight for each visible satellite
96
- # or weight based on elevation
99
+ # or weight based on elevation, for example:
97
100
  # elv = GPS_PVT::Coordinate::ENU::relative_rel(GPS_PVT::Coordinate::XYZ::new(*los_neg), usr_pos).elevation
98
101
  # weight = (Math::sin(elv)/0.8)**2
99
102
 
100
103
  [weight, range_c, range_r, rate_rel_neg] + los_neg # must return relative property
101
104
  }
102
105
 
106
+ # Range correction (since v0.3.0)
107
+ receiver.solver.correction = { # provide by using a Hash
108
+ # ionospheric and transpheric models are changeable, and current configuration
109
+ # can be obtained by receiver.solver.correction without assigner.
110
+ :gps_ionospheric => proc{|t, usr_pos_xyz, sat_pos_enu|
111
+ # t, usr_pos_xyz, sat_pos_enu are temporary solution of
112
+ # time of arrival [s], user position in ECEF,
113
+ # and satellite position in ENU respectively.
114
+ 0 # must return correction value, delaying is negative.
115
+ },
116
+ # combination of (gps or sbas) and (ionospheric or tropospheric) are available
117
+ }
118
+
103
119
  # Dynamic customization of weight for each epoch
104
120
  (class << receiver; self; end).instance_eval{ # do before parse_XXX
105
121
  alias_method(:run_orig, :run)
data/Rakefile CHANGED
File without changes
data/exe/gps_pvt CHANGED
File without changes
@@ -2507,6 +2507,26 @@ struct SBAS_SolverOptions
2507
2507
  };
2508
2508
 
2509
2509
 
2510
+ template <class FloatT>
2511
+ struct GPS_RangeCorrector
2512
+ : public GPS_Solver_Base<FloatT>::range_corrector_t {
2513
+ VALUE callback;
2514
+ GPS_RangeCorrector(const VALUE &callback_)
2515
+ : GPS_Solver_Base<FloatT>::range_corrector_t(),
2516
+ callback(callback_) {}
2517
+ bool is_available(const typename GPS_Solver_Base<FloatT>::gps_time_t &t) const {
2518
+ return false;
2519
+ }
2520
+ FloatT *calculate(
2521
+ const typename GPS_Solver_Base<FloatT>::gps_time_t &t,
2522
+ const typename GPS_Solver_Base<FloatT>::pos_t &usr_pos,
2523
+ const typename GPS_Solver_Base<FloatT>::enu_t &sat_rel_pos,
2524
+ FloatT &buf) const {
2525
+ return NULL;
2526
+ }
2527
+ };
2528
+
2529
+
2510
2530
  template <class FloatT>
2511
2531
  struct GPS_Solver
2512
2532
  : public GPS_Solver_RAIM_LSR<FloatT,
@@ -2527,14 +2547,22 @@ struct GPS_Solver
2527
2547
  sbas_t() : space_node(), options(), solver(space_node) {}
2528
2548
  } sbas;
2529
2549
  VALUE hooks;
2550
+ typedef std::vector<GPS_RangeCorrector<FloatT> > user_correctors_t;
2551
+ user_correctors_t user_correctors;
2530
2552
 
2531
2553
  static void mark(void *ptr){
2532
2554
  GPS_Solver<FloatT> *solver = (GPS_Solver<FloatT> *)ptr;
2533
- if(solver->hooks == Qnil){return;}
2534
2555
  rb_gc_mark(solver->hooks);
2556
+ for(typename user_correctors_t::const_iterator
2557
+ it(solver->user_correctors.begin()), it_end(solver->user_correctors.end());
2558
+ it != it_end; ++it){
2559
+ rb_gc_mark(it->callback);
2560
+ }
2535
2561
  }
2536
2562
 
2537
- GPS_Solver() : super_t(), gps(), sbas(), hooks() {
2563
+ GPS_Solver() : super_t(),
2564
+ gps(), sbas(),
2565
+ hooks(), user_correctors() {
2538
2566
 
2539
2567
  hooks = rb_hash_new();
2540
2568
 
@@ -2606,6 +2634,19 @@ struct GPS_Solver
2606
2634
  if(!update){break;}
2607
2635
  typename range_correction_list_t::const_iterator it(list.find(i));
2608
2636
  if(it == list.end()){break;}
2637
+
2638
+ // Remove user defined unused correctors
2639
+ for(typename base_t::range_correction_t::const_iterator
2640
+ it2(root[i]->begin()), it2_end(root[i]->end());
2641
+ it2 != it2_end; ++it2){
2642
+ for(typename user_correctors_t::const_iterator
2643
+ it3(user_correctors.begin()), it3_end(user_correctors.end());
2644
+ it3 != it3_end; ++it3){
2645
+ if(*it2 != &(*it3)){continue;}
2646
+ user_correctors.erase(it3);
2647
+ }
2648
+ }
2649
+
2609
2650
  root[i]->clear();
2610
2651
  for(typename range_correction_list_t::mapped_type::const_iterator
2611
2652
  it2(it->second.begin()), it2_end(it->second.end());
@@ -3794,6 +3835,38 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
3794
3835
  }
3795
3836
 
3796
3837
 
3838
+ template <>
3839
+ bool GPS_RangeCorrector<double>::is_available(
3840
+ const typename GPS_Solver_Base<double>::gps_time_t &t) const {
3841
+ VALUE values[] = {
3842
+ SWIG_NewPointerObj(
3843
+ const_cast< GPS_Time<double> * >(&t), SWIGTYPE_p_GPS_TimeT_double_t, 0),
3844
+ };
3845
+ VALUE res(proc_call_throw_if_error(
3846
+ callback, sizeof(values) / sizeof(values[0]), values));
3847
+ return RTEST(res) ? true : false;
3848
+ }
3849
+ template <>
3850
+ double *GPS_RangeCorrector<double>::calculate(
3851
+ const typename GPS_Solver_Base<double>::gps_time_t &t,
3852
+ const typename GPS_Solver_Base<double>::pos_t &usr_pos,
3853
+ const typename GPS_Solver_Base<double>::enu_t &sat_rel_pos,
3854
+ double &buf) const {
3855
+ VALUE values[] = {
3856
+ SWIG_NewPointerObj(
3857
+ const_cast< GPS_Time<double> * >(&t),
3858
+ SWIGTYPE_p_GPS_TimeT_double_t, 0),
3859
+ SWIG_NewPointerObj(
3860
+ (const_cast< System_XYZ<double,WGS84> * >(&usr_pos.xyz)),
3861
+ SWIGTYPE_p_System_XYZT_double_WGS84_t, 0),
3862
+ SWIG_NewPointerObj(
3863
+ (const_cast< System_ENU<double,WGS84> * >(&sat_rel_pos)),
3864
+ SWIGTYPE_p_System_ENUT_double_WGS84_t, 0),
3865
+ };
3866
+ VALUE res(proc_call_throw_if_error(
3867
+ callback, sizeof(values) / sizeof(values[0]), values));
3868
+ return SWIG_IsOK(swig::asval(res, &buf)) ? &buf : NULL;
3869
+ }
3797
3870
  template<>
3798
3871
  VALUE GPS_Solver<double>::update_correction(
3799
3872
  const bool &update, const VALUE &hash){
@@ -3836,10 +3909,12 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
3836
3909
  for(; k < sizeof(item) / sizeof(item[0]); ++k){
3837
3910
  if(v == item[k].sym){break;}
3838
3911
  }
3839
- if(k >= sizeof(item) / sizeof(item[0])){
3840
- continue; // TODO other than symbol
3912
+ if(k >= sizeof(item) / sizeof(item[0])){ // other than symbol
3913
+ user_correctors.push_back(GPS_RangeCorrector<double>(v));
3914
+ input[i].push_back(&user_correctors.back());
3915
+ }else{
3916
+ input[i].push_back(item[k].obj);
3841
3917
  }
3842
- input[i].push_back(item[k].obj);
3843
3918
  }
3844
3919
  }
3845
3920
  VALUE opt(rb_hash_lookup(hash, k_opt));
@@ -3866,10 +3941,12 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
3866
3941
  for(; i < sizeof(item) / sizeof(item[0]); ++i){
3867
3942
  if(*it2 == item[i].obj){break;}
3868
3943
  }
3869
- if(i >= sizeof(item) / sizeof(item[0])){
3870
- continue; // TODO other than built-in corrector
3944
+ if(i >= sizeof(item) / sizeof(item[0])){ // other than built-in corrector
3945
+ rb_ary_push(v,
3946
+ reinterpret_cast<const GPS_RangeCorrector<double> *>(*it2)->callback);
3947
+ }else{
3948
+ rb_ary_push(v, item[i].sym);
3871
3949
  }
3872
- rb_ary_push(v, item[i].sym);
3873
3950
  }
3874
3951
  rb_hash_aset(res, k, v);
3875
3952
  }
File without changes
File without changes
File without changes
@@ -652,14 +652,14 @@ protected:
652
652
  // Least square
653
653
  matrix_t delta_x(geomat.partial(res.used_satellites).least_square());
654
654
 
655
- xyz_t delta_user_position(delta_x.partial(3, 1, 0, 0));
655
+ xyz_t delta_user_position(delta_x.partial(3, 1));
656
656
  res.user_position.xyz += delta_user_position;
657
657
  res.user_position.llh = res.user_position.xyz.llh();
658
658
 
659
659
  const float_t &delta_receiver_error(delta_x(3, 0));
660
660
  res.receiver_error += delta_receiver_error;
661
661
 
662
- return ((delta_x.transpose() * delta_x)(0, 0) <= 1E-6); // equivalent to abs(x) <= 1E-3 [m]
662
+ return (delta_x.partial(4, 1).norm2F() <= 1E-6); // equivalent to abs(x) <= 1E-3 [m]
663
663
  }
664
664
 
665
665
  struct user_pvt_opt_t {
File without changes
File without changes
File without changes
@@ -878,6 +878,27 @@ struct SBAS_SolverOptions
878
878
  };
879
879
  %}
880
880
 
881
+ %header {
882
+ template <class FloatT>
883
+ struct GPS_RangeCorrector
884
+ : public GPS_Solver_Base<FloatT>::range_corrector_t {
885
+ SWIG_Object callback;
886
+ GPS_RangeCorrector(const SWIG_Object &callback_)
887
+ : GPS_Solver_Base<FloatT>::range_corrector_t(),
888
+ callback(callback_) {}
889
+ bool is_available(const typename GPS_Solver_Base<FloatT>::gps_time_t &t) const {
890
+ return false;
891
+ }
892
+ FloatT *calculate(
893
+ const typename GPS_Solver_Base<FloatT>::gps_time_t &t,
894
+ const typename GPS_Solver_Base<FloatT>::pos_t &usr_pos,
895
+ const typename GPS_Solver_Base<FloatT>::enu_t &sat_rel_pos,
896
+ FloatT &buf) const {
897
+ return NULL;
898
+ }
899
+ };
900
+ }
901
+
881
902
  %extend GPS_Solver {
882
903
  %ignore super_t;
883
904
  %ignore base_t;
@@ -889,6 +910,8 @@ struct SBAS_SolverOptions
889
910
  %ignore relative_property;
890
911
  %ignore satellite_position;
891
912
  %ignore update_position_solution;
913
+ %ignore user_correctors_t;
914
+ %ignore user_correctors;
892
915
  %immutable hooks;
893
916
  %ignore mark;
894
917
  %fragment("hook"{GPS_Solver<FloatT>}, "header",
@@ -1033,6 +1056,38 @@ struct SBAS_SolverOptions
1033
1056
  %fragment("correction"{GPS_Solver<FloatT>}, "header",
1034
1057
  fragment=SWIG_From_frag(int),
1035
1058
  fragment=SWIG_Traits_frag(FloatT)){
1059
+ template <>
1060
+ bool GPS_RangeCorrector<FloatT>::is_available(
1061
+ const typename GPS_Solver_Base<FloatT>::gps_time_t &t) const {
1062
+ VALUE values[] = {
1063
+ SWIG_NewPointerObj(
1064
+ %const_cast(&t, GPS_Time<FloatT> *), $descriptor(GPS_Time<FloatT> *), 0),
1065
+ };
1066
+ VALUE res(proc_call_throw_if_error(
1067
+ callback, sizeof(values) / sizeof(values[0]), values));
1068
+ return RTEST(res) ? true : false;
1069
+ }
1070
+ template <>
1071
+ FloatT *GPS_RangeCorrector<FloatT>::calculate(
1072
+ const typename GPS_Solver_Base<FloatT>::gps_time_t &t,
1073
+ const typename GPS_Solver_Base<FloatT>::pos_t &usr_pos,
1074
+ const typename GPS_Solver_Base<FloatT>::enu_t &sat_rel_pos,
1075
+ FloatT &buf) const {
1076
+ VALUE values[] = {
1077
+ SWIG_NewPointerObj(
1078
+ %const_cast(&t, GPS_Time<FloatT> *),
1079
+ $descriptor(GPS_Time<FloatT> *), 0),
1080
+ SWIG_NewPointerObj(
1081
+ (%const_cast(&usr_pos.xyz, System_XYZ<FloatT, WGS84> *)),
1082
+ $descriptor(System_XYZ<FloatT, WGS84> *), 0),
1083
+ SWIG_NewPointerObj(
1084
+ (%const_cast(&sat_rel_pos, System_ENU<FloatT, WGS84> *)),
1085
+ $descriptor(System_ENU<FloatT, WGS84> *), 0),
1086
+ };
1087
+ VALUE res(proc_call_throw_if_error(
1088
+ callback, sizeof(values) / sizeof(values[0]), values));
1089
+ return SWIG_IsOK(swig::asval(res, &buf)) ? &buf : NULL;
1090
+ }
1036
1091
  template<>
1037
1092
  VALUE GPS_Solver<FloatT>::update_correction(
1038
1093
  const bool &update, const VALUE &hash){
@@ -1075,10 +1130,12 @@ struct SBAS_SolverOptions
1075
1130
  for(; k < sizeof(item) / sizeof(item[0]); ++k){
1076
1131
  if(v == item[k].sym){break;}
1077
1132
  }
1078
- if(k >= sizeof(item) / sizeof(item[0])){
1079
- continue; // TODO other than symbol
1133
+ if(k >= sizeof(item) / sizeof(item[0])){ // other than symbol
1134
+ user_correctors.push_back(GPS_RangeCorrector<FloatT>(v));
1135
+ input[i].push_back(&user_correctors.back());
1136
+ }else{
1137
+ input[i].push_back(item[k].obj);
1080
1138
  }
1081
- input[i].push_back(item[k].obj);
1082
1139
  }
1083
1140
  }
1084
1141
  VALUE opt(rb_hash_lookup(hash, k_opt));
@@ -1105,10 +1162,12 @@ struct SBAS_SolverOptions
1105
1162
  for(; i < sizeof(item) / sizeof(item[0]); ++i){
1106
1163
  if(*it2 == item[i].obj){break;}
1107
1164
  }
1108
- if(i >= sizeof(item) / sizeof(item[0])){
1109
- continue; // TODO other than built-in corrector
1165
+ if(i >= sizeof(item) / sizeof(item[0])){ // other than built-in corrector
1166
+ rb_ary_push(v,
1167
+ reinterpret_cast<const GPS_RangeCorrector<FloatT> *>(*it2)->callback);
1168
+ }else{
1169
+ rb_ary_push(v, item[i].sym);
1110
1170
  }
1111
- rb_ary_push(v, item[i].sym);
1112
1171
  }
1113
1172
  rb_hash_aset(res, k, v);
1114
1173
  }
@@ -1160,14 +1219,22 @@ struct GPS_Solver
1160
1219
  sbas_t() : space_node(), options(), solver(space_node) {}
1161
1220
  } sbas;
1162
1221
  SWIG_Object hooks;
1222
+ typedef std::vector<GPS_RangeCorrector<FloatT> > user_correctors_t;
1223
+ user_correctors_t user_correctors;
1163
1224
  #ifdef SWIGRUBY
1164
1225
  static void mark(void *ptr){
1165
1226
  GPS_Solver<FloatT> *solver = (GPS_Solver<FloatT> *)ptr;
1166
- if(solver->hooks == Qnil){return;}
1167
1227
  rb_gc_mark(solver->hooks);
1228
+ for(typename user_correctors_t::const_iterator
1229
+ it(solver->user_correctors.begin()), it_end(solver->user_correctors.end());
1230
+ it != it_end; ++it){
1231
+ rb_gc_mark(it->callback);
1232
+ }
1168
1233
  }
1169
1234
  #endif
1170
- GPS_Solver() : super_t(), gps(), sbas(), hooks() {
1235
+ GPS_Solver() : super_t(),
1236
+ gps(), sbas(),
1237
+ hooks(), user_correctors() {
1171
1238
  #ifdef SWIGRUBY
1172
1239
  hooks = rb_hash_new();
1173
1240
  #endif
@@ -1239,6 +1306,19 @@ struct GPS_Solver
1239
1306
  if(!update){break;}
1240
1307
  typename range_correction_list_t::const_iterator it(list.find(i));
1241
1308
  if(it == list.end()){break;}
1309
+
1310
+ // Remove user defined unused correctors
1311
+ for(typename base_t::range_correction_t::const_iterator
1312
+ it2(root[i]->begin()), it2_end(root[i]->end());
1313
+ it2 != it2_end; ++it2){
1314
+ for(typename user_correctors_t::const_iterator
1315
+ it3(user_correctors.begin()), it3_end(user_correctors.end());
1316
+ it3 != it3_end; ++it3){
1317
+ if(*it2 != &(*it3)){continue;}
1318
+ user_correctors.erase(it3);
1319
+ }
1320
+ }
1321
+
1242
1322
  root[i]->clear();
1243
1323
  for(typename range_correction_list_t::mapped_type::const_iterator
1244
1324
  it2(it->second.begin()), it2_end(it->second.end());
File without changes
@@ -343,7 +343,12 @@ __RINEX_OBS_TEXT__
343
343
  expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
344
344
  expect{solver.correction = nil}.to raise_error(RuntimeError)
345
345
  expect{solver.correction = {
346
- :gps_ionospheric => [:klobuchar, :no_correction],
346
+ :gps_ionospheric => [proc{|t, usr_pos, sat_pos|
347
+ expect(t).to be_a_kind_of(GPS::Time)
348
+ expect(usr_pos).to be_a_kind_of(Coordinate::XYZ) unless usr_pos
349
+ expect(sat_pos).to be_a_kind_of(Coordinate::ENU) unless sat_pos
350
+ false
351
+ }, :klobuchar, :no_correction],
347
352
  :options => {:f_10_7 => 10},
348
353
  }}.not_to raise_error
349
354
  expect(solver.correction[:gps_ionospheric]).to include(:no_correction)
data/gps_pvt.gemspec CHANGED
File without changes
@@ -47,23 +47,31 @@ class Receiver
47
47
  ]] + [
48
48
  [:used_satellites, proc{|pvt| pvt.used_satellites}],
49
49
  ] + opt[:system].collect{|sys, range|
50
- bit_flip = if range.kind_of?(Array) then
51
- proc{|res, i|
50
+ range = range.kind_of?(Array) ? proc{
51
+ # check whether inputs can be converted to Range
52
+ next nil if range.empty?
53
+ a, b = range.minmax
54
+ ((b - a) == (range.length - 1)) ? (a..b) : range
55
+ }.call : range
56
+ next nil unless range
57
+ bit_flip, label = case range
58
+ when Array
59
+ [proc{|res, i|
52
60
  res[i] = "1" if i = range.index(i)
53
61
  res
54
- }
55
- else # expect Range
62
+ }, range.collect{|pen| pen & 0xFF}.reverse.join('+')]
63
+ when Range
56
64
  base_prn = range.min
57
- proc{|res, i|
65
+ [proc{|res, i|
58
66
  res[i - base_prn] = "1" if range.include?(i)
59
67
  res
60
- }
68
+ }, [:max, :min].collect{|f| range.send(f) & 0xFF}.join('..')]
61
69
  end
62
- ["#{sys}_PRN", proc{|pvt|
70
+ ["#{sys}_PRN(#{label})", proc{|pvt|
63
71
  pvt.used_satellite_list.inject("0" * range.size, &bit_flip) \
64
72
  .scan(/.{1,8}/).join('_').reverse
65
73
  }]
66
- } + [[
74
+ }.compact + [[
67
75
  opt[:satellites].collect{|prn, label|
68
76
  [:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str|
69
77
  "#{str}(#{label || prn})"
@@ -138,8 +146,11 @@ class Receiver
138
146
  rel_prop
139
147
  }
140
148
  @debug = {}
141
- [:gps_options, :sbas_options].each{|target|
142
- opt = @solver.send(target) # default solver options
149
+ solver_opts = [:gps_options, :sbas_options].collect{|target|
150
+ @solver.send(target)
151
+ }
152
+ solver_opts.each{|opt|
153
+ # default solver options
143
154
  opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg (use satellite over horizon)
144
155
  opt.residual_mask = 1E4 # 10 km (without residual filter, practically)
145
156
  }
@@ -168,6 +179,13 @@ class Receiver
168
179
  when :identical # same as default
169
180
  next true
170
181
  end
182
+ when :elevation_mask_deg
183
+ raise "Unknown elevation mask angle: #{v}" unless elv_deg = (Float(v) rescue nil)
184
+ $stderr.puts "Elevation mask: #{elv_deg} deg"
185
+ solver_opts.each{|opt|
186
+ opt.elevation_mask = elv_deg / 180 * Math::PI # 0 deg (use satellite over horizon)
187
+ }
188
+ next true
171
189
  when :base_station
172
190
  crd, sys = v.split(/ *, */).collect.with_index{|item, i|
173
191
  case item
@@ -219,12 +237,14 @@ class Receiver
219
237
  i = -1
220
238
  output_options[:system] << [sys_target, []]
221
239
  else
222
- output_options[:system][i].reject!{|prn| prns.include?(prn)}
240
+ output_options[:system][i][1].reject!{|prn| prns.include?(prn)}
223
241
  end
224
242
  output_options[:satellites].reject!{|prn, label| prns.include?(prn)}
225
243
  if mode == :include then
226
244
  output_options[:system][i][1] += prns
245
+ output_options[:system][i][1].sort!
227
246
  output_options[:satellites] += (labels ? prns.zip(labels) : prns)
247
+ output_options[:satellites].sort!{|a, b| [a].flatten[0] <=> [b].flatten[0]}
228
248
  end
229
249
  }
230
250
  check_sys_svid = proc{|sys_target, range_in_sys, offset|
@@ -328,6 +348,11 @@ class Receiver
328
348
  [:azimuth, :elevation, :slopeH, :slopeV].each{|k|
329
349
  eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
330
350
  }
351
+ define_method(:other_state){
352
+ # If a design matrix G has columns larger than 4,
353
+ # other states excluding position and time are estimated.
354
+ (self.G.rows <= 4) ? [] : (self.S * self.delta_r).transpose.to_a[0][4..-1]
355
+ }
331
356
  }
332
357
 
333
358
  proc{
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.2.3"
4
+ VERSION = "0.3.0"
5
5
  end
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.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fenrir(M.Naruoka)
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-02-22 00:00:00.000000000 Z
11
+ date: 2022-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -100,7 +100,7 @@ licenses: []
100
100
  metadata:
101
101
  homepage_uri: https://github.com/fenrir-naru/gps_pvt
102
102
  source_code_uri: https://github.com/fenrir-naru/gps_pvt
103
- post_install_message:
103
+ post_install_message:
104
104
  rdoc_options: []
105
105
  require_paths:
106
106
  - lib
@@ -115,8 +115,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  requirements: []
118
- rubygems_version: 3.3.7
119
- signing_key:
118
+ rubygems_version: 3.1.2
119
+ signing_key:
120
120
  specification_version: 4
121
121
  summary: GPS position, velocity, and time (PVT) solver
122
122
  test_files: []