gps_pvt 0.1.3 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +28 -6
- data/Rakefile +7 -4
- data/ext/ninja-scan-light/tool/navigation/GPS.h +1 -1
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +263 -103
- data/lib/gps_pvt/receiver.rb +83 -13
- data/lib/gps_pvt/version.rb +1 -1
- metadata +6 -7
- data/gps_pvt.gemspec +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b05eabd6a7d771c772d6146722153e6ae2e9292ed9f41892e8285a2aa7d2f4ee
|
|
4
|
+
data.tar.gz: e8566da7fc64f0f330771ee8420bef47dfd819ae703d5ef46fe0bcf310ddf11f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56054b607dc23efa2826f02ab117fc07080e447a3775215ea642aa4e77f481929ec0f8d66a4634c873cb453c9a5e9fc36fe73c528c361e50aaa844ebf385506f
|
|
7
|
+
data.tar.gz: 4d47da0ba28881434c03ef1eb270c5d3e20dc17e5abbb222bb076ae2c2c8295a973b38764e7616e6f4adcd102904ed21389397a52ec3f91853c938e98464248e
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
GPS_PVT is a Ruby GPS (Global positioning system) PVT (position, velocity and time) solver. It accepts RINEX NAV and OBS files in addition to u-blox ubx format. Its significant features are easy to use with highly flexibility to customize internal solver behavior such as weight for each available satellite.
|
|
4
4
|
|
|
5
|
-
The PVT solution is obtained with a stand alone positioning (i.e. neither differential nor kinematic) with least square. Its main internal codes are derived from ones of [ninja-scan-light](https://github.com/fenrir-naru/ninja-scan-light) having capability to calculate tightly-coupled GNSS/INS integrated solution. These codes are written by C++, and wrapped by [SWIG](http://www.swig.org/).
|
|
5
|
+
The PVT solution is obtained with a stand alone positioning (i.e. neither differential nor kinematic) with application of least square to each snapshot. Its main internal codes are derived from ones of [ninja-scan-light](https://github.com/fenrir-naru/ninja-scan-light) having capability to calculate tightly-coupled GNSS/INS integrated solution. These codes are written by C++, and wrapped by [SWIG](http://www.swig.org/).
|
|
6
6
|
|
|
7
7
|
[](https://badge.fury.io/rb/gps_pvt)
|
|
8
8
|
[](https://github.com/fenrir-naru/gps_pvt/actions/workflows/main.yml)
|
|
@@ -46,7 +46,8 @@ receiver.parse_rinex_obs(rinex_obs_file)
|
|
|
46
46
|
|
|
47
47
|
# Or precise control of outputs
|
|
48
48
|
receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
|
|
49
|
-
meas # => measurement, array of [prn, key, value]; key is represented by GPS_PVT::GPS::Measurement::L1_PSEUDORANGE
|
|
49
|
+
meas.to_a # => measurement, array of [prn, key, value]; key is represented by GPS_PVT::GPS::Measurement::L1_PSEUDORANGE; instead of .to_a, .to_hash returns {prn => {key => value, ...}, ...}
|
|
50
|
+
|
|
50
51
|
pvt # => PVT solution, all properties are shown by pvt.methods
|
|
51
52
|
# for example
|
|
52
53
|
if(pvt.position_solved?){
|
|
@@ -59,6 +60,9 @@ receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
|
|
|
59
60
|
pvt.receiver_error_rate # clock error rate in m/s
|
|
60
61
|
}
|
|
61
62
|
pvt.used_satellite_list # array of used, i.e., visible and weight>0, satellite
|
|
63
|
+
pvt.azimuth # azimuth angle [rad] to used satellites in Hash {prn => value, ...}
|
|
64
|
+
pvt.elevation # elevation angle [rad]
|
|
65
|
+
|
|
62
66
|
pvt.G # design matrix in Earth-centered-Earth-fixed (ECEF); .to_a returns double array converted from matrix. its row corresponds to one of used_satellite_list
|
|
63
67
|
pvt.G_enu # design matrix in East-North-Up (ENU)
|
|
64
68
|
pvt.W # weight for each satellite
|
|
@@ -68,16 +72,34 @@ receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
# Customize solution
|
|
71
|
-
receiver.solver.
|
|
72
|
-
receiver.solver.
|
|
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
|
|
73
78
|
receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
|
|
74
79
|
# control weight per satellite per iteration
|
|
75
80
|
weight, range_c, range_r, rate_rel_neg, *los_neg = rel_prop # relative property
|
|
76
|
-
|
|
81
|
+
# rcv_e, t_arv, usr_pos, usr_vel are temporary solution of
|
|
82
|
+
# receiver clock error [m], time of arrival [s], user position and velocity in ECEF, respectively.
|
|
83
|
+
|
|
84
|
+
weight = 1 # same as default; identical weight for each visible satellite
|
|
77
85
|
# or weight based on elevation
|
|
78
86
|
# elv = GPS_PVT::Coordinate::ENU::relative_rel(GPS_PVT::Coordinate::XYZ::new(*los_neg), usr_pos).elevation
|
|
79
87
|
# weight = (Math::sin(elv)/0.8)**2
|
|
80
|
-
|
|
88
|
+
|
|
89
|
+
[weight, range_c, range_r, rate_rel_neg] + los_neg # must return relative property
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Dynamic customization of weight for each epoch
|
|
93
|
+
(class << receiver; self; end).instance_eval{ # do before parse_XXX
|
|
94
|
+
alias_method(:run_orig, :run)
|
|
95
|
+
define_method(:run){|meas, t_meas, &b|
|
|
96
|
+
meas # observation, same as the 2nd argument of parse_XXX
|
|
97
|
+
receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
|
|
98
|
+
# Do something based on meas, t_meas.
|
|
99
|
+
rel_prop
|
|
100
|
+
}
|
|
101
|
+
run_orig(meas, t_meas, &b)
|
|
102
|
+
}
|
|
81
103
|
}
|
|
82
104
|
```
|
|
83
105
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 -= (
|
|
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
|
|
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))
|
|
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
|
|
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))
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
274
|
-
|
|
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
|
|
525
|
+
void seek_next_v2_gps() {
|
|
484
526
|
char buf[256];
|
|
485
527
|
|
|
486
|
-
for(int i
|
|
487
|
-
if(super_t::src.
|
|
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
|
|
555
|
+
void seek_next_v2_glonass() {
|
|
512
556
|
char buf[256];
|
|
513
557
|
|
|
514
|
-
for(int i
|
|
515
|
-
if(super_t::src.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
642
|
-
|
|
643
|
-
|
|
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();
|
|
646
|
-
|
|
647
|
-
|
|
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
|
|
968
|
-
{super_t::template conv_t<
|
|
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
|
|
971
|
-
{super_t::template conv_t<
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
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
|
|
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>
|
data/lib/gps_pvt/receiver.rb
CHANGED
|
@@ -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] *
|
|
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,17 +166,42 @@ class Receiver
|
|
|
137
166
|
}.call(@solver.gps_options)
|
|
138
167
|
end
|
|
139
168
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
+
}
|
|
144
190
|
|
|
191
|
+
def run(meas, t_meas, ref_pos = @base_station)
|
|
192
|
+
=begin
|
|
193
|
+
$stderr.puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %d:%d:%d UTC"%[*t_meas.c_tm]})"
|
|
145
194
|
meas.to_a.collect{|prn, k, v| prn}.uniq.each{|prn|
|
|
146
|
-
eph =
|
|
195
|
+
eph = @solver.gps_space_node.ephemeris(prn)
|
|
147
196
|
$stderr.puts "XYZ(PRN:#{prn}): #{eph.constellation(t_meas)[0].to_a} (iodc: #{eph.iodc}, iode: #{eph.iode})"
|
|
148
|
-
}
|
|
197
|
+
}
|
|
198
|
+
=end
|
|
149
199
|
|
|
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)
|
|
@@ -242,9 +296,15 @@ class Receiver
|
|
|
242
296
|
{
|
|
243
297
|
:L1_PSEUDORANGE => [16, 8, "E"],
|
|
244
298
|
:L1_DOPPLER => [24, 4, "e"],
|
|
299
|
+
:L1_CARRIER_PHASE => [8, 8, "E"],
|
|
300
|
+
:L1_SIGNAL_STRENGTH_dBHz => [30, 1, "c"],
|
|
245
301
|
}.each{|k, prop|
|
|
246
|
-
meas.add(prn,
|
|
302
|
+
meas.add(prn, k, loader.call(*prop))
|
|
247
303
|
}
|
|
304
|
+
# bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
|
|
305
|
+
# between previous and current observation, which maps negative lock seconds
|
|
306
|
+
meas.add(prn, :L1_LOCK_SEC,
|
|
307
|
+
(packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
|
|
248
308
|
}
|
|
249
309
|
after_run.call(run(meas, t_meas), [meas, t_meas])
|
|
250
310
|
when [0x02, 0x15] # RXM-RAWX
|
|
@@ -270,9 +330,15 @@ class Receiver
|
|
|
270
330
|
}],
|
|
271
331
|
:L1_DOPPLER => [32, 4, "e"],
|
|
272
332
|
:L1_DOPPLER_SIGMA => [45, 1, nil, proc{|v| 2E-3 * (v[0] & 0xF)}],
|
|
333
|
+
:L1_CARRIER_PHASE => [24, 8, "E", proc{|v| (trk_stat & 0x2 == 0x2) ? v : nil}],
|
|
334
|
+
:L1_CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
|
|
335
|
+
(trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
|
|
336
|
+
}],
|
|
337
|
+
:L1_SIGNAL_STRENGTH_dBHz => [42, 1, "C"],
|
|
338
|
+
:L1_LOCK_SEC => [40, 2, "v", proc{|v| 1E-3 * v}],
|
|
273
339
|
}.each{|k, prop|
|
|
274
340
|
next unless v = loader.call(*prop)
|
|
275
|
-
meas.add(svid,
|
|
341
|
+
meas.add(svid, k, v)
|
|
276
342
|
}
|
|
277
343
|
}
|
|
278
344
|
after_run.call(run(meas, t_meas), [meas, t_meas])
|
|
@@ -314,9 +380,13 @@ class Receiver
|
|
|
314
380
|
types ||= (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
|
|
315
381
|
case type_
|
|
316
382
|
when "C1", "C1C"
|
|
317
|
-
[i,
|
|
383
|
+
[i, :L1_PSEUDORANGE]
|
|
384
|
+
when "L1", "L1C"
|
|
385
|
+
[i, :L1_CARRIER_PHASE]
|
|
318
386
|
when "D1", "D1C"
|
|
319
|
-
[i,
|
|
387
|
+
[i, :L1_DOPPLER]
|
|
388
|
+
when "S1", "S1C"
|
|
389
|
+
[i, :L1_SIGNAL_STRENGTH_dBHz]
|
|
320
390
|
else
|
|
321
391
|
nil
|
|
322
392
|
end
|
data/lib/gps_pvt/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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
|