gps_pvt 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f4007b50ab655503a9f52a09cccd613ba7f21463989067887a71d2e45449d51
4
- data.tar.gz: 945ef479edd3fdec649d8a4dffe4ce8cadb4d7a20e927171fbd817020f689fb6
3
+ metadata.gz: b05eabd6a7d771c772d6146722153e6ae2e9292ed9f41892e8285a2aa7d2f4ee
4
+ data.tar.gz: e8566da7fc64f0f330771ee8420bef47dfd819ae703d5ef46fe0bcf310ddf11f
5
5
  SHA512:
6
- metadata.gz: f42a861328b4f987061889268aaf2efbba1bf0c42f5cf6a830b97454ba45dc89d19203492366af2b3d746eb135e5a2553ee4bcff618fae0837c26f713015f0bf
7
- data.tar.gz: 29b0e2e7e4eaf07bc8918a56942842763f4b4c3497986aabecea70a808a9d2c05289f1a07a2c5c745cb12358d622fc6d772e75dd7440e9063d5e517bcb76ac9f
6
+ metadata.gz: 56054b607dc23efa2826f02ab117fc07080e447a3775215ea642aa4e77f481929ec0f8d66a4634c873cb453c9a5e9fc36fe73c528c361e50aaa844ebf385506f
7
+ data.tar.gz: 4d47da0ba28881434c03ef1eb270c5d3e20dc17e5abbb222bb076ae2c2c8295a973b38764e7616e6f4adcd102904ed21389397a52ec3f91853c938e98464248e
data/README.md CHANGED
@@ -72,8 +72,9 @@ receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
72
72
  }
73
73
 
74
74
  # Customize solution
75
- receiver.solver.options.exclude(prn) # Exclude satellite; the default is to use every satellite if visible
76
- receiver.solver.options.include(prn) # Discard previous setting of exclusion
75
+ receiver.solver.gps_options.exclude(prn) # Exclude satellite; the default is to use every satellite if visible
76
+ receiver.solver.gps_options.include(prn) # Discard previous setting of exclusion
77
+ receiver.solver.gps_options.elevation_mask = Math::PI / 180 * 10 # example 10 [deg] elevation mask
77
78
  receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
78
79
  # control weight per satellite per iteration
79
80
  weight, range_c, range_r, rate_rel_neg, *los_neg = rel_prop # relative property
data/Rakefile CHANGED
@@ -69,12 +69,15 @@ task :swig do
69
69
  sh [:make, :clean, wrapper,
70
70
  "BUILD_DIR=#{out_dir}",
71
71
  "SWIGFLAGS='-c++ -ruby -prefix \"GPS_PVT::\"#{" -D__MINGW__" if ENV["MSYSTEM"]}'"].join(' ')
72
- lines = open(wrapper, 'r').read.lines.collect{|line|
73
- line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
74
- "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
72
+ open(wrapper, 'r+'){|io|
73
+ lines = io.read.lines.collect{|line|
74
+ line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
75
+ "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
76
+ }
75
77
  }
78
+ io.rewind
79
+ io.write(lines.join)
76
80
  }
77
- open(wrapper, 'w').write(lines.join)
78
81
  }
79
82
  }
80
83
  end
@@ -243,7 +243,7 @@ struct GPS_Time {
243
243
  GPS_Time &canonicalize(){
244
244
  int quot(std::floor(seconds / seconds_week));
245
245
  week += quot;
246
- seconds -= (seconds_week * quot);
246
+ seconds -= (quot * (int)seconds_week);
247
247
  return *this;
248
248
  }
249
249
  GPS_Time(const std::tm &t, const float_t &leap_seconds = 0) {
@@ -50,6 +50,7 @@
50
50
  #include <algorithm>
51
51
  #include <cstddef>
52
52
  #include <cstring>
53
+ #include <limits>
53
54
 
54
55
  #include "GPS.h"
55
56
 
@@ -180,22 +181,27 @@ class RINEX_Reader {
180
181
  const header_t &header() const {return const_cast<self_t *>(this)->header();}
181
182
  bool has_next() const {return _has_next;}
182
183
 
183
- template <class T>
184
+ template <class T, bool is_integer = std::numeric_limits<T>::is_integer>
184
185
  struct conv_t {
185
- static void d(
186
+ static bool d(
186
187
  std::string &buf, const int &offset, const int &length, void *value, const int &opt = 0, const bool &str2val = true){
187
188
  if(str2val){
188
- std::stringstream(buf.substr(offset, length)) >> *(T *)value;
189
+ std::stringstream ss(buf.substr(offset, length));
190
+ ss >> *(T *)value;
191
+ return (ss.rdstate() & std::ios_base::failbit) == 0;
189
192
  }else{
190
193
  std::stringstream ss;
191
194
  ss << std::setfill(opt == 1 ? '0' : ' ') << std::right << std::setw(length) << *(T *)value;
192
195
  buf.replace(offset, length, ss.str());
196
+ return true;
193
197
  }
194
198
  }
195
- static void f(
199
+ static bool f(
196
200
  std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
197
201
  if(str2val){
198
- std::stringstream(buf.substr(offset, length)) >> *(T *)value;
202
+ std::stringstream ss(buf.substr(offset, length));
203
+ ss >> *(T *)value;
204
+ return (ss.rdstate() & std::ios_base::failbit) == 0;
199
205
  }else{
200
206
  std::stringstream ss;
201
207
  ss << std::setfill(' ') << std::right << std::setw(length)
@@ -215,9 +221,10 @@ class RINEX_Reader {
215
221
  }
216
222
  }
217
223
  buf.replace(offset, length, s);
224
+ return true;
218
225
  }
219
226
  }
220
- static void e(
227
+ static bool e(
221
228
  std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
222
229
  if(str2val){
223
230
  std::string s(buf.substr(offset, length));
@@ -225,7 +232,9 @@ class RINEX_Reader {
225
232
  if(pos != std::string::npos){
226
233
  s.replace(pos, 1, "E");
227
234
  }
228
- std::stringstream(s) >> *(T *)value;
235
+ std::stringstream ss(s);
236
+ ss >> *(T *)value;
237
+ return (ss.rdstate() & std::ios_base::failbit) == 0;
229
238
  }else{
230
239
  int w((std::max)(length, precision + 6)); // parentheses of std::max mitigates error C2589 under Windows VC
231
240
 
@@ -247,12 +256,36 @@ class RINEX_Reader {
247
256
  ss.str("");
248
257
  ss << std::setfill(' ') << std::right << std::setw(w) << s;
249
258
  buf.replace(offset, length, ss.str());
259
+ return true;
250
260
  }
251
261
  }
252
262
  };
263
+ template <class T>
264
+ struct conv_t<T, true> {
265
+ static bool d(
266
+ std::string &buf, const int &offset, const int &length, void *value, const int &opt = 0, const bool &str2val = true){
267
+ return conv_t<T, false>::d(buf, offset, length, value, opt, str2val);
268
+ }
269
+ static bool f(
270
+ std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
271
+ double v(*(T *)value);
272
+ bool res(
273
+ conv_t<double, false>::f(buf, offset, length, &v, precision, str2val));
274
+ *(T *)value = static_cast<T>(v);
275
+ return res;
276
+ }
277
+ static bool e(
278
+ std::string &buf, const int &offset, const int &length, void *value, const int &precision = 0, const bool &str2val = true){
279
+ double v(*(T *)value);
280
+ bool res(
281
+ conv_t<double, false>::e(buf, offset, length, &v, precision, str2val));
282
+ *(T *)value = static_cast<T>(v);
283
+ return res;
284
+ }
285
+ };
253
286
 
254
287
  struct convert_item_t {
255
- void (*func)(
288
+ bool (*func)(
256
289
  std::string &buf, const int &offset, const int &length, void *value,
257
290
  const int &opt, const bool &str2val);
258
291
  int offset;
@@ -261,17 +294,30 @@ class RINEX_Reader {
261
294
  int opt;
262
295
  };
263
296
 
264
- static void convert(const convert_item_t *items, const int &size, const std::string &buf, void *values){
297
+ /**
298
+ * @param recovery if conversion fails, then this functor is invoked.
299
+ * If recovery is successfully performed, this functor should return true.
300
+ * The return value of this function reflects it.
301
+ * @return (bool) if all conversion are successfully performed, true is returned; otherwise false.
302
+ */
303
+ static bool convert(
304
+ const convert_item_t *items, const int &size, const std::string &buf, void *values,
305
+ bool (*recovery)(const int &, const std::string &, void *) = NULL){
265
306
  // str => value
307
+ bool res(true);
266
308
  for(int i(0); i < size; ++i){
267
- (*items[i].func)(
309
+ if((*items[i].func)(
268
310
  const_cast<std::string &>(buf), items[i].offset, items[i].length, (char *)values + items[i].value_offset,
269
- items[i].opt, true);
311
+ items[i].opt, true)){continue;}
312
+ res &= (recovery ? (*recovery)(i, buf, values) : false);
270
313
  }
314
+ return res;
271
315
  }
272
316
  template <int N>
273
- static inline void convert(const convert_item_t (&items)[N], const std::string &buf, void *values){
274
- convert(items, N, buf, values);
317
+ static inline bool convert(
318
+ const convert_item_t (&items)[N], const std::string &buf, void *values,
319
+ bool (*recovery)(const int &, const std::string &, void *) = NULL){
320
+ return convert(items, N, buf, values, recovery);
275
321
  }
276
322
  };
277
323
 
@@ -404,7 +450,6 @@ struct RINEX_NAV {
404
450
  std::tm t_oc_tm;
405
451
  int t_oc_year4, t_oc_year2, t_oc_mon12;
406
452
  FloatT t_oc_sec;
407
- FloatT iodc_f, iode_f; // originally int type
408
453
  FloatT t_oe_WN;
409
454
  FloatT ura_meter;
410
455
  FloatT SV_health_f;
@@ -420,7 +465,6 @@ struct RINEX_NAV {
420
465
  t_oc_year2(t_oc_tm.tm_year % 100),
421
466
  t_oc_mon12(t_oc_tm.tm_mon + 1),
422
467
  t_oc_sec(std::fmod(eph.t_oc, 60)),
423
- iodc_f(eph.iodc), iode_f(eph.iode),
424
468
  t_oe_WN(eph.WN),
425
469
  ura_meter(ephemeris_t::URA_meter(eph.URA)),
426
470
  SV_health_f(((eph.SV_health & 0x20) && (eph.SV_health & 0x1F == 0)) ? 1 : eph.SV_health),
@@ -434,9 +478,6 @@ struct RINEX_NAV {
434
478
  eph.WN = t_oc.week;
435
479
  eph.t_oc = t_oc.seconds;
436
480
 
437
- eph.iodc = iodc_f;
438
- eph.iode = iode_f;
439
-
440
481
  eph.URA = ephemeris_t::URA_index(ura_meter); // meter to index
441
482
 
442
483
  /* @see ftp://igs.org/pub/data/format/rinex210.txt
@@ -478,13 +519,15 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
478
519
  static const typename super_t::convert_item_t eph7_v2[2], eph7_v3[2];
479
520
 
480
521
  protected:
522
+ typename super_t::version_type_t::sat_system_t sys_of_msg;
481
523
  message_t msg;
482
524
 
483
- void seek_next_v2() {
525
+ void seek_next_v2_gps() {
484
526
  char buf[256];
485
527
 
486
- for(int i = 0; (i < 8) && (super_t::src.good()); i++){
487
- if(super_t::src.getline(buf, sizeof(buf)).fail()){return;}
528
+ for(int i(0); i < 8; i++){
529
+ if((!super_t::src.good())
530
+ || super_t::src.getline(buf, sizeof(buf)).fail()){return;}
488
531
  std::string line(buf);
489
532
 
490
533
  switch(i){
@@ -505,25 +548,43 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
505
548
  }
506
549
  }
507
550
  msg.update();
551
+ sys_of_msg = super_t::version_type_t::SYS_GPS;
508
552
  super_t::_has_next = true;
509
553
  }
510
554
 
511
- void seek_next_v3() {
555
+ void seek_next_v2_glonass() {
512
556
  char buf[256];
513
557
 
514
- for(int i = 0; (i < 8) && (super_t::src.good()); i++){
515
- if(super_t::src.getline(buf, sizeof(buf)).fail()){return;}
558
+ for(int i(0); i < 4; i++){
559
+ if((!super_t::src.good())
560
+ || super_t::src.getline(buf, sizeof(buf)).fail()){return;}
561
+ }
562
+ //sys_of_msg = super_t::version_type_t::SYS_GLONASS; // TODO currently not implemented
563
+ sys_of_msg = super_t::version_type_t::SYS_UNKNOWN;
564
+ super_t::_has_next = true;
565
+ }
566
+
567
+ void seek_next_v2() {
568
+ switch(super_t::version_type.sat_system){
569
+ case super_t::version_type_t::SYS_GPS: seek_next_v2_gps(); return;
570
+ case super_t::version_type_t::SYS_GLONASS: seek_next_v2_glonass(); return;
571
+ default: break;
572
+ }
573
+ }
574
+
575
+ template <std::size_t N>
576
+ void seek_next_v3_gps(char (&buf)[N]) {
577
+ super_t::convert(eph0_v3, std::string(buf), &msg);
578
+ msg.t_oc_tm.tm_year = msg.t_oc_year4 - 1900; // tm_year base is 1900
579
+ msg.t_oc_tm.tm_mon = msg.t_oc_mon12 - 1; // month [0, 11]
580
+ msg.t_oc_sec = msg.t_oc_tm.tm_sec;
581
+
582
+ for(int i(1); i < 8; i++){
583
+ if((!super_t::src.good())
584
+ || super_t::src.getline(buf, sizeof(buf)).fail()){return;}
516
585
  std::string line(buf);
517
586
 
518
587
  switch(i){
519
- case 0: {
520
- // if(line_data[0] != 'G'){} // TODO check GPS before parsing
521
- super_t::convert(eph0_v3, line, &msg);
522
- msg.t_oc_tm.tm_year = msg.t_oc_year4 - 1900; // tm_year base is 1900
523
- msg.t_oc_tm.tm_mon = msg.t_oc_mon12 - 1; // month [0, 11]
524
- msg.t_oc_sec = msg.t_oc_tm.tm_sec;
525
- break;
526
- }
527
588
  case 1: super_t::convert(eph1_v3, line, &msg); break;
528
589
  case 2: super_t::convert(eph2_v3, line, &msg); break;
529
590
  case 3: super_t::convert(eph3_v3, line, &msg); break;
@@ -534,10 +595,42 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
534
595
  }
535
596
  }
536
597
  msg.update();
598
+ sys_of_msg = super_t::version_type_t::SYS_GPS;
537
599
  super_t::_has_next = true;
538
600
  }
539
601
 
602
+ template <std::size_t N>
603
+ void seek_next_v3_not_implemented(char (&buf)[N], const int &lines) {
604
+ for(int i(1); i < lines; i++){
605
+ if((!super_t::src.good())
606
+ || super_t::src.getline(buf, sizeof(buf)).fail()){return;}
607
+ }
608
+ sys_of_msg = super_t::version_type_t::SYS_UNKNOWN;
609
+ super_t::_has_next = true;
610
+ }
611
+
612
+ void seek_next_v3() {
613
+ char buf[256];
614
+
615
+ while(super_t::src.good()
616
+ && (!super_t::src.getline(buf, sizeof(buf)).fail())){
617
+
618
+ switch(buf[0]){
619
+ case 'G': seek_next_v3_gps(buf); return; // GPS
620
+ case 'E': seek_next_v3_not_implemented(buf, 8); return; // Galileo
621
+ case 'R': seek_next_v3_not_implemented(buf, 4); return; // Glonass
622
+ case 'J': seek_next_v3_not_implemented(buf, 8); return; // QZSS
623
+ case 'C': seek_next_v3_not_implemented(buf, 8); return; // Beido
624
+ case 'S': seek_next_v3_not_implemented(buf, 4); return; // SBAS
625
+ case 'T': seek_next_v3_not_implemented(buf, 8); return; // IRNSS
626
+ default: break;
627
+ }
628
+ }
629
+ }
630
+
540
631
  void seek_next() {
632
+ super_t::_has_next = false;
633
+ sys_of_msg = super_t::version_type_t::SYS_UNKNOWN;
541
634
  super_t::version_type.version >= 300 ? seek_next_v3() : seek_next_v2();
542
635
  }
543
636
 
@@ -547,11 +640,8 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
547
640
  }
548
641
  ~RINEX_NAV_Reader(){}
549
642
 
550
- typename space_node_t::Satellite::Ephemeris next() {
551
- typename space_node_t::Satellite::Ephemeris current(msg.eph);
552
- super_t::_has_next = false;
643
+ void next() {
553
644
  seek_next();
554
- return current;
555
645
  }
556
646
 
557
647
  static const typename super_t::convert_item_t iono_alpha_v2[4];
@@ -623,6 +713,7 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
623
713
  }
624
714
 
625
715
  if((it = _header.find("LEAP SECONDS")) != _header.end()){
716
+ iono_utc.delta_t_LSF = iono_utc.WN_LSF = iono_utc.DN = 0;
626
717
  if(version_type.version >= 301){
627
718
  super_t::convert(utc_leap_v301, it->second.front(), &iono_utc);
628
719
  }else{
@@ -635,19 +726,38 @@ class RINEX_NAV_Reader : public RINEX_Reader<> {
635
726
  return alpha && beta && utc && leap;
636
727
  }
637
728
 
638
- static int read_all(std::istream &in, space_node_t &space_node){
729
+ struct space_node_list_t {
730
+ space_node_t *gps;
731
+ };
732
+
733
+ static int read_all(std::istream &in, space_node_list_t &space_nodes = {0}){
639
734
  int res(-1);
640
735
  RINEX_NAV_Reader reader(in);
641
- (reader.version_type.version >= 300)
642
- ? reader.extract_iono_utc_v3(space_node)
643
- : reader.extract_iono_utc_v2(space_node);
736
+ if(space_nodes.gps){
737
+ (reader.version_type.version >= 300)
738
+ ? reader.extract_iono_utc_v3(*space_nodes.gps)
739
+ : reader.extract_iono_utc_v2(*space_nodes.gps);
740
+ }
644
741
  res++;
645
- for(; reader.has_next(); ++res){
646
- typename space_node_t::Satellite::Ephemeris eph(reader.next());
647
- space_node.satellite(eph.svid).register_ephemeris(eph);
742
+ for(; reader.has_next(); reader.next()){
743
+ switch(reader.sys_of_msg){
744
+ case super_t::version_type_t::SYS_GPS:
745
+ if(!space_nodes.gps){break;}
746
+ space_nodes.gps->satellite(reader.msg.eph.svid).register_ephemeris(reader.msg.eph);
747
+ res++;
748
+ break;
749
+ default: break;
750
+ }
648
751
  }
649
752
  return res;
650
753
  }
754
+
755
+ static int read_all(std::istream &in, space_node_t &space_node){
756
+ space_node_list_t list = {
757
+ &space_node,
758
+ };
759
+ return read_all(in, list);
760
+ }
651
761
  };
652
762
 
653
763
  template <class FloatT>
@@ -964,12 +1074,16 @@ class RINEX_OBS_Reader : public RINEX_Reader<> {
964
1074
  #define GEN_I(offset, length, container_type, container_member, value_type) \
965
1075
  {super_t::template conv_t<value_type>::d, offset, length, \
966
1076
  offsetof(container_type, container_member), 1}
967
- #define GEN_F(offset, length, precision, container_type, container_member) \
968
- {super_t::template conv_t<FloatT>::f, offset, length, \
1077
+ #define GEN_F2(offset, length, precision, container_type, container_member, value_type) \
1078
+ {super_t::template conv_t<value_type>::f, offset, length, \
969
1079
  offsetof(container_type, container_member), precision}
970
- #define GEN_E(offset, length, precision, container_type, container_member) \
971
- {super_t::template conv_t<FloatT>::e, offset, length, \
1080
+ #define GEN_E2(offset, length, precision, container_type, container_member, value_type) \
1081
+ {super_t::template conv_t<value_type>::e, offset, length, \
972
1082
  offsetof(container_type, container_member), precision}
1083
+ #define GEN_F(offset, length, precision, container_type, container_member) \
1084
+ GEN_F2(offset, length, precision, container_type, container_member, FloatT)
1085
+ #define GEN_E(offset, length, precision, container_type, container_member) \
1086
+ GEN_E2(offset, length, precision, container_type, container_member, FloatT)
973
1087
 
974
1088
  template <class FloatT>
975
1089
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph0_v2[] = {
@@ -1000,17 +1114,17 @@ const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>
1000
1114
 
1001
1115
  template <class FloatT>
1002
1116
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph1_v2[] = {
1003
- GEN_E( 3, 19, 12, message_t, iode_f),
1004
- GEN_E(22, 19, 12, message_t, eph.c_rs),
1005
- GEN_E(41, 19, 12, message_t, eph.delta_n),
1006
- GEN_E(60, 19, 12, message_t, eph.M0),
1117
+ GEN_E2( 3, 19, 12, message_t, eph.iode, int),
1118
+ GEN_E (22, 19, 12, message_t, eph.c_rs),
1119
+ GEN_E (41, 19, 12, message_t, eph.delta_n),
1120
+ GEN_E (60, 19, 12, message_t, eph.M0),
1007
1121
  };
1008
1122
  template <class FloatT>
1009
1123
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph1_v3[] = {
1010
- GEN_E( 4, 19, 12, message_t, iode_f),
1011
- GEN_E(23, 19, 12, message_t, eph.c_rs),
1012
- GEN_E(42, 19, 12, message_t, eph.delta_n),
1013
- GEN_E(61, 19, 12, message_t, eph.M0),
1124
+ GEN_E2( 4, 19, 12, message_t, eph.iode, int),
1125
+ GEN_E (23, 19, 12, message_t, eph.c_rs),
1126
+ GEN_E (42, 19, 12, message_t, eph.delta_n),
1127
+ GEN_E (61, 19, 12, message_t, eph.M0),
1014
1128
  };
1015
1129
 
1016
1130
  template <class FloatT>
@@ -1075,17 +1189,17 @@ const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>
1075
1189
 
1076
1190
  template <class FloatT>
1077
1191
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v2[] = {
1078
- GEN_E( 3, 19, 12, message_t, ura_meter),
1079
- GEN_E(22, 19, 12, message_t, SV_health_f),
1080
- GEN_E(41, 19, 12, message_t, eph.t_GD),
1081
- GEN_E(60, 19, 12, message_t, iodc_f),
1192
+ GEN_E ( 3, 19, 12, message_t, ura_meter),
1193
+ GEN_E (22, 19, 12, message_t, SV_health_f),
1194
+ GEN_E (41, 19, 12, message_t, eph.t_GD),
1195
+ GEN_E2(60, 19, 12, message_t, eph.iodc, int),
1082
1196
  };
1083
1197
  template <class FloatT>
1084
1198
  const typename RINEX_NAV_Reader<FloatT>::convert_item_t RINEX_NAV_Reader<FloatT>::eph6_v3[] = {
1085
- GEN_E( 4, 19, 12, message_t, ura_meter),
1086
- GEN_E(23, 19, 12, message_t, SV_health_f),
1087
- GEN_E(42, 19, 12, message_t, eph.t_GD),
1088
- GEN_E(61, 19, 12, message_t, iodc_f),
1199
+ GEN_E ( 4, 19, 12, message_t, ura_meter),
1200
+ GEN_E (23, 19, 12, message_t, SV_health_f),
1201
+ GEN_E (42, 19, 12, message_t, eph.t_GD),
1202
+ GEN_E2(61, 19, 12, message_t, eph.iodc, int),
1089
1203
  };
1090
1204
 
1091
1205
  template <class FloatT>
@@ -1224,7 +1338,7 @@ class RINEX_Writer {
1224
1338
  for(typename super_t::reverse_iterator it(header.rbegin()), it_end(header.rend());
1225
1339
  it != it_end; ++it){
1226
1340
  if(it->first != key){continue;}
1227
- it_tail = it.base() - 1;
1341
+ it_tail = it.base(); // it_tail points to the next element of an element having the smae key
1228
1342
  break;
1229
1343
  }
1230
1344
  }
@@ -1235,14 +1349,16 @@ class RINEX_Writer {
1235
1349
  const typename super_t::value_type::second_type &value){
1236
1350
  if(it_head == header.end()){
1237
1351
  header.push_back(typename super_t::value_type(key, value));
1238
- it_head = it_tail = header.rbegin().base();
1352
+ it_tail = header.end(); // in case of invalidation
1353
+ it_head = it_tail - 1;
1239
1354
  return *this;
1240
1355
  }
1241
1356
  it_head->second = value;
1242
- for(; it_tail != it_head; --it_tail){
1243
- if(it_tail->first == key){continue;}
1357
+ for(--it_tail; it_tail != it_head; --it_tail){
1358
+ if(it_tail->first != key){continue;}
1244
1359
  header.erase(it_tail);
1245
1360
  }
1361
+ it_tail = it_head + 1;
1246
1362
  return *this;
1247
1363
  }
1248
1364
  /**
@@ -1250,14 +1366,24 @@ class RINEX_Writer {
1250
1366
  */
1251
1367
  bracket_accessor_t &operator<<(
1252
1368
  const typename super_t::value_type::second_type &value){
1253
- if(it_tail == header.end()){
1254
- header.push_back(typename super_t::value_type(key, value));
1255
- it_head = it_tail = header.rbegin().base();
1256
- return *this;
1257
- }
1258
- header.insert(++it_tail, typename super_t::value_type(key, value));
1369
+ super_t::size_type i_head(it_head - header.begin()), i_tail(it_tail - header.begin());
1370
+ header.insert(it_tail, typename super_t::value_type(key, value));
1371
+ it_head = header.begin() + i_head; // in case of invalidation
1372
+ it_tail = header.begin() + i_tail + 1;
1259
1373
  return *this;
1260
1374
  }
1375
+ unsigned int entries() const {
1376
+ return it_tail - it_head;
1377
+ }
1378
+ typename super_t::iterator find(
1379
+ const typename super_t::value_type::second_type &value) const {
1380
+ for(typename super_t::iterator it(it_head); it != it_tail; ++it){
1381
+ if(it->second.find(value) != super_t::value_type::second_type::npos){
1382
+ return it;
1383
+ }
1384
+ }
1385
+ return header.end();
1386
+ }
1261
1387
  };
1262
1388
  bracket_accessor_t operator[]( // mimic of std::map::operator[]=
1263
1389
  const typename super_t::value_type::first_type &key){
@@ -1336,20 +1462,25 @@ class RINEX_Writer {
1336
1462
  return s;
1337
1463
  }
1338
1464
 
1339
- static void convert(
1465
+ /**
1466
+ * @return If all conversion are successfully performed, then true; otherwise false;
1467
+ */
1468
+ static bool convert(
1340
1469
  const typename RINEX_Reader<U>::convert_item_t *items, const int &size,
1341
1470
  std::string &buf, const void *values){
1342
1471
  // value => string
1472
+ bool res(true);
1343
1473
  for(int i(0); i < size; ++i){
1344
- (*items[i].func)(
1474
+ res &= (*items[i].func)(
1345
1475
  buf, items[i].offset, items[i].length, (char *)(const_cast<void *>(values)) + items[i].value_offset,
1346
1476
  items[i].opt, false);
1347
1477
  }
1478
+ return res;
1348
1479
  }
1349
1480
  template <int N>
1350
- static inline void convert(
1481
+ static inline bool convert(
1351
1482
  const typename RINEX_Reader<U>::convert_item_t (&items)[N], std::string &buf, const void *values){
1352
- convert(items, N, buf, values);
1483
+ return convert(items, N, buf, values);
1353
1484
  }
1354
1485
 
1355
1486
  void pgm_runby_date(
@@ -1426,6 +1557,9 @@ class RINEX_NAV_Writer : public RINEX_Writer<> {
1426
1557
  std::string s(60, ' ');
1427
1558
  if(super_t::_version_type.version >= 301){
1428
1559
  super_t::convert(reader_t::utc_leap_v301, s, &space_node.iono_utc());
1560
+ if(space_node.iono_utc().WN_LSF == 0){
1561
+ s.replace(6, 18, 18, ' ');
1562
+ }
1429
1563
  }else{
1430
1564
  super_t::convert(reader_t::utc_leap_v2, s, &space_node.iono_utc());
1431
1565
  }
@@ -1475,16 +1609,6 @@ class RINEX_NAV_Writer : public RINEX_Writer<> {
1475
1609
  return *this;
1476
1610
  }
1477
1611
 
1478
- protected:
1479
- struct WriteAllFunctor {
1480
- RINEX_NAV_Writer &w;
1481
- int &counter;
1482
- void operator()(const typename space_node_t::Satellite::Ephemeris &eph) {
1483
- w << message_t(eph);
1484
- counter++;
1485
- }
1486
- };
1487
-
1488
1612
  public:
1489
1613
  void set_version(
1490
1614
  const int &version,
@@ -1493,31 +1617,67 @@ class RINEX_NAV_Writer : public RINEX_Writer<> {
1493
1617
  version, super_t::version_type_t::FTYPE_NAVIGATION, sys));
1494
1618
  }
1495
1619
 
1496
- int write_all(const space_node_t &space_node, const int &version = 304){
1620
+ int write_all(
1621
+ const typename reader_t::space_node_list_t &space_nodes,
1622
+ const int &version = 304){
1497
1623
  int res(-1);
1498
- set_version(version);
1499
- if(space_node.is_valid_iono_utc()){
1500
- iono_alpha(space_node);
1501
- iono_beta(space_node);
1502
- utc_params(space_node);
1503
- leap_seconds(space_node);
1624
+ int systems(0);
1625
+ set_version(version, super_t::version_type_t::SYS_UNKNOWN);
1626
+ if(space_nodes.gps && space_nodes.gps->is_valid_iono_utc()){
1627
+ ++systems;
1628
+ set_version(version, super_t::version_type_t::SYS_GPS);
1629
+ switch(version / 100){
1630
+ case 2:
1631
+ if(_header["ION ALPHA"].entries() == 0){iono_alpha(*space_nodes.gps);}
1632
+ if(_header["ION BETA"].entries() == 0){iono_beta(*space_nodes.gps);}
1633
+ if(_header["DELTA-UTC: A0,A1,T,W"].entries() == 0){utc_params(*space_nodes.gps);}
1634
+ if(_header["LEAP SECONDS"].entries() == 0){leap_seconds(*space_nodes.gps);}
1635
+ break;
1636
+ case 3:
1637
+ if(_header["IONOSPHERIC CORR"].find("GPSA") == _header.end()){iono_alpha(*space_nodes.gps);}
1638
+ if(_header["IONOSPHERIC CORR"].find("GPSB") == _header.end()){iono_beta(*space_nodes.gps);}
1639
+ if(_header["TIME SYSTEM CORR"].find("GPUT") == _header.end()){utc_params(*space_nodes.gps);}
1640
+ if(_header["LEAP SECONDS"].entries() == 0){leap_seconds(*space_nodes.gps);}
1641
+ break;
1642
+ }
1643
+ }
1644
+ if(systems > 1){
1645
+ set_version(version, super_t::version_type_t::SYS_MIXED);
1504
1646
  }
1505
1647
  super_t::dist << header();
1506
1648
  res++;
1507
1649
 
1508
- WriteAllFunctor functor = {*this, res};
1509
- for(typename space_node_t::satellites_t::const_iterator
1510
- it(space_node.satellites().begin()), it_end(space_node.satellites().end());
1511
- it != it_end; ++it){
1512
- it->second.each_ephemeris(
1513
- functor,
1514
- space_node_t::Satellite::eph_list_t::EACH_ALL_INVERTED);
1650
+ struct {
1651
+ RINEX_NAV_Writer &w;
1652
+ int &counter;
1653
+ void operator()(const typename space_node_t::Satellite::Ephemeris &eph) {
1654
+ w << message_t(eph);
1655
+ counter++;
1656
+ }
1657
+ } functor = {*this, res};
1658
+ if(space_nodes.gps){
1659
+ for(typename space_node_t::satellites_t::const_iterator
1660
+ it(space_nodes.gps->satellites().begin()), it_end(space_nodes.gps->satellites().end());
1661
+ it != it_end; ++it){
1662
+ it->second.each_ephemeris(
1663
+ functor,
1664
+ space_node_t::Satellite::eph_list_t::EACH_ALL_INVERTED);
1665
+ }
1515
1666
  }
1516
1667
  return res;
1517
1668
  }
1669
+ static int write_all(
1670
+ std::ostream &out,
1671
+ const typename reader_t::space_node_list_t &space_nodes,
1672
+ const int &version = 304){
1673
+ return RINEX_NAV_Writer(out).write_all(space_nodes, version);
1674
+ }
1675
+ int write_all(const space_node_t &space_node, const int &version = 304){
1676
+ const typename reader_t::space_node_list_t list = {&const_cast<space_node_t &>(space_node)};
1677
+ return write_all(list, version);
1678
+ }
1518
1679
  static int write_all(std::ostream &out, const space_node_t &space_node, const int &version = 304){
1519
- RINEX_NAV_Writer writer(out);
1520
- return writer.write_all(space_node, version);
1680
+ return RINEX_NAV_Writer(out).write_all(space_node, version);
1521
1681
  }
1522
1682
  };
1523
1683
  template <class FloatT>
@@ -13,15 +13,15 @@ class Receiver
13
13
  [:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
14
14
  }
15
15
  ]] + [[
16
- [:receiver_clock_error_meter, :longitude, :latitude, :height],
16
+ [:receiver_clock_error_meter, :longitude, :latitude, :height, :rel_E, :rel_N, :rel_U],
17
17
  proc{|pvt|
18
- next [nil] * 4 unless pvt.position_solved?
18
+ next [nil] * 7 unless pvt.position_solved?
19
19
  [
20
20
  pvt.receiver_error,
21
21
  pvt.llh.lng / Math::PI * 180,
22
22
  pvt.llh.lat / Math::PI * 180,
23
23
  pvt.llh.alt,
24
- ]
24
+ ] + (pvt.rel_ENU.to_a rescue [nil] * 3)
25
25
  }
26
26
  ]] + [proc{
27
27
  labels = [:g, :p, :h, :v, :t].collect{|k| "#{k}dop".to_sym}
@@ -103,6 +103,7 @@ class Receiver
103
103
  end
104
104
 
105
105
  attr_accessor :solver
106
+ attr_accessor :base_station
106
107
 
107
108
  def initialize(options = {})
108
109
  @solver = GPS::Solver::new
@@ -127,6 +128,34 @@ class Receiver
127
128
  when :identical # same as default
128
129
  next true
129
130
  end
131
+ when :base_station
132
+ crd, sys = v.split(/ *, */).collect.with_index{|item, i|
133
+ case item
134
+ when /^([\+-]?\d+\.?\d*)([XYZNEDU]?)$/ # ex) meter[X], degree[N]
135
+ [$1.to_f, ($2 + "XY?"[i])[0]]
136
+ when /^([\+-]?\d+)_(?:(\d+)_(\d+\.?\d*)|(\d+\.?\d*))([NE])$/ # ex) deg_min_secN
137
+ [$1.to_f + ($2 || $4).to_f / 60 + ($3 || 0).to_f / 3600, $5]
138
+ else
139
+ raise "Unknown coordinate spec.: #{item}"
140
+ end
141
+ }.transpose
142
+ raise "Unknown base station: #{v}" if crd.size != 3
143
+ @base_station = case (sys = sys.join.to_sym)
144
+ when :XYZ, :XY?
145
+ Coordinate::XYZ::new(*crd)
146
+ when :NED, :ENU, :NE?, :EN? # :NE? => :NEU, :EN? => :ENU
147
+ (0..1).each{|i| crd[i] *= (Math::PI / 180)}
148
+ ([:NED, :NE?].include?(sys) ?
149
+ Coordinate::LLH::new(crd[0], crd[1], crd[2] * (:NED == sys ? -1 : 1)) :
150
+ Coordinate::LLH::new(crd[1], crd[0], crd[2])).xyz
151
+ else
152
+ raise "Unknown coordinate system: #{sys}"
153
+ end
154
+ $stderr.puts "Base station (LLH): #{
155
+ llh = @base_station.llh.to_a
156
+ llh[0..1].collect{|rad| rad / Math::PI * 180} + [llh[2]]
157
+ }"
158
+ next true
130
159
  end
131
160
  false
132
161
  }
@@ -137,7 +166,29 @@ class Receiver
137
166
  }.call(@solver.gps_options)
138
167
  end
139
168
 
140
- def run(meas, t_meas)
169
+ GPS::Measurement.class_eval{
170
+ proc{
171
+ key2sym = []
172
+ GPS::Measurement.constants.each{|k|
173
+ i = GPS::Measurement.const_get(k)
174
+ key2sym[i] = k if i.kind_of?(Integer)
175
+ }
176
+ define_method(:to_a2){
177
+ to_a.collect{|prn, k, v| [prn, key2sym[k] || k, v]}
178
+ }
179
+ define_method(:to_hash2){
180
+ Hash[*(to_hash.collect{|prn, k_v|
181
+ [prn, Hash[*(k_v.collect{|k, v| [key2sym[k] || k, v]}.flatten(1))]]
182
+ }.flatten(1))]
183
+ }
184
+ }.call
185
+ alias_method(:add_orig, :add)
186
+ define_method(:add){|prn, key, value|
187
+ add_orig(prn, key.kind_of?(Symbol) ? GPS::Measurement.const_get(key) : key, value)
188
+ }
189
+ }
190
+
191
+ def run(meas, t_meas, ref_pos = @base_station)
141
192
  =begin
142
193
  $stderr.puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %d:%d:%d UTC"%[*t_meas.c_tm]})"
143
194
  meas.to_a.collect{|prn, k, v| prn}.uniq.each{|prn|
@@ -148,6 +199,9 @@ class Receiver
148
199
 
149
200
  #@solver.gps_space_node.update_all_ephemeris(t_meas) # internally called in the following solver.solve
150
201
  pvt = @solver.solve(meas, t_meas)
202
+ pvt.define_singleton_method(:rel_ENU){
203
+ Coordinate::ENU::relative(xyz, ref_pos)
204
+ } if (ref_pos && pvt.position_solved?)
151
205
  pvt.define_singleton_method(:to_s){
152
206
  (OUTPUT_PVT_ITEMS.transpose[1].collect{|task|
153
207
  task.call(pvt)
@@ -245,11 +299,11 @@ class Receiver
245
299
  :L1_CARRIER_PHASE => [8, 8, "E"],
246
300
  :L1_SIGNAL_STRENGTH_dBHz => [30, 1, "c"],
247
301
  }.each{|k, prop|
248
- meas.add(prn, GPS::Measurement.const_get(k), loader.call(*prop))
302
+ meas.add(prn, k, loader.call(*prop))
249
303
  }
250
304
  # bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
251
305
  # between previous and current observation, which maps negative lock seconds
252
- meas.add(prn, GPS::Measurement::L1_LOCK_SEC,
306
+ meas.add(prn, :L1_LOCK_SEC,
253
307
  (packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
254
308
  }
255
309
  after_run.call(run(meas, t_meas), [meas, t_meas])
@@ -280,11 +334,11 @@ class Receiver
280
334
  :L1_CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
281
335
  (trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
282
336
  }],
283
- :L1_SIGNAL_STRENGTH_dBHz => [42, 1],
337
+ :L1_SIGNAL_STRENGTH_dBHz => [42, 1, "C"],
284
338
  :L1_LOCK_SEC => [40, 2, "v", proc{|v| 1E-3 * v}],
285
339
  }.each{|k, prop|
286
340
  next unless v = loader.call(*prop)
287
- meas.add(svid, GPS::Measurement.const_get(k), v)
341
+ meas.add(svid, k, v)
288
342
  }
289
343
  }
290
344
  after_run.call(run(meas, t_meas), [meas, t_meas])
@@ -326,13 +380,13 @@ class Receiver
326
380
  types ||= (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
327
381
  case type_
328
382
  when "C1", "C1C"
329
- [i, GPS::Measurement::L1_PSEUDORANGE]
383
+ [i, :L1_PSEUDORANGE]
330
384
  when "L1", "L1C"
331
- [i, GPS::Measurement::L1_CARRIER_PHASE]
385
+ [i, :L1_CARRIER_PHASE]
332
386
  when "D1", "D1C"
333
- [i, GPS::Measurement::L1_DOPPLER]
387
+ [i, :L1_DOPPLER]
334
388
  when "S1", "S1C"
335
- [i, GPS::Measurement::L1_SIGNAL_STRENGTH_dBHz]
389
+ [i, :L1_SIGNAL_STRENGTH_dBHz]
336
390
  else
337
391
  nil
338
392
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.7"
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.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - fenrir(M.Naruoka)
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -86,7 +86,6 @@ files:
86
86
  - ext/ninja-scan-light/tool/swig/makefile
87
87
  - ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb
88
88
  - ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb
89
- - gps_pvt.gemspec
90
89
  - lib/gps_pvt.rb
91
90
  - lib/gps_pvt/receiver.rb
92
91
  - lib/gps_pvt/ubx.rb
@@ -97,7 +96,7 @@ licenses: []
97
96
  metadata:
98
97
  homepage_uri: https://github.com/fenrir-naru/gps_pvt
99
98
  source_code_uri: https://github.com/fenrir-naru/gps_pvt
100
- post_install_message:
99
+ post_install_message:
101
100
  rdoc_options: []
102
101
  require_paths:
103
102
  - lib
@@ -112,8 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
111
  - !ruby/object:Gem::Version
113
112
  version: '0'
114
113
  requirements: []
115
- rubygems_version: 3.1.2
116
- signing_key:
114
+ rubygems_version: 3.0.3
115
+ signing_key:
117
116
  specification_version: 4
118
117
  summary: GPS position, velocity, and time (PVT) solver
119
118
  test_files: []
data/gps_pvt.gemspec DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/gps_pvt/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "gps_pvt"
7
- spec.version = GPS_PVT::VERSION
8
- spec.authors = ["fenrir(M.Naruoka)"]
9
- spec.email = ["fenrir.naru@gmail.com"]
10
-
11
- spec.summary = "GPS position, velocity, and time (PVT) solver"
12
- spec.description = "This module calculate PVT by using raw observation obtained from a GPS receiver"
13
- spec.homepage = "https://github.com/fenrir-naru/gps_pvt"
14
- spec.required_ruby_version = ">= 2.3.0"
15
-
16
- #spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
17
-
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = spec.homepage
20
- #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
21
-
22
- spec.extensions = ["ext/gps_pvt/extconf.rb"]
23
-
24
- # Specify which files should be added to the gem when it is released.
25
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
- `git ls-files -z`.split("\x0").reject do |f|
28
- (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
29
- end
30
- end
31
- spec.bindir = "exe"
32
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
- spec.require_paths = ["lib"]
34
-
35
- spec.files += proc{
36
- require 'pathname'
37
- base_dir = Pathname::new(File::absolute_path(File.dirname(__FILE__)))
38
- # get an array of submodule dirs by executing 'pwd' inside each submodule
39
- `git submodule --quiet foreach pwd`.split($/).collect{|dir|
40
- # issue git ls-files in submodule's directory
41
- `git -C #{dir} ls-files -v`.split($/).collect{|f|
42
- next nil unless f =~ /^H */ # consider git sparse checkout
43
- # get relative path
44
- f = Pathname::new(File::join(dir, $'))
45
- (f.relative? ? f : f.relative_path_from(base_dir)).to_s
46
- }.compact
47
- }.flatten
48
- }.call
49
-
50
- # Uncomment to register a new dependency of your gem
51
- # spec.add_dependency "example-gem", "~> 1.0"
52
- spec.add_development_dependency "rake"
53
- spec.add_development_dependency "rake-compiler"
54
-
55
- # For more information and examples about making a new gem, checkout our
56
- # guide at: https://bundler.io/guides/creating_gem.html
57
- end