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.
- checksums.yaml +4 -4
- data/README.md +16 -4
- data/Rakefile +2 -0
- data/exe/gps_pvt +65 -2
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +5806 -380
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +453 -429
- data/ext/ninja-scan-light/tool/algorithm/integral.h +91 -0
- data/ext/ninja-scan-light/tool/navigation/GLONASS.h +1329 -0
- data/ext/ninja-scan-light/tool/navigation/GLONASS_Solver.h +306 -0
- data/ext/ninja-scan-light/tool/navigation/GPS.h +7 -1
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +7 -0
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +389 -4
- data/ext/ninja-scan-light/tool/swig/GPS.i +223 -6
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +38 -15
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +1 -0
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +13 -3
- data/lib/gps_pvt/receiver.rb +71 -15
- data/lib/gps_pvt/version.rb +1 -1
- metadata +5 -2
@@ -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
|
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
|
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
|
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
|
-
|
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
|
-
|
1016
|
-
|
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
|
-
|
1020
|
-
|
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
|
-
|
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
|
-
|
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
|
1040
|
-
%bang
|
1062
|
+
%bang swap_rows;
|
1063
|
+
%bang swap_columns;
|
1041
1064
|
%rename("replace!") replace;
|
1042
1065
|
|
1043
|
-
|
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
|
-
|
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
|
}
|
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -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, :
|
23
|
+
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec_rcv_UTC],
|
17
24
|
proc{|pvt|
|
18
|
-
[:week, :seconds, :
|
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
|
-
|
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]
|
data/lib/gps_pvt/version.rb
CHANGED