gps_pvt 0.4.0 → 0.5.1

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: 7ba5a3a5c8364753e1f6384519a015f29459e51e17bd82eb81e398c89e80619f
4
- data.tar.gz: 565bd717bb178be9784b9b249860270b9d45e33e8af456ab1042db5d75b34c04
3
+ metadata.gz: 6b4f5df780c4ff0977b025fab381c327b04a3e8fae742cc6ff672e08839d9c51
4
+ data.tar.gz: cbbb233acbe548944d9b317449160465ec04d2041e56f54decc286a900503723
5
5
  SHA512:
6
- metadata.gz: 1fef4a8e70a4872f17219f3f97a2ac21d0b5feb44a611d747291d842dd6939cd297426c53cbd16875ece618b610d557542f98c720177a05e95db63f9dcd788c9
7
- data.tar.gz: 60450ca6f661f0eaeb0c194caa97f4246dd723edbf2f566c5775fa91dffb621357aca7732b54fb7d27b636cb0864b2f26c40efa661fb3afde029b6a4bc985d63
6
+ metadata.gz: a724b51f5fe55fe9c0d74d02176c26dbad63bcc1fc1fbd477c7a0707e06f832df5110f4a80c5c2807ac5e1ae2d0717846858a30365eec874847c96fb11e12b5e
7
+ data.tar.gz: 97b3670fd401bc860d9f16ab473fe4c6c33ee87fcf6855757490ec724d8405a6aeb7df22aabaf7e37f559d80ce9b4ce1c764c550acccf591bb940805b3c660a6
data/README.md CHANGED
@@ -32,7 +32,7 @@ An attached executable is useful. After installation, type
32
32
 
33
33
  $ gps_pvt RINEX_or_UBX_file(s)
34
34
 
35
- The format of RINEX_or_UBX_file is automatically determined with its extention, such as .ubx will be treated as UBX format. If you want to specify the file format, instead of RINEX_or_UBX_file(s), use the following arguments:
35
+ The format of RINEX_or_UBX_file is automatically determined with its extension, such as .ubx will be treated as UBX format. A gz compressed file can be specified directly, and URI such as https://... is also acceptable since version 0.5.0. If you want to specify the file format, instead of RINEX_or_UBX_file(s), use the following arguments:
36
36
 
37
37
  --rinex_nav=filename
38
38
  --rinex_obs=filename
@@ -50,6 +50,7 @@ Additionally, the following command options *--key=value* are available.
50
50
  | elevation_mask_deg | numeric | satellite elevation mask specified in degrees. *ex) --elevation_mask_deg=10* | v0.3.0 |
51
51
  | start_time | time string | start time to perform solution. GPS, UTC and other formats are supported. *ex1) --start_time=1234:5678* represents 5678 seconds in 1234 GPS week, *ex2) --start_time="2000-01-01 00:00:00 UTC"* is in UTC format. | v0.3.3 |
52
52
  | end_time | time string | end time to perform solution. Its format is the same as start_time. | v0.3.3 |
53
+ | online_ephemeris | | automatically load ephemeris published online based on observation | v0.5.0 |
53
54
 
54
55
  ### For developer
55
56
 
data/exe/gps_pvt CHANGED
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'gps_pvt'
4
+ require 'uri'
4
5
 
5
6
  # runnable quick example to solve PVT by using RINEX NAV/OBS or u-blox ubx
6
7
 
7
8
  $stderr.puts <<__STRING__
8
9
  Usage: #{__FILE__} GPS_file1 GPS_file2 ...
9
10
  As GPS_file, rinex_nav(*.YYn, *.YYh, *.YYq, *.YYg), rinex_obs(*.YYo), and ubx(*.ubx) format are currently supported.
11
+ (YY = last two digit of year)
10
12
  File format is automatically determined based on its extention described in above parentheses.
11
13
  If you want to specify its format manually, --rinex_(nav|obs)=file_name or --ubx=file_name are available.
12
14
  Supported RINEX versions are 2 and 3.
13
- Note: YY = last two digit of year.
15
+ A file having additional ".gz" extension is recognized as a file compressed by zlib.
16
+ Major URL such as http(s)://... is acceptable as an input file name.
14
17
  __STRING__
15
18
 
16
19
  options = []
@@ -54,6 +57,9 @@ options.reject!{|opt|
54
57
  end
55
58
  misc_options[opt[0]] = t
56
59
  true
60
+ when :online_ephemeris
61
+ misc_options[opt[0]] = opt[1]
62
+ true
57
63
  else
58
64
  false
59
65
  end
@@ -61,19 +67,45 @@ options.reject!{|opt|
61
67
 
62
68
  # Check file existence and extension
63
69
  files.collect!{|fname, ftype|
64
- raise "File not found: #{fname}" unless File::exist?(fname)
65
70
  ftype ||= case fname
66
- when /\.\d{2}[nhqg]$/; :rinex_nav
67
- when /\.\d{2}o$/; :rinex_obs
71
+ when /\.\d{2}[nhqg](?:\.gz)?$/; :rinex_nav
72
+ when /\.\d{2}o(?:\.gz)?$/; :rinex_obs
68
73
  when /\.ubx$/; :ubx
69
74
  else
70
75
  raise "Format cannot be guessed, use --(format, ex. rinex_nav)=#{fname}"
71
76
  end
77
+ fname = proc{
78
+ next fname if File::exist?(fname)
79
+ if uri = URI::parse(fname) and !uri.instance_of?(URI::Generic) then
80
+ next uri
81
+ end
82
+ raise "File not found: #{fname}"
83
+ }.call
72
84
  [fname, ftype]
73
85
  }
74
86
 
75
87
  rcv = GPS_PVT::Receiver::new(options)
76
88
 
89
+ proc{|src|
90
+ next unless src
91
+ loader = proc{|t_meas|
92
+ utc = Time::utc(*t_meas.c_tm)
93
+ y, yday = [:year, :yday].collect{|f| utc.send(f)}
94
+ uri = URI::parse(
95
+ "ftp://gssc.esa.int/gnss/data/daily/" +
96
+ "%04d/brdc/BRDC00IGS_R_%04d%03d0000_01D_MN.rnx.gz"%[y, y, yday])
97
+ rcv.parse_rinex_nav(uri)
98
+ uri
99
+ }
100
+ run_orig = rcv.method(:run)
101
+ eph_list = {}
102
+ rcv.define_singleton_method(:run){|meas, t_meas, *args|
103
+ w_d = [t_meas.week, (t_meas.seconds.to_i / 86400)]
104
+ eph_list[w_d] ||= loader.call(t_meas)
105
+ run_orig.call(meas, t_meas, *args)
106
+ }
107
+ }.call(misc_options[:online_ephemeris])
108
+
77
109
  proc{
78
110
  run_orig = rcv.method(:run)
79
111
  t_start, t_end = [nil, nil]
@@ -2671,10 +2671,9 @@ struct GPS_Solver
2671
2671
  const typename base_t::gps_time_t &time_arrival,
2672
2672
  const typename base_t::pos_t &usr_pos,
2673
2673
  const typename base_t::xyz_t &usr_vel) const;
2674
- virtual typename base_t::xyz_t *satellite_position(
2674
+ virtual typename base_t::satellite_t select_satellite(
2675
2675
  const typename base_t::prn_t &prn,
2676
- const typename base_t::gps_time_t &time,
2677
- typename base_t::xyz_t &res) const;
2676
+ const typename base_t::gps_time_t &time) const;
2678
2677
  virtual bool update_position_solution(
2679
2678
  const typename base_t::geometric_matrices_t &geomat,
2680
2679
  typename base_t::user_pvt_t &res) const;
@@ -3867,47 +3866,17 @@ SWIGINTERN double const &GPS_SolverOptions_Common_Sl_double_Sg__get_residual_mas
3867
3866
  return super_t::update_position_solution(geomat, res);
3868
3867
  }
3869
3868
  template <>
3870
- GPS_Solver<double>::base_t::xyz_t *GPS_Solver<double>::satellite_position(
3869
+ GPS_Solver<double>::base_t::satellite_t GPS_Solver<double>::select_satellite(
3871
3870
  const GPS_Solver<double>::base_t::prn_t &prn,
3872
- const GPS_Solver<double>::base_t::gps_time_t &time,
3873
- GPS_Solver<double>::base_t::xyz_t &buf) const {
3874
- GPS_Solver<double>::base_t::xyz_t *res(
3875
- select_solver(prn).satellite_position(prn, time, buf));
3871
+ const GPS_Solver<double>::base_t::gps_time_t &time) const {
3872
+ GPS_Solver<double>::base_t::satellite_t res(
3873
+ select_solver(prn).select_satellite(prn, time));
3876
3874
 
3877
- do{
3878
- static const VALUE key(ID2SYM(rb_intern("satellite_position")));
3875
+ if(!res.is_available()){
3876
+ static const VALUE key(ID2SYM(rb_intern("relative_property")));
3879
3877
  VALUE hook(rb_hash_lookup(hooks, key));
3880
- if(NIL_P(hook)){break;}
3881
- VALUE values[] = {
3882
- SWIG_From_int (prn), // prn
3883
- SWIG_NewPointerObj( // time
3884
- new base_t::gps_time_t(time), SWIGTYPE_p_GPS_TimeT_double_t, SWIG_POINTER_OWN),
3885
- (res // position (internally calculated)
3886
- ? SWIG_NewPointerObj(res, SWIGTYPE_p_System_XYZT_double_WGS84_t, 0)
3887
- : Qnil)};
3888
- VALUE res_hook(proc_call_throw_if_error(hook, sizeof(values) / sizeof(values[0]), values));
3889
- if(NIL_P(res_hook)){ // unknown position
3890
- res = NULL;
3891
- break;
3892
- }else if(RB_TYPE_P(res_hook, T_ARRAY) && (RARRAY_LEN(res_hook) == 3)){
3893
- int i(0);
3894
- for(; i < 3; ++i){
3895
- VALUE v(RARRAY_AREF(res_hook, i));
3896
- if(!SWIG_IsOK(swig::asval(v, &buf[i]))){break;}
3897
- }
3898
- if(i == 3){
3899
- res = &buf;
3900
- break;
3901
- }
3902
- }else if(SWIG_IsOK(SWIG_ConvertPtr(
3903
- res_hook, (void **)&res, SWIGTYPE_p_System_XYZT_double_WGS84_t, 0))){
3904
- res = &(buf = *res);
3905
- break;
3906
- }
3907
- throw std::runtime_error(
3908
- std::string("System_XYZ or [d * 3] is expected (d: " "double" "), however ")
3909
- .append(inspect_str(res_hook)));
3910
- }while(false);
3878
+ if(!NIL_P(hook)){res.impl = this;}
3879
+ }
3911
3880
 
3912
3881
  return res;
3913
3882
  }
@@ -952,6 +952,8 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
952
952
  float_t sidereal_t_b_rad;
953
953
  typename Ephemeris::differential_t eq_of_motion;
954
954
 
955
+ static const float_t time_step_max_per_one_integration;
956
+
955
957
  void calculate_additional() {
956
958
  sidereal_t_b_rad = TimeProperties::date_t::Greenwich_sidereal_time_deg(
957
959
  TimeProperties::date.c_tm(),
@@ -1009,36 +1011,52 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1009
1011
  return calculate_clock_error(t_arrival_onboard - Ephemeris::t_b, pseudo_range);
1010
1012
  }
1011
1013
  /**
1012
- * Calculate constellation(t) based on constellation(t_0).
1014
+ * Calculate absolute constellation(t) based on constellation(t_0).
1013
1015
  * t_0 is a time around t_b, and is used to calculate
1014
- * an intermediate result specified with the 3rd argument.
1015
- * This method is useful to calculate constellation effectively
1016
- * by using a cache.
1017
- * @param delta_t time interval from t_0 to t
1018
- * @param pseudo_range measured pusedo_range to correct delta_t
1016
+ * @param t_step time interval from t_0 to t
1017
+ * @param times number of integration steps (assume times >=0)
1019
1018
  * @param xa_t_0 constellation(t_0)
1020
- * @param t_0_from_t_b time interval from t_b to t_0
1019
+ * @param t_0_from_t_b time interval from t_b to t_0 (= t_0 - t_b)
1021
1020
  */
1022
- constellation_t calculate_constellation(
1023
- float_t delta_t, const float_t &pseudo_range,
1021
+ constellation_t constellation_abs(
1022
+ const float_t &t_step,
1023
+ int times,
1024
1024
  const constellation_t &xa_t_0, const float_t &t_0_from_t_b) const {
1025
1025
 
1026
1026
  constellation_t res(xa_t_0);
1027
- { // time integration from t_b to t_transmit
1028
- float_t delta_t_to_transmit(delta_t - pseudo_range / light_speed);
1029
- float_t t_step_max(delta_t_to_transmit >= 0 ? 60 : -60);
1030
- int i(std::floor(delta_t_to_transmit / t_step_max));
1031
- float_t t_step_remain(delta_t_to_transmit - t_step_max * i);
1032
- float_t delta_t_itg(0); // accumulative time of integration
1033
- for(; i > 0; --i, delta_t_itg += t_step_max){
1034
- res = nextByRK4(eq_of_motion, delta_t_itg, res, t_step_max);
1035
- }
1036
- res = nextByRK4(eq_of_motion, delta_t_itg, res, t_step_remain);
1027
+ float_t delta_t_itg(t_0_from_t_b); // accumulative time of integration
1028
+ for(; times > 0; --times, delta_t_itg += t_step){
1029
+ res = nextByRK4(eq_of_motion, delta_t_itg, res, t_step);
1037
1030
  }
1031
+ return res;
1032
+ }
1033
+ /**
1034
+ * Calculate absolute constellation(t) based on constellation(t_0).
1035
+ * t_0 is a time around t_b, and is used to calculate
1036
+ * an intermediate result specified with the 2nd argument.
1037
+ * This method is useful to calculate constellation effectively
1038
+ * by using a cache.
1039
+ * @param delta_t time interval from t_0 to t
1040
+ * @param xa_t_0 constellation(t_0)
1041
+ * @param t_0_from_t_b time interval from t_b to t_0 (= t_0 - t_b)
1042
+ */
1043
+ constellation_t constellation_abs(
1044
+ const float_t &delta_t,
1045
+ const constellation_t &xa_t_0, const float_t &t_0_from_t_b) const {
1038
1046
 
1039
- static const float_t omega_E(0.7292115E-4); // Earth's rotation rate, TODO move to PZ-90.02
1040
- return res.rel_corrdinate(
1041
- sidereal_t_b_rad + (omega_E * (delta_t + t_0_from_t_b))); // transform from abs to PZ-90.02
1047
+ float_t t_step_max(delta_t >= 0
1048
+ ? time_step_max_per_one_integration
1049
+ : -time_step_max_per_one_integration);
1050
+ int n(std::floor(delta_t / t_step_max));
1051
+ float_t delta_t_steps(t_step_max * n), delta_t_remain(delta_t - delta_t_steps);
1052
+
1053
+ // To perform time integration from t_0 to (t_0 + delta_t),
1054
+ // n-times integration with t_step_max,
1055
+ // then, one-time integration with delta_t_remain are conducted.
1056
+ return constellation_abs(
1057
+ delta_t_remain, 1,
1058
+ constellation_abs(t_step_max, n, xa_t_0, t_0_from_t_b),
1059
+ t_0_from_t_b + delta_t_steps);
1042
1060
  }
1043
1061
  /**
1044
1062
  * Calculate constellation(t) based on constellation(t_b).
@@ -1046,8 +1064,14 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1046
1064
  * @param pseudo_range measured pusedo_range to correct delta_t, default is 0.
1047
1065
  */
1048
1066
  constellation_t calculate_constellation(
1049
- float_t delta_t, const float_t &pseudo_range = 0) const {
1050
- return calculate_constellation(delta_t, pseudo_range, xa_t_b, float_t(0));
1067
+ const float_t &delta_t, const float_t &pseudo_range = 0) const {
1068
+
1069
+ float_t delta_t_to_transmit(delta_t - pseudo_range / light_speed);
1070
+ constellation_t res(
1071
+ constellation_abs(delta_t_to_transmit, xa_t_b, float_t(0)));
1072
+
1073
+ return res.rel_corrdinate(
1074
+ sidereal_t_b_rad + (constellation_t::omega_E * delta_t)); // transform from abs to PZ-90.02
1051
1075
  }
1052
1076
  /**
1053
1077
  * @param t_arrival_glonass signal arrival time in glonass time scale,
@@ -1098,7 +1122,37 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1098
1122
  struct Satellite : public SatelliteProperties {
1099
1123
  public:
1100
1124
  typedef typename SatelliteProperties::Ephemeris_with_GPS_Time eph_t;
1101
- typedef typename GPS_SpaceNode<float_t>::template PropertyHistory<eph_t> eph_list_t;
1125
+
1126
+ struct eph_cached_t : public eph_t {
1127
+ mutable typename eph_t::constellation_t xa_t_0;
1128
+ mutable float_t t_0_from_t_b;
1129
+ eph_cached_t() : eph_t(), xa_t_0(eph_t::xa_t_b), t_0_from_t_b(0) {}
1130
+ eph_cached_t(const eph_t &eph) : eph_t(eph), xa_t_0(eph.xa_t_b), t_0_from_t_b(0) {}
1131
+ using eph_t::constellation;
1132
+ typename eph_t::constellation_t constellation(
1133
+ const GPS_Time<float_t> &t_arrival_gps, const float_t &pseudo_range = 0) const {
1134
+ float_t delta_t(t_arrival_gps - eph_t::t_b_gps);
1135
+ float_t delta_t_transmit_from_t_0(delta_t - pseudo_range / light_speed - t_0_from_t_b);
1136
+
1137
+ float_t t_step_max(delta_t_transmit_from_t_0 >= 0
1138
+ ? eph_t::time_step_max_per_one_integration
1139
+ : -eph_t::time_step_max_per_one_integration);
1140
+
1141
+ int big_steps(std::floor(delta_t_transmit_from_t_0 / t_step_max));
1142
+ if(big_steps > 0){ // perform integration and update cache
1143
+ xa_t_0 = eph_t::constellation_abs(t_step_max, big_steps, xa_t_0, t_0_from_t_b);
1144
+ float_t delta_t_updated(t_step_max * big_steps);
1145
+ t_0_from_t_b += delta_t_updated;
1146
+ delta_t_transmit_from_t_0 -= delta_t_updated;
1147
+ }
1148
+
1149
+ return eph_t::constellation_abs(delta_t_transmit_from_t_0, 1, xa_t_0, t_0_from_t_b)
1150
+ .rel_corrdinate(
1151
+ eph_t::sidereal_t_b_rad + (eph_t::constellation_t::omega_E * delta_t)); // transform from abs to PZ-90.02
1152
+ }
1153
+ };
1154
+
1155
+ typedef typename GPS_SpaceNode<float_t>::template PropertyHistory<eph_cached_t> eph_list_t;
1102
1156
  protected:
1103
1157
  eph_list_t eph_history;
1104
1158
  public:
@@ -1115,7 +1169,7 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1115
1169
  }
1116
1170
 
1117
1171
  void register_ephemeris(const eph_t &eph, const int &priority_delta = 1){
1118
- eph_history.add(eph, priority_delta);
1172
+ eph_history.add(eph_cached_t(eph), priority_delta);
1119
1173
  }
1120
1174
 
1121
1175
  void merge(const Satellite &another, const bool &keep_original = true){
@@ -1143,7 +1197,7 @@ if(std::abs(TARGET - eph.TARGET) > raw_t::sf[raw_t::SF_ ## TARGET]){break;}
1143
1197
  typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t constellation(
1144
1198
  const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const {
1145
1199
  return (typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t)(
1146
- ephemeris().constellation(t, pseudo_range));
1200
+ eph_history.current().constellation(t, pseudo_range));
1147
1201
  }
1148
1202
 
1149
1203
  typename GPS_SpaceNode<float_t>::xyz_t position(const GPS_Time<float_t> &t, const float_t &pseudo_range = 0) const {
@@ -1267,4 +1321,9 @@ typename GLONASS_SpaceNode<FloatT>::u8_t GLONASS_SpaceNode<FloatT>::SatellitePro
1267
1321
  return res;
1268
1322
  }
1269
1323
 
1324
+ template <class FloatT>
1325
+ const typename GLONASS_SpaceNode<FloatT>::float_t
1326
+ GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris_with_Time
1327
+ ::time_step_max_per_one_integration = 60;
1328
+
1270
1329
  #endif /* __GLONASS_H__ */
@@ -75,7 +75,6 @@ class GLONASS_SinglePositioning : public SolverBaseT {
75
75
 
76
76
  typedef GLONASS_SpaceNode<float_t> space_node_t;
77
77
  inheritate_type(gps_time_t);
78
- typedef typename space_node_t::Satellite satellite_t;
79
78
 
80
79
  inheritate_type(xyz_t);
81
80
  inheritate_type(enu_t);
@@ -83,9 +82,10 @@ class GLONASS_SinglePositioning : public SolverBaseT {
83
82
  inheritate_type(pos_t);
84
83
 
85
84
  typedef typename base_t::measurement_t measurement_t;
86
- inheritate_type(range_error_t);
87
- inheritate_type(range_corrector_t);
88
- inheritate_type(range_correction_t);
85
+ typedef typename base_t::satellite_t satellite_t;
86
+ typedef typename base_t::range_error_t range_error_t;
87
+ typedef typename base_t::range_corrector_t range_corrector_t;
88
+ typedef typename base_t::range_correction_t range_correction_t;
89
89
 
90
90
  inheritate_type(relative_property_t);
91
91
  typedef typename super_t::measurement_items_t measurement_items_t;
@@ -97,11 +97,51 @@ class GLONASS_SinglePositioning : public SolverBaseT {
97
97
  GLONASS_SinglePositioning_Options<float_t>, base_t> options_t;
98
98
 
99
99
  protected:
100
- const space_node_t &_space_node;
101
100
  GLONASS_SinglePositioning_Options<float_t> _options;
102
101
 
103
102
  public:
104
- const space_node_t &space_node() const {return _space_node;}
103
+ struct satellites_t {
104
+ const void *impl;
105
+ satellite_t (*impl_select)(const void *, const prn_t &, const gps_time_t &);
106
+ inline satellite_t select(const prn_t &prn, const gps_time_t &receiver_time) const {
107
+ return impl_select(impl, prn, receiver_time);
108
+ }
109
+ static satellite_t select_broadcast(
110
+ const void *ptr, const prn_t &prn, const gps_time_t &receiver_time){
111
+ const typename space_node_t::satellites_t &sats(
112
+ reinterpret_cast<const space_node_t *>(ptr)->satellites());
113
+ const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn));
114
+ if((it_sat == sats.end()) // has ephemeris?
115
+ || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
116
+ return satellite_t::unavailable();
117
+ }
118
+ struct impl_t {
119
+ static inline const typename space_node_t::Satellite &sat(const void *ptr) {
120
+ return *reinterpret_cast<const typename space_node_t::Satellite *>(ptr);
121
+ }
122
+ static xyz_t position(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
123
+ return sat(ptr).constellation(t, pseudo_range).position;
124
+ }
125
+ static xyz_t velocity(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
126
+ return sat(ptr).constellation(t, pseudo_range).velocity;
127
+ }
128
+ static float_t clock_error(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
129
+ return sat(ptr).clock_error(t, pseudo_range);
130
+ }
131
+ static float_t clock_error_dot(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
132
+ // Clock rate error is already taken into account in constellation()
133
+ return 0;
134
+ }
135
+ };
136
+ satellite_t res = {
137
+ &(it_sat->second),
138
+ impl_t::position, impl_t::velocity,
139
+ impl_t::clock_error, impl_t::clock_error_dot};
140
+ return res;
141
+ }
142
+ satellites_t(const space_node_t &sn)
143
+ : impl(&sn), impl_select(select_broadcast) {}
144
+ } satellites;
105
145
 
106
146
  range_correction_t ionospheric_correction, tropospheric_correction;
107
147
 
@@ -120,7 +160,8 @@ class GLONASS_SinglePositioning : public SolverBaseT {
120
160
  }
121
161
 
122
162
  GLONASS_SinglePositioning(const space_node_t &sn, const options_t &opt_wish = options_t())
123
- : base_t(), _space_node(sn), _options(available_options(opt_wish)),
163
+ : base_t(), _options(available_options(opt_wish)),
164
+ satellites(sn),
124
165
  ionospheric_correction(), tropospheric_correction() {
125
166
 
126
167
  // default ionospheric correction: no correction
@@ -163,25 +204,18 @@ class GLONASS_SinglePositioning : public SolverBaseT {
163
204
  }
164
205
 
165
206
  /**
166
- * Check availability of a satellite with which observation is associated
207
+ * Select satellite by using PRN and time
167
208
  *
168
209
  * @param prn satellite number
169
210
  * @param receiver_time receiver time
170
- * @return (const satellite_t *) If available, const pointer to satellite is returned,
171
- * otherwise NULL.
211
+ * @return (satellite_t) If available, satellite.is_available() returning true is returned.
172
212
  */
173
- const satellite_t *is_available(
174
- const typename space_node_t::satellites_t::key_type &prn,
213
+ satellite_t select_satellite(
214
+ const prn_t &prn,
175
215
  const gps_time_t &receiver_time) const {
176
-
177
- const typename space_node_t::satellites_t &sats(_space_node.satellites());
178
- const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn & 0xFF));
179
- if((it_sat == sats.end()) // has ephemeris?
180
- || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
181
- return NULL;
182
- }
183
-
184
- return &(it_sat->second);
216
+ prn_t prn_(prn & 0xFF);
217
+ if(_options.exclude_prn[prn_]){return satellite_t::unavailable();}
218
+ return satellites.select(prn_, receiver_time);
185
219
  }
186
220
 
187
221
  /**
@@ -205,32 +239,27 @@ class GLONASS_SinglePositioning : public SolverBaseT {
205
239
 
206
240
  relative_property_t res = {0};
207
241
 
208
- if(_options.exclude_prn[prn & 0xFF]){return res;}
209
-
210
242
  float_t range;
211
243
  range_error_t range_error;
212
244
  if(!this->range(measurement, range, &range_error)){
213
245
  return res; // If no range entry, return with weight = 0
214
246
  }
215
247
 
216
- const satellite_t *sat(is_available(prn, time_arrival));
217
- if(!sat){return res;} // If satellite is unavailable, return with weight = 0
248
+ satellite_t sat(select_satellite(prn, time_arrival));
249
+ if(!sat.is_available()){return res;} // If satellite is unavailable, return with weight = 0
218
250
 
219
251
  range -= receiver_error;
220
252
 
221
253
  // Clock correction, which will be considered in the next constellation()
222
254
  // as extra transmission time by using extra psuedo range.
223
255
  if(range_error.unknown_flag & range_error_t::SATELLITE_CLOCK){
224
- range += (sat->clock_error(time_arrival, range) * space_node_t::light_speed);
256
+ range += (sat.clock_error(time_arrival, range) * space_node_t::light_speed);
225
257
  }else{
226
258
  range += range_error.value[range_error_t::SATELLITE_CLOCK];
227
259
  }
228
260
 
229
- // Calculate satellite position and velocity
230
- typename GPS_SpaceNode<float_t>::SatelliteProperties::constellation_t sat_pos_vel(
231
- sat->constellation(time_arrival, range));
232
-
233
- const xyz_t &sat_pos(sat_pos_vel.position);
261
+ // Calculate satellite position
262
+ xyz_t sat_pos(sat.position(time_arrival, range));
234
263
  float_t geometric_range(usr_pos.xyz.dist(sat_pos));
235
264
 
236
265
  // Calculate residual
@@ -278,24 +307,15 @@ class GLONASS_SinglePositioning : public SolverBaseT {
278
307
 
279
308
  res.range_corrected = range;
280
309
 
281
- xyz_t rel_vel(sat_pos_vel.velocity - usr_vel); // Calculate velocity
310
+ xyz_t rel_vel(sat.velocity(time_arrival, range) - usr_vel); // Calculate velocity
282
311
 
283
- // Note: clock rate error is already accounted for in constellation()
284
312
  res.rate_relative_neg = res.los_neg[0] * rel_vel.x()
285
313
  + res.los_neg[1] * rel_vel.y()
286
- + res.los_neg[2] * rel_vel.z();
314
+ + res.los_neg[2] * rel_vel.z()
315
+ + sat.clock_error_dot(time_arrival, range) * space_node_t::light_speed;
287
316
 
288
317
  return res;
289
318
  }
290
-
291
- xyz_t *satellite_position(
292
- const prn_t &prn,
293
- const gps_time_t &time,
294
- xyz_t &res) const {
295
-
296
- const satellite_t *sat(is_available(prn, time));
297
- return sat ? &(res = sat->position(time)) : NULL;
298
- }
299
319
  };
300
320
 
301
321
  template <class FloatT, class SolverBaseT>
@@ -95,7 +95,6 @@ class GPS_SinglePositioning : public SolverBaseT {
95
95
 
96
96
  typedef typename base_t::space_node_t space_node_t;
97
97
  inheritate_type(gps_time_t);
98
- typedef typename space_node_t::Satellite satellite_t;
99
98
 
100
99
  inheritate_type(xyz_t);
101
100
  inheritate_type(llh_t);
@@ -106,6 +105,7 @@ class GPS_SinglePositioning : public SolverBaseT {
106
105
  inheritate_type(prn_obs_t);
107
106
  typedef typename base_t::measurement_t measurement_t;
108
107
  inheritate_type(measurement_items_t);
108
+ typedef typename base_t::satellite_t satellite_t;
109
109
  typedef typename base_t::range_error_t range_error_t;
110
110
  typedef typename base_t::range_corrector_t range_corrector_t;
111
111
  typedef typename base_t::range_correction_t range_correction_t;
@@ -119,11 +119,52 @@ class GPS_SinglePositioning : public SolverBaseT {
119
119
  GPS_SinglePositioning_Options<float_t>, base_t> options_t;
120
120
 
121
121
  protected:
122
- const space_node_t &_space_node;
123
122
  GPS_SinglePositioning_Options<float_t> _options;
124
123
 
125
124
  public:
126
- const space_node_t &space_node() const {return _space_node;}
125
+ struct satellites_t {
126
+ const void *impl;
127
+ satellite_t (*impl_select)(const void *, const prn_t &, const gps_time_t &);
128
+ inline satellite_t select(const prn_t &prn, const gps_time_t &receiver_time) const {
129
+ return impl_select(impl, prn, receiver_time);
130
+ }
131
+ static satellite_t select_broadcast(
132
+ const void *ptr, const prn_t &prn, const gps_time_t &receiver_time){
133
+ // If this static function is defined in inner struct,
134
+ // C2440 error raises with VC2010
135
+ const typename space_node_t::satellites_t &sats(
136
+ reinterpret_cast<const space_node_t *>(ptr)->satellites());
137
+ const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn));
138
+ if((it_sat == sats.end()) // has ephemeris?
139
+ || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
140
+ return satellite_t::unavailable();
141
+ }
142
+ struct impl_t {
143
+ static inline const typename space_node_t::Satellite &sat(const void *ptr) {
144
+ return *reinterpret_cast<const typename space_node_t::Satellite *>(ptr);
145
+ }
146
+ static xyz_t position(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
147
+ return sat(ptr).position(t, pseudo_range);
148
+ }
149
+ static xyz_t velocity(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
150
+ return sat(ptr).velocity(t, pseudo_range);
151
+ }
152
+ static float_t clock_error(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
153
+ return sat(ptr).clock_error(t, pseudo_range);
154
+ }
155
+ static float_t clock_error_dot(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
156
+ return sat(ptr).clock_error_dot(t, pseudo_range);
157
+ }
158
+ };
159
+ satellite_t res = {
160
+ &(it_sat->second),
161
+ impl_t::position, impl_t::velocity,
162
+ impl_t::clock_error, impl_t::clock_error_dot};
163
+ return res;
164
+ }
165
+ satellites_t(const space_node_t &sn)
166
+ : impl(&sn), impl_select(select_broadcast) {}
167
+ } satellites;
127
168
 
128
169
  struct klobuchar_t : public range_corrector_t {
129
170
  const space_node_t &space_node;
@@ -134,6 +175,7 @@ class GPS_SinglePositioning : public SolverBaseT {
134
175
  float_t *calculate(
135
176
  const gps_time_t &t, const pos_t &usr_pos, const enu_t &sat_rel_pos,
136
177
  float_t &buf) const {
178
+ if(!is_available(t)){return NULL;}
137
179
  return &(buf = space_node.iono_correction(sat_rel_pos, usr_pos.llh, t));
138
180
  }
139
181
  } ionospheric_klobuchar;
@@ -185,7 +227,8 @@ class GPS_SinglePositioning : public SolverBaseT {
185
227
  }
186
228
 
187
229
  GPS_SinglePositioning(const space_node_t &sn)
188
- : base_t(), _space_node(sn), _options(available_options(options_t())),
230
+ : base_t(), _options(available_options(options_t())),
231
+ satellites(sn),
189
232
  ionospheric_klobuchar(sn), ionospheric_ntcm_gl(),
190
233
  tropospheric_simplified(),
191
234
  ionospheric_correction(), tropospheric_correction() {
@@ -309,27 +352,17 @@ class GPS_SinglePositioning : public SolverBaseT {
309
352
  }
310
353
 
311
354
  /**
312
- * Check availability of a satellite with which observation is associated
355
+ * Select satellite by using PRN and time
313
356
  *
314
357
  * @param prn satellite number
315
358
  * @param receiver_time receiver time
316
- * @return (const satellite_t *) If available, const pointer to satellite is returned,
317
- * otherwise NULL.
359
+ * @return (satellite_t) If available, satellite.is_available() returning true is returned.
318
360
  */
319
- const satellite_t *is_available(
320
- const typename space_node_t::satellites_t::key_type &prn,
361
+ satellite_t select_satellite(
362
+ const prn_t &prn,
321
363
  const gps_time_t &receiver_time) const {
322
-
323
- if(_options.exclude_prn[prn]){return NULL;}
324
-
325
- const typename space_node_t::satellites_t &sats(_space_node.satellites());
326
- const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn));
327
- if((it_sat == sats.end()) // has ephemeris?
328
- || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
329
- return NULL;
330
- }
331
-
332
- return &(it_sat->second);
364
+ if(_options.exclude_prn[prn]){return satellite_t::unavailable();}
365
+ return satellites.select(prn, receiver_time);
333
366
  }
334
367
 
335
368
  /**
@@ -359,8 +392,8 @@ class GPS_SinglePositioning : public SolverBaseT {
359
392
  return res; // If no range entry, return with weight = 0
360
393
  }
361
394
 
362
- const satellite_t *sat(is_available(prn, time_arrival));
363
- if(!sat){return res;} // If satellite is unavailable, return with weight = 0
395
+ satellite_t sat(select_satellite(prn, time_arrival));
396
+ if(!sat.is_available()){return res;} // If satellite is unavailable, return with weight = 0
364
397
 
365
398
  residual_t residual = {
366
399
  res.range_residual,
@@ -369,9 +402,9 @@ class GPS_SinglePositioning : public SolverBaseT {
369
402
  };
370
403
 
371
404
  res.range_corrected = range_corrected(
372
- *sat, range - receiver_error, time_arrival,
405
+ sat, range - receiver_error, time_arrival,
373
406
  usr_pos, residual, range_error);
374
- res.rate_relative_neg = rate_relative_neg(*sat, res.range_corrected, time_arrival, usr_vel,
407
+ res.rate_relative_neg = rate_relative_neg(sat, res.range_corrected, time_arrival, usr_vel,
375
408
  res.los_neg[0], res.los_neg[1], res.los_neg[2]);
376
409
 
377
410
  return res;
@@ -415,7 +448,7 @@ class GPS_SinglePositioning : public SolverBaseT {
415
448
  float_t range;
416
449
  if(!this->range(it->second, range)){continue;} // No range entry
417
450
 
418
- if(!is_available(it->first, receiver_time)){continue;} // No satellite
451
+ if(!(select_satellite(it->first, receiver_time).is_available())){continue;} // No satellite
419
452
 
420
453
  typename base_t::measurement2_t::value_type v = {
421
454
  it->first, &(it->second), this}; // prn, measurement, solver
@@ -426,15 +459,6 @@ class GPS_SinglePositioning : public SolverBaseT {
426
459
  measurement2, receiver_time, user_position_init, receiver_error_init,
427
460
  typename base_t::user_pvt_opt_t(good_init, with_velocity));
428
461
  }
429
-
430
- xyz_t *satellite_position(
431
- const prn_t &prn,
432
- const gps_time_t &time,
433
- xyz_t &res) const {
434
-
435
- const satellite_t *sat(is_available(prn, time));
436
- return sat ? &(res = sat->position(time)) : NULL;
437
- }
438
462
  };
439
463
 
440
464
  #endif /* __GPS_SOLVER_H__ */
@@ -226,6 +226,47 @@ struct GPS_Solver_Base {
226
226
  return res;
227
227
  }
228
228
 
229
+ struct satellite_t {
230
+ const void *impl;
231
+ xyz_t (*impl_position)(const void *, const gps_time_t &, const float_t &);
232
+ xyz_t (*impl_velocity)(const void *, const gps_time_t &, const float_t &);
233
+ float_t (*impl_clock_error)(const void *, const gps_time_t &, const float_t &);
234
+ float_t (*impl_clock_error_dot)(const void *, const gps_time_t &, const float_t &);
235
+ inline bool is_available() const {
236
+ return impl != NULL;
237
+ }
238
+ inline xyz_t position(const gps_time_t &t, const float_t &pseudo_range = 0) const {
239
+ return impl_position(impl, t, pseudo_range);
240
+ }
241
+ inline xyz_t velocity(const gps_time_t &t, const float_t &pseudo_range = 0) const {
242
+ return impl_velocity(impl, t, pseudo_range);
243
+ }
244
+ inline float_t clock_error(const gps_time_t &t, const float_t &pseudo_range = 0) const {
245
+ return impl_clock_error(impl, t, pseudo_range);
246
+ }
247
+ inline float_t clock_error_dot(const gps_time_t &t, const float_t &pseudo_range = 0) const {
248
+ return impl_clock_error_dot(impl, t, pseudo_range);
249
+ }
250
+ static const satellite_t &unavailable() {
251
+ static const satellite_t res = {NULL};
252
+ return res;
253
+ }
254
+ };
255
+
256
+ /**
257
+ * Select satellite by using PRN and time
258
+ * This function should be overridden.
259
+ *
260
+ * @param prn satellite number
261
+ * @param receiver_time receiver time
262
+ * @return (satellite_t) If available, satellite.is_available() returning true is returned.
263
+ */
264
+ virtual satellite_t select_satellite(
265
+ const prn_t &prn,
266
+ const gps_time_t &receiver_time) const {
267
+ return satellite_t::unavailable();
268
+ }
269
+
229
270
  struct range_corrector_t {
230
271
  virtual ~range_corrector_t() {}
231
272
  virtual bool is_available(const gps_time_t &t) const {
@@ -838,21 +879,6 @@ protected:
838
879
  }
839
880
 
840
881
  public:
841
- /**
842
- * Calculate satellite position
843
- *
844
- * @param prn satellite number
845
- * @param time GPS time
846
- * @param res object to which results are stored
847
- * @return If position is available, &res will be returned, otherwise NULL.
848
- */
849
- virtual xyz_t *satellite_position(
850
- const prn_t &prn,
851
- const gps_time_t &time,
852
- xyz_t &res) const {
853
- return NULL;
854
- }
855
-
856
882
  /**
857
883
  * Calculate User position/velocity with hint
858
884
  * This is multi-constellation version,
@@ -882,8 +908,7 @@ public:
882
908
  it != it_end; ++it){
883
909
  typename measurement2_t::value_type v = {
884
910
  it->first, &(it->second), &select(it->first)}; // prn, measurement, solver
885
- xyz_t sat;
886
- if(!v.solver->satellite_position(v.prn, receiver_time, sat)){
911
+ if(!v.solver->select_satellite(v.prn, receiver_time).is_available()){
887
912
  // pre-check satellite availability; remove it when its position is unknown
888
913
  continue;
889
914
  }
@@ -62,8 +62,6 @@ class RINEX_Reader {
62
62
  typedef RINEX_Reader<U> self_t;
63
63
  typedef std::map<std::string, std::vector<std::string> > header_t;
64
64
 
65
- protected:
66
- header_t _header;
67
65
  struct src_stream_t : public std::istream {
68
66
  src_stream_t(std::istream &is) : std::istream(is.rdbuf()) {}
69
67
  /**
@@ -106,7 +104,11 @@ class RINEX_Reader {
106
104
  }
107
105
  return *this;
108
106
  }
109
- } src;
107
+ };
108
+
109
+ protected:
110
+ header_t _header;
111
+ src_stream_t src;
110
112
  bool _has_next;
111
113
 
112
114
  public:
@@ -198,7 +200,7 @@ class RINEX_Reader {
198
200
  return true;
199
201
  }
200
202
  }
201
- static bool f(
203
+ static bool f_pure(
202
204
  std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
203
205
  if(str2val){
204
206
  std::stringstream ss(buf.substr(offset, length));
@@ -209,22 +211,30 @@ class RINEX_Reader {
209
211
  ss << std::setfill(' ') << std::right << std::setw(length)
210
212
  << std::setprecision(precision) << std::fixed
211
213
  << *(T *)value;
212
- std::string s(ss.str());
214
+ buf.replace(offset, length, ss.str());
215
+ return true;
216
+ }
217
+ }
218
+ static bool f(
219
+ std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
220
+ bool res(f_pure(buf, offset, length, value, precision, str2val));
221
+ if((!str2val) && res){
213
222
  if((*(T *)value) >= 0){
214
223
  if((*(T *)value) < 1){
215
224
  int i(length - precision - 2);
216
- if(i >= 0){s[i] = ' ';}
225
+ // 0.12345 => .12345
226
+ if(i >= 0){buf[i + offset] = ' ';}
217
227
  }
218
228
  }else{
219
229
  if((*(T *)value) > -1){
220
230
  int i(length - precision - 2);
221
- if(i >= 0){s[i] = '-';}
222
- if(--i >= 0){s[i] = ' ';}
231
+ // -0.12345 => -.12345
232
+ if(i >= 0){buf[i + offset] = '-';}
233
+ if(--i >= 0){buf[i + offset] = ' ';}
223
234
  }
224
235
  }
225
- buf.replace(offset, length, s);
226
- return true;
227
236
  }
237
+ return res;
228
238
  }
229
239
  static bool e(
230
240
  std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
@@ -268,6 +278,14 @@ class RINEX_Reader {
268
278
  std::string &buf, const int &offset, const int &length, void *value, const int &opt = 0, const bool &str2val = true){
269
279
  return conv_t<T, false>::d(buf, offset, length, value, opt, str2val);
270
280
  }
281
+ static bool f_pure(
282
+ std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
283
+ double v(*(T *)value);
284
+ bool res(
285
+ conv_t<double, false>::f_pure(buf, offset, length, &v, precision, str2val));
286
+ *(T *)value = static_cast<T>(v);
287
+ return res;
288
+ }
271
289
  static bool f(
272
290
  std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
273
291
  double v(*(T *)value);
@@ -2183,10 +2183,11 @@ template <typename T>
2183
2183
  typename SBAS_SpaceNode<FloatT>::KnownSatellites::list_t
2184
2184
  SBAS_SpaceNode<FloatT>::KnownSatellites::sort(T sorter){
2185
2185
  static const typename SBAS_SpaceNode<FloatT>::RangingCode codes[] = {
2186
+ {120, 145, 01106, 5, "ASECNA (A-SBAS)"},
2186
2187
  {121, 175, 01241, 5, "EGNOS (Eutelsat 5WB)"},
2187
2188
  {122, 52, 00267, 143.5, "SPAN (INMARSAT 4F1)"},
2188
2189
  {123, 21, 00232, 31.5, "EGNOS (ASTRA 5B)"},
2189
- {124, 237, 01617, 0, "EGNOS (Reserved)"},
2190
+ //{124, 237, 01617, 0, "(Reserved)"},
2190
2191
  {125, 235, 01076, -16, "SDCM (Luch-5A)"},
2191
2192
  {126, 886, 01764, 63.9, "EGNOS (INMARSAT 4F2)"},
2192
2193
  {127, 657, 00717, 55, "GAGAN (GSAT-8)"},
@@ -2198,16 +2199,30 @@ typename SBAS_SpaceNode<FloatT>::KnownSatellites::list_t
2198
2199
  {133, 603, 01731, -129, "WAAS (SES-15)"},
2199
2200
  {134, 130, 00706, 91.5, "KASS (MEASAT-3D)"},
2200
2201
  {135, 359, 01216, -125, "WAAS (Intelsat Galaxy 30)"},
2201
- {136, 595, 00740, 5, "EGNOS (SES-5)"},
2202
+ {136, 595, 00740, 5, "EGNOS (HOTBIRD 13G)"},
2202
2203
  {137, 68, 01007, 127, "MSAS (QZS-3)"},
2203
2204
  {138, 386, 00450, 107.3, "WAAS (ANIK F1R)"},
2205
+ //{139, 797, 00305, 0, "MSAS (QZS-7)"},
2204
2206
  {140, 456, 01653, 95, "SDCM (Luch-5V)"},
2205
2207
  {141, 499, 01411, 167, "SDCM (Luch-5A)"},
2208
+ //{142, 883, 01644, 0, "(Unallocated)"},
2206
2209
  {143, 307, 01312, 110.5, "BDSBAS (G3)"},
2207
2210
  {144, 127, 01060, 80, "BDSBAS (G2)"},
2208
- {147, 118, 00355, 42.5, "NSAS (NIGCOMSAT-1R)"},
2211
+ //{145, 211, 01560, 0, "(Unallocated)"},
2212
+ //{146, 121, 00035, 0, "(Unallocated)"},
2213
+ {147, 118, 00355, 5, "ASECNA (A-SBAS)"},
2209
2214
  {148, 163, 00335, -24.8, "ASAL (ALCOMSAT-1)"},
2210
- }; ///< @see https://www.gps.gov/technical/prn-codes/L1-CA-PRN-code-assignments-2020-Apr.pdf
2215
+ //{149, 628, 01254, 0, "(Unallocated)"},
2216
+ //{150, 853, 01041, 0, "EGNOS"},
2217
+ //{151, 484, 00142, 0, "(Unallocated)"},
2218
+ //{152, 289, 01641, 0, "(Unallocated)"},
2219
+ //{153, 811, 01504, 0, "(Unallocated)"},
2220
+ //{154, 202, 00751, 0, "(Unallocated)"},
2221
+ //{155, 1021, 01774, 0, "(Unallocated)"},
2222
+ //{156, 463, 00107, 0, "(Unallocated)"},
2223
+ //{157, 568, 01153, 0, "(Unallocated)"},
2224
+ //{158, 904, 01542, 0, "(Unallocated)"},
2225
+ }; ///< @see https://www.gps.gov/technical/prn-codes/L1-CA-PRN-code-assignments-2021-Jun.pdf
2211
2226
  list_t res;
2212
2227
  res.reserve(sizeof(codes) / sizeof(codes[0]));
2213
2228
  for(unsigned int i(0); i < sizeof(codes) / sizeof(codes[0]); ++i){
@@ -73,7 +73,6 @@ class SBAS_SinglePositioning : public SolverBaseT {
73
73
 
74
74
  typedef SBAS_SpaceNode<float_t> space_node_t;
75
75
  inheritate_type(gps_time_t);
76
- typedef typename space_node_t::Satellite satellite_t;
77
76
 
78
77
  inheritate_type(xyz_t);
79
78
  inheritate_type(enu_t);
@@ -81,6 +80,7 @@ class SBAS_SinglePositioning : public SolverBaseT {
81
80
  inheritate_type(pos_t);
82
81
 
83
82
  typedef typename base_t::measurement_t measurement_t;
83
+ typedef typename base_t::satellite_t satellite_t;
84
84
  typedef typename base_t::range_error_t range_error_t;
85
85
  typedef typename base_t::range_corrector_t range_corrector_t;
86
86
  typedef typename base_t::range_correction_t range_correction_t;
@@ -92,11 +92,52 @@ class SBAS_SinglePositioning : public SolverBaseT {
92
92
  SBAS_SinglePositioning_Options<float_t>, base_t> options_t;
93
93
 
94
94
  protected:
95
- const space_node_t &_space_node;
96
95
  SBAS_SinglePositioning_Options<float_t> _options;
97
96
 
98
97
  public:
99
- const space_node_t &space_node() const {return _space_node;}
98
+ struct satellites_t {
99
+ const void *impl;
100
+ satellite_t (*impl_select)(const void *, const prn_t &, const gps_time_t &);
101
+ inline satellite_t select(const prn_t &prn, const gps_time_t &receiver_time) const {
102
+ return impl_select(impl, prn, receiver_time);
103
+ }
104
+ static satellite_t select_broadcast(
105
+ const void *ptr, const prn_t &prn, const gps_time_t &receiver_time){
106
+ const typename space_node_t::satellites_t &sats(
107
+ reinterpret_cast<const space_node_t *>(ptr)->satellites());
108
+ const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn));
109
+ if((it_sat == sats.end()) // has ephemeris?
110
+ || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
111
+ return satellite_t::unavailable();
112
+ }
113
+ struct impl_t {
114
+ static inline const typename space_node_t::Satellite &sat(const void *ptr) {
115
+ return *reinterpret_cast<const typename space_node_t::Satellite *>(ptr);
116
+ }
117
+ static xyz_t position(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
118
+ return sat(ptr).ephemeris().constellation(t, pseudo_range, false).position;
119
+ }
120
+ static xyz_t velocity(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
121
+ return sat(ptr).ephemeris().constellation(t, pseudo_range, true).velocity;
122
+ }
123
+ static float_t clock_error(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
124
+ // Clock correction is taken into account in position()
125
+ return 0;
126
+ }
127
+ static float_t clock_error_dot(const void *ptr, const gps_time_t &t, const float_t &pseudo_range) {
128
+ // Clock rate error is taken in account in velocity()
129
+ return 0;
130
+ }
131
+ };
132
+ satellite_t res = {
133
+ &(it_sat->second),
134
+ impl_t::position, impl_t::velocity,
135
+ impl_t::clock_error, impl_t::clock_error_dot};
136
+ return res;
137
+ }
138
+ satellites_t(const space_node_t &sn)
139
+ : impl(&sn), impl_select(select_broadcast) {}
140
+ } satellites;
100
141
 
101
142
  struct ionospheric_sbas_t : public range_corrector_t {
102
143
  const space_node_t &space_node;
@@ -155,7 +196,8 @@ class SBAS_SinglePositioning : public SolverBaseT {
155
196
  }
156
197
 
157
198
  SBAS_SinglePositioning(const space_node_t &sn)
158
- : base_t(), _space_node(sn), _options(available_options(options_t())),
199
+ : base_t(), _options(available_options(options_t())),
200
+ satellites(sn),
159
201
  ionospheric_sbas(sn), tropospheric_sbas() {
160
202
 
161
203
  // default ionospheric correction: Broadcasted IGP.
@@ -168,25 +210,17 @@ class SBAS_SinglePositioning : public SolverBaseT {
168
210
  ~SBAS_SinglePositioning(){}
169
211
 
170
212
  /**
171
- * Check availability of a satellite with which observation is associated
213
+ * Select satellite by using PRN and time
172
214
  *
173
215
  * @param prn satellite number
174
216
  * @param receiver_time receiver time
175
- * @return (const satellite_t *) If available, const pointer to satellite is returned,
176
- * otherwise NULL.
217
+ * @return (satellite_t) If available, satellite.is_available() returning true is returned.
177
218
  */
178
- const satellite_t *is_available(
179
- const typename space_node_t::satellites_t::key_type &prn,
219
+ satellite_t select_satellite(
220
+ const prn_t &prn,
180
221
  const gps_time_t &receiver_time) const {
181
-
182
- const typename space_node_t::satellites_t &sats(_space_node.satellites());
183
- const typename space_node_t::satellites_t::const_iterator it_sat(sats.find(prn));
184
- if((it_sat == sats.end()) // has ephemeris?
185
- || (!it_sat->second.ephemeris().is_valid(receiver_time))){ // valid ephemeris?
186
- return NULL;
187
- }
188
-
189
- return &(it_sat->second);
222
+ if(_options.exclude_prn[prn]){return satellite_t::unavailable();}
223
+ return satellites.select(prn, receiver_time);
190
224
  }
191
225
 
192
226
  /**
@@ -210,33 +244,28 @@ class SBAS_SinglePositioning : public SolverBaseT {
210
244
 
211
245
  relative_property_t res = {0};
212
246
 
213
- if(_options.exclude_prn[prn]){return res;}
214
-
215
247
  float_t range;
216
248
  range_error_t range_error;
217
249
  if(!this->range(measurement, range, &range_error)){
218
250
  return res; // If no range entry, return with weight = 0
219
251
  }
220
252
 
221
- const satellite_t *sat(is_available(prn, time_arrival));
222
- if(!sat){return res;} // If satellite is unavailable, return with weight = 0
253
+ satellite_t sat(select_satellite(prn, time_arrival));
254
+ if(!sat.is_available()){return res;} // If satellite is unavailable, return with weight = 0
223
255
 
224
256
  ///< The following procedure is based on Appendix.S with modification
225
257
 
226
258
  range -= receiver_error;
227
259
 
228
- // Clock correction will be performed in the following constellation()
229
- if(!(range_error.unknown_flag & range_error_t::SATELLITE_CLOCK)){
230
- range += range_error.value[range_error_t::SATELLITE_CLOCK];
231
- }
260
+ // Clock error correction
261
+ range += ((range_error.unknown_flag & range_error_t::SATELLITE_CLOCK)
262
+ ? (sat.clock_error(time_arrival, range) * GPS_SpaceNode<float_t>::light_speed)
263
+ : range_error.value[range_error_t::SATELLITE_CLOCK]);
232
264
 
233
265
  // TODO WAAS long term clock correction (2.1.1.4.11)
234
266
 
235
- // Calculate satellite position and velocity
236
- typename space_node_t::SatelliteProperties::constellation_t sat_pos_vel(
237
- sat->ephemeris().constellation(time_arrival, range));
238
-
239
- const xyz_t &sat_pos(sat_pos_vel.position);
267
+ // Calculate satellite position
268
+ xyz_t sat_pos(sat.position(time_arrival, range));
240
269
  float_t geometric_range(usr_pos.xyz.dist(sat_pos));
241
270
 
242
271
  // Calculate residual with Sagnac correction (A.4.4.11)
@@ -283,24 +312,15 @@ class SBAS_SinglePositioning : public SolverBaseT {
283
312
 
284
313
  res.range_corrected = range;
285
314
 
286
- xyz_t rel_vel(sat_pos_vel.velocity - usr_vel); // Calculate velocity
315
+ xyz_t rel_vel(sat.velocity(time_arrival, range) - usr_vel); // Calculate velocity
287
316
 
288
- // Note: clock rate error is already accounted for in constellation()
289
317
  res.rate_relative_neg = res.los_neg[0] * rel_vel.x()
290
318
  + res.los_neg[1] * rel_vel.y()
291
- + res.los_neg[2] * rel_vel.z();
319
+ + res.los_neg[2] * rel_vel.z()
320
+ + sat.clock_error_dot(time_arrival, range) * GPS_SpaceNode<float_t>::light_speed;
292
321
 
293
322
  return res;
294
323
  }
295
-
296
- xyz_t *satellite_position(
297
- const prn_t &prn,
298
- const gps_time_t &time,
299
- xyz_t &res) const {
300
-
301
- const satellite_t *sat(is_available(prn, time));
302
- return sat ? &(res = sat->ephemeris().constellation(time).position) : NULL;
303
- }
304
324
  };
305
325
 
306
326
  #endif /* __SBAS_SOLVER_H__ */
@@ -1104,7 +1104,7 @@ struct GPS_RangeCorrector
1104
1104
  %ignore glonass;
1105
1105
  %ignore select_solver;
1106
1106
  %ignore relative_property;
1107
- %ignore satellite_position;
1107
+ %ignore select_satellite;
1108
1108
  %ignore update_position_solution;
1109
1109
  %ignore user_correctors_t;
1110
1110
  %ignore user_correctors;
@@ -1201,47 +1201,17 @@ struct GPS_RangeCorrector
1201
1201
  return super_t::update_position_solution(geomat, res);
1202
1202
  }
1203
1203
  template <>
1204
- GPS_Solver<FloatT>::base_t::xyz_t *GPS_Solver<FloatT>::satellite_position(
1204
+ GPS_Solver<FloatT>::base_t::satellite_t GPS_Solver<FloatT>::select_satellite(
1205
1205
  const GPS_Solver<FloatT>::base_t::prn_t &prn,
1206
- const GPS_Solver<FloatT>::base_t::gps_time_t &time,
1207
- GPS_Solver<FloatT>::base_t::xyz_t &buf) const {
1208
- GPS_Solver<FloatT>::base_t::xyz_t *res(
1209
- select_solver(prn).satellite_position(prn, time, buf));
1206
+ const GPS_Solver<FloatT>::base_t::gps_time_t &time) const {
1207
+ GPS_Solver<FloatT>::base_t::satellite_t res(
1208
+ select_solver(prn).select_satellite(prn, time));
1210
1209
  #ifdef SWIGRUBY
1211
- do{
1212
- static const VALUE key(ID2SYM(rb_intern("satellite_position")));
1210
+ if(!res.is_available()){
1211
+ static const VALUE key(ID2SYM(rb_intern("relative_property")));
1213
1212
  VALUE hook(rb_hash_lookup(hooks, key));
1214
- if(NIL_P(hook)){break;}
1215
- VALUE values[] = {
1216
- SWIG_From(int)(prn), // prn
1217
- SWIG_NewPointerObj( // time
1218
- new base_t::gps_time_t(time), $descriptor(GPS_Time<FloatT> *), SWIG_POINTER_OWN),
1219
- (res // position (internally calculated)
1220
- ? SWIG_NewPointerObj(res, $descriptor(System_XYZ<FloatT, WGS84> *), 0)
1221
- : Qnil)};
1222
- VALUE res_hook(proc_call_throw_if_error(hook, sizeof(values) / sizeof(values[0]), values));
1223
- if(NIL_P(res_hook)){ // unknown position
1224
- res = NULL;
1225
- break;
1226
- }else if(RB_TYPE_P(res_hook, T_ARRAY) && (RARRAY_LEN(res_hook) == 3)){
1227
- int i(0);
1228
- for(; i < 3; ++i){
1229
- VALUE v(RARRAY_AREF(res_hook, i));
1230
- if(!SWIG_IsOK(swig::asval(v, &buf[i]))){break;}
1231
- }
1232
- if(i == 3){
1233
- res = &buf;
1234
- break;
1235
- }
1236
- }else if(SWIG_IsOK(SWIG_ConvertPtr(
1237
- res_hook, (void **)&res, $descriptor(System_XYZ<FloatT, WGS84> *), 0))){
1238
- res = &(buf = *res);
1239
- break;
1240
- }
1241
- throw std::runtime_error(
1242
- std::string("System_XYZ or [d * 3] is expected (d: " %str(FloatT) "), however ")
1243
- .append(inspect_str(res_hook)));
1244
- }while(false);
1213
+ if(!NIL_P(hook)){res.impl = this;}
1214
+ }
1245
1215
  #endif
1246
1216
  return res;
1247
1217
  }
@@ -1481,10 +1451,9 @@ struct GPS_Solver
1481
1451
  const typename base_t::gps_time_t &time_arrival,
1482
1452
  const typename base_t::pos_t &usr_pos,
1483
1453
  const typename base_t::xyz_t &usr_vel) const;
1484
- virtual typename base_t::xyz_t *satellite_position(
1454
+ virtual typename base_t::satellite_t select_satellite(
1485
1455
  const typename base_t::prn_t &prn,
1486
- const typename base_t::gps_time_t &time,
1487
- typename base_t::xyz_t &res) const;
1456
+ const typename base_t::gps_time_t &time) const;
1488
1457
  virtual bool update_position_solution(
1489
1458
  const typename base_t::geometric_matrices_t &geomat,
1490
1459
  typename base_t::user_pvt_t &res) const;
@@ -375,22 +375,6 @@ __RINEX_OBS_TEXT__
375
375
  expect(mat).to respond_to(:resize!)
376
376
  }
377
377
  }
378
- solver.hooks[:satellite_position] = proc{
379
- i = 0
380
- proc{|prn, time, pos|
381
- expect(input[:measurement]).to include(prn)
382
- expect(pos).to be_a_kind_of(Coordinate::XYZ).or eq(nil)
383
- # System_XYZ or [x,y,z] or nil(= unknown position) are acceptable
384
- case (i += 1) % 5
385
- when 0
386
- nil
387
- when 1
388
- pos.to_a
389
- else
390
- pos
391
- end
392
- }
393
- }.call
394
378
  pvt = solver.solve(
395
379
  input[:measurement].collect{|prn, items|
396
380
  items.collect{|k, v| [prn, k, v]}
File without changes
@@ -4,6 +4,7 @@ Receiver class to be an top level interface to a user
4
4
  =end
5
5
 
6
6
  require_relative 'GPS'
7
+ require_relative 'util'
7
8
 
8
9
  module GPS_PVT
9
10
  class Receiver
@@ -547,22 +548,24 @@ class Receiver
547
548
  $stderr.puts ", found packets are %s"%[ubx_kind.inspect]
548
549
  end
549
550
 
550
- def parse_rinex_nav(fname)
551
+ def parse_rinex_nav(src)
552
+ fname = Util::get_txt(src)
551
553
  items = [
552
554
  @solver.gps_space_node,
553
555
  @solver.sbas_space_node,
554
556
  @solver.glonass_space_node,
555
557
  ].inject(0){|res, sn|
556
558
  loaded_items = sn.send(:read, fname)
557
- raise "Format error! (Not RINEX) #{fname}" if loaded_items < 0
559
+ raise "Format error! (Not RINEX) #{src}" if loaded_items < 0
558
560
  res + loaded_items
559
561
  }
560
- $stderr.puts "Read RINEX NAV file (%s): %d items."%[fname, items]
562
+ $stderr.puts "Read RINEX NAV file (%s): %d items."%[src, items]
561
563
  end
562
564
 
563
- def parse_rinex_obs(fname, &b)
565
+ def parse_rinex_obs(src, &b)
566
+ fname = Util::get_txt(src)
564
567
  after_run = b || proc{|pvt| puts pvt.to_s if pvt}
565
- $stderr.print "Reading RINEX observation file (%s)"%[fname]
568
+ $stderr.print "Reading RINEX observation file (%s)"%[src]
566
569
  types = nil
567
570
  glonass_freq = nil
568
571
  count = 0
@@ -0,0 +1,32 @@
1
+ require 'open-uri'
2
+ require 'tempfile'
3
+ require 'uri'
4
+ require 'zlib'
5
+
6
+ module GPS_PVT
7
+ module Util
8
+ class << self
9
+ def inflate(src)
10
+ Zlib::GzipReader.send(*(src.kind_of?(IO) ? [:new, src] : [:open, src]))
11
+ end
12
+ def get_txt(fname_or_uri)
13
+ is_uri = fname_or_uri.kind_of?(URI)
14
+ ((is_uri && (RUBY_VERSION >= "2.5.0")) ? URI : Kernel) \
15
+ .send(:open, fname_or_uri){|src|
16
+ is_gz = (src.content_type =~ /gzip/) if is_uri
17
+ is_gz ||= (fname_or_uri.to_s =~ /\.gz$/)
18
+ is_file = src.kind_of?(File) || src.kind_of?(Tempfile)
19
+
20
+ return src.path if ((!is_gz) and is_file)
21
+
22
+ Tempfile::open(File::basename($0, '.*')){|dst|
23
+ dst.binmode
24
+ dst.write((is_gz ? inflate(is_file ? src.path : src) : src).read)
25
+ dst.rewind
26
+ dst.path
27
+ }
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.1"
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.4.0
4
+ version: 0.5.1
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-03-17 00:00:00.000000000 Z
11
+ date: 2022-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -93,11 +93,12 @@ files:
93
93
  - ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb
94
94
  - ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb
95
95
  - gps_pvt.gemspec
96
+ - gps_pvt.rbs
96
97
  - lib/gps_pvt.rb
97
98
  - lib/gps_pvt/receiver.rb
98
99
  - lib/gps_pvt/ubx.rb
100
+ - lib/gps_pvt/util.rb
99
101
  - lib/gps_pvt/version.rb
100
- - sig/gps_pvt.rbs
101
102
  homepage: https://github.com/fenrir-naru/gps_pvt
102
103
  licenses: []
103
104
  metadata: