gps_pvt 0.2.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|