gps_pvt 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []