gps_pvt 0.7.0 → 0.8.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.
@@ -60,6 +60,12 @@ struct GPS_PVT_RAIM_LSR : public PVT_BaseT {
60
60
  } FDE_min, FDE_2nd; ///< Fault exclusion
61
61
  };
62
62
 
63
+ template <class FloatT>
64
+ struct GPS_Solver_RAIM_LSR_Options {
65
+ bool skip_exclusion;
66
+ GPS_Solver_RAIM_LSR_Options() : skip_exclusion(false) {}
67
+ };
68
+
63
69
  /*
64
70
  * Comment on implementation of protection level (PL) calculation
65
71
  *
@@ -96,6 +102,27 @@ struct GPS_Solver_RAIM_LSR : public SolverBaseT {
96
102
  inheritate_type(measurement2_t);
97
103
  #undef inheritate_type
98
104
 
105
+ typedef typename GPS_Solver_Base<float_t>::options_t::template merge_t<
106
+ GPS_Solver_RAIM_LSR_Options<float_t>, super_t> options_t;
107
+
108
+ protected:
109
+ GPS_Solver_RAIM_LSR_Options<float_t> _options;
110
+
111
+ public:
112
+ options_t available_options() const {
113
+ return options_t(super_t::available_options(), _options);
114
+ }
115
+
116
+ options_t available_options(const options_t &opt_wish) const {
117
+ GPS_Solver_RAIM_LSR_Options<float_t> opt(opt_wish);
118
+ return options_t(super_t::available_options(opt_wish), opt);
119
+ }
120
+
121
+ options_t update_options(const options_t &opt_wish){
122
+ _options = opt_wish;
123
+ return options_t(super_t::update_options(opt_wish), _options);
124
+ }
125
+
99
126
  typedef GPS_PVT_RAIM_LSR<float_t, typename super_t::user_pvt_t> user_pvt_t;
100
127
 
101
128
  typename super_t::template solver_interface_t<self_t> solve() const {
@@ -155,7 +182,8 @@ protected:
155
182
  user_position_init, receiver_error_init,
156
183
  opt);
157
184
 
158
- if(!pvt.position_solved()
185
+ if(_options.skip_exclusion
186
+ || !pvt.position_solved()
159
187
  || (!pvt.FD.valid)
160
188
  || (pvt.used_satellites < 6)){return;}
161
189
 
@@ -353,7 +353,6 @@ struct RINEX_NAV {
353
353
  int t_oc_year4, t_oc_year2, t_oc_mon12;
354
354
  FloatT t_oc_sec;
355
355
  FloatT t_oe_WN;
356
- FloatT ura_meter;
357
356
  FloatT t_ot; ///< Transmitting time [s]
358
357
  FloatT fit_interval_hr;
359
358
  FloatT dummy;
@@ -367,7 +366,6 @@ struct RINEX_NAV {
367
366
  t_oc_mon12(t_oc_tm.tm_mon + 1),
368
367
  t_oc_sec(std::fmod(eph.t_oc, 60)),
369
368
  t_oe_WN(eph.WN),
370
- ura_meter(ephemeris_t::URA_meter(eph.URA)),
371
369
  t_ot(0), // TODO
372
370
  fit_interval_hr(eph.fit_interval / (60 * 60)),
373
371
  dummy(0) {
@@ -378,8 +376,6 @@ struct RINEX_NAV {
378
376
  eph.WN = t_oc.week;
379
377
  eph.t_oc = t_oc.seconds;
380
378
 
381
- eph.URA = ephemeris_t::URA_index(ura_meter); // meter to index
382
-
383
379
  /* @see ftp://igs.org/pub/data/format/rinex210.txt
384
380
  * 6.7 Satellite Health
385
381
  * RINEX Value: 0 Health OK
@@ -1434,14 +1430,14 @@ const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>
1434
1430
 
1435
1431
  template <class FloatT>
1436
1432
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v2[] = {
1437
- GEN_E ( 3, 19, 12, message_t, ura_meter),
1433
+ GEN_E ( 3, 19, 12, message_t, eph.URA),
1438
1434
  GEN_E2(22, 19, 12, message_t, eph.SV_health, unsigned int),
1439
1435
  GEN_E (41, 19, 12, message_t, eph.t_GD),
1440
1436
  GEN_E2(60, 19, 12, message_t, eph.iodc, int),
1441
1437
  };
1442
1438
  template <class FloatT>
1443
1439
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v3[] = {
1444
- GEN_E ( 4, 19, 12, message_t, ura_meter),
1440
+ GEN_E ( 4, 19, 12, message_t, eph.URA),
1445
1441
  GEN_E2(23, 19, 12, message_t, eph.SV_health, unsigned int),
1446
1442
  GEN_E (42, 19, 12, message_t, eph.t_GD),
1447
1443
  GEN_E2(61, 19, 12, message_t, eph.iodc, int),
@@ -1711,7 +1711,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
1711
1711
  uint_t WN; ///< Week number
1712
1712
 
1713
1713
  float_t t_0; ///< Time of applicability (s) <= time of a week
1714
- int_t URA; ///< User range accuracy (index)
1714
+ float_t URA; ///< User range accuracy (m)
1715
1715
  float_t x, y, z; ///< ECEF position (m)
1716
1716
  float_t dx, dy, dz; ///< ECEF velocity (m/s)
1717
1717
  float_t ddx, ddy, ddz; ///< ECEF acceleration (m/s^2)
@@ -1804,6 +1804,15 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
1804
1804
  return res;
1805
1805
  }
1806
1806
 
1807
+ static const float_t URA_table[15];
1808
+
1809
+ inline static float_t URA_meter(const int_t &index){
1810
+ return gps_space_node_t::SatelliteProperties::Ephemeris::URA_meter(index, URA_table);
1811
+ }
1812
+ inline static int_t URA_index(const float_t &meter){
1813
+ return gps_space_node_t::SatelliteProperties::Ephemeris::URA_index(meter, URA_table);
1814
+ }
1815
+
1807
1816
  struct raw_t {
1808
1817
  u8_t svid; ///< Satellite number
1809
1818
 
@@ -1850,7 +1859,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
1850
1859
  converted.svid = svid;
1851
1860
  converted.WN = 0; // Week number (must be configured later) @see adjust_time
1852
1861
 
1853
- converted.URA = URA;
1862
+ converted.URA = URA_meter(URA);
1854
1863
  CONVERT(t_0); // Time of a day => time of a week (must be configured later) @see adjust_time
1855
1864
  CONVERT2(x, xy); CONVERT2(y, xy); CONVERT(z);
1856
1865
  CONVERT2(dx, dxy); CONVERT2(dy, dxy); CONVERT(dz);
@@ -1869,7 +1878,7 @@ sf[SF_ ## TARGET] * msg_t::TARGET(buf)
1869
1878
  #define CONVERT(TARGET) CONVERT2(TARGET, TARGET)
1870
1879
  svid = eph.svid;
1871
1880
 
1872
- URA = eph.URA;
1881
+ URA = URA_index(eph.URA);
1873
1882
  CONVERT3(t_0, std::fmod(t_0, gps_time_t::seconds_day), t_0);
1874
1883
  CONVERT2(x, xy); CONVERT2(y, xy); CONVERT(z);
1875
1884
  CONVERT2(dx, dxy); CONVERT2(dy, dxy); CONVERT(dz);
@@ -2350,4 +2359,23 @@ const typename SBAS_SpaceNode<FloatT>::float_t SBAS_SpaceNode<FloatT>::UTC_Param
2350
2359
 
2351
2360
  #undef POWER_2
2352
2361
 
2362
+ template <class FloatT>
2363
+ const typename SBAS_SpaceNode<FloatT>::float_t SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris::URA_table[] = {
2364
+ 2,
2365
+ 2.8,
2366
+ 4,
2367
+ 5.7,
2368
+ 8,
2369
+ 11.3,
2370
+ 16,
2371
+ 32,
2372
+ 64,
2373
+ 128,
2374
+ 256,
2375
+ 512,
2376
+ 1024,
2377
+ 2048,
2378
+ 4096,
2379
+ };
2380
+
2353
2381
  #endif /* __SBAS_H__ */
@@ -126,11 +126,15 @@ class SBAS_SinglePositioning : public SolverBaseT {
126
126
  static float_t clock_error_dot(const void *ptr, const gps_time_t &t_tx) {
127
127
  return sat(ptr).ephemeris().clock_error_dot(t_tx);
128
128
  }
129
+ static float_t range_sigma(const void *ptr, const gps_time_t &t_tx) {
130
+ return sat(ptr).ephemeris().URA;
131
+ }
129
132
  };
130
133
  satellite_t res = {
131
- &(it_sat->second), &(it_sat->second),
134
+ &(it_sat->second), &(it_sat->second), // position, time
132
135
  impl_t::position, impl_t::velocity,
133
- impl_t::clock_error, impl_t::clock_error_dot};
136
+ impl_t::clock_error, impl_t::clock_error_dot,
137
+ &(it_sat->second), impl_t::range_sigma, NULL}; // error model
134
138
  return res;
135
139
  }
136
140
  satellites_t(const space_node_t &sn)
@@ -245,7 +249,7 @@ class SBAS_SinglePositioning : public SolverBaseT {
245
249
  float_t range;
246
250
  range_error_t range_error;
247
251
  if(!this->range(measurement, range, &range_error)){
248
- return res; // If no range entry, return with weight = 0
252
+ return res; // If no range entry, return with sigma = 0
249
253
  }
250
254
 
251
255
  satellite_t sat(select_satellite(prn, time_arrival));
@@ -297,21 +301,29 @@ class SBAS_SinglePositioning : public SolverBaseT {
297
301
 
298
302
  // TODO Fast corrections (2.1.1.4.12)
299
303
 
300
- // TODO Setup weight
301
- if(std::abs(res.range_residual) > _options.residual_mask){
302
- // If residual is too big, gently exclude it by decreasing its weight.
303
- res.weight = 1E-8;
304
- }else{
304
+ // Setup weight
305
+ res.range_sigma = 1E+4; // sufficiently big value, 1E4 [m]
306
+ do{
307
+ // If residual is too big, gently exclude it.
308
+ if(std::abs(res.range_residual) > _options.residual_mask){break;}
305
309
 
306
310
  float_t elv(relative_pos.elevation());
307
311
  if(elv < _options.elevation_mask){
308
- res.weight = 0; // exclude it when elevation is less than threshold
309
- }else{
310
- // elevation weight based on "GPS���p�v���O���~���O" @see GPS_Solver.h
311
- res.weight = std::pow(sin(elv)/0.8, 2);
312
- if(res.weight < 1E-3){res.weight = 1E-3;}
312
+ res.range_sigma = 0; // exclude it when elevation is less than threshold
313
+ break;
313
314
  }
314
- }
315
+
316
+ res.range_sigma = sat.range_sigma(t_tx);
317
+
318
+ /* elevation weight based on "GPS���p�v���O���~���O"
319
+ * elevation[deg] : 90 53 45 30 15 10 5
320
+ * sf_sigma(k) : 0.80 1.00 1.13 1.60 3.09 4.61 9.18
321
+ * weight(k^-2) : 1.56 1.00 0.78 0.39 0.10 0.05 0.01
322
+ */
323
+ static const float_t max_sf(10);
324
+ static const float_t elv_limit(std::asin(0.8/max_sf)); // limit
325
+ res.range_sigma *= (elv > elv_limit) ? (0.8 / sin(elv)) : max_sf;
326
+ }while(false);
315
327
 
316
328
  res.range_corrected = range;
317
329
 
@@ -185,9 +185,10 @@ struct SP3_Product {
185
185
  }
186
186
  };
187
187
  typename GPS_Solver_Base<FloatT>::satellite_t res = {
188
- this, this,
188
+ this, this, // position, time
189
189
  impl_t::position, impl_t::velocity,
190
- impl_t::clock_error, impl_t::clock_error_dot
190
+ impl_t::clock_error, impl_t::clock_error_dot,
191
+ NULL, NULL, NULL, // TODO error model
191
192
  };
192
193
  return res;
193
194
  }
@@ -125,9 +125,9 @@ struct BitArray {
125
125
  return (unsigned int)res;
126
126
  }
127
127
  }
128
- std::vector<int> indices_one() const {
128
+ std::vector<int> indices_one(const int &offset = 0) const {
129
129
  std::vector<int> res;
130
- int idx(0);
130
+ int idx(offset);
131
131
  static const const_div_t<bits_per_addr> qr(MAX_SIZE);
132
132
  int rem(qr.rem), i(0);
133
133
  for(; i < qr.quot; ++i, idx += bits_per_addr){
@@ -1124,10 +1124,13 @@ struct GPS_RangeCorrector
1124
1124
  static const int prop_items(sizeof(res.values) / sizeof(res.values[0]));
1125
1125
  VALUE hook(rb_hash_lookup(hooks, key));
1126
1126
  if(NIL_P(hook)){break;}
1127
+ FloatT weight((res.prop.range_sigma > 0)
1128
+ ? (1. / std::pow(res.prop.range_sigma, 2)) // weight=1/(sigma^2)
1129
+ : res.prop.range_sigma);
1127
1130
  VALUE values[] = {
1128
1131
  SWIG_From(int)(prn), // prn
1129
1132
  rb_ary_new_from_args(prop_items, // relative_property
1130
- swig::from(res.prop.weight),
1133
+ swig::from(weight),
1131
1134
  swig::from(res.prop.range_corrected),
1132
1135
  swig::from(res.prop.range_residual),
1133
1136
  swig::from(res.prop.rate_relative_neg),
@@ -1159,6 +1162,9 @@ struct GPS_RangeCorrector
1159
1162
  .append(" @ [").append(std::to_string(i)).append("]"));
1160
1163
  }
1161
1164
  }
1165
+ if(res.values[0] > 0){
1166
+ res.values[0] = std::pow(1. / res.values[0], 0.5); // sigma=(1/weight)^0.5
1167
+ }
1162
1168
  }while(false);
1163
1169
  #endif
1164
1170
  return res.prop;
@@ -1356,6 +1362,28 @@ struct GPS_RangeCorrector
1356
1362
  return self->update_correction(true, hash);
1357
1363
  }
1358
1364
  #endif
1365
+ #ifdef SWIGRUBY
1366
+ %typemap(out) typename super_t::options_t {
1367
+ VALUE res(rb_hash_new());
1368
+ rb_hash_aset(res, ID2SYM(rb_intern("skip_exclusion")), SWIG_From(bool)($1.skip_exclusion));
1369
+ %set_output(res);
1370
+ }
1371
+ #endif
1372
+ %rename("options") get_options;
1373
+ typename super_t::options_t get_options() const {
1374
+ return self->available_options();
1375
+ }
1376
+ %rename("options=") set_options;
1377
+ typename super_t::options_t set_options(SWIG_Object obj) {
1378
+ GPS_Solver<FloatT>::super_t::options_t opt(self->available_options());
1379
+ #ifdef SWIGRUBY
1380
+ if(!RB_TYPE_P(obj, T_HASH)){SWIG_exception(SWIG_TypeError, "Hash is expected");}
1381
+ SWIG_AsVal(bool)(
1382
+ rb_hash_lookup(obj, ID2SYM(rb_intern("skip_exclusion"))),
1383
+ &opt.skip_exclusion);
1384
+ #endif
1385
+ return self->update_options(opt);
1386
+ }
1359
1387
  }
1360
1388
  %inline {
1361
1389
  template <class FloatT>
@@ -658,6 +658,73 @@ __ANTEX_TEXT__
658
658
  # https://files.igs.org/pub/station/general/igs14.atx
659
659
  f.path
660
660
  },
661
+ :rinex_clk => Tempfile::open{|f|
662
+ f.write(<<-__RINEX_CLK_TEXT__)
663
+ 3.00 C RINEX VERSION / TYPE
664
+ CCLOCK IGSACC @ NOAA NGS PGM / RUN BY / DATE
665
+ GPS week: 1849 Day: 2 MJD: 57189 COMMENT
666
+ THE COMBINED CLOCKS ARE A WEIGHTED AVERAGE OF: COMMENT
667
+ cod emr esa gfz grg jpl COMMENT
668
+ THE FOLLOWING REFERENCE CLOCKS WERE USED BY ACs: COMMENT
669
+ WSRT AMC2 BRUX HRAO COMMENT
670
+ THE COMBINED CLOCKS ARE ALIGNED TO GPS TIME COMMENT
671
+ USING THE SATELLITE BROADCAST EPHEMERIDES COMMENT
672
+ All clocks have been re-aligned to the IGS time scale: IGST COMMENT
673
+ 16 LEAP SECONDS
674
+ 1 AS # / TYPES OF DATA
675
+ IGS IGSACC @ NOAA NGS ANALYSIS CENTER
676
+ 6 # OF SOLN SATS
677
+ G12 G18 G24 G25 G29 G31 PRN LIST
678
+ G igs08_1848.atx SYS / PCVS APPLIED
679
+ END OF HEADER
680
+ AS G12 2015 06 15 23 45 0.000000 2 3.017937472687e-04 1.069824072610e-11
681
+ AS G18 2015 06 15 23 45 0.000000 2 4.096815344517e-04 9.225410060960e-12
682
+ AS G24 2015 06 15 23 45 0.000000 2 -4.998574751545e-05 2.372704308220e-11
683
+ AS G25 2015 06 15 23 45 0.000000 2 -2.290169594092e-06 1.683218228880e-11
684
+ AS G29 2015 06 15 23 45 0.000000 2 6.168866864097e-04 1.206217360840e-11
685
+ AS G31 2015 06 15 23 45 0.000000 2 3.128244102077e-04 2.173867579920e-11
686
+ AS G12 2015 06 15 23 50 0.000000 2 3.017948525918e-04 1.300769315700e-11
687
+ AS G18 2015 06 15 23 50 0.000000 2 4.096823340968e-04 8.242414325510e-12
688
+ AS G24 2015 06 15 23 50 0.000000 2 -4.998578937856e-05 3.095238106380e-11
689
+ AS G25 2015 06 15 23 50 0.000000 2 -2.291410963946e-06 2.242880773880e-11
690
+ AS G29 2015 06 15 23 50 0.000000 2 6.168873857128e-04 1.695910016600e-11
691
+ AS G31 2015 06 15 23 50 0.000000 2 3.128241216228e-04 1.904362802980e-11
692
+ AS G12 2015 06 15 23 55 0.000000 2 3.017958412577e-04 1.171495359430e-11
693
+ AS G18 2015 06 15 23 55 0.000000 2 4.096833491457e-04 1.008770038860e-11
694
+ AS G24 2015 06 15 23 55 0.000000 2 -4.998596967628e-05 2.600074192880e-11
695
+ AS G25 2015 06 15 23 55 0.000000 2 -2.292650290468e-06 2.671557682290e-11
696
+ AS G29 2015 06 15 23 55 0.000000 2 6.168880208367e-04 1.729095211710e-11
697
+ AS G31 2015 06 15 23 55 0.000000 2 3.128236493567e-04 1.807462018720e-11
698
+ AS G12 2015 06 16 00 00 0.000000 2 3.017967728708e-04 1.000801049580e-11
699
+ AS G18 2015 06 16 00 00 0.000000 2 4.096840539308e-04 1.041670195750e-11
700
+ AS G24 2015 06 16 00 00 0.000000 2 -4.998614775959e-05 1.699628132880e-11
701
+ AS G25 2015 06 16 00 00 0.000000 2 -2.293760646361e-06 1.805118466830e-11
702
+ AS G29 2015 06 16 00 00 0.000000 2 6.168886213338e-04 2.220055594770e-11
703
+ AS G31 2015 06 16 00 00 0.000000 2 3.128233311248e-04 1.215284180360e-11
704
+ AS G12 2015 06 16 00 05 0.000000 2 3.017980078016e-04 1.152529430320e-11
705
+ AS G18 2015 06 16 00 05 0.000000 2 4.096851745816e-04 1.517949585220e-11
706
+ AS G24 2015 06 16 00 05 0.000000 2 -4.998569129850e-05 1.987308774570e-11
707
+ AS G25 2015 06 16 00 05 0.000000 2 -2.295014987154e-06 1.568891027900e-11
708
+ AS G29 2015 06 16 00 05 0.000000 2 6.168894672506e-04 1.906237292480e-11
709
+ AS G31 2015 06 16 00 05 0.000000 2 3.128231387506e-04 1.007829428500e-11
710
+ AS G12 2015 06 16 00 10 0.000000 2 3.017987334831e-04 8.726251168980e-12
711
+ AS G18 2015 06 16 00 10 0.000000 2 4.096859333261e-04 1.403166740100e-11
712
+ AS G24 2015 06 16 00 10 0.000000 2 -4.998597422529e-05 1.727695888430e-11
713
+ AS G25 2015 06 16 00 10 0.000000 2 -2.296262879345e-06 1.460867818340e-11
714
+ AS G29 2015 06 16 00 10 0.000000 2 6.168900483001e-04 1.556536485070e-11
715
+ AS G31 2015 06 16 00 10 0.000000 2 3.128226535741e-04 1.258498073020e-11
716
+ AS G12 2015 06 16 00 15 0.000000 2 3.017999301583e-04 1.006627990480e-11
717
+ AS G18 2015 06 16 00 15 0.000000 2 4.096864636193e-04 1.460541676660e-11
718
+ AS G24 2015 06 16 00 15 0.000000 2 -4.998605241609e-05 1.970841363000e-11
719
+ AS G25 2015 06 16 00 15 0.000000 2 -2.297477664413e-06 1.598459869060e-11
720
+ AS G29 2015 06 16 00 15 0.000000 2 6.168906691983e-04 1.729396319140e-11
721
+ AS G31 2015 06 16 00 15 0.000000 2 3.128222195893e-04 1.358710059900e-11
722
+ __RINEX_CLK_TEXT__
723
+ # modified version. original data are
724
+ # https://cddis.nasa.gov/archive/gnss/products/1849/igs1849[12].clk.Z
725
+ # mirrored: ftp://garner.ucsd.edu/pub/products/1849/igs1849[12].clk.Z
726
+ f.path
727
+ },
661
728
  }}
662
729
  let(:solver){
663
730
  res = GPS::Solver::new
@@ -733,20 +800,20 @@ __ANTEX_TEXT__
733
800
  expect(pvt.position_solved?).to be(true)
734
801
  expect(pvt.receiver_time.to_a).to eq([1849, 172413])
735
802
  expect(pvt.llh.to_a).to eq([:lat, :lng, :alt].collect{|k| pvt.llh.send(k)})
736
- expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-9).of(35.6992591268) # latitude
737
- expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-9).of(139.541502292) # longitude
738
- expect(pvt.llh.alt) .to be_within(1E-4).of(104.279402455) # altitude
739
- expect(pvt.receiver_error).to be_within(1E-4).of(1259087.83603)
740
- expect(pvt.gdop).to be_within(1E-10).of(3.83282723293)
741
- expect(pvt.pdop).to be_within(1E-10).of(3.30873220653)
742
- expect(pvt.hdop).to be_within(1E-10).of(2.05428293774)
743
- expect(pvt.vdop).to be_within(1E-10).of(2.59376761222)
744
- expect(pvt.tdop).to be_within(1E-10).of(1.9346461648)
803
+ expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-3).of(35.7) # latitude
804
+ expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-3).of(139.542) # longitude
805
+ expect(pvt.llh.alt) .to be_within(1E-1).of(104.3) # altitude
806
+ expect(pvt.receiver_error).to be_within(1E-1).of(1259087.8)
807
+ expect(pvt.gdop).to be_within(1E-2).of(2.42)
808
+ expect(pvt.pdop).to be_within(1E-2).of(2.17)
809
+ expect(pvt.hdop).to be_within(1E-2).of(1.11)
810
+ expect(pvt.vdop).to be_within(1E-2).of(1.87)
811
+ expect(pvt.tdop).to be_within(1E-2).of(1.08)
745
812
  expect(pvt.velocity.to_a).to eq([:e, :n, :u].collect{|k| pvt.velocity.send(k)})
746
- expect(pvt.velocity.north).to be_within(1E-7).of(-0.839546227836) # north
747
- expect(pvt.velocity.east) .to be_within(1E-7).of(-1.05805616381) # east
748
- expect(pvt.velocity.down) .to be_within(1E-7).of(-0.12355474006) # down
749
- expect(pvt.receiver_error_rate).to be_within(1E-7).of(-1061.92654151)
813
+ expect(pvt.velocity.north).to be_within(1E-2).of(-0.86) # north
814
+ expect(pvt.velocity.east) .to be_within(1E-2).of(-1.10) # east
815
+ expect(pvt.velocity.down) .to be_within(1E-2).of(-0.22) # down
816
+ expect(pvt.receiver_error_rate).to be_within(1E-2).of(-1061.86)
750
817
  expect(pvt.G.rows).to eq(6)
751
818
  expect(pvt.W.rows).to eq(6)
752
819
  expect(pvt.delta_r.rows).to eq(6)
@@ -778,6 +845,10 @@ __ANTEX_TEXT__
778
845
 
779
846
  it 'can be modified through hooks' do
780
847
  sn = solver.gps_space_node
848
+ expect(solver.options).to be_a_kind_of(Hash)
849
+ expect(solver.options.keys).to include(:skip_exclusion)
850
+ expect{solver.options = {:skip_exclusion => true}}.not_to raise_error
851
+ expect(solver.options[:skip_exclusion]).to eq(true)
781
852
  expect(solver.correction[:gps_ionospheric]).to include(:klobuchar)
782
853
  expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
783
854
  expect{solver.correction = nil}.to raise_error(RuntimeError)
@@ -937,5 +1008,55 @@ __ANTEX_TEXT__
937
1008
  puts ([:lat, :lng].collect{|f| pvt.llh.send(f) / Math::PI * 180} + [pvt.llh.alt]).inspect
938
1009
  }
939
1010
  end
1011
+ it 'calculates satellite clock error based on RINEX clock' do
1012
+ clk, sn = [GPS::RINEX_Clock::new, solver.gps_space_node]
1013
+ expect(clk.read(input[:rinex_clk])).to eq(6 * 7)
1014
+ proc{|sats|
1015
+ expect(sats.kind_of?(Array)).to eq(true)
1016
+ expect(sats[clk.class::SYS_GPS]).to eq(6)
1017
+ }.call(clk.satellites)
1018
+ sn.read(input[:rinex_nav])
1019
+ t0 = GPS::Time::new(1849, 172800)
1020
+ sn.update_all_ephemeris(t0)
1021
+ (-5..5).step(1){|dt_min|
1022
+ t = t0 + (dt_min * 60)
1023
+ [12, 18, 24, 25, 29, 31].each{|sat_id|
1024
+ eph = sn.ephemeris(sat_id).constellation(t)
1025
+ expect(clk.clock_error(sat_id, t)).to be_within(1E-7).of(eph[2]) # 100 ns
1026
+ expect(clk.clock_error_dot(sat_id, t)).to be_within(1E-10).of(eph[3]) # 100 ps
1027
+ }
1028
+ }
1029
+ end
1030
+ it 'calculates position without any error with RINEX NAV and CLK' do
1031
+ sn = solver.gps_space_node
1032
+ sn.read(input[:rinex_nav])
1033
+ clk = GPS::RINEX_Clock::new
1034
+ clk.read(input[:rinex_clk])
1035
+ expect(clk.push(solver, clk.class::SYS_GPS)).to eq(true)
1036
+ GPS::RINEX_Observation::read(input[:rinex_obs]){|item|
1037
+ t_meas = item[:time]
1038
+ sn.update_all_ephemeris(t_meas)
1039
+ meas = GPS::Measurement::new
1040
+ types = (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
1041
+ type_ = {
1042
+ "C1" => :L1_PSEUDORANGE,
1043
+ "D1" => :L1_RANGE_RATE,
1044
+ }[type_]
1045
+ type_ && [i, GPS::Measurement::const_get(type_)]
1046
+ }.compact
1047
+ item[:meas].each{|k, v|
1048
+ sys, prn = k
1049
+ next unless sys == 'G' # GPS only
1050
+ types.each{|i, type_|
1051
+ meas.add(prn, type_, v[i][0]) if v[i]
1052
+ }
1053
+ }
1054
+ pvt = solver.solve(meas, t_meas)
1055
+ expect(pvt.position_solved?).to eq(true)
1056
+ [-3952590.4754, 3360273.8926, 3697987.2632].zip(pvt.xyz.to_a).each{|a, b|
1057
+ expect(a).to be_within(1E+2).of(b) # 10 m
1058
+ }
1059
+ }
1060
+ end
940
1061
  end
941
1062
  end
data/gps_pvt.gemspec CHANGED
@@ -58,6 +58,7 @@ Gem::Specification.new do |spec|
58
58
 
59
59
  # Uncomment to register a new dependency of your gem
60
60
  # spec.add_dependency "example-gem", "~> 1.0"
61
+ spec.add_dependency "rubyserial"
61
62
  spec.add_development_dependency "rake"
62
63
  spec.add_development_dependency "rake-compiler"
63
64