gps_pvt 0.7.0 → 0.8.0

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