gps_pvt 0.1.3 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/gps_pvt.svg)](https://badge.fury.io/rb/gps_pvt)
|
8
8
|
[![Ruby](https://github.com/fenrir-naru/gps_pvt/actions/workflows/main.yml/badge.svg)](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
|