gps_pvt 0.1.6 → 0.1.7

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: 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