gps_pvt 0.3.0 → 0.4.1

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.
@@ -20,12 +20,14 @@
20
20
  #include "navigation/GPS.h"
21
21
  #include "navigation/SBAS.h"
22
22
  #include "navigation/QZSS.h"
23
+ #include "navigation/GLONASS.h"
23
24
  #include "navigation/RINEX.h"
24
25
 
25
26
  #include "navigation/GPS_Solver_Base.h"
26
27
  #include "navigation/GPS_Solver.h"
27
28
  #include "navigation/GPS_Solver_RAIM.h"
28
29
  #include "navigation/SBAS_Solver.h"
30
+ #include "navigation/GLONASS_Solver.h"
29
31
 
30
32
  #if defined(__cplusplus) && (__cplusplus < 201103L)
31
33
  namespace std {
@@ -153,6 +155,12 @@ static std::string inspect_str(const VALUE &v){
153
155
  $1 = (TYPE($input) == T_ARRAY) ? 1 : 0;
154
156
  }
155
157
  #endif
158
+ %ignore canonicalize();
159
+ %ignore GPS_Time(const int &_week, const float_t &_seconds);
160
+ %typemap(in, numinputs=0) void *dummy "";
161
+ GPS_Time(const int &week_, const float_t &seconds_, void *dummy){
162
+ return &((new GPS_Time<FloatT>(week_, seconds_))->canonicalize());
163
+ }
156
164
  %apply int *OUTPUT { int *week };
157
165
  %apply FloatT *OUTPUT { FloatT *seconds };
158
166
  void to_a(int *week, FloatT *seconds) const {
@@ -500,6 +508,160 @@ struct SBAS_Ephemeris : public SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephe
500
508
 
501
509
  %include navigation/SBAS.h
502
510
 
511
+ %inline %{
512
+ template <class FloatT>
513
+ struct GLONASS_Ephemeris
514
+ : public GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris_with_GPS_Time {
515
+ typedef typename GLONASS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris_with_GPS_Time eph_t;
516
+ unsigned int super_frame, has_string;
517
+ typename eph_t::raw_t raw;
518
+ void invalidate() {
519
+ super_frame = 0;
520
+ has_string = 0;
521
+ }
522
+ bool is_consistent() const {
523
+ return has_string == 0x1F;
524
+ }
525
+ GLONASS_Ephemeris() : eph_t() {
526
+ invalidate();
527
+ }
528
+ GLONASS_Ephemeris(const eph_t &eph)
529
+ : eph_t(eph),
530
+ super_frame(0), has_string(0), raw() {
531
+ raw = *this;
532
+ has_string = 0x1F;
533
+ }
534
+ };
535
+ %}
536
+ %extend GLONASS_Ephemeris {
537
+ MAKE_ACCESSOR(svid, unsigned int);
538
+
539
+ MAKE_ACCESSOR(freq_ch, int); // frequency channel to be configured
540
+ MAKE_ACCESSOR(t_k, unsigned int);
541
+ MAKE_ACCESSOR(t_b, unsigned int);
542
+ MAKE_ACCESSOR(M, unsigned int);
543
+ MAKE_ACCESSOR(gamma_n, FloatT);
544
+ MAKE_ACCESSOR(tau_n, FloatT);
545
+
546
+ MAKE_ACCESSOR(xn, FloatT); MAKE_ACCESSOR(xn_dot, FloatT); MAKE_ACCESSOR(xn_ddot, FloatT);
547
+ MAKE_ACCESSOR(yn, FloatT); MAKE_ACCESSOR(yn_dot, FloatT); MAKE_ACCESSOR(yn_ddot, FloatT);
548
+ MAKE_ACCESSOR(zn, FloatT); MAKE_ACCESSOR(zn_dot, FloatT); MAKE_ACCESSOR(zn_ddot, FloatT);
549
+
550
+ MAKE_ACCESSOR(B_n, unsigned int);
551
+ MAKE_ACCESSOR(p, unsigned int);
552
+ MAKE_ACCESSOR(N_T, unsigned int);
553
+ MAKE_ACCESSOR(F_T, FloatT);
554
+ MAKE_ACCESSOR(n, unsigned int);
555
+ MAKE_ACCESSOR(delta_tau_n, FloatT);
556
+ MAKE_ACCESSOR(E_n, unsigned int);
557
+ MAKE_ACCESSOR(P1, unsigned int);
558
+ MAKE_ACCESSOR(P2, bool);
559
+ MAKE_ACCESSOR(P4, bool);
560
+
561
+ MAKE_ACCESSOR(tau_c, FloatT);
562
+ MAKE_ACCESSOR(tau_GPS, FloatT);
563
+
564
+ FloatT frequency_L1() const {
565
+ return self->L1_frequency();
566
+ };
567
+ FloatT frequency_L2() const {
568
+ return self->L2_frequency();
569
+ };
570
+ GPS_Time<FloatT> base_time() const {
571
+ return self->base_time();
572
+ }
573
+
574
+ //MAKE_ACCESSOR(l_n, bool); // exists in both Ephemeris and Time_Properties
575
+
576
+ MAKE_ARRAY_INPUT(const unsigned int, buf, SWIG_AsVal(unsigned int));
577
+ bool parse(const unsigned int buf[4], const unsigned int &leap_seconds = 0){
578
+ typedef typename GLONASS_SpaceNode<FloatT>
579
+ ::template BroadcastedMessage<unsigned int> parser_t;
580
+ unsigned int super_frame(buf[3] >> 16), frame(buf[3] & 0xF), string_no(parser_t::m(buf));
581
+ unsigned int has_string(self->has_string);
582
+ if((has_string > 0) && (self->super_frame != super_frame)){
583
+ has_string = 0; // clean up
584
+ }
585
+ self->super_frame = super_frame;
586
+ has_string |= (0x1 << (string_no - 1));
587
+ switch(string_no){
588
+ case 1: self->raw.template update_string1<0, 0>(buf); break;
589
+ case 2: self->raw.template update_string2<0, 0>(buf); break;
590
+ case 3: self->raw.template update_string3<0, 0>(buf); break;
591
+ case 4: self->raw.template update_string4<0, 0>(buf); break;
592
+ case 5: {
593
+ self->raw.template update_string5<0, 0>(buf);
594
+ if(frame == 4){
595
+ // TODO: require special care for 50th frame? @see Table 4.9 note (4)
596
+ }
597
+ break;
598
+ }
599
+ }
600
+ bool updated(false);
601
+ if((has_string == 0x1F) && (has_string != self->has_string)){
602
+ updated = true;
603
+ // All ephemeris and time info. in the same super frame has been acquired,
604
+ // and this block is called once per one same super frame.
605
+ // Ephemeris_with_Time::raw_t =(cast)=> Ephemeris_with_Time => Ephemeris_with_GPS_Time
606
+ static_cast<GLONASS_Ephemeris<FloatT>::eph_t &>(*self)
607
+ = GLONASS_Ephemeris<FloatT>::eph_t(self->raw);
608
+ self->t_b_gps += leap_seconds;
609
+ }
610
+ self->has_string = has_string;
611
+ return updated;
612
+ }
613
+ FloatT clock_error(
614
+ const GPS_Time<FloatT> &t_arrival, const FloatT &pseudo_range = 0) const {
615
+ return self->clock_error(t_arrival, pseudo_range);
616
+ }
617
+ %typemap(in,numinputs=0) System_XYZ<FloatT, WGS84> & (System_XYZ<FloatT, WGS84> temp) %{
618
+ $1 = &temp;
619
+ %}
620
+ %typemap(argout) System_XYZ<FloatT, WGS84> & {
621
+ %append_output(SWIG_NewPointerObj((new $*1_ltype(*$1)), $1_descriptor, SWIG_POINTER_OWN));
622
+ }
623
+ void constellation(
624
+ System_XYZ<FloatT, WGS84> &position, System_XYZ<FloatT, WGS84> &velocity,
625
+ const GPS_Time<FloatT> &t, const FloatT &pseudo_range = 0) const {
626
+ typename GPS_SpaceNode<FloatT>::SatelliteProperties::constellation_t res(
627
+ self->constellation(t, pseudo_range));
628
+ position = res.position;
629
+ velocity = res.velocity;
630
+ }
631
+ #if defined(SWIGRUBY)
632
+ %rename("consistent?") is_consistent;
633
+ %rename("in_range?") is_in_range;
634
+ #endif
635
+ bool is_in_range(const GPS_Time<FloatT> &t) const {
636
+ // "invalidate()" is used to make raw and converted data inconsistent.
637
+ return self->is_valid(t);
638
+ }
639
+ }
640
+
641
+ %extend GLONASS_SpaceNode {
642
+ %fragment(SWIG_Traits_frag(FloatT));
643
+ %ignore satellites() const;
644
+ %ignore satellite(const int &);
645
+ %ignore latest_ephemeris() const;
646
+ void register_ephemeris(
647
+ const int &prn, const GLONASS_Ephemeris<FloatT> &eph,
648
+ const int &priority_delta = 1){
649
+ self->satellite(prn).register_ephemeris(eph, priority_delta);
650
+ }
651
+ GLONASS_Ephemeris<FloatT> ephemeris(const int &prn) const {
652
+ return GLONASS_Ephemeris<FloatT>(
653
+ %const_cast(self, GLONASS_SpaceNode<FloatT> *)->satellite(prn).ephemeris());
654
+ }
655
+ int read(const char *fname) {
656
+ std::fstream fin(fname, std::ios::in | std::ios::binary);
657
+ typename RINEX_NAV_Reader<FloatT>::space_node_list_t list = {NULL};
658
+ list.glonass = self;
659
+ return RINEX_NAV_Reader<FloatT>::read_all(fin, list);
660
+ }
661
+ }
662
+
663
+ %include navigation/GLONASS.h
664
+
503
665
  %extend GPS_User_PVT {
504
666
  %ignore solver_t;
505
667
  %ignore base_t;
@@ -591,8 +753,9 @@ struct GPS_User_PVT
591
753
  Matrix<FloatT, Array2D_Dense<FloatT> > G_enu() const {
592
754
  return proxy_t::linear_solver_t::rotate_G(base_t::G, base_t::user_position.ecef2enu());
593
755
  }
594
- typename proxy_t::linear_solver_t linear_solver() const {
595
- return typename proxy_t::linear_solver_t(base_t::G, base_t::W, base_t::delta_r);
756
+ typename proxy_t::linear_solver_t::partial_t linear_solver() const {
757
+ return typename proxy_t::linear_solver_t(base_t::G, base_t::W, base_t::delta_r)
758
+ .partial(used_satellites());
596
759
  }
597
760
  Matrix<FloatT, Array2D_Dense<FloatT> > C() const {
598
761
  return linear_solver().C();
@@ -605,14 +768,26 @@ struct GPS_User_PVT
605
768
  linear_solver().least_square(res);
606
769
  return res;
607
770
  }
771
+ Matrix<FloatT, Array2D_Dense<FloatT> > S_enu(
772
+ const Matrix<FloatT, Array2D_Dense<FloatT> > &s) const {
773
+ return proxy_t::linear_solver_t::rotate_S(s, base_t::user_position.ecef2enu());
774
+ }
608
775
  Matrix<FloatT, Array2D_Dense<FloatT> > S_enu() const {
609
- return proxy_t::linear_solver_t::rotate_S(S(), base_t::user_position.ecef2enu());
776
+ return S_enu(S());
777
+ }
778
+ Matrix<FloatT, Array2D_Dense<FloatT> > slope_HV(
779
+ const Matrix<FloatT, Array2D_Dense<FloatT> > &s) const {
780
+ return linear_solver().slope_HV(s);
610
781
  }
611
782
  Matrix<FloatT, Array2D_Dense<FloatT> > slope_HV() const {
612
- return linear_solver().slope_HV(S());
783
+ return slope_HV(S());
784
+ }
785
+ Matrix<FloatT, Array2D_Dense<FloatT> > slope_HV_enu(
786
+ const Matrix<FloatT, Array2D_Dense<FloatT> > &s) const {
787
+ return linear_solver().slope_HV(s, base_t::user_position.ecef2enu());
613
788
  }
614
789
  Matrix<FloatT, Array2D_Dense<FloatT> > slope_HV_enu() const {
615
- return linear_solver().slope_HV(S(), base_t::user_position.ecef2enu());
790
+ return slope_HV_enu(S());
616
791
  }
617
792
 
618
793
  void fd(const typename base_t::detection_t **out) const {*out = &(base_t::FD);}
@@ -878,6 +1053,25 @@ struct SBAS_SolverOptions
878
1053
  };
879
1054
  %}
880
1055
 
1056
+ %extend GLONASS_SolverOptions {
1057
+ %ignore base_t;
1058
+ %ignore cast_general;
1059
+ MAKE_VECTOR2ARRAY(int);
1060
+ }
1061
+ %inline %{
1062
+ template <class FloatT>
1063
+ struct GLONASS_SolverOptions
1064
+ : public GLONASS_SinglePositioning<FloatT>::options_t,
1065
+ GPS_SolverOptions_Common<FloatT> {
1066
+ typedef typename GLONASS_SinglePositioning<FloatT>::options_t base_t;
1067
+ void exclude(const int &prn){base_t::exclude_prn.set(prn);}
1068
+ void include(const int &prn){base_t::exclude_prn.reset(prn);}
1069
+ std::vector<int> excluded() const {return base_t::exclude_prn.excluded();}
1070
+ GPS_Solver_GeneralOptions<FloatT> *cast_general(){return this;}
1071
+ const GPS_Solver_GeneralOptions<FloatT> *cast_general() const {return this;}
1072
+ };
1073
+ %}
1074
+
881
1075
  %header {
882
1076
  template <class FloatT>
883
1077
  struct GPS_RangeCorrector
@@ -906,6 +1100,8 @@ struct GPS_RangeCorrector
906
1100
  %ignore gps;
907
1101
  %ignore sbas_t;
908
1102
  %ignore sbas;
1103
+ %ignore glonass_t;
1104
+ %ignore glonass;
909
1105
  %ignore select_solver;
910
1106
  %ignore relative_property;
911
1107
  %ignore satellite_position;
@@ -1097,6 +1293,8 @@ struct GPS_RangeCorrector
1097
1293
  ID2SYM(rb_intern("gps_tropospheric")),
1098
1294
  ID2SYM(rb_intern("sbas_ionospheric")),
1099
1295
  ID2SYM(rb_intern("sbas_tropospheric")),
1296
+ ID2SYM(rb_intern("glonass_ionospheric")),
1297
+ ID2SYM(rb_intern("glonass_tropospheric")),
1100
1298
  };
1101
1299
  static const VALUE k_opt(ID2SYM(rb_intern("options")));
1102
1300
  static const VALUE k_f_10_7(ID2SYM(rb_intern("f_10_7")));
@@ -1218,6 +1416,12 @@ struct GPS_Solver
1218
1416
  SBAS_SinglePositioning<FloatT> solver;
1219
1417
  sbas_t() : space_node(), options(), solver(space_node) {}
1220
1418
  } sbas;
1419
+ struct glonass_t {
1420
+ GLONASS_SpaceNode<FloatT> space_node;
1421
+ GLONASS_SolverOptions<FloatT> options;
1422
+ GLONASS_SinglePositioning<FloatT> solver;
1423
+ glonass_t() : space_node(), options(), solver(space_node) {}
1424
+ } glonass;
1221
1425
  SWIG_Object hooks;
1222
1426
  typedef std::vector<GPS_RangeCorrector<FloatT> > user_correctors_t;
1223
1427
  user_correctors_t user_correctors;
@@ -1233,7 +1437,7 @@ struct GPS_Solver
1233
1437
  }
1234
1438
  #endif
1235
1439
  GPS_Solver() : super_t(),
1236
- gps(), sbas(),
1440
+ gps(), sbas(), glonass(),
1237
1441
  hooks(), user_correctors() {
1238
1442
  #ifdef SWIGRUBY
1239
1443
  hooks = rb_hash_new();
@@ -1245,20 +1449,25 @@ struct GPS_Solver
1245
1449
  tropospheric.push_back(&gps.solver.tropospheric_simplified);
1246
1450
  gps.solver.ionospheric_correction
1247
1451
  = sbas.solver.ionospheric_correction
1452
+ = glonass.solver.ionospheric_correction
1248
1453
  = ionospheric;
1249
1454
  gps.solver.tropospheric_correction
1250
1455
  = sbas.solver.tropospheric_correction
1456
+ = glonass.solver.tropospheric_correction
1251
1457
  = tropospheric;
1252
1458
  }
1253
1459
  GPS_SpaceNode<FloatT> &gps_space_node() {return gps.space_node;}
1254
1460
  GPS_SolverOptions<FloatT> &gps_options() {return gps.options;}
1255
1461
  SBAS_SpaceNode<FloatT> &sbas_space_node() {return sbas.space_node;}
1256
1462
  SBAS_SolverOptions<FloatT> &sbas_options() {return sbas.options;}
1463
+ GLONASS_SpaceNode<FloatT> &glonass_space_node() {return glonass.space_node;}
1464
+ GLONASS_SolverOptions<FloatT> &glonass_options() {return glonass.options;}
1257
1465
  const base_t &select_solver(
1258
1466
  const typename base_t::prn_t &prn) const {
1259
1467
  if(prn > 0 && prn <= 32){return gps.solver;}
1260
1468
  if(prn >= 120 && prn <= 158){return sbas.solver;}
1261
1469
  if(prn > 192 && prn <= 202){return gps.solver;}
1470
+ if(prn > 0x100 && prn <= (0x100 + 24)){return glonass.solver;}
1262
1471
  // call order: base_t::solve => this returned by select()
1263
1472
  // => relative_property() => select_solver()
1264
1473
  // For not supported satellite, call loop prevention is required.
@@ -1286,6 +1495,8 @@ struct GPS_Solver
1286
1495
  const_cast<gps_t &>(gps).solver.update_options(gps.options);
1287
1496
  const_cast<sbas_t &>(sbas).space_node.update_all_ephemeris(receiver_time);
1288
1497
  const_cast<sbas_t &>(sbas).solver.update_options(sbas.options);
1498
+ const_cast<glonass_t &>(glonass).space_node.update_all_ephemeris(receiver_time);
1499
+ const_cast<glonass_t &>(glonass).solver.update_options(glonass.options);
1289
1500
  return super_t::solve().user_pvt(measurement.items, receiver_time);
1290
1501
  }
1291
1502
  typedef
@@ -1300,6 +1511,8 @@ struct GPS_Solver
1300
1511
  &gps.solver.tropospheric_correction,
1301
1512
  &sbas.solver.ionospheric_correction,
1302
1513
  &sbas.solver.tropospheric_correction,
1514
+ &glonass.solver.ionospheric_correction,
1515
+ &glonass.solver.tropospheric_correction,
1303
1516
  };
1304
1517
  for(std::size_t i(0); i < sizeof(root) / sizeof(root[0]); ++i){
1305
1518
  do{
@@ -1476,6 +1689,10 @@ struct RINEX_Observation {};
1476
1689
  %template(SpaceNode_SBAS) SBAS_SpaceNode<type>;
1477
1690
  %template(SolverOptions_SBAS) SBAS_SolverOptions<type>;
1478
1691
 
1692
+ %template(SpaceNode_GLONASS) GLONASS_SpaceNode<type>;
1693
+ %template(Ephemeris_GLONASS) GLONASS_Ephemeris<type>;
1694
+ %template(SolverOptions_GLONASS) GLONASS_SolverOptions<type>;
1695
+
1479
1696
  %template(RINEX_Observation) RINEX_Observation<type>;
1480
1697
  %enddef
1481
1698
 
@@ -437,8 +437,6 @@ class Matrix : public Matrix_Frozen<T, Array2D_Type, ViewType> {
437
437
  #endif
438
438
 
439
439
  typedef Matrix<T, Array2D_Type, ViewType> self_t;
440
- self_t &swapRows(const unsigned int &row1, const unsigned int &row2);
441
- self_t &swapColumns(const unsigned int &column1, const unsigned int &column2);
442
440
  };
443
441
 
444
442
  %inline {
@@ -995,7 +993,23 @@ MAKE_TO_S(Matrix_Frozen)
995
993
  }
996
994
  #endif
997
995
 
998
- %typemap(out) self_t & "$result = self;"
996
+ /*
997
+ * Returning (*this) requires special care;
998
+ * "self_t &func(){return (*this);}" in a C++ file may generate
999
+ * a new wrapped object deleted by GC in the target language
1000
+ * unless the care.
1001
+ *
1002
+ * Work around 1)
1003
+ * %typemap(in, numinputs=0) self_t *self_p "";
1004
+ * %typemap(argout) self_t *self_p "$result = self;";
1005
+ * void func(self_t *self_p){...}
1006
+ *
1007
+ * Work around 2) (useful without overwrite of the original source, but may overfit)
1008
+ * %typemap(out) self_t & "$result = self;"
1009
+ * self_t &func(){...; return *$self;}
1010
+ */
1011
+ %typemap(in, numinputs=0) self_t *self_p "";
1012
+ %typemap(argout) self_t *self_p "$result = self;";
999
1013
 
1000
1014
  T &__setitem__(const unsigned int &row, const unsigned int &column, const T &value) {
1001
1015
  return (($self)->operator()(row, column) = value);
@@ -1012,41 +1026,50 @@ MAKE_TO_S(Matrix_Frozen)
1012
1026
  #endif
1013
1027
  %rename("scalar") getScalar;
1014
1028
  %rename("I") getI;
1015
- %rename("swap_rows") swapRows;
1016
- %rename("swap_columns") swapColumns;
1029
+
1030
+ void swap_rows(
1031
+ self_t *self_p,
1032
+ const unsigned int &r1, const unsigned int &r2){
1033
+ $self->swapRows(r1, r2);
1034
+ }
1035
+ void swap_columns(
1036
+ self_t *self_p,
1037
+ const unsigned int &c1, const unsigned int &c2){
1038
+ $self->swapColumns(c1, c2);
1039
+ }
1017
1040
 
1018
1041
  template <class T2, class Array2D_Type2, class ViewType2>
1019
- self_t &replace(const Matrix_Frozen<T2, Array2D_Type2, ViewType2> &matrix){
1020
- return $self->replace(matrix);
1042
+ void replace(
1043
+ self_t *self_p,
1044
+ const Matrix_Frozen<T2, Array2D_Type2, ViewType2> &matrix){
1045
+ $self->replace(matrix);
1021
1046
  }
1022
1047
  INSTANTIATE_MATRIX_FUNC(replace, replace);
1023
1048
 
1024
- self_t &replace(const void *replacer = NULL){
1049
+ void replace(self_t *self_p, const void *replacer = NULL){
1025
1050
  if(!MatrixUtil::replace(*$self, replacer)){
1026
1051
  throw std::runtime_error("Unsupported replacement");
1027
1052
  }
1028
- return *$self;
1029
1053
  }
1030
1054
 
1031
- self_t &replace(const T *serialized){
1055
+ void replace(self_t *self_p, const T *serialized){
1032
1056
  if(!MatrixUtil::replace(*$self, serialized)){
1033
1057
  throw std::runtime_error("Unsupported replacement");
1034
1058
  }
1035
- return *$self;
1036
1059
  }
1037
1060
 
1038
1061
  #ifdef SWIGRUBY
1039
- %bang swapRows(const unsigned int &, const unsigned int &);
1040
- %bang swapColumns(const unsigned int &, const unsigned int &);
1062
+ %bang swap_rows;
1063
+ %bang swap_columns;
1041
1064
  %rename("replace!") replace;
1042
1065
 
1043
- self_t &map_bang(
1066
+ void map_bang(
1067
+ self_t *self_p,
1044
1068
  void (*each_func)(
1045
1069
  const T &src, T *dst,
1046
1070
  const unsigned int &i, const unsigned int &j),
1047
1071
  const typename MatrixUtil::each_which_t &each_which = MatrixUtil::EACH_ALL){
1048
1072
  MatrixUtil::each(*$self, each_func, each_which, $self);
1049
- return *$self;
1050
1073
  }
1051
1074
  %rename("map!") map_bang;
1052
1075
  %alias map_bang "collect!,map_with_index!,collect_with_index!";
@@ -253,6 +253,7 @@ __RINEX_OBS_TEXT__
253
253
  t_meas = GPS::Time::new(1849, 172413)
254
254
  puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %02d:%02d:%02d UTC"%[*t_meas.c_tm]})"
255
255
  expect(t_meas.c_tm).to eq([2015, 6, 15, 23, 53, 33])
256
+ expect(GPS::Time::new(0, t_meas.serialize)).to eq(t_meas)
256
257
 
257
258
  sn.update_all_ephemeris(t_meas)
258
259
 
@@ -214,9 +214,19 @@ shared_examples 'Matrix' do
214
214
  it 'is swappable with swap_rows! or swap_cloumns!' do
215
215
  mat_builtin = Matrix[*compare_with[0]]
216
216
  [:swap_rows!, :swap_columns!].each.with_index{|func, i|
217
- params[:rc][i].times.to_a.combination(2).to_a{|a, b|
218
- mat[0].send(func, a, b)
219
- mat_builtin.send(func, a, b)
217
+ params[:rc][i].times.to_a.combination(2).to_a.each{|a, b|
218
+ mat_mod = mat[0].send(func, a, b)
219
+ case func
220
+ when :swap_rows!
221
+ idxs = mat_builtin.row_count.times.to_a
222
+ idxs[a], idxs[b] = [b, a]
223
+ mat_builtin = Matrix::rows(mat_builtin.row_vectors.values_at(*idxs))
224
+ when :swap_columns!
225
+ idxs = mat_builtin.column_count.times.to_a
226
+ idxs[a], idxs[b] = [b, a]
227
+ mat_builtin = Matrix::columns(mat_builtin.column_vectors.values_at(*idxs))
228
+ end
229
+ expect(mat_mod).to equal(mat[0])
220
230
  expect(mat[0].to_a).to eq(mat_builtin.to_a)
221
231
  }
222
232
  }
@@ -7,15 +7,22 @@ require_relative 'GPS'
7
7
 
8
8
  module GPS_PVT
9
9
  class Receiver
10
+
11
+ GPS::Time.send(:define_method, :utc){ # send as work around of old Ruby
12
+ res = c_tm(GPS::Time::guess_leap_seconds(self))
13
+ res[-1] += (seconds % 1)
14
+ res
15
+ }
16
+
10
17
  def self.pvt_items(opt = {})
11
18
  opt = {
12
19
  :system => [[:GPS, 1..32]],
13
20
  :satellites => (1..32).to_a,
14
21
  }.merge(opt)
15
22
  [[
16
- [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec],
23
+ [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec_rcv_UTC],
17
24
  proc{|pvt|
18
- [:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
25
+ [:week, :seconds, :utc].collect{|f| pvt.receiver_time.send(f)}.flatten
19
26
  }
20
27
  ]] + [[
21
28
  [:receiver_clock_error_meter, :longitude, :latitude, :height, :rel_E, :rel_N, :rel_U],
@@ -146,7 +153,7 @@ class Receiver
146
153
  rel_prop
147
154
  }
148
155
  @debug = {}
149
- solver_opts = [:gps_options, :sbas_options].collect{|target|
156
+ solver_opts = [:gps_options, :sbas_options, :glonass_options].collect{|target|
150
157
  @solver.send(target)
151
158
  }
152
159
  solver_opts.each{|opt|
@@ -261,6 +268,11 @@ class Receiver
261
268
  prns.each{|prn| @solver.sbas_options.send(mode, prn)}
262
269
  elsif check_sys_svid.call(:QZSS, 193..202) then
263
270
  [svid || (193..202).to_a].flatten.each{|prn| @solver.gps_options.send(mode, prn)}
271
+ elsif check_sys_svid.call(:GLONASS, 1..24, 0x100) then
272
+ prns = [svid || (1..24).to_a].flatten.collect{|i| (i & 0xFF) + 0x100}
273
+ labels = prns.collect{|prn| "GLONASS:#{prn & 0xFF}"}
274
+ update_output.call(:GLONASS, prns, labels)
275
+ prns.each{|prn| @solver.glonass_options.send(mode, prn & 0xFF)}
264
276
  else
265
277
  raise "Unknown satellite: #{spec}"
266
278
  end
@@ -337,22 +349,23 @@ class Receiver
337
349
  [[:@azimuth, az], [:@elevation, el]].each{|k, values|
338
350
  self.instance_variable_set(k, Hash[*(sats.zip(values).flatten(1))])
339
351
  }
352
+ mat_S = self.S
340
353
  [:@slopeH, :@slopeV] \
341
- .zip((self.fd ? self.slope_HV_enu.to_a.transpose : [nil, nil])) \
354
+ .zip((self.fd ? self.slope_HV_enu(mat_S).to_a.transpose : [nil, nil])) \
342
355
  .each{|k, values|
343
356
  self.instance_variable_set(k,
344
357
  Hash[*(values ? sats.zip(values).flatten(1) : [])])
345
358
  }
359
+ # If a design matrix G has columns larger than 4,
360
+ # other states excluding position and time are estimated.
361
+ @other_state = self.position_solved? \
362
+ ? (mat_S * self.delta_r.partial(self.used_satellites, 1, 0, 0)).transpose.to_a[0][4..-1] \
363
+ : []
346
364
  instance_variable_get(target)
347
365
  }
348
- [:azimuth, :elevation, :slopeH, :slopeV].each{|k|
366
+ [:azimuth, :elevation, :slopeH, :slopeV, :other_state].each{|k|
349
367
  eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
350
368
  }
351
- define_method(:other_state){
352
- # If a design matrix G has columns larger than 4,
353
- # other states excluding position and time are estimated.
354
- (self.G.rows <= 4) ? [] : (self.S * self.delta_r).transpose.to_a[0][4..-1]
355
- }
356
369
  }
357
370
 
358
371
  proc{
@@ -361,7 +374,13 @@ class Receiver
361
374
  eph.svid = prn
362
375
  [prn, eph]
363
376
  }.flatten(1)]
364
- define_method(:register_ephemeris){|t_meas, sys, prn, bcast_data|
377
+ eph_glonass_list = Hash[*(1..24).collect{|num|
378
+ eph = GPS::Ephemeris_GLONASS::new
379
+ eph.svid = num
380
+ [num, eph]
381
+ }.flatten(1)]
382
+ define_method(:register_ephemeris){|t_meas, sys, prn, bcast_data, *options|
383
+ opt = options[0] || {}
365
384
  case sys
366
385
  when :GPS, :QZSS
367
386
  next unless eph = eph_list[prn]
@@ -391,6 +410,15 @@ class Receiver
391
410
  $stderr.puts str
392
411
  } if @debug[:SBAS_IGP]
393
412
  end
413
+ when :GLONASS
414
+ next unless eph = eph_glonass_list[prn]
415
+ leap_sec = @solver.gps_space_node.is_valid_utc ?
416
+ @solver.gps_space_node.iono_utc.delta_t_LS :
417
+ GPS::Time::guess_leap_seconds(t_meas)
418
+ next unless eph.parse(bcast_data[0..3], leap_sec)
419
+ eph.freq_ch = opt[:freq_ch] || 0
420
+ @solver.glonass_space_node.register_ephemeris(prn, eph)
421
+ eph.invalidate
394
422
  end
395
423
  }
396
424
  }.call
@@ -402,7 +430,7 @@ class Receiver
402
430
  ubx = UBX::new(open(ubx_fname))
403
431
  ubx_kind = Hash::new(0)
404
432
 
405
- after_run = b || proc{|pvt| puts pvt.to_s}
433
+ after_run = b || proc{|pvt| puts pvt.to_s if pvt}
406
434
 
407
435
  gnss_serial = proc{|svid, sys|
408
436
  if sys then # new numbering
@@ -468,7 +496,11 @@ class Receiver
468
496
  }
469
497
  sys, svid = gnss_serial.call(*loader.call(36, 2).reverse)
470
498
  case sys
471
- when :GPS, :QZSS;
499
+ when :GPS, :SBAS, :QZSS;
500
+ when :GLONASS
501
+ svid += 0x100
502
+ meas.add(svid, :L1_FREQUENCY,
503
+ GPS::SpaceNode_GLONASS::L1_frequency(loader.call(39, 1, "C") - 7))
472
504
  else; next
473
505
  end
474
506
  trk_stat = loader.call(46, 1)[0]
@@ -502,12 +534,14 @@ class Receiver
502
534
  })
503
535
  when [0x02, 0x13] # RXM-SFRBX
504
536
  sys, svid = gnss_serial.call(packet[6 + 1], packet[6])
537
+ opt = {}
538
+ opt[:freq_ch] = packet[6 + 3] - 7 if sys == :GLONASS
505
539
  register_ephemeris(
506
540
  t_meas,
507
541
  sys, svid,
508
542
  packet.slice(6 + 8, 4 * packet[6 + 4]).each_slice(4).collect{|v|
509
543
  v.pack("C*").unpack("V")[0]
510
- })
544
+ }, opt)
511
545
  end
512
546
  }
513
547
  $stderr.puts ", found packets are %s"%[ubx_kind.inspect]
@@ -517,6 +551,7 @@ class Receiver
517
551
  items = [
518
552
  @solver.gps_space_node,
519
553
  @solver.sbas_space_node,
554
+ @solver.glonass_space_node,
520
555
  ].inject(0){|res, sn|
521
556
  loaded_items = sn.send(:read, fname)
522
557
  raise "Format error! (Not RINEX) #{fname}" if loaded_items < 0
@@ -526,9 +561,10 @@ class Receiver
526
561
  end
527
562
 
528
563
  def parse_rinex_obs(fname, &b)
529
- after_run = b || proc{|pvt| puts pvt.to_s}
564
+ after_run = b || proc{|pvt| puts pvt.to_s if pvt}
530
565
  $stderr.print "Reading RINEX observation file (%s)"%[fname]
531
566
  types = nil
567
+ glonass_freq = nil
532
568
  count = 0
533
569
  GPS::RINEX_Observation::read(fname){|item|
534
570
  $stderr.print '.' if (count += 1) % 1000 == 0
@@ -551,12 +587,32 @@ class Receiver
551
587
  }.compact]
552
588
  }.flatten(1))]
553
589
 
590
+ glonass_freq ||= proc{|spec|
591
+ # frequency channels described in observation file
592
+ next {} unless spec
593
+ Hash[*(spec.collect{|line|
594
+ line[4..-1].scan(/R(\d{2}).([\s+-]\d)./).collect{|prn, ch|
595
+ [prn.to_i, GPS::SpaceNode_GLONASS::L1_frequency(ch.to_i)]
596
+ }
597
+ }.flatten(2))]
598
+ }.call(item[:header]["GLONASS SLOT / FRQ #"])
599
+
554
600
  meas = GPS::Measurement::new
555
601
  item[:meas].each{|k, v|
556
602
  sys, prn = k
557
603
  case sys
558
604
  when 'G', ' '
605
+ when 'S'; prn += 100
559
606
  when 'J'; prn += 192
607
+ when 'R'
608
+ freq = (glonass_freq[prn] ||= proc{|sn|
609
+ # frequency channels saved with ephemeris
610
+ sn.update_all_ephemeris(t_meas)
611
+ next nil unless sn.ephemeris(prn).in_range?(t_meas)
612
+ sn.ephemeris(prn).frequency_L1
613
+ }.call(@solver.glonass_space_node))
614
+ prn += 0x100
615
+ meas.add(prn, :L1_FREQUENCY, freq) if freq
560
616
  else; next
561
617
  end
562
618
  types[sys] = (types[' '] || []) unless types[sys]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.1"
5
5
  end