gps_pvt 0.10.3 → 0.10.4

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 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