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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d6b65fbf88b765e83cf8d2341d42e6919c8b79be5e98d2c493ca23ec5445e8e
4
- data.tar.gz: 11bd177fd1cd8ba4ecf5134b64977f6e9a3b0e08cff3a290d1bca6897958f23b
3
+ metadata.gz: 60413c3add439fa66e0ff908f43f0049ad306fcefca50e2c485bc28790597143
4
+ data.tar.gz: 788f430151ca35a95abd4f96e4f30239e9f53768014af9ddc5897305b25b7fb9
5
5
  SHA512:
6
- metadata.gz: e20cfb1dd15fe7ff0ff7320e6acc32d8966c1ad6d67f3203e27da757fb095e512085bdea4e7a9ac90282df8a4ea92235cb94a3b6210f6d1b8938c17f505f9ee3
7
- data.tar.gz: 6897b4cdedcfd23edb23bdaf57f6d008f9c2562107dc7ccbf1788e7249637b5a1a7fc79b0af09d68a967aed5d86811947b4f29b2465b4db8835727adf69af4a0
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
- obs = Queue::new
74
- rcv.define_singleton_method(:run){|meas, t_meas, *args|
75
- obs << [t_meas, meas]
76
- nil
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
- obs = proc{
125
+ outputs = proc{
117
126
  tmp = []
118
- tmp << obs.pop until obs.empty?
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, 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
- gen_list = []
378
- gen_list << gen_gpstime unless misc_options[:ubx_rawx]
379
- gen_list << (misc_options[:ubx_rawx] ? gen_sfrbx : gen_sfrb) if misc_options[:broadcast_data]
380
- gen_list << (misc_options[:ubx_rawx] ? gen_rawx : gen_raw)
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 obs.empty? do
385
- t_meas, meas = obs.pop
386
- meas2 = meas.to_hash
387
- gen_list.each{|gen| print gen.call(t_meas, meas2)}
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
@@ -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
@@ -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] * 4 unless pvt.velocity_solved?
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
  }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.10.3"
4
+ VERSION = "0.10.4"
5
5
 
6
6
  def GPS_PVT.version_compare(a, b)
7
7
  Gem::Version::new(a) <=> Gem::Version::new(b)
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.3
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-10-24 00:00:00.000000000 Z
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