gps_pvt 0.2.3 → 0.4.0
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 +35 -7
- data/Rakefile +2 -0
- data/exe/gps_pvt +65 -2
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +5898 -395
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +453 -429
- data/ext/ninja-scan-light/tool/algorithm/integral.h +91 -0
- data/ext/ninja-scan-light/tool/navigation/GLONASS.h +1270 -0
- data/ext/ninja-scan-light/tool/navigation/GLONASS_Solver.h +306 -0
- data/ext/ninja-scan-light/tool/navigation/GPS.h +7 -1
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +0 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +9 -2
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +389 -4
- data/ext/ninja-scan-light/tool/navigation/SBAS.h +0 -0
- data/ext/ninja-scan-light/tool/navigation/SBAS_Solver.h +0 -0
- data/ext/ninja-scan-light/tool/param/bit_array.h +0 -0
- data/ext/ninja-scan-light/tool/swig/GPS.i +310 -13
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +38 -15
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +7 -1
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +13 -3
- data/gps_pvt.gemspec +0 -0
- data/lib/gps_pvt/receiver.rb +101 -20
- data/lib/gps_pvt/version.rb +1 -1
- metadata +9 -6
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -7,15 +7,22 @@ require_relative 'GPS'
|
|
7
7
|
|
8
8
|
module GPS_PVT
|
9
9
|
class Receiver
|
10
|
+
|
11
|
+
GPS::Time.send(:define_method, :utc){ # send as work around of old Ruby
|
12
|
+
res = c_tm(GPS::Time::guess_leap_seconds(self))
|
13
|
+
res[-1] += (seconds % 1)
|
14
|
+
res
|
15
|
+
}
|
16
|
+
|
10
17
|
def self.pvt_items(opt = {})
|
11
18
|
opt = {
|
12
19
|
:system => [[:GPS, 1..32]],
|
13
20
|
:satellites => (1..32).to_a,
|
14
21
|
}.merge(opt)
|
15
22
|
[[
|
16
|
-
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :
|
23
|
+
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec_rcv_UTC],
|
17
24
|
proc{|pvt|
|
18
|
-
[:week, :seconds, :
|
25
|
+
[:week, :seconds, :utc].collect{|f| pvt.receiver_time.send(f)}.flatten
|
19
26
|
}
|
20
27
|
]] + [[
|
21
28
|
[:receiver_clock_error_meter, :longitude, :latitude, :height, :rel_E, :rel_N, :rel_U],
|
@@ -47,23 +54,31 @@ class Receiver
|
|
47
54
|
]] + [
|
48
55
|
[:used_satellites, proc{|pvt| pvt.used_satellites}],
|
49
56
|
] + opt[:system].collect{|sys, range|
|
50
|
-
|
51
|
-
|
57
|
+
range = range.kind_of?(Array) ? proc{
|
58
|
+
# check whether inputs can be converted to Range
|
59
|
+
next nil if range.empty?
|
60
|
+
a, b = range.minmax
|
61
|
+
((b - a) == (range.length - 1)) ? (a..b) : range
|
62
|
+
}.call : range
|
63
|
+
next nil unless range
|
64
|
+
bit_flip, label = case range
|
65
|
+
when Array
|
66
|
+
[proc{|res, i|
|
52
67
|
res[i] = "1" if i = range.index(i)
|
53
68
|
res
|
54
|
-
}
|
55
|
-
|
69
|
+
}, range.collect{|pen| pen & 0xFF}.reverse.join('+')]
|
70
|
+
when Range
|
56
71
|
base_prn = range.min
|
57
|
-
proc{|res, i|
|
72
|
+
[proc{|res, i|
|
58
73
|
res[i - base_prn] = "1" if range.include?(i)
|
59
74
|
res
|
60
|
-
}
|
75
|
+
}, [:max, :min].collect{|f| range.send(f) & 0xFF}.join('..')]
|
61
76
|
end
|
62
|
-
["#{sys}_PRN", proc{|pvt|
|
77
|
+
["#{sys}_PRN(#{label})", proc{|pvt|
|
63
78
|
pvt.used_satellite_list.inject("0" * range.size, &bit_flip) \
|
64
79
|
.scan(/.{1,8}/).join('_').reverse
|
65
80
|
}]
|
66
|
-
} + [[
|
81
|
+
}.compact + [[
|
67
82
|
opt[:satellites].collect{|prn, label|
|
68
83
|
[:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str|
|
69
84
|
"#{str}(#{label || prn})"
|
@@ -138,8 +153,11 @@ class Receiver
|
|
138
153
|
rel_prop
|
139
154
|
}
|
140
155
|
@debug = {}
|
141
|
-
[:gps_options, :sbas_options].
|
142
|
-
|
156
|
+
solver_opts = [:gps_options, :sbas_options, :glonass_options].collect{|target|
|
157
|
+
@solver.send(target)
|
158
|
+
}
|
159
|
+
solver_opts.each{|opt|
|
160
|
+
# default solver options
|
143
161
|
opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg (use satellite over horizon)
|
144
162
|
opt.residual_mask = 1E4 # 10 km (without residual filter, practically)
|
145
163
|
}
|
@@ -168,6 +186,13 @@ class Receiver
|
|
168
186
|
when :identical # same as default
|
169
187
|
next true
|
170
188
|
end
|
189
|
+
when :elevation_mask_deg
|
190
|
+
raise "Unknown elevation mask angle: #{v}" unless elv_deg = (Float(v) rescue nil)
|
191
|
+
$stderr.puts "Elevation mask: #{elv_deg} deg"
|
192
|
+
solver_opts.each{|opt|
|
193
|
+
opt.elevation_mask = elv_deg / 180 * Math::PI # 0 deg (use satellite over horizon)
|
194
|
+
}
|
195
|
+
next true
|
171
196
|
when :base_station
|
172
197
|
crd, sys = v.split(/ *, */).collect.with_index{|item, i|
|
173
198
|
case item
|
@@ -219,12 +244,14 @@ class Receiver
|
|
219
244
|
i = -1
|
220
245
|
output_options[:system] << [sys_target, []]
|
221
246
|
else
|
222
|
-
output_options[:system][i].reject!{|prn| prns.include?(prn)}
|
247
|
+
output_options[:system][i][1].reject!{|prn| prns.include?(prn)}
|
223
248
|
end
|
224
249
|
output_options[:satellites].reject!{|prn, label| prns.include?(prn)}
|
225
250
|
if mode == :include then
|
226
251
|
output_options[:system][i][1] += prns
|
252
|
+
output_options[:system][i][1].sort!
|
227
253
|
output_options[:satellites] += (labels ? prns.zip(labels) : prns)
|
254
|
+
output_options[:satellites].sort!{|a, b| [a].flatten[0] <=> [b].flatten[0]}
|
228
255
|
end
|
229
256
|
}
|
230
257
|
check_sys_svid = proc{|sys_target, range_in_sys, offset|
|
@@ -241,6 +268,11 @@ class Receiver
|
|
241
268
|
prns.each{|prn| @solver.sbas_options.send(mode, prn)}
|
242
269
|
elsif check_sys_svid.call(:QZSS, 193..202) then
|
243
270
|
[svid || (193..202).to_a].flatten.each{|prn| @solver.gps_options.send(mode, prn)}
|
271
|
+
elsif check_sys_svid.call(:GLONASS, 1..24, 0x100) then
|
272
|
+
prns = [svid || (1..24).to_a].flatten.collect{|i| (i & 0xFF) + 0x100}
|
273
|
+
labels = prns.collect{|prn| "GLONASS:#{prn & 0xFF}"}
|
274
|
+
update_output.call(:GLONASS, prns, labels)
|
275
|
+
prns.each{|prn| @solver.glonass_options.send(mode, prn & 0xFF)}
|
244
276
|
else
|
245
277
|
raise "Unknown satellite: #{spec}"
|
246
278
|
end
|
@@ -317,15 +349,21 @@ class Receiver
|
|
317
349
|
[[:@azimuth, az], [:@elevation, el]].each{|k, values|
|
318
350
|
self.instance_variable_set(k, Hash[*(sats.zip(values).flatten(1))])
|
319
351
|
}
|
352
|
+
mat_S = self.S
|
320
353
|
[:@slopeH, :@slopeV] \
|
321
|
-
.zip((self.fd ? self.slope_HV_enu.to_a.transpose : [nil, nil])) \
|
354
|
+
.zip((self.fd ? self.slope_HV_enu(mat_S).to_a.transpose : [nil, nil])) \
|
322
355
|
.each{|k, values|
|
323
356
|
self.instance_variable_set(k,
|
324
357
|
Hash[*(values ? sats.zip(values).flatten(1) : [])])
|
325
358
|
}
|
359
|
+
# If a design matrix G has columns larger than 4,
|
360
|
+
# other states excluding position and time are estimated.
|
361
|
+
@other_state = self.position_solved? \
|
362
|
+
? (mat_S * self.delta_r.partial(self.used_satellites, 1, 0, 0)).transpose.to_a[0][4..-1] \
|
363
|
+
: []
|
326
364
|
instance_variable_get(target)
|
327
365
|
}
|
328
|
-
[:azimuth, :elevation, :slopeH, :slopeV].each{|k|
|
366
|
+
[:azimuth, :elevation, :slopeH, :slopeV, :other_state].each{|k|
|
329
367
|
eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
|
330
368
|
}
|
331
369
|
}
|
@@ -336,7 +374,13 @@ class Receiver
|
|
336
374
|
eph.svid = prn
|
337
375
|
[prn, eph]
|
338
376
|
}.flatten(1)]
|
339
|
-
|
377
|
+
eph_glonass_list = Hash[*(1..24).collect{|num|
|
378
|
+
eph = GPS::Ephemeris_GLONASS::new
|
379
|
+
eph.svid = num
|
380
|
+
[num, eph]
|
381
|
+
}.flatten(1)]
|
382
|
+
define_method(:register_ephemeris){|t_meas, sys, prn, bcast_data, *options|
|
383
|
+
opt = options[0] || {}
|
340
384
|
case sys
|
341
385
|
when :GPS, :QZSS
|
342
386
|
next unless eph = eph_list[prn]
|
@@ -366,6 +410,15 @@ class Receiver
|
|
366
410
|
$stderr.puts str
|
367
411
|
} if @debug[:SBAS_IGP]
|
368
412
|
end
|
413
|
+
when :GLONASS
|
414
|
+
next unless eph = eph_glonass_list[prn]
|
415
|
+
leap_sec = @solver.gps_space_node.is_valid_utc ?
|
416
|
+
@solver.gps_space_node.iono_utc.delta_t_LS :
|
417
|
+
GPS::Time::guess_leap_seconds(t_meas)
|
418
|
+
next unless eph.parse(bcast_data[0..3], leap_sec)
|
419
|
+
eph.freq_ch = opt[:freq_ch] || 0
|
420
|
+
@solver.glonass_space_node.register_ephemeris(prn, eph)
|
421
|
+
eph.invalidate
|
369
422
|
end
|
370
423
|
}
|
371
424
|
}.call
|
@@ -377,7 +430,7 @@ class Receiver
|
|
377
430
|
ubx = UBX::new(open(ubx_fname))
|
378
431
|
ubx_kind = Hash::new(0)
|
379
432
|
|
380
|
-
after_run = b || proc{|pvt| puts pvt.to_s}
|
433
|
+
after_run = b || proc{|pvt| puts pvt.to_s if pvt}
|
381
434
|
|
382
435
|
gnss_serial = proc{|svid, sys|
|
383
436
|
if sys then # new numbering
|
@@ -443,7 +496,11 @@ class Receiver
|
|
443
496
|
}
|
444
497
|
sys, svid = gnss_serial.call(*loader.call(36, 2).reverse)
|
445
498
|
case sys
|
446
|
-
when :GPS, :QZSS;
|
499
|
+
when :GPS, :SBAS, :QZSS;
|
500
|
+
when :GLONASS
|
501
|
+
svid += 0x100
|
502
|
+
meas.add(svid, :L1_FREQUENCY,
|
503
|
+
GPS::SpaceNode_GLONASS::L1_frequency(loader.call(39, 1, "C") - 7))
|
447
504
|
else; next
|
448
505
|
end
|
449
506
|
trk_stat = loader.call(46, 1)[0]
|
@@ -477,12 +534,14 @@ class Receiver
|
|
477
534
|
})
|
478
535
|
when [0x02, 0x13] # RXM-SFRBX
|
479
536
|
sys, svid = gnss_serial.call(packet[6 + 1], packet[6])
|
537
|
+
opt = {}
|
538
|
+
opt[:freq_ch] = packet[6 + 3] - 7 if sys == :GLONASS
|
480
539
|
register_ephemeris(
|
481
540
|
t_meas,
|
482
541
|
sys, svid,
|
483
542
|
packet.slice(6 + 8, 4 * packet[6 + 4]).each_slice(4).collect{|v|
|
484
543
|
v.pack("C*").unpack("V")[0]
|
485
|
-
})
|
544
|
+
}, opt)
|
486
545
|
end
|
487
546
|
}
|
488
547
|
$stderr.puts ", found packets are %s"%[ubx_kind.inspect]
|
@@ -492,6 +551,7 @@ class Receiver
|
|
492
551
|
items = [
|
493
552
|
@solver.gps_space_node,
|
494
553
|
@solver.sbas_space_node,
|
554
|
+
@solver.glonass_space_node,
|
495
555
|
].inject(0){|res, sn|
|
496
556
|
loaded_items = sn.send(:read, fname)
|
497
557
|
raise "Format error! (Not RINEX) #{fname}" if loaded_items < 0
|
@@ -501,9 +561,10 @@ class Receiver
|
|
501
561
|
end
|
502
562
|
|
503
563
|
def parse_rinex_obs(fname, &b)
|
504
|
-
after_run = b || proc{|pvt| puts pvt.to_s}
|
564
|
+
after_run = b || proc{|pvt| puts pvt.to_s if pvt}
|
505
565
|
$stderr.print "Reading RINEX observation file (%s)"%[fname]
|
506
566
|
types = nil
|
567
|
+
glonass_freq = nil
|
507
568
|
count = 0
|
508
569
|
GPS::RINEX_Observation::read(fname){|item|
|
509
570
|
$stderr.print '.' if (count += 1) % 1000 == 0
|
@@ -526,12 +587,32 @@ class Receiver
|
|
526
587
|
}.compact]
|
527
588
|
}.flatten(1))]
|
528
589
|
|
590
|
+
glonass_freq ||= proc{|spec|
|
591
|
+
# frequency channels described in observation file
|
592
|
+
next {} unless spec
|
593
|
+
Hash[*(spec.collect{|line|
|
594
|
+
line[4..-1].scan(/R(\d{2}).([\s+-]\d)./).collect{|prn, ch|
|
595
|
+
[prn.to_i, GPS::SpaceNode_GLONASS::L1_frequency(ch.to_i)]
|
596
|
+
}
|
597
|
+
}.flatten(2))]
|
598
|
+
}.call(item[:header]["GLONASS SLOT / FRQ #"])
|
599
|
+
|
529
600
|
meas = GPS::Measurement::new
|
530
601
|
item[:meas].each{|k, v|
|
531
602
|
sys, prn = k
|
532
603
|
case sys
|
533
604
|
when 'G', ' '
|
605
|
+
when 'S'; prn += 100
|
534
606
|
when 'J'; prn += 192
|
607
|
+
when 'R'
|
608
|
+
freq = (glonass_freq[prn] ||= proc{|sn|
|
609
|
+
# frequency channels saved with ephemeris
|
610
|
+
sn.update_all_ephemeris(t_meas)
|
611
|
+
next nil unless sn.ephemeris(prn).in_range?(t_meas)
|
612
|
+
sn.ephemeris(prn).frequency_L1
|
613
|
+
}.call(@solver.glonass_space_node))
|
614
|
+
prn += 0x100
|
615
|
+
meas.add(prn, :L1_FREQUENCY, freq) if freq
|
535
616
|
else; next
|
536
617
|
end
|
537
618
|
types[sys] = (types[' '] || []) unless types[sys]
|
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.
|
4
|
+
version: 0.4.0
|
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: 2022-
|
11
|
+
date: 2022-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -61,7 +61,10 @@ files:
|
|
61
61
|
- ext/gps_pvt/GPS/GPS_wrap.cxx
|
62
62
|
- ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx
|
63
63
|
- ext/gps_pvt/extconf.rb
|
64
|
+
- ext/ninja-scan-light/tool/algorithm/integral.h
|
64
65
|
- ext/ninja-scan-light/tool/navigation/EGM.h
|
66
|
+
- ext/ninja-scan-light/tool/navigation/GLONASS.h
|
67
|
+
- ext/ninja-scan-light/tool/navigation/GLONASS_Solver.h
|
65
68
|
- ext/ninja-scan-light/tool/navigation/GPS.h
|
66
69
|
- ext/ninja-scan-light/tool/navigation/GPS_Solver.h
|
67
70
|
- ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h
|
@@ -100,7 +103,7 @@ licenses: []
|
|
100
103
|
metadata:
|
101
104
|
homepage_uri: https://github.com/fenrir-naru/gps_pvt
|
102
105
|
source_code_uri: https://github.com/fenrir-naru/gps_pvt
|
103
|
-
post_install_message:
|
106
|
+
post_install_message:
|
104
107
|
rdoc_options: []
|
105
108
|
require_paths:
|
106
109
|
- lib
|
@@ -115,8 +118,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
118
|
- !ruby/object:Gem::Version
|
116
119
|
version: '0'
|
117
120
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
119
|
-
signing_key:
|
121
|
+
rubygems_version: 3.1.2
|
122
|
+
signing_key:
|
120
123
|
specification_version: 4
|
121
124
|
summary: GPS position, velocity, and time (PVT) solver
|
122
125
|
test_files: []
|