gps_pvt 0.10.3 → 0.10.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/gps2ubx +111 -17
- data/lib/gps_pvt/pvt.rb +83 -0
- data/lib/gps_pvt/receiver.rb +1 -1
- data/lib/gps_pvt/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60413c3add439fa66e0ff908f43f0049ad306fcefca50e2c485bc28790597143
|
4
|
+
data.tar.gz: 788f430151ca35a95abd4f96e4f30239e9f53768014af9ddc5897305b25b7fb9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c0ddc7d6a4f589cf3b3692eacde62405c78890f20cadf5abde07d189ae35bda83fd0b9e905728e4fecaf111aeee14bfb615166ef72d355fcac1a3368330ee89
|
7
|
+
data.tar.gz: 457d23f7e4032549961e499638403b3d01d681a8051231616ee2dde5183967c119028bd2a4475f2fe1c1e3ae2ffda61729162ced5d30b33113bfe21c161c188e
|
data/exe/gps2ubx
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'gps_pvt'
|
4
4
|
require 'uri'
|
5
5
|
require 'gps_pvt/ubx'
|
6
|
+
require 'gps_pvt/pvt'
|
6
7
|
|
7
8
|
# Convert file(s) to ubx format
|
8
9
|
# TODO currently only RINEX observation file is supported.
|
@@ -25,6 +26,7 @@ options = []
|
|
25
26
|
misc_options = {
|
26
27
|
:broadcast_data => false,
|
27
28
|
:ubx_rawx => false,
|
29
|
+
:ubx_nav => false,
|
28
30
|
:eph_interval => 60 * 5,
|
29
31
|
}
|
30
32
|
|
@@ -32,7 +34,7 @@ misc_options = {
|
|
32
34
|
files = ARGV.collect{|arg|
|
33
35
|
next [arg, nil] unless arg =~ /^--([^=]+)=?/
|
34
36
|
k, v = [$1.downcase.to_sym, $']
|
35
|
-
next [v, k] if [:rinex_nav, :rinex_obs, :ubx, :rtcm3].include?(k) # file type
|
37
|
+
next [v, k] if [:rinex_nav, :rinex_obs, :ubx, :rtcm3, :pvt_csv].include?(k) # file type
|
36
38
|
options << [$1.to_sym, $']
|
37
39
|
nil
|
38
40
|
}.compact
|
@@ -43,6 +45,7 @@ files.collect!{|fname, ftype|
|
|
43
45
|
when /\.\d{2}[nhqg](?:\.gz)?$/; :rinex_nav
|
44
46
|
when /\.\d{2}o(?:\.gz)?$/; :rinex_obs
|
45
47
|
when /\.ubx$/; :ubx
|
48
|
+
when /\.csv$/; misc_options[:ubx_nav] = true; :pvt_csv
|
46
49
|
end
|
47
50
|
if (!(uri = URI::parse(fname)).instance_of?(URI::Generic) rescue false) then
|
48
51
|
ftype ||= case uri
|
@@ -57,7 +60,7 @@ files.collect!{|fname, ftype|
|
|
57
60
|
|
58
61
|
options.reject!{|opt|
|
59
62
|
case opt[0]
|
60
|
-
when :ubx_rawx, :broadcast_data, :eph_interval
|
63
|
+
when :ubx_rawx, :ubx_nav, :broadcast_data, :eph_interval
|
61
64
|
misc_options[opt[0]] = opt[1]
|
62
65
|
true
|
63
66
|
when :online_ephemeris
|
@@ -70,10 +73,13 @@ options.reject!{|opt|
|
|
70
73
|
|
71
74
|
rcv = GPS_PVT::Receiver::new(options)
|
72
75
|
|
73
|
-
|
74
|
-
rcv.
|
75
|
-
|
76
|
-
|
76
|
+
outputs = Queue::new # [[time, item1, item2, ...], ...]
|
77
|
+
rcv.instance_eval{
|
78
|
+
nav_task = misc_options[:ubx_nav] ? method(:run) : proc{}
|
79
|
+
define_singleton_method(:run){|meas, t_meas, *args|
|
80
|
+
outputs << [t_meas, meas, nav_task.call(meas, t_meas, *args)].compact
|
81
|
+
nil
|
82
|
+
}
|
77
83
|
}
|
78
84
|
|
79
85
|
proc{|src|
|
@@ -106,6 +112,9 @@ threads = files.collect{|fname, ftype|
|
|
106
112
|
when :rtcm3; proc{rcv.parse_rtcm3(fname){}}
|
107
113
|
when :supl; proc{rcv.parse_supl(fname)}
|
108
114
|
when :rinex_nav; proc{}
|
115
|
+
when :pvt_csv; proc{GPS_PVT::GPS::PVT_minimal::parse_csv(fname){|t, pvt|
|
116
|
+
outputs << [t, pvt]
|
117
|
+
}}
|
109
118
|
end
|
110
119
|
case fname
|
111
120
|
when URI::Ntrip, URI::Supl; Thread::new(&task)
|
@@ -113,9 +122,9 @@ threads = files.collect{|fname, ftype|
|
|
113
122
|
end
|
114
123
|
}.compact
|
115
124
|
|
116
|
-
|
125
|
+
outputs = proc{
|
117
126
|
tmp = []
|
118
|
-
tmp <<
|
127
|
+
tmp << outputs.pop until outputs.empty?
|
119
128
|
tmp
|
120
129
|
}.call.sort!{|a, b| b[0] <=> a[0]} if threads.empty? # Sort by measurement time
|
121
130
|
|
@@ -124,7 +133,7 @@ gen_gpstime = proc{
|
|
124
133
|
tmpl = [0xB5, 0x62, 0x01, 0x20, 16, 0]
|
125
134
|
gpst = GPS_PVT::GPS::Time
|
126
135
|
t_next = gpst::new(*gpst::leap_second_events.find{|wn, sec, leap| leap == 1}[0..1])
|
127
|
-
proc{|t_meas
|
136
|
+
proc{|t_meas|
|
128
137
|
next nil if t_meas < t_next
|
129
138
|
t_next = t_meas + 60 # 1 min. interval
|
130
139
|
ubx = tmpl.clone
|
@@ -374,17 +383,102 @@ gen_rawx = proc{|t_meas, meas| # Convert to RXM-RAWX(0x15)
|
|
374
383
|
GPS_PVT::UBX::update(ubx).pack("C*")
|
375
384
|
}
|
376
385
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
386
|
+
gen_nav = proc{|t_meas, pvt|
|
387
|
+
t_msec = (t_meas.seconds * 1E3).round
|
388
|
+
packet = []
|
389
|
+
|
390
|
+
proc{ # Convert to NAV-SOL (0x01-0x06)
|
391
|
+
ubx_sol = [0xB5, 0x62, 0x01, 0x06, 52, 0]
|
392
|
+
ubx_sol += [
|
393
|
+
t_msec,
|
394
|
+
0, # frac
|
395
|
+
t_meas.week, # week
|
396
|
+
if (pvt.position_solved? and pvt.velocity_solved?) then
|
397
|
+
[
|
398
|
+
0x03, # 3D-Fix
|
399
|
+
0x0D, # GPSfixOK, WKNSET, TOWSET
|
400
|
+
pvt.xyz.to_a.collect{|v| (v * 1E2).to_i}, # ECEF_XYZ [cm]
|
401
|
+
(Math::sqrt((pvt.hsigma ** 2) + (pvt.vsigma ** 2)) * 1E2).to_i, # 3D pos accuracy [cm]
|
402
|
+
(pvt.velocity.absolute(pvt.xyz) - pvt.xyz).to_a.collect{|v| (v * 1E2).to_i}, # ECEF_VXYZ [cm/s]
|
403
|
+
(pvt.vel_sigma * 1E2).to_i, # Speed accuracy [cm/s]
|
404
|
+
(pvt.pdop * 1E2).to_i, # PDOP [0.01]
|
405
|
+
]
|
406
|
+
else
|
407
|
+
[
|
408
|
+
0, # 3D-Fix
|
409
|
+
0x08, # TOWSET
|
410
|
+
[0] * 3, # ECEF_XYZ [cm]
|
411
|
+
0, # 3D pos accuracy [cm]
|
412
|
+
[0] * 3, # ECEF_VXYZ [cm/s]
|
413
|
+
0, # Speed accuracy [cm/s]
|
414
|
+
0, # PDOP
|
415
|
+
]
|
416
|
+
end,
|
417
|
+
0,
|
418
|
+
pvt.used_satellites,
|
419
|
+
0].flatten.pack('V2vc2l<3Vl<3VvC2V').unpack('C*')
|
420
|
+
packet += GPS_PVT::UBX::update(ubx_sol + [0, 0])
|
421
|
+
}.call
|
422
|
+
|
423
|
+
# Convert to NAV-POSLLH (0x01-0x02)
|
424
|
+
if pvt.position_solved? then
|
425
|
+
llh = pvt.llh
|
426
|
+
ubx_posllh = [0xB5, 0x62, 0x01, 0x02, 28, 0]
|
427
|
+
ubx_posllh += [
|
428
|
+
t_msec,
|
429
|
+
(llh.lng / Math::PI * 180 * 1E7).to_i, # Longitude [1E-7 deg]
|
430
|
+
(llh.lat / Math::PI * 180 * 1E7).to_i, # Latitude [1E-7 deg]
|
431
|
+
(llh.alt * 1E3).to_i, # WGS-84 altitude [mm]
|
432
|
+
(llh.alt * 1E3).to_i, # mean sea level TODO fix
|
433
|
+
(pvt.hsigma * 1E3).to_i, # HAcc [mm]
|
434
|
+
(pvt.vsigma * 1E3).to_i, # VAcc [mm]
|
435
|
+
].pack('V*').unpack("C*")
|
436
|
+
packet += GPS_PVT::UBX::update(ubx_posllh + [0, 0])
|
437
|
+
end
|
438
|
+
|
439
|
+
# Convert to NAV-VELNED (0x01-0x12)
|
440
|
+
if pvt.velocity_solved? then
|
441
|
+
vel = pvt.velocity
|
442
|
+
ubx_velned = [0xB5, 0x62, 0x01, 0x12, 36, 0]
|
443
|
+
ubx_velned += [
|
444
|
+
t_msec,
|
445
|
+
(vel.n * 1E2).to_i, # N speed [cm/s]
|
446
|
+
(vel.e * 1E2).to_i, # E speed [cm/s]
|
447
|
+
(vel.d * 1E2).to_i, # D speed [cm/s]
|
448
|
+
(vel.distance * 1E2).to_i, # 3D speed [cm/s]
|
449
|
+
(vel.horizontal * 1E2).to_i, # 2D speed [cm/s]
|
450
|
+
(vel.azimuth / Math::PI * 180 * 1E5).to_i, # Heading [1E-5 deg]
|
451
|
+
(pvt.vel_sigma * 1E2).to_i, # sAcc speed accuracy [cm/s]
|
452
|
+
(2 * 1E5).to_i, # cAcc heading accuracy [1E-5 deg] TODO
|
453
|
+
].pack('V*').unpack("C*")
|
454
|
+
packet += GPS_PVT::UBX::update(ubx_velned + [0, 0])
|
455
|
+
end
|
456
|
+
|
457
|
+
(gen_gpstime.call(t_meas) || '') + packet.pack('C*')
|
458
|
+
}
|
459
|
+
|
460
|
+
gen_list = Hash[*({
|
461
|
+
:Measurement => proc{
|
462
|
+
gen_ary = []
|
463
|
+
gen_ary << gen_gpstime unless misc_options[:ubx_rawx]
|
464
|
+
gen_ary << (misc_options[:ubx_rawx] ? gen_sfrbx : gen_sfrb) if misc_options[:broadcast_data]
|
465
|
+
gen_ary << (misc_options[:ubx_rawx] ? gen_rawx : gen_raw)
|
466
|
+
proc{|t_meas, meas|
|
467
|
+
meas = meas.to_hash
|
468
|
+
gen_ary.collect{|gen| gen.call(t_meas, meas)}.join
|
469
|
+
}
|
470
|
+
}.call,
|
471
|
+
:PVT => gen_nav,
|
472
|
+
:PVT_minimal => gen_nav,
|
473
|
+
}.collect{|k, v| [GPS_PVT::GPS.const_get(k), v]}.flatten(1))]
|
381
474
|
STDOUT.binmode
|
382
475
|
|
383
476
|
task_dump = proc{
|
384
|
-
until
|
385
|
-
t_meas,
|
386
|
-
|
387
|
-
|
477
|
+
until outputs.empty? do
|
478
|
+
t_meas, *items = outputs.pop
|
479
|
+
items.each{|item|
|
480
|
+
print gen_list[item.class].call(t_meas, item)
|
481
|
+
}
|
388
482
|
end
|
389
483
|
}
|
390
484
|
if threads.empty? then
|
data/lib/gps_pvt/pvt.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'gps_pvt/GPS'
|
2
|
+
require 'gps_pvt/Coordinate'
|
3
|
+
|
4
|
+
module GPS_PVT
|
5
|
+
class GPS::PVT_minimal
|
6
|
+
def initialize
|
7
|
+
# GLOBAL POSITIONING SYSTEM STANDARD POSITIONING SERVICE
|
8
|
+
# PERFORMANCE STANDARD 5th edition (Apr. 2020) 3.8
|
9
|
+
@hsigma = 8.0 # 95%
|
10
|
+
@vsigma = 13.0 # 95%
|
11
|
+
@pdop = 6.0 # 98% global
|
12
|
+
@vel_sigma = 0.2 # 85%
|
13
|
+
@use_satellites = 0 # unknown
|
14
|
+
end
|
15
|
+
def position_solved?; @xyz || @llh; end
|
16
|
+
def velocity_solved?; @velocity; end
|
17
|
+
def xyz; @xyz || @llh.xyz; end
|
18
|
+
def llh; @xyz ? @xyz.llh : @llh; end
|
19
|
+
def xyz=(args); @xyz = Coordinate::XYZ::new(*args); end
|
20
|
+
def llh=(args); @llh = Coordinate::LLH::new(*args); end
|
21
|
+
def velocity=(args); @velocity = Coordinate::ENU::new(*args); end
|
22
|
+
attr_reader :velocity
|
23
|
+
attr_accessor :hsigma, :vsigma, :pdop, :vel_sigma, :used_satellites
|
24
|
+
class <<self
|
25
|
+
def parse_csv(csv_fname, &b)
|
26
|
+
require_relative 'util'
|
27
|
+
$stderr.puts "Reading CSV file (%s) "%[csv_fname]
|
28
|
+
io = open(Util::get_txt(csv_fname), 'r')
|
29
|
+
|
30
|
+
header = io.readline.chomp.split(/ *, */)
|
31
|
+
idx_table = [
|
32
|
+
:week, [:tow, /^i?t(?:ime_)?o(?:f_)?w(?:eek)?/],
|
33
|
+
:year, :month, :mday, :hour, :min, [:sec, /^sec/],
|
34
|
+
[:lng, /^(?:long(?:itude)?|lng)/], [:lat, /^lat(?:itude)/],
|
35
|
+
[:alt, /^(?:alt(?:itude)?|h(?:$|eight|_))/], # assumption: [deg], [deg], [m]
|
36
|
+
:x, :y, :z, # ECEF xyz
|
37
|
+
:hsigma, :vsigma, :pdop,
|
38
|
+
[:vn, /^v(?:el)?_?n(?:orth)?/], [:ve, /^v(?:el)?_?e(?:ast)?/],
|
39
|
+
[:vd, /^v(?:el)?_?d(?:own)?/], [:vu, /^v(?:el)?_?u(?:p)?/],
|
40
|
+
:vx, :vy, :vz, # ECEF xyz
|
41
|
+
[:vel_sigma, /^v(?:el)?_sigma/],
|
42
|
+
[:used_satellites, /^(?:used_)?sat(?:ellite)?s/],
|
43
|
+
].collect{|k, re|
|
44
|
+
re ||= k.to_s
|
45
|
+
idx = header.find_index{|str| re === str}
|
46
|
+
idx && [k, idx]
|
47
|
+
}.compact
|
48
|
+
enum = Enumerator::new{|y|
|
49
|
+
io.each_line{|line|
|
50
|
+
values = line.chomp.split(/ *, */)
|
51
|
+
items = Hash[*(idx_table.collect{|k, idx|
|
52
|
+
v = values[idx]
|
53
|
+
(v == '') ? nil : [k, (Integer(v) rescue Float(v))]
|
54
|
+
}.compact.flatten(1))]
|
55
|
+
if items.include?(:week) then
|
56
|
+
t = GPS::Time::new(items[:week], items[:tow])
|
57
|
+
else
|
58
|
+
# UTC assumption, thus leap seconds must be added.
|
59
|
+
t = GPS::Time::new([:year, :month, :mday, :hour, :min, :sec].collect{|k| items[k]})
|
60
|
+
t += GPS::Time::guess_leap_seconds(t)
|
61
|
+
end
|
62
|
+
pvt = GPS::PVT_minimal::new
|
63
|
+
if items.include?(:lat) then
|
64
|
+
pvt.llh = ([:lat, :lng].collect{|k| Math::PI / 180 * items[k]} + [items[:alt]])
|
65
|
+
elsif items.include?(:xyz) then
|
66
|
+
pvt.xyz = [:x, :y, :z].collect{|k| items[k]}
|
67
|
+
end
|
68
|
+
if items.include?(:vn) then
|
69
|
+
pvt.velocity = ([:vn, :ve].collect{|k| items[k]} + [items[:vu] || -items[:vd]])
|
70
|
+
elsif items.include?(:vx) then
|
71
|
+
pvt.velocity = Coordinate::ENU::relative_rel(
|
72
|
+
Coordinate::XYZ::new(*([:vx, :vy, :vz].collect{|k| items[k]})),
|
73
|
+
pvt.llh)
|
74
|
+
end
|
75
|
+
[:pdop, :hsigma, :vsigma, :vel_sigma, :used_satellites].each{|k| pvt.send("#{k}=", items[k])}
|
76
|
+
y.yield(t, pvt)
|
77
|
+
}
|
78
|
+
}
|
79
|
+
b ? enum.each{|*res| b.call(*res)} : enum
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -50,7 +50,7 @@ class Receiver
|
|
50
50
|
}.call] + [[
|
51
51
|
[:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms, :vel_sigma],
|
52
52
|
proc{|pvt|
|
53
|
-
next [nil] *
|
53
|
+
next [nil] * 5 unless pvt.velocity_solved?
|
54
54
|
[:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
|
55
55
|
+ [pvt.receiver_error_rate, pvt.vel_sigma]
|
56
56
|
}
|
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.10.
|
4
|
+
version: 0.10.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fenrir(M.Naruoka)
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyserial
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/gps_pvt/asn1/asn1.y
|
181
181
|
- lib/gps_pvt/asn1/per.rb
|
182
182
|
- lib/gps_pvt/ntrip.rb
|
183
|
+
- lib/gps_pvt/pvt.rb
|
183
184
|
- lib/gps_pvt/receiver.rb
|
184
185
|
- lib/gps_pvt/receiver/agps.rb
|
185
186
|
- lib/gps_pvt/receiver/almanac.rb
|