gps_pvt 0.1.7 → 0.2.3
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 +13 -2
- data/Rakefile +2 -0
- data/exe/gps_pvt +32 -23
- data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +5 -3
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +4444 -671
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +284 -19
- data/ext/ninja-scan-light/tool/navigation/GPS.h +12 -46
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +61 -100
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +87 -25
- data/ext/ninja-scan-light/tool/navigation/QZSS.h +62 -0
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +335 -27
- data/ext/ninja-scan-light/tool/navigation/SBAS.h +2330 -0
- data/ext/ninja-scan-light/tool/navigation/SBAS_Solver.h +306 -0
- data/ext/ninja-scan-light/tool/param/bit_array.h +4 -3
- data/ext/ninja-scan-light/tool/swig/GPS.i +429 -138
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +53 -6
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +37 -6
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +38 -4
- data/gps_pvt.gemspec +63 -0
- data/lib/gps_pvt/receiver.rb +281 -140
- data/lib/gps_pvt/version.rb +1 -1
- metadata +7 -3
@@ -15,16 +15,19 @@
|
|
15
15
|
#include <iostream>
|
16
16
|
#include <fstream>
|
17
17
|
#include <exception>
|
18
|
+
#include <sstream>
|
18
19
|
|
19
20
|
#include "navigation/GPS.h"
|
21
|
+
#include "navigation/SBAS.h"
|
22
|
+
#include "navigation/QZSS.h"
|
20
23
|
#include "navigation/RINEX.h"
|
21
24
|
|
22
25
|
#include "navigation/GPS_Solver_Base.h"
|
23
26
|
#include "navigation/GPS_Solver.h"
|
24
27
|
#include "navigation/GPS_Solver_RAIM.h"
|
28
|
+
#include "navigation/SBAS_Solver.h"
|
25
29
|
|
26
30
|
#if defined(__cplusplus) && (__cplusplus < 201103L)
|
27
|
-
#include <sstream>
|
28
31
|
namespace std {
|
29
32
|
template <class T>
|
30
33
|
inline std::string to_string(const T &value){
|
@@ -156,6 +159,14 @@ static std::string inspect_str(const VALUE &v){
|
|
156
159
|
*week = self->week;
|
157
160
|
*seconds = self->seconds;
|
158
161
|
}
|
162
|
+
#if defined(SWIG)
|
163
|
+
int __cmp__(const GPS_Time<FloatT> &t) const {
|
164
|
+
return ((self->week < t.week) ? -1
|
165
|
+
: ((self->week > t.week) ? 1
|
166
|
+
: (self->seconds < t.seconds ? -1
|
167
|
+
: (self->seconds > t.seconds ? 1 : 0))));
|
168
|
+
}
|
169
|
+
#endif
|
159
170
|
}
|
160
171
|
|
161
172
|
%define MAKE_ACCESSOR(name, type)
|
@@ -180,6 +191,26 @@ const type &get_ ## name () const {
|
|
180
191
|
}
|
181
192
|
}
|
182
193
|
%enddef
|
194
|
+
#if defined(SWIGRUBY)
|
195
|
+
%define MAKE_ARRAY_INPUT(type, arg_name, f_conv)
|
196
|
+
%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER) type arg_name[ANY] {
|
197
|
+
$1 = RB_TYPE_P($input, T_ARRAY) ? 1 : 0;
|
198
|
+
}
|
199
|
+
%typemap(in) type arg_name[ANY] ($*1_ltype temp[$1_dim0]) {
|
200
|
+
if(!(RB_TYPE_P($input, T_ARRAY) && (RARRAY_LEN($input) == $1_dim0))){
|
201
|
+
SWIG_exception(SWIG_TypeError, "array[$1_dim0] is expected");
|
202
|
+
}
|
203
|
+
for(int i(0); i < $1_dim0; ++i){
|
204
|
+
if(!SWIG_IsOK(f_conv(RARRAY_AREF($input, i), &temp[i]))){
|
205
|
+
SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
|
206
|
+
}
|
207
|
+
}
|
208
|
+
$1 = temp;
|
209
|
+
}
|
210
|
+
%enddef
|
211
|
+
#else
|
212
|
+
#define MAKE_ARRAY_INPUT(type, arg_name, f_conv)
|
213
|
+
#endif
|
183
214
|
|
184
215
|
%inline %{
|
185
216
|
template <class FloatT>
|
@@ -195,34 +226,8 @@ struct GPS_Ionospheric_UTC_Parameters : public GPS_SpaceNode<FloatT>::Ionospheri
|
|
195
226
|
%append_output(swig::from($1[i]));
|
196
227
|
}
|
197
228
|
}
|
198
|
-
|
199
|
-
|
200
|
-
if(!(RB_TYPE_P($input, T_ARRAY) && (RARRAY_LEN($input) == 4))){
|
201
|
-
SWIG_exception(SWIG_TypeError, "array[4] is expected");
|
202
|
-
}
|
203
|
-
for(int i(0); i < 4; ++i){
|
204
|
-
SWIG_Object obj(RARRAY_AREF($input, i));
|
205
|
-
if(!SWIG_IsOK(swig::asval(obj, &temp[i]))){
|
206
|
-
SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
|
207
|
-
}
|
208
|
-
}
|
209
|
-
#endif
|
210
|
-
$1 = temp;
|
211
|
-
}
|
212
|
-
%typemap(in) const unsigned int *buf (unsigned int temp[10]) {
|
213
|
-
#ifdef SWIGRUBY
|
214
|
-
if((!RB_TYPE_P($input, T_ARRAY))
|
215
|
-
|| (RARRAY_LEN($input) < sizeof(temp) / sizeof(temp[0]))){
|
216
|
-
SWIG_exception(SWIG_TypeError, "array is expected, or too short array");
|
217
|
-
}
|
218
|
-
$1 = temp;
|
219
|
-
for(int i(0); i < sizeof(temp) / sizeof(temp[0]); ++i){
|
220
|
-
if(!SWIG_IsOK(SWIG_AsVal(unsigned int)(RARRAY_AREF($input, i), &($1[i])))){
|
221
|
-
SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
|
222
|
-
}
|
223
|
-
}
|
224
|
-
#endif
|
225
|
-
}
|
229
|
+
MAKE_ARRAY_INPUT(const FloatT, values, swig::asval);
|
230
|
+
MAKE_ARRAY_INPUT(const unsigned int, buf, SWIG_AsVal(unsigned int));
|
226
231
|
%rename("alpha=") set_alpha;
|
227
232
|
void set_alpha(const FloatT values[4]){
|
228
233
|
for(int i(0); i < 4; ++i){
|
@@ -255,7 +260,7 @@ struct GPS_Ionospheric_UTC_Parameters : public GPS_SpaceNode<FloatT>::Ionospheri
|
|
255
260
|
MAKE_ACCESSOR(WN_LSF, unsigned int);
|
256
261
|
MAKE_ACCESSOR(DN, unsigned int);
|
257
262
|
MAKE_ACCESSOR(delta_t_LSF, int);
|
258
|
-
static GPS_Ionospheric_UTC_Parameters<FloatT> parse(const unsigned int
|
263
|
+
static GPS_Ionospheric_UTC_Parameters<FloatT> parse(const unsigned int buf[10]){
|
259
264
|
typedef typename GPS_SpaceNode<FloatT>
|
260
265
|
::BroadcastedMessage<unsigned int, 30> parser_t;
|
261
266
|
if((parser_t::subframe_id(buf) != 4) || (parser_t::sv_page_id(buf) != 56)){
|
@@ -321,22 +326,10 @@ struct GPS_Ephemeris : public GPS_SpaceNode<FloatT>::SatelliteProperties::Epheme
|
|
321
326
|
MAKE_ACCESSOR(omega, FloatT);
|
322
327
|
MAKE_ACCESSOR(dot_Omega0, FloatT);
|
323
328
|
MAKE_ACCESSOR(dot_i0, FloatT);
|
324
|
-
|
325
|
-
|
326
|
-
if((!RB_TYPE_P($input, T_ARRAY))
|
327
|
-
|| (RARRAY_LEN($input) < sizeof(temp) / sizeof(temp[0]))){
|
328
|
-
SWIG_exception(SWIG_TypeError, "array is expected, or too short array");
|
329
|
-
}
|
330
|
-
$1 = temp;
|
331
|
-
for(int i(0); i < sizeof(temp) / sizeof(temp[0]); ++i){
|
332
|
-
if(!SWIG_IsOK(SWIG_AsVal(unsigned int)(RARRAY_AREF($input, i), &($1[i])))){
|
333
|
-
SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
|
334
|
-
}
|
335
|
-
}
|
336
|
-
#endif
|
337
|
-
}
|
329
|
+
|
330
|
+
MAKE_ARRAY_INPUT(const unsigned int, buf, SWIG_AsVal(unsigned int));
|
338
331
|
%apply int *OUTPUT { int *subframe_no, int *iodc_or_iode };
|
339
|
-
void parse(const unsigned int
|
332
|
+
void parse(const unsigned int buf[10], int *subframe_no, int *iodc_or_iode){
|
340
333
|
typedef typename GPS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris eph_t;
|
341
334
|
typename eph_t::raw_t raw;
|
342
335
|
eph_t eph;
|
@@ -428,15 +421,84 @@ struct GPS_Ephemeris : public GPS_SpaceNode<FloatT>::SatelliteProperties::Epheme
|
|
428
421
|
%append_output(swig::from($1.latitude));
|
429
422
|
%append_output(swig::from($1.longitude));
|
430
423
|
}
|
431
|
-
%ignore iono_correction() const;
|
432
|
-
%ignore tropo_correction() const;
|
433
424
|
int read(const char *fname) {
|
434
425
|
std::fstream fin(fname, std::ios::in | std::ios::binary);
|
435
|
-
|
426
|
+
typename RINEX_NAV_Reader<FloatT>::space_node_list_t space_nodes = {self};
|
427
|
+
space_nodes.qzss = self;
|
428
|
+
return RINEX_NAV_Reader<FloatT>::read_all(fin, space_nodes);
|
436
429
|
}
|
437
430
|
}
|
438
431
|
|
439
432
|
%include navigation/GPS.h
|
433
|
+
%include navigation/QZSS.h
|
434
|
+
|
435
|
+
%inline %{
|
436
|
+
template <class FloatT>
|
437
|
+
struct SBAS_Ephemeris : public SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris {
|
438
|
+
SBAS_Ephemeris() : SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris() {}
|
439
|
+
SBAS_Ephemeris(const typename SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris &eph)
|
440
|
+
: SBAS_SpaceNode<FloatT>::SatelliteProperties::Ephemeris(eph) {}
|
441
|
+
};
|
442
|
+
%}
|
443
|
+
%extend SBAS_Ephemeris {
|
444
|
+
MAKE_ACCESSOR(svid, unsigned int);
|
445
|
+
|
446
|
+
MAKE_ACCESSOR(WN, unsigned int);
|
447
|
+
MAKE_ACCESSOR(t_0, FloatT);
|
448
|
+
MAKE_ACCESSOR(URA, int);
|
449
|
+
MAKE_ACCESSOR( x, FloatT); MAKE_ACCESSOR( y, FloatT); MAKE_ACCESSOR( z, FloatT);
|
450
|
+
MAKE_ACCESSOR( dx, FloatT); MAKE_ACCESSOR( dy, FloatT); MAKE_ACCESSOR( dz, FloatT);
|
451
|
+
MAKE_ACCESSOR(ddx, FloatT); MAKE_ACCESSOR(ddy, FloatT); MAKE_ACCESSOR(ddz, FloatT);
|
452
|
+
MAKE_ACCESSOR(a_Gf0, FloatT);
|
453
|
+
MAKE_ACCESSOR(a_Gf1, FloatT);
|
454
|
+
%apply GPS_Ephemeris::System_XYZ<FloatT, WGS84> & { System_XYZ<FloatT, WGS84> & }; // TODO ineffective?
|
455
|
+
void constellation(
|
456
|
+
System_XYZ<FloatT, WGS84> &position, System_XYZ<FloatT, WGS84> &velocity,
|
457
|
+
const GPS_Time<FloatT> &t, const FloatT &pseudo_range = 0,
|
458
|
+
const bool &with_velocity = true) const {
|
459
|
+
typename SBAS_SpaceNode<FloatT>::SatelliteProperties::constellation_t res(
|
460
|
+
self->constellation(t, pseudo_range, with_velocity));
|
461
|
+
position = res.position;
|
462
|
+
velocity = res.velocity;
|
463
|
+
}
|
464
|
+
}
|
465
|
+
|
466
|
+
%extend SBAS_SpaceNode {
|
467
|
+
%fragment(SWIG_Traits_frag(FloatT));
|
468
|
+
%ignore satellites() const;
|
469
|
+
%ignore satellite(const int &);
|
470
|
+
%ignore available_satellites(const FloatT &lng_deg) const;
|
471
|
+
void register_ephemeris(
|
472
|
+
const int &prn, const SBAS_Ephemeris<FloatT> &eph,
|
473
|
+
const int &priority_delta = 1){
|
474
|
+
self->satellite(prn).register_ephemeris(eph, priority_delta);
|
475
|
+
}
|
476
|
+
SBAS_Ephemeris<FloatT> ephemeris(const int &prn) const {
|
477
|
+
return SBAS_Ephemeris<FloatT>(
|
478
|
+
%const_cast(self, SBAS_SpaceNode<FloatT> *)->satellite(prn).ephemeris());
|
479
|
+
}
|
480
|
+
int read(const char *fname) {
|
481
|
+
std::fstream fin(fname, std::ios::in | std::ios::binary);
|
482
|
+
RINEX_NAV_Reader<FloatT>::space_node_list_t space_nodes = {NULL};
|
483
|
+
space_nodes.sbas = self;
|
484
|
+
return RINEX_NAV_Reader<FloatT>::read_all(fin, space_nodes);
|
485
|
+
}
|
486
|
+
MAKE_ARRAY_INPUT(const unsigned int, buf, SWIG_AsVal(unsigned int));
|
487
|
+
int decode_message(const unsigned int buf[8],
|
488
|
+
const int &prn, const GPS_Time<FloatT> &t_reception,
|
489
|
+
const bool &LNAV_VNAV_LP_LPV_approach = false){
|
490
|
+
return static_cast<int>(
|
491
|
+
self->decode_message(buf, prn, t_reception, LNAV_VNAV_LP_LPV_approach));
|
492
|
+
}
|
493
|
+
std::string ionospheric_grid_points(const int &prn) const {
|
494
|
+
std::ostringstream ss;
|
495
|
+
ss << %const_cast(self, SBAS_SpaceNode<FloatT> *)->satellite(prn)
|
496
|
+
.ionospheric_grid_points();
|
497
|
+
return ss.str();
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
%include navigation/SBAS.h
|
440
502
|
|
441
503
|
%extend GPS_User_PVT {
|
442
504
|
%ignore solver_t;
|
@@ -604,23 +666,6 @@ struct GPS_User_PVT
|
|
604
666
|
}
|
605
667
|
}
|
606
668
|
}
|
607
|
-
#ifdef SWIGRUBY
|
608
|
-
VALUE to_hash() const {
|
609
|
-
VALUE res(rb_hash_new());
|
610
|
-
for(typename GPS_Measurement<FloatT>::items_t::const_iterator
|
611
|
-
it(self->items.begin()), it_end(self->items.end());
|
612
|
-
it != it_end; ++it){
|
613
|
-
VALUE per_sat(rb_hash_new());
|
614
|
-
rb_hash_aset(res, SWIG_From(int)(it->first), per_sat);
|
615
|
-
for(typename GPS_Measurement<FloatT>::items_t::mapped_type::const_iterator
|
616
|
-
it2(it->second.begin()), it2_end(it->second.end());
|
617
|
-
it2 != it2_end; ++it2){
|
618
|
-
rb_hash_aset(per_sat, SWIG_From(int)(it2->first), swig::from(it2->second));
|
619
|
-
}
|
620
|
-
}
|
621
|
-
return res;
|
622
|
-
}
|
623
|
-
#endif
|
624
669
|
%fragment(SWIG_Traits_frag(GPS_Measurement<FloatT>), "header",
|
625
670
|
fragment=SWIG_Traits_frag(FloatT)){
|
626
671
|
namespace swig {
|
@@ -704,9 +749,32 @@ struct GPS_User_PVT
|
|
704
749
|
#endif
|
705
750
|
return SWIG_ERROR;
|
706
751
|
}
|
752
|
+
#ifdef SWIGRUBY
|
753
|
+
template <>
|
754
|
+
VALUE from(const GPS_Measurement<FloatT>::items_t::mapped_type &val) {
|
755
|
+
VALUE per_sat(rb_hash_new());
|
756
|
+
for(typename GPS_Measurement<FloatT>::items_t::mapped_type::const_iterator
|
757
|
+
it(val.begin()), it_end(val.end());
|
758
|
+
it != it_end; ++it){
|
759
|
+
rb_hash_aset(per_sat, SWIG_From(int)(it->first), swig::from(it->second));
|
760
|
+
}
|
761
|
+
return per_sat;
|
762
|
+
}
|
763
|
+
#endif
|
707
764
|
}
|
708
765
|
}
|
709
766
|
%fragment(SWIG_Traits_frag(GPS_Measurement<FloatT>));
|
767
|
+
#ifdef SWIGRUBY
|
768
|
+
VALUE to_hash() const {
|
769
|
+
VALUE res(rb_hash_new());
|
770
|
+
for(typename GPS_Measurement<FloatT>::items_t::const_iterator
|
771
|
+
it(self->items.begin()), it_end(self->items.end());
|
772
|
+
it != it_end; ++it){
|
773
|
+
rb_hash_aset(res, SWIG_From(int)(it->first), swig::from(it->second));
|
774
|
+
}
|
775
|
+
return res;
|
776
|
+
}
|
777
|
+
#endif
|
710
778
|
%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER) const GPS_Measurement<FloatT> & {
|
711
779
|
$1 = SWIG_CheckState(SWIG_ConvertPtr($input, (void **)0, $1_descriptor, 0))
|
712
780
|
|| swig::check<GPS_Measurement<FloatT> >($input);
|
@@ -738,6 +806,7 @@ struct GPS_Measurement {
|
|
738
806
|
L1_RANGE_RATE_SIGMA,
|
739
807
|
L1_SIGNAL_STRENGTH_dBHz,
|
740
808
|
L1_LOCK_SEC,
|
809
|
+
L1_FREQUENCY,
|
741
810
|
ITEMS_PREDEFINED,
|
742
811
|
};
|
743
812
|
void add(const int &prn, const int &key, const FloatT &value){
|
@@ -746,70 +815,66 @@ struct GPS_Measurement {
|
|
746
815
|
};
|
747
816
|
}
|
748
817
|
|
818
|
+
%extend GPS_SolverOptions_Common {
|
819
|
+
%define MAKE_ACCESSOR2(name, type)
|
820
|
+
%rename(%str(name ## =)) set_ ## name;
|
821
|
+
type set_ ## name (const type &v) {
|
822
|
+
return self->cast_general()->name = v;
|
823
|
+
}
|
824
|
+
%rename(%str(name)) get_ ## name;
|
825
|
+
const type &get_ ## name () const {
|
826
|
+
return self->cast_general()->name;
|
827
|
+
}
|
828
|
+
%enddef
|
829
|
+
MAKE_ACCESSOR2(elevation_mask, FloatT);
|
830
|
+
MAKE_ACCESSOR2(residual_mask, FloatT);
|
831
|
+
#undef MAKE_ACCESSOR2
|
832
|
+
MAKE_VECTOR2ARRAY(int);
|
833
|
+
%ignore cast_base;
|
834
|
+
}
|
835
|
+
%inline %{
|
836
|
+
template <class FloatT>
|
837
|
+
struct GPS_SolverOptions_Common {
|
838
|
+
virtual GPS_Solver_GeneralOptions<FloatT> *cast_general() = 0;
|
839
|
+
virtual const GPS_Solver_GeneralOptions<FloatT> *cast_general() const = 0;
|
840
|
+
};
|
841
|
+
%}
|
842
|
+
|
749
843
|
%extend GPS_SolverOptions {
|
750
844
|
%ignore base_t;
|
751
|
-
|
752
|
-
MAKE_ACCESSOR(residual_mask, FloatT);
|
753
|
-
MAKE_ACCESSOR(f_10_7, FloatT);
|
845
|
+
%ignore cast_general;
|
754
846
|
MAKE_VECTOR2ARRAY(int);
|
755
|
-
#ifdef SWIGRUBY
|
756
|
-
%rename("ionospheric_models=") set_ionospheric_models;
|
757
|
-
%rename("ionospheric_models") get_ionospheric_models;
|
758
|
-
#endif
|
759
|
-
%typemap(in) const std::vector<int> &models (std::vector<int> temp) {
|
760
|
-
$1 = &temp;
|
761
|
-
#ifdef SWIGRUBY
|
762
|
-
if(RB_TYPE_P($input, T_ARRAY)){
|
763
|
-
for(int i(0), i_max(RARRAY_LEN($input)); i < i_max; ++i){
|
764
|
-
SWIG_Object obj(RARRAY_AREF($input, i));
|
765
|
-
int v;
|
766
|
-
if(SWIG_IsOK(SWIG_AsVal(int)(obj, &v))){
|
767
|
-
temp.push_back(v);
|
768
|
-
}else{
|
769
|
-
SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
|
770
|
-
}
|
771
|
-
}
|
772
|
-
}
|
773
|
-
#endif
|
774
|
-
}
|
775
847
|
}
|
776
848
|
%inline %{
|
777
849
|
template <class FloatT>
|
778
|
-
struct GPS_SolverOptions
|
850
|
+
struct GPS_SolverOptions
|
851
|
+
: public GPS_SinglePositioning<FloatT>::options_t,
|
852
|
+
GPS_SolverOptions_Common<FloatT> {
|
779
853
|
typedef typename GPS_SinglePositioning<FloatT>::options_t base_t;
|
780
854
|
void exclude(const int &prn){base_t::exclude_prn.set(prn);}
|
781
855
|
void include(const int &prn){base_t::exclude_prn.reset(prn);}
|
782
856
|
std::vector<int> excluded() const {return base_t::exclude_prn.excluded();}
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
if((models[j] >= 0) && (models[j] < base_t::IONOSPHERIC_SKIP)){
|
805
|
-
v = (model_t)models[j];
|
806
|
-
}
|
807
|
-
++j;
|
808
|
-
}
|
809
|
-
base_t::ionospheric_models[i] = v;
|
810
|
-
}
|
811
|
-
return get_ionospheric_models();
|
812
|
-
}
|
857
|
+
GPS_Solver_GeneralOptions<FloatT> *cast_general(){return this;}
|
858
|
+
const GPS_Solver_GeneralOptions<FloatT> *cast_general() const {return this;}
|
859
|
+
};
|
860
|
+
%}
|
861
|
+
|
862
|
+
%extend SBAS_SolverOptions {
|
863
|
+
%ignore base_t;
|
864
|
+
%ignore cast_general;
|
865
|
+
MAKE_VECTOR2ARRAY(int);
|
866
|
+
}
|
867
|
+
%inline %{
|
868
|
+
template <class FloatT>
|
869
|
+
struct SBAS_SolverOptions
|
870
|
+
: public SBAS_SinglePositioning<FloatT>::options_t,
|
871
|
+
GPS_SolverOptions_Common<FloatT> {
|
872
|
+
typedef typename SBAS_SinglePositioning<FloatT>::options_t base_t;
|
873
|
+
void exclude(const int &prn){base_t::exclude_prn.set(prn);}
|
874
|
+
void include(const int &prn){base_t::exclude_prn.reset(prn);}
|
875
|
+
std::vector<int> excluded() const {return base_t::exclude_prn.excluded();}
|
876
|
+
GPS_Solver_GeneralOptions<FloatT> *cast_general(){return this;}
|
877
|
+
const GPS_Solver_GeneralOptions<FloatT> *cast_general() const {return this;}
|
813
878
|
};
|
814
879
|
%}
|
815
880
|
|
@@ -818,6 +883,8 @@ struct GPS_SolverOptions : public GPS_SinglePositioning<FloatT>::options_t {
|
|
818
883
|
%ignore base_t;
|
819
884
|
%ignore gps_t;
|
820
885
|
%ignore gps;
|
886
|
+
%ignore sbas_t;
|
887
|
+
%ignore sbas;
|
821
888
|
%ignore select_solver;
|
822
889
|
%ignore relative_property;
|
823
890
|
%ignore satellite_position;
|
@@ -826,18 +893,19 @@ struct GPS_SolverOptions : public GPS_SinglePositioning<FloatT>::options_t {
|
|
826
893
|
%ignore mark;
|
827
894
|
%fragment("hook"{GPS_Solver<FloatT>}, "header",
|
828
895
|
fragment=SWIG_From_frag(int),
|
829
|
-
fragment=SWIG_Traits_frag(FloatT)
|
896
|
+
fragment=SWIG_Traits_frag(FloatT),
|
897
|
+
fragment=SWIG_Traits_frag(GPS_Measurement<FloatT>)){
|
830
898
|
template <>
|
831
|
-
|
899
|
+
GPS_Solver<FloatT>::base_t::relative_property_t
|
832
900
|
GPS_Solver<FloatT>::relative_property(
|
833
|
-
const
|
834
|
-
const
|
835
|
-
const
|
836
|
-
const
|
837
|
-
const
|
838
|
-
const
|
901
|
+
const GPS_Solver<FloatT>::base_t::prn_t &prn,
|
902
|
+
const GPS_Solver<FloatT>::base_t::measurement_t::mapped_type &measurement,
|
903
|
+
const GPS_Solver<FloatT>::base_t::float_t &receiver_error,
|
904
|
+
const GPS_Solver<FloatT>::base_t::gps_time_t &time_arrival,
|
905
|
+
const GPS_Solver<FloatT>::base_t::pos_t &usr_pos,
|
906
|
+
const GPS_Solver<FloatT>::base_t::xyz_t &usr_vel) const {
|
839
907
|
union {
|
840
|
-
|
908
|
+
base_t::relative_property_t prop;
|
841
909
|
FloatT values[7];
|
842
910
|
} res = {
|
843
911
|
select_solver(prn).relative_property(
|
@@ -859,6 +927,7 @@ struct GPS_SolverOptions : public GPS_SinglePositioning<FloatT>::options_t {
|
|
859
927
|
swig::from(res.prop.los_neg[0]),
|
860
928
|
swig::from(res.prop.los_neg[1]),
|
861
929
|
swig::from(res.prop.los_neg[2])),
|
930
|
+
swig::from(measurement), // measurement => Hash
|
862
931
|
swig::from(receiver_error), // receiver_error
|
863
932
|
SWIG_NewPointerObj( // time_arrival
|
864
933
|
new base_t::gps_time_t(time_arrival), $descriptor(GPS_Time<FloatT> *), SWIG_POINTER_OWN),
|
@@ -889,29 +958,186 @@ struct GPS_SolverOptions : public GPS_SinglePositioning<FloatT>::options_t {
|
|
889
958
|
}
|
890
959
|
template <>
|
891
960
|
bool GPS_Solver<FloatT>::update_position_solution(
|
892
|
-
const
|
893
|
-
|
961
|
+
const GPS_Solver<FloatT>::base_t::geometric_matrices_t &geomat,
|
962
|
+
GPS_Solver<FloatT>::base_t::user_pvt_t &res) const {
|
894
963
|
#ifdef SWIGRUBY
|
895
964
|
do{
|
896
965
|
static const VALUE key(ID2SYM(rb_intern("update_position_solution")));
|
897
966
|
VALUE hook(rb_hash_lookup(hooks, key));
|
898
967
|
if(NIL_P(hook)){break;}
|
899
|
-
|
900
|
-
%const_cast(geomat,
|
968
|
+
base_t::geometric_matrices_t &geomat_(
|
969
|
+
%const_cast(geomat, base_t::geometric_matrices_t &));
|
901
970
|
VALUE values[] = {
|
902
971
|
SWIG_NewPointerObj(&geomat_.G,
|
903
972
|
$descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0),
|
904
973
|
SWIG_NewPointerObj(&geomat_.W,
|
905
974
|
$descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0),
|
906
975
|
SWIG_NewPointerObj(&geomat_.delta_r,
|
907
|
-
$descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0)
|
976
|
+
$descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0),
|
977
|
+
SWIG_NewPointerObj(&res,
|
978
|
+
$descriptor(GPS_User_PVT<FloatT> *), 0)};
|
908
979
|
proc_call_throw_if_error(hook, sizeof(values) / sizeof(values[0]), values);
|
909
980
|
}while(false);
|
910
981
|
#endif
|
911
982
|
return super_t::update_position_solution(geomat, res);
|
912
983
|
}
|
984
|
+
template <>
|
985
|
+
GPS_Solver<FloatT>::base_t::xyz_t *GPS_Solver<FloatT>::satellite_position(
|
986
|
+
const GPS_Solver<FloatT>::base_t::prn_t &prn,
|
987
|
+
const GPS_Solver<FloatT>::base_t::gps_time_t &time,
|
988
|
+
GPS_Solver<FloatT>::base_t::xyz_t &buf) const {
|
989
|
+
GPS_Solver<FloatT>::base_t::xyz_t *res(
|
990
|
+
select_solver(prn).satellite_position(prn, time, buf));
|
991
|
+
#ifdef SWIGRUBY
|
992
|
+
do{
|
993
|
+
static const VALUE key(ID2SYM(rb_intern("satellite_position")));
|
994
|
+
VALUE hook(rb_hash_lookup(hooks, key));
|
995
|
+
if(NIL_P(hook)){break;}
|
996
|
+
VALUE values[] = {
|
997
|
+
SWIG_From(int)(prn), // prn
|
998
|
+
SWIG_NewPointerObj( // time
|
999
|
+
new base_t::gps_time_t(time), $descriptor(GPS_Time<FloatT> *), SWIG_POINTER_OWN),
|
1000
|
+
(res // position (internally calculated)
|
1001
|
+
? SWIG_NewPointerObj(res, $descriptor(System_XYZ<FloatT, WGS84> *), 0)
|
1002
|
+
: Qnil)};
|
1003
|
+
VALUE res_hook(proc_call_throw_if_error(hook, sizeof(values) / sizeof(values[0]), values));
|
1004
|
+
if(NIL_P(res_hook)){ // unknown position
|
1005
|
+
res = NULL;
|
1006
|
+
break;
|
1007
|
+
}else if(RB_TYPE_P(res_hook, T_ARRAY) && (RARRAY_LEN(res_hook) == 3)){
|
1008
|
+
int i(0);
|
1009
|
+
for(; i < 3; ++i){
|
1010
|
+
VALUE v(RARRAY_AREF(res_hook, i));
|
1011
|
+
if(!SWIG_IsOK(swig::asval(v, &buf[i]))){break;}
|
1012
|
+
}
|
1013
|
+
if(i == 3){
|
1014
|
+
res = &buf;
|
1015
|
+
break;
|
1016
|
+
}
|
1017
|
+
}else if(SWIG_IsOK(SWIG_ConvertPtr(
|
1018
|
+
res_hook, (void **)&res, $descriptor(System_XYZ<FloatT, WGS84> *), 0))){
|
1019
|
+
res = &(buf = *res);
|
1020
|
+
break;
|
1021
|
+
}
|
1022
|
+
throw std::runtime_error(
|
1023
|
+
std::string("System_XYZ or [d * 3] is expected (d: " %str(FloatT) "), however ")
|
1024
|
+
.append(inspect_str(res_hook)));
|
1025
|
+
}while(false);
|
1026
|
+
#endif
|
1027
|
+
return res;
|
1028
|
+
}
|
913
1029
|
}
|
914
1030
|
%fragment("hook"{GPS_Solver<FloatT>});
|
1031
|
+
%ignore update_correction;
|
1032
|
+
#ifdef SWIGRUBY
|
1033
|
+
%fragment("correction"{GPS_Solver<FloatT>}, "header",
|
1034
|
+
fragment=SWIG_From_frag(int),
|
1035
|
+
fragment=SWIG_Traits_frag(FloatT)){
|
1036
|
+
template<>
|
1037
|
+
VALUE GPS_Solver<FloatT>::update_correction(
|
1038
|
+
const bool &update, const VALUE &hash){
|
1039
|
+
typedef range_correction_list_t list_t;
|
1040
|
+
static const VALUE k_root[] = {
|
1041
|
+
ID2SYM(rb_intern("gps_ionospheric")),
|
1042
|
+
ID2SYM(rb_intern("gps_tropospheric")),
|
1043
|
+
ID2SYM(rb_intern("sbas_ionospheric")),
|
1044
|
+
ID2SYM(rb_intern("sbas_tropospheric")),
|
1045
|
+
};
|
1046
|
+
static const VALUE k_opt(ID2SYM(rb_intern("options")));
|
1047
|
+
static const VALUE k_f_10_7(ID2SYM(rb_intern("f_10_7")));
|
1048
|
+
static const VALUE k_known(ID2SYM(rb_intern("known")));
|
1049
|
+
struct {
|
1050
|
+
VALUE sym;
|
1051
|
+
list_t::mapped_type::value_type obj;
|
1052
|
+
} item[] = {
|
1053
|
+
{ID2SYM(rb_intern("no_correction")), &base_t::no_correction},
|
1054
|
+
{ID2SYM(rb_intern("klobuchar")), &this->gps.solver.ionospheric_klobuchar},
|
1055
|
+
{ID2SYM(rb_intern("ntcm_gl")), &this->gps.solver.ionospheric_ntcm_gl},
|
1056
|
+
{ID2SYM(rb_intern("hopfield")), &this->gps.solver.tropospheric_simplified},
|
1057
|
+
{ID2SYM(rb_intern("sbas_igp")), &this->sbas.solver.ionospheric_sbas},
|
1058
|
+
{ID2SYM(rb_intern("sbas_tropo")), &this->sbas.solver.tropospheric_sbas},
|
1059
|
+
};
|
1060
|
+
list_t input;
|
1061
|
+
if(update){
|
1062
|
+
if(!RB_TYPE_P(hash, T_HASH)){
|
1063
|
+
throw std::runtime_error(
|
1064
|
+
std::string("Hash is expected, however ").append(inspect_str(hash)));
|
1065
|
+
}
|
1066
|
+
for(std::size_t i(0); i < sizeof(k_root) / sizeof(k_root[0]); ++i){
|
1067
|
+
VALUE ary = rb_hash_lookup(hash, k_root[i]);
|
1068
|
+
if(NIL_P(ary)){continue;}
|
1069
|
+
if(!RB_TYPE_P(ary, T_ARRAY)){
|
1070
|
+
ary = rb_ary_new_from_values(1, &ary);
|
1071
|
+
}
|
1072
|
+
for(int j(0); j < RARRAY_LEN(ary); ++j){
|
1073
|
+
std::size_t k(0);
|
1074
|
+
VALUE v(rb_ary_entry(ary, j));
|
1075
|
+
for(; k < sizeof(item) / sizeof(item[0]); ++k){
|
1076
|
+
if(v == item[k].sym){break;}
|
1077
|
+
}
|
1078
|
+
if(k >= sizeof(item) / sizeof(item[0])){
|
1079
|
+
continue; // TODO other than symbol
|
1080
|
+
}
|
1081
|
+
input[i].push_back(item[k].obj);
|
1082
|
+
}
|
1083
|
+
}
|
1084
|
+
VALUE opt(rb_hash_lookup(hash, k_opt));
|
1085
|
+
if(RB_TYPE_P(opt, T_HASH)){
|
1086
|
+
swig::asval(rb_hash_lookup(opt, k_f_10_7), // ntcm_gl
|
1087
|
+
&this->gps.solver.ionospheric_ntcm_gl.f_10_7);
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
list_t output(update_correction(update, input));
|
1091
|
+
VALUE res = rb_hash_new();
|
1092
|
+
for(list_t::const_iterator it(output.begin()), it_end(output.end());
|
1093
|
+
it != it_end; ++it){
|
1094
|
+
VALUE k;
|
1095
|
+
if((it->first < 0) || (it->first >= (int)(sizeof(k_root) / sizeof(k_root[0])))){
|
1096
|
+
k = SWIG_From(int)(it->first);
|
1097
|
+
}else{
|
1098
|
+
k = k_root[it->first];
|
1099
|
+
}
|
1100
|
+
VALUE v = rb_ary_new();
|
1101
|
+
for(list_t::mapped_type::const_iterator
|
1102
|
+
it2(it->second.begin()), it2_end(it->second.end());
|
1103
|
+
it2 != it2_end; ++it2){
|
1104
|
+
std::size_t i(0);
|
1105
|
+
for(; i < sizeof(item) / sizeof(item[0]); ++i){
|
1106
|
+
if(*it2 == item[i].obj){break;}
|
1107
|
+
}
|
1108
|
+
if(i >= sizeof(item) / sizeof(item[0])){
|
1109
|
+
continue; // TODO other than built-in corrector
|
1110
|
+
}
|
1111
|
+
rb_ary_push(v, item[i].sym);
|
1112
|
+
}
|
1113
|
+
rb_hash_aset(res, k, v);
|
1114
|
+
}
|
1115
|
+
{ // common options
|
1116
|
+
VALUE opt = rb_hash_new();
|
1117
|
+
rb_hash_aset(res, k_opt, opt);
|
1118
|
+
rb_hash_aset(opt, k_f_10_7, // ntcm_gl
|
1119
|
+
swig::from(this->gps.solver.ionospheric_ntcm_gl.f_10_7));
|
1120
|
+
}
|
1121
|
+
{ // known models
|
1122
|
+
VALUE ary = rb_ary_new_capa((int)(sizeof(item) / sizeof(item[0])));
|
1123
|
+
for(std::size_t i(0); i < sizeof(item) / sizeof(item[0]); ++i){
|
1124
|
+
rb_ary_push(ary, item[i].sym);
|
1125
|
+
}
|
1126
|
+
rb_hash_aset(res, k_known, ary);
|
1127
|
+
}
|
1128
|
+
return res;
|
1129
|
+
}
|
1130
|
+
}
|
1131
|
+
%fragment("correction"{GPS_Solver<FloatT>});
|
1132
|
+
%rename("correction") get_correction;
|
1133
|
+
%rename("correction=") set_correction;
|
1134
|
+
VALUE get_correction() const {
|
1135
|
+
return const_cast<GPS_Solver<FloatT> *>(self)->update_correction(false, Qnil);
|
1136
|
+
}
|
1137
|
+
VALUE set_correction(VALUE hash){
|
1138
|
+
return self->update_correction(true, hash);
|
1139
|
+
}
|
1140
|
+
#endif
|
915
1141
|
}
|
916
1142
|
%inline {
|
917
1143
|
template <class FloatT>
|
@@ -927,6 +1153,12 @@ struct GPS_Solver
|
|
927
1153
|
GPS_SinglePositioning<FloatT> solver;
|
928
1154
|
gps_t() : space_node(), options(), solver(space_node) {}
|
929
1155
|
} gps;
|
1156
|
+
struct sbas_t {
|
1157
|
+
SBAS_SpaceNode<FloatT> space_node;
|
1158
|
+
SBAS_SolverOptions<FloatT> options;
|
1159
|
+
SBAS_SinglePositioning<FloatT> solver;
|
1160
|
+
sbas_t() : space_node(), options(), solver(space_node) {}
|
1161
|
+
} sbas;
|
930
1162
|
SWIG_Object hooks;
|
931
1163
|
#ifdef SWIGRUBY
|
932
1164
|
static void mark(void *ptr){
|
@@ -935,17 +1167,36 @@ struct GPS_Solver
|
|
935
1167
|
rb_gc_mark(solver->hooks);
|
936
1168
|
}
|
937
1169
|
#endif
|
938
|
-
GPS_Solver() : super_t(), gps(), hooks() {
|
1170
|
+
GPS_Solver() : super_t(), gps(), sbas(), hooks() {
|
939
1171
|
#ifdef SWIGRUBY
|
940
1172
|
hooks = rb_hash_new();
|
941
1173
|
#endif
|
1174
|
+
typename base_t::range_correction_t ionospheric, tropospheric;
|
1175
|
+
ionospheric.push_back(&sbas.solver.ionospheric_sbas);
|
1176
|
+
ionospheric.push_back(&gps.solver.ionospheric_klobuchar);
|
1177
|
+
tropospheric.push_back(&sbas.solver.tropospheric_sbas);
|
1178
|
+
tropospheric.push_back(&gps.solver.tropospheric_simplified);
|
1179
|
+
gps.solver.ionospheric_correction
|
1180
|
+
= sbas.solver.ionospheric_correction
|
1181
|
+
= ionospheric;
|
1182
|
+
gps.solver.tropospheric_correction
|
1183
|
+
= sbas.solver.tropospheric_correction
|
1184
|
+
= tropospheric;
|
942
1185
|
}
|
943
1186
|
GPS_SpaceNode<FloatT> &gps_space_node() {return gps.space_node;}
|
944
1187
|
GPS_SolverOptions<FloatT> &gps_options() {return gps.options;}
|
1188
|
+
SBAS_SpaceNode<FloatT> &sbas_space_node() {return sbas.space_node;}
|
1189
|
+
SBAS_SolverOptions<FloatT> &sbas_options() {return sbas.options;}
|
945
1190
|
const base_t &select_solver(
|
946
1191
|
const typename base_t::prn_t &prn) const {
|
947
1192
|
if(prn > 0 && prn <= 32){return gps.solver;}
|
948
|
-
return
|
1193
|
+
if(prn >= 120 && prn <= 158){return sbas.solver;}
|
1194
|
+
if(prn > 192 && prn <= 202){return gps.solver;}
|
1195
|
+
// call order: base_t::solve => this returned by select()
|
1196
|
+
// => relative_property() => select_solver()
|
1197
|
+
// For not supported satellite, call loop prevention is required.
|
1198
|
+
static const base_t dummy;
|
1199
|
+
return dummy;
|
949
1200
|
}
|
950
1201
|
virtual typename base_t::relative_property_t relative_property(
|
951
1202
|
const typename base_t::prn_t &prn,
|
@@ -957,9 +1208,7 @@ struct GPS_Solver
|
|
957
1208
|
virtual typename base_t::xyz_t *satellite_position(
|
958
1209
|
const typename base_t::prn_t &prn,
|
959
1210
|
const typename base_t::gps_time_t &time,
|
960
|
-
typename base_t::xyz_t &res) const
|
961
|
-
return select_solver(prn).satellite_position(prn, time, res);
|
962
|
-
}
|
1211
|
+
typename base_t::xyz_t &res) const;
|
963
1212
|
virtual bool update_position_solution(
|
964
1213
|
const typename base_t::geometric_matrices_t &geomat,
|
965
1214
|
typename base_t::user_pvt_t &res) const;
|
@@ -968,8 +1217,44 @@ struct GPS_Solver
|
|
968
1217
|
const GPS_Time<FloatT> &receiver_time) const {
|
969
1218
|
const_cast<gps_t &>(gps).space_node.update_all_ephemeris(receiver_time);
|
970
1219
|
const_cast<gps_t &>(gps).solver.update_options(gps.options);
|
1220
|
+
const_cast<sbas_t &>(sbas).space_node.update_all_ephemeris(receiver_time);
|
1221
|
+
const_cast<sbas_t &>(sbas).solver.update_options(sbas.options);
|
971
1222
|
return super_t::solve().user_pvt(measurement.items, receiver_time);
|
972
1223
|
}
|
1224
|
+
typedef
|
1225
|
+
std::map<int, std::vector<const typename base_t::range_corrector_t *> >
|
1226
|
+
range_correction_list_t;
|
1227
|
+
range_correction_list_t update_correction(
|
1228
|
+
const bool &update,
|
1229
|
+
const range_correction_list_t &list = range_correction_list_t()){
|
1230
|
+
range_correction_list_t res;
|
1231
|
+
typename base_t::range_correction_t *root[] = {
|
1232
|
+
&gps.solver.ionospheric_correction,
|
1233
|
+
&gps.solver.tropospheric_correction,
|
1234
|
+
&sbas.solver.ionospheric_correction,
|
1235
|
+
&sbas.solver.tropospheric_correction,
|
1236
|
+
};
|
1237
|
+
for(std::size_t i(0); i < sizeof(root) / sizeof(root[0]); ++i){
|
1238
|
+
do{
|
1239
|
+
if(!update){break;}
|
1240
|
+
typename range_correction_list_t::const_iterator it(list.find(i));
|
1241
|
+
if(it == list.end()){break;}
|
1242
|
+
root[i]->clear();
|
1243
|
+
for(typename range_correction_list_t::mapped_type::const_iterator
|
1244
|
+
it2(it->second.begin()), it2_end(it->second.end());
|
1245
|
+
it2 != it2_end; ++it2){
|
1246
|
+
root[i]->push_back(*it2);
|
1247
|
+
}
|
1248
|
+
}while(false);
|
1249
|
+
for(typename base_t::range_correction_t::const_iterator
|
1250
|
+
it(root[i]->begin()), it_end(root[i]->end());
|
1251
|
+
it != it_end; ++it){
|
1252
|
+
res[i].push_back(*it);
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
return res;
|
1256
|
+
}
|
1257
|
+
SWIG_Object update_correction(const bool &update, const SWIG_Object &hash);
|
973
1258
|
};
|
974
1259
|
}
|
975
1260
|
|
@@ -1091,6 +1376,7 @@ struct RINEX_Observation {};
|
|
1091
1376
|
|
1092
1377
|
#undef MAKE_ACCESSOR
|
1093
1378
|
#undef MAKE_VECTOR2ARRAY
|
1379
|
+
#undef MAKE_ARRAY_INPUT
|
1094
1380
|
|
1095
1381
|
%define CONCRETIZE(type)
|
1096
1382
|
%template(Time) GPS_Time<type>;
|
@@ -1099,12 +1385,17 @@ struct RINEX_Observation {};
|
|
1099
1385
|
%template(Ephemeris) GPS_Ephemeris<type>;
|
1100
1386
|
%template(PVT) GPS_User_PVT<type>;
|
1101
1387
|
%template(Measurement) GPS_Measurement<type>;
|
1388
|
+
%template(SolverOptionsCommon) GPS_SolverOptions_Common<type>;
|
1102
1389
|
%template(SolverOptions) GPS_SolverOptions<type>;
|
1103
1390
|
#if defined(SWIGRUBY)
|
1104
1391
|
%markfunc GPS_Solver<type> "GPS_Solver<type>::mark";
|
1105
1392
|
#endif
|
1106
1393
|
%template(Solver) GPS_Solver<type>;
|
1107
1394
|
|
1395
|
+
%template(Ephemeris_SBAS) SBAS_Ephemeris<type>;
|
1396
|
+
%template(SpaceNode_SBAS) SBAS_SpaceNode<type>;
|
1397
|
+
%template(SolverOptions_SBAS) SBAS_SolverOptions<type>;
|
1398
|
+
|
1108
1399
|
%template(RINEX_Observation) RINEX_Observation<type>;
|
1109
1400
|
%enddef
|
1110
1401
|
|