gps_pvt 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +10 -0
  6. data/README.md +86 -0
  7. data/Rakefile +86 -0
  8. data/bin/console +15 -0
  9. data/bin/setup +8 -0
  10. data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -0
  11. data/ext/gps_pvt/GPS/GPS_wrap.cxx +16019 -0
  12. data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -0
  13. data/ext/gps_pvt/extconf.rb +70 -0
  14. data/ext/ninja-scan-light/tool/navigation/EGM.h +2971 -0
  15. data/ext/ninja-scan-light/tool/navigation/GPS.h +2432 -0
  16. data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +479 -0
  17. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +1081 -0
  18. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +199 -0
  19. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +210 -0
  20. data/ext/ninja-scan-light/tool/navigation/MagneticField.h +928 -0
  21. data/ext/ninja-scan-light/tool/navigation/NTCM.h +211 -0
  22. data/ext/ninja-scan-light/tool/navigation/RINEX.h +1781 -0
  23. data/ext/ninja-scan-light/tool/navigation/WGS84.h +186 -0
  24. data/ext/ninja-scan-light/tool/navigation/coordinate.h +406 -0
  25. data/ext/ninja-scan-light/tool/param/bit_array.h +145 -0
  26. data/ext/ninja-scan-light/tool/param/complex.h +558 -0
  27. data/ext/ninja-scan-light/tool/param/matrix.h +4049 -0
  28. data/ext/ninja-scan-light/tool/param/matrix_fixed.h +665 -0
  29. data/ext/ninja-scan-light/tool/param/matrix_special.h +562 -0
  30. data/ext/ninja-scan-light/tool/param/quaternion.h +765 -0
  31. data/ext/ninja-scan-light/tool/param/vector3.h +651 -0
  32. data/ext/ninja-scan-light/tool/swig/Coordinate.i +177 -0
  33. data/ext/ninja-scan-light/tool/swig/GPS.i +1102 -0
  34. data/ext/ninja-scan-light/tool/swig/SylphideMath.i +1234 -0
  35. data/ext/ninja-scan-light/tool/swig/extconf.rb +5 -0
  36. data/ext/ninja-scan-light/tool/swig/makefile +53 -0
  37. data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +417 -0
  38. data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +489 -0
  39. data/gps_pvt.gemspec +57 -0
  40. data/lib/gps_pvt/receiver.rb +375 -0
  41. data/lib/gps_pvt/ubx.rb +148 -0
  42. data/lib/gps_pvt/version.rb +5 -0
  43. data/lib/gps_pvt.rb +9 -0
  44. data/sig/gps_pvt.rbs +4 -0
  45. metadata +117 -0
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/ruby
2
+
3
+ =begin
4
+ Receiver class to be an top level interface to a user
5
+ (The origin is ninja-scan-light/tool/misc/receiver_debug.rb)
6
+ =end
7
+
8
+ require_relative 'GPS'
9
+
10
+ module GPS_PVT
11
+ class Receiver
12
+ OUTPUT_PVT_ITEMS = [[
13
+ [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec],
14
+ proc{|pvt|
15
+ [:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
16
+ }
17
+ ]] + [[
18
+ [:receiver_clock_error_meter, :longitude, :latitude, :height],
19
+ proc{|pvt|
20
+ next [nil] * 4 unless pvt.position_solved?
21
+ [
22
+ pvt.receiver_error,
23
+ pvt.llh.lng / Math::PI * 180,
24
+ pvt.llh.lat / Math::PI * 180,
25
+ pvt.llh.alt,
26
+ ]
27
+ }
28
+ ]] + [proc{
29
+ labels = [:g, :p, :h, :v, :t].collect{|k| "#{k}dop".to_sym}
30
+ [
31
+ labels,
32
+ proc{|pvt|
33
+ next [nil] * 5 unless pvt.position_solved?
34
+ labels.collect{|k| pvt.send(k)}
35
+ }
36
+ ]
37
+ }.call] + [[
38
+ [:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms],
39
+ proc{|pvt|
40
+ next [nil] * 4 unless pvt.velocity_solved?
41
+ [:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
42
+ + [pvt.receiver_error_rate]
43
+ }
44
+ ]] + [
45
+ [:used_satellites, proc{|pvt| pvt.used_satellites}],
46
+ [:PRN, proc{|pvt|
47
+ ("%32s"%[pvt.used_satellite_list.collect{|i|
48
+ 1 << (i - 1)
49
+ }.inject(0){|res, v| res | v}.to_s(2)]).scan(/.{8}/).collect{|str|
50
+ str.gsub(' ', '0')
51
+ }.join('_')
52
+ }],
53
+ ] + [[
54
+ (1..32).collect{|prn|
55
+ [:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str| "#{str}(#{prn})"}
56
+ }.flatten,
57
+ proc{|pvt|
58
+ next ([nil] * 6 * 32) unless pvt.position_solved?
59
+ sats = pvt.used_satellite_list
60
+ r, w = [:delta_r, :W].collect{|f| pvt.send(f)}
61
+ (1..32).collect{|i|
62
+ next ([nil] * 6) unless i2 = sats.index(i)
63
+ [r[i2, 0], w[i2, i2]] +
64
+ [:azimuth, :elevation].collect{|f|
65
+ pvt.send(f)[i] / Math::PI * 180
66
+ } + [pvt.slopeH[i], pvt.slopeV[i]]
67
+ }.flatten
68
+ },
69
+ ]] + [[
70
+ [:wssr, :wssr_sf, :weight_max,
71
+ :slopeH_max, :slopeH_max_PRN, :slopeH_max_elevation,
72
+ :slopeV_max, :slopeV_max_PRN, :slopeV_max_elevation],
73
+ proc{|pvt|
74
+ next [nil] * 9 unless fd = pvt.fd
75
+ el_deg = [4, 6].collect{|i| pvt.elevation[fd[i]] / Math::PI * 180}
76
+ fd[0..4] + [el_deg[0]] + fd[5..6] + [el_deg[1]]
77
+ }
78
+ ]] + [[
79
+ [:wssr_FDE_min, :wssr_FDE_min_PRN, :wssr_FDE_2nd, :wssr_FDE_2nd_PRN],
80
+ proc{|pvt|
81
+ [:fde_min, :fde_2nd].collect{|f|
82
+ info = pvt.send(f)
83
+ next ([nil] * 2) if (!info) || info.empty?
84
+ [info[0], info[-3]]
85
+ }.flatten
86
+ }
87
+ ]]
88
+
89
+ OUTPUT_MEAS_ITEMS = [[
90
+ (1..32).collect{|prn|
91
+ [:L1_range, :L1_rate].collect{|str| "#{str}(#{prn})"}
92
+ }.flatten,
93
+ proc{|meas|
94
+ meas_hash = Hash[*(meas.collect{|prn, k, v| [[prn, k], v]}.flatten(1))]
95
+ (1..32).collect{|prn|
96
+ [:L1_PSEUDORANGE, [:L1_DOPPLER, GPS::SpaceNode.L1_WaveLength]].collect{|k, sf|
97
+ meas_hash[[prn, GPS::Measurement.const_get(k)]] * (sf || 1) rescue nil
98
+ }
99
+ }
100
+ }
101
+ ]]
102
+
103
+ def self.header
104
+ (OUTPUT_PVT_ITEMS + OUTPUT_MEAS_ITEMS).transpose[0].flatten.join(',')
105
+ end
106
+
107
+ attr_accessor :solver
108
+
109
+ def initialize(options = {})
110
+ @solver = GPS::Solver::new
111
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
112
+ rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
113
+ rel_prop
114
+ }
115
+ options = options.reject{|k, v|
116
+ case k
117
+ when :weight
118
+ case v.to_sym
119
+ when :elevation # (same as underneath C++ library)
120
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
121
+ if rel_prop[0] > 0 then
122
+ elv = Coordinate::ENU::relative_rel(
123
+ Coordinate::XYZ::new(*rel_prop[4..6]), usr_pos).elevation
124
+ rel_prop[0] = (Math::sin(elv)/0.8)**2
125
+ end
126
+ rel_prop
127
+ }
128
+ next true
129
+ when :identical # same as default
130
+ next true
131
+ end
132
+ end
133
+ false
134
+ }
135
+ raise "Unknown receiver options: #{options.inspect}" unless options.empty?
136
+ proc{|opt|
137
+ opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg
138
+ opt.residual_mask = 1E4 # 10 km
139
+ }.call(@solver.gps_options)
140
+ end
141
+
142
+ def run(meas, t_meas)
143
+ #$stderr.puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %d:%d:%d UTC"%[*t_meas.c_tm]})"
144
+ sn = @solver.gps_space_node
145
+ sn.update_all_ephemeris(t_meas)
146
+
147
+ meas.to_a.collect{|prn, k, v| prn}.uniq.each{|prn|
148
+ eph = sn.ephemeris(prn)
149
+ $stderr.puts "XYZ(PRN:#{prn}): #{eph.constellation(t_meas)[0].to_a} (iodc: #{eph.iodc}, iode: #{eph.iode})"
150
+ } if false
151
+
152
+ pvt = @solver.solve(meas, t_meas)
153
+ pvt.define_singleton_method(:to_s){
154
+ (OUTPUT_PVT_ITEMS.transpose[1].collect{|task|
155
+ task.call(pvt)
156
+ } + OUTPUT_MEAS_ITEMS.transpose[1].collect{|task|
157
+ task.call(meas)
158
+ }).flatten.join(',')
159
+ }
160
+ pvt
161
+ end
162
+
163
+ GPS::PVT.class_eval{
164
+ define_method(:post_solution){|target|
165
+ sats, az, el = proc{|g|
166
+ self.used_satellite_list.collect.with_index{|prn, i|
167
+ # G_enu is measured in the direction from satellite to user positions
168
+ [prn,
169
+ Math::atan2(-g[i, 0], -g[i, 1]),
170
+ Math::asin(-g[i, 2])]
171
+ }.transpose
172
+ }.call(self.G_enu) rescue [[], [], []]
173
+ [[:@azimuth, az], [:@elevation, el]].each{|k, values|
174
+ self.instance_variable_set(k, Hash[*(sats.zip(values).flatten(1))])
175
+ }
176
+ [:@slopeH, :@slopeV] \
177
+ .zip((self.slope_HV_enu.to_a.transpose rescue [nil, nil])) \
178
+ .each{|k, values|
179
+ self.instance_variable_set(k,
180
+ Hash[*(values ? sats.zip(values).flatten(1) : [])])
181
+ }
182
+ instance_variable_get(target)
183
+ }
184
+ [:azimuth, :elevation, :slopeH, :slopeV].each{|k|
185
+ eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
186
+ }
187
+ }
188
+
189
+ proc{
190
+ eph_list = Hash[*(1..32).collect{|prn|
191
+ eph = GPS::Ephemeris::new
192
+ eph.svid = prn
193
+ [prn, eph]
194
+ }.flatten(1)]
195
+ define_method(:register_ephemeris){|t_meas, prn, bcast_data|
196
+ next unless eph = eph_list[prn]
197
+ sn = @solver.gps_space_node
198
+ subframe, iodc_or_iode = eph.parse(bcast_data)
199
+ if iodc_or_iode < 0 then
200
+ begin
201
+ sn.update_iono_utc(
202
+ GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
203
+ [:alpha, :beta].each{|k|
204
+ $stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
205
+ } if false
206
+ rescue
207
+ end
208
+ next
209
+ end
210
+ if t_meas and eph.consistent? then
211
+ eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
212
+ sn.register_ephemeris(prn, eph)
213
+ eph.invalidate
214
+ end
215
+ }
216
+ }.call
217
+
218
+ def parse_ubx(ubx_fname, &b)
219
+ $stderr.print "Reading UBX file (%s) "%[ubx_fname]
220
+ require_relative 'ubx'
221
+
222
+ ubx = UBX::new(open(ubx_fname))
223
+ ubx_kind = Hash::new(0)
224
+
225
+ after_run = b || proc{|pvt| puts pvt.to_s}
226
+
227
+ t_meas = nil
228
+ ubx.each_packet.with_index(1){|packet, i|
229
+ $stderr.print '.' if i % 1000 == 0
230
+ ubx_kind[packet[2..3]] += 1
231
+ case packet[2..3]
232
+ when [0x02, 0x10] # RXM-RAW
233
+ msec, week = [[0, 4, "V"], [4, 2, "v"]].collect{|offset, len, str|
234
+ packet.slice(6 + offset, len).pack("C*").unpack(str)[0]
235
+ }
236
+ t_meas = GPS::Time::new(week, msec.to_f / 1000)
237
+ meas = GPS::Measurement::new
238
+ packet[6 + 6].times{|i|
239
+ loader = proc{|offset, len, str|
240
+ ary = packet.slice(6 + offset + (i * 24), len)
241
+ str ? ary.pack("C*").unpack(str)[0] : ary
242
+ }
243
+ prn = loader.call(28, 1)[0]
244
+ {
245
+ :L1_PSEUDORANGE => [16, 8, "E"],
246
+ :L1_DOPPLER => [24, 4, "e"],
247
+ }.each{|k, prop|
248
+ meas.add(prn, GPS::Measurement.const_get(k), loader.call(*prop))
249
+ }
250
+ }
251
+ after_run.call(run(meas, t_meas), [meas, t_meas])
252
+ when [0x02, 0x15] # RXM-RAWX
253
+ sec, week = [[0, 8, "E"], [8, 2, "v"]].collect{|offset, len, str|
254
+ packet.slice(6 + offset, len).pack("C*").unpack(str)[0]
255
+ }
256
+ t_meas = GPS::Time::new(week, sec)
257
+ meas = GPS::Measurement::new
258
+ packet[6 + 11].times{|i|
259
+ loader = proc{|offset, len, str, post|
260
+ v = packet.slice(6 + offset + (i * 32), len)
261
+ v = str ? v.pack("C*").unpack(str)[0] : v
262
+ v = post.call(v) if post
263
+ v
264
+ }
265
+ next unless (gnss = loader.call(36, 1)[0]) == 0
266
+ svid = loader.call(37, 1)[0]
267
+ trk_stat = loader.call(46, 1)[0]
268
+ {
269
+ :L1_PSEUDORANGE => [16, 8, "E", proc{|v| (trk_stat & 0x1 == 0x1) ? v : nil}],
270
+ :L1_PSEUDORANGE_SIGMA => [43, 1, nil, proc{|v|
271
+ (trk_stat & 0x1 == 0x1) ? (1E-2 * (v[0] & 0xF)) : nil
272
+ }],
273
+ :L1_DOPPLER => [32, 4, "e"],
274
+ :L1_DOPPLER_SIGMA => [45, 1, nil, proc{|v| 2E-3 * (v[0] & 0xF)}],
275
+ }.each{|k, prop|
276
+ next unless v = loader.call(*prop)
277
+ meas.add(svid, GPS::Measurement.const_get(k), v)
278
+ }
279
+ }
280
+ after_run.call(run(meas, t_meas), [meas, t_meas])
281
+ when [0x02, 0x11] # RXM-SFRB
282
+ register_ephemeris(
283
+ t_meas,
284
+ packet[6 + 1],
285
+ packet.slice(6 + 2, 40).each_slice(4).collect{|v|
286
+ (v.pack("C*").unpack("V")[0] & 0xFFFFFF) << 6
287
+ })
288
+ when [0x02, 0x13] # RXM-SFRBX
289
+ next unless (gnss = packet[6]) == 0
290
+ register_ephemeris(
291
+ t_meas,
292
+ packet[6 + 1],
293
+ packet.slice(6 + 8, 4 * packet[6 + 4]).each_slice(4).collect{|v|
294
+ v.pack("C*").unpack("V")[0]
295
+ })
296
+ end
297
+ }
298
+ $stderr.puts ", found packets are %s"%[ubx_kind.inspect]
299
+ end
300
+
301
+ def parse_rinex_nav(fname)
302
+ $stderr.puts "Read RINEX NAV file (%s): %d items."%[
303
+ fname, @solver.gps_space_node.read(fname)]
304
+ end
305
+
306
+ def parse_rinex_obs(fname, &b)
307
+ after_run = b || proc{|pvt| puts pvt.to_s}
308
+ $stderr.print "Reading RINEX observation file (%s)"%[fname]
309
+ types = nil
310
+ count = 0
311
+ GPS::RINEX_Observation::read(fname){|item|
312
+ $stderr.print '.' if (count += 1) % 1000 == 0
313
+ t_meas = item[:time]
314
+
315
+ meas = GPS::Measurement::new
316
+ types ||= (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
317
+ case type_
318
+ when "C1", "C1C"
319
+ [i, GPS::Measurement::L1_PSEUDORANGE]
320
+ when "D1", "D1C"
321
+ [i, GPS::Measurement::L1_DOPPLER]
322
+ else
323
+ nil
324
+ end
325
+ }.compact
326
+ item[:meas].each{|k, v|
327
+ sys, prn = k
328
+ next unless sys == 'G' # GPS only
329
+ types.each{|i, type_|
330
+ meas.add(prn, type_, v[i][0]) if v[i]
331
+ }
332
+ }
333
+ after_run.call(run(meas, t_meas), [meas, t_meas])
334
+ }
335
+ $stderr.puts ", %d epochs."%[count]
336
+ end
337
+ end
338
+ end
339
+
340
+ if __FILE__ == $0 then
341
+ # runnable quick example to solve PVT by using RINEX NAV/OBS or u-blox ubx
342
+ options = {}
343
+
344
+ # check options
345
+ ARGV.reject!{|arg|
346
+ next false unless arg =~ /^--([^=]+)=?/
347
+ options[$1.to_sym] = $'
348
+ true
349
+ }
350
+
351
+ # Check file existence
352
+ ARGV.each{|arg|
353
+ raise "File not found: #{arg}" unless File::exist?(arg)
354
+ }
355
+
356
+ rcv = GPS_PVT::Receiver::new(options)
357
+
358
+ puts GPS_PVT::Receiver::header
359
+
360
+ # parse RINEX NAV
361
+ ARGV.reject!{|arg|
362
+ next false unless arg =~ /\.\d{2}n$/
363
+ rcv.parse_rinex_nav(arg)
364
+ }
365
+
366
+ # other files
367
+ ARGV.each{|arg|
368
+ case arg
369
+ when /\.ubx$/
370
+ rcv.parse_ubx(arg)
371
+ when /\.\d{2}o$/
372
+ rcv.parse_rinex_obs(arg)
373
+ end
374
+ }
375
+ end
@@ -0,0 +1,148 @@
1
+ # U-blox file utilities
2
+ # The origin is ninja-scan-light/tool/misc/ubx.rb
3
+
4
+ # Copyright (c) 2016, M.Naruoka (fenrir)
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without modification,
8
+ # are permitted provided that the following conditions are met:
9
+ #
10
+ # - Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ # - Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ # - Neither the name of the naruoka.org nor the names of its contributors
16
+ # may be used to endorse or promote products derived from this software
17
+ # without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
23
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
+ # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30
+ # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ module GPS_PVT
33
+ class UBX
34
+ def initialize(io)
35
+ @io = io
36
+ @buf = []
37
+ end
38
+ def UBX.checksum(packet, range = 2..-3)
39
+ ck_a, ck_b = [0, 0]
40
+ packet[range].each{|b|
41
+ ck_a += b
42
+ ck_b += ck_a
43
+ }
44
+ ck_a &= 0xFF
45
+ ck_b &= 0xFF
46
+ [ck_a, ck_b]
47
+ end
48
+ def UBX.update_checksum(packet)
49
+ packet[-2..-1] = checksum(packet)
50
+ packet
51
+ end
52
+ def UBX.update_size(packet, size = nil)
53
+ size ||= packet.size - 8
54
+ size = size.divmod(0x100)
55
+ packet[4] = size[1]
56
+ packet[5] = size[0]
57
+ packet
58
+ end
59
+ def UBX.update(packet)
60
+ [:update_size, :update_checksum].inject(packet){|arg, f|
61
+ UBX.send(f, arg)
62
+ }
63
+ end
64
+ def read_packet
65
+ while !@io.eof?
66
+ if @buf.size < 8 then
67
+ @buf += @io.read(8 - @buf.size).unpack('C*')
68
+ return nil if @buf.size < 8
69
+ end
70
+
71
+ if @buf[0] != 0xB5 then
72
+ @buf.shift
73
+ next
74
+ elsif @buf[1] != 0x62 then
75
+ @buf = @buf[2..-1]
76
+ next
77
+ end
78
+
79
+ len = (@buf[5] << 8) + @buf[4]
80
+ if @buf.size < len + 8 then
81
+ @buf += @io.read(len + 8 - @buf.size).unpack('C*')
82
+ return nil if @buf.size < len + 8
83
+ end
84
+
85
+ ck_a, ck_b = UBX::checksum(@buf, 2..(len + 5))
86
+ if (@buf[len + 6] != ck_a) || (@buf[len + 7] != ck_b) then
87
+ @buf = @buf[2..-1]
88
+ next
89
+ end
90
+
91
+ packet = @buf[0..(len + 7)]
92
+ @buf = @buf[(len + 8)..-1]
93
+
94
+ return packet
95
+ end
96
+ return nil
97
+ end
98
+
99
+ def each_packet(&b)
100
+ res = Enumerator::new{|y|
101
+ while packet = read_packet
102
+ y << packet
103
+ end
104
+ }
105
+ b ? res.each(&b) : res
106
+ end
107
+
108
+ def read_packets
109
+ each_packet.to_a
110
+ end
111
+
112
+ GNSS_ID = {
113
+ :GPS => 0,
114
+ :SBAS => 1,
115
+ :Galileo => 2,
116
+ :BeiDou => 3,
117
+ :QZSS => 5,
118
+ :GLONASS => 6,
119
+ }
120
+
121
+ SIGNAL_ID = {
122
+ :GPS => {:L1CA => 0, :L2CL => 3, :L2CM => 4},
123
+ :SBAS => {:L1CA => 0},
124
+ :Galileo => {:E1C => 0, :E1B => 1, :E5_bI => 5, :E5_bQ => 6},
125
+ :BeiDou => {:B1I_D1 => 0, :B1I_D2 => 1, :B2I_D1 => 2, :B2I_D2 => 3},
126
+ :QZSS => {:L1CA => 0, :L2CL => 4, :L2CM => 5},
127
+ :GLONASS => {:L1OF => 0, :L2OF => 2},
128
+ }
129
+
130
+ def UBX.svid(id, gnss = :GPS)
131
+ gnss = GNSS_ID[gnss] if gnss.kind_of?(Symbol)
132
+ case gnss
133
+ when GNSS_ID[:GPS], GNSS_ID[:SBAS]
134
+ id
135
+ when GNSS_ID[:Galileo]
136
+ id + 210
137
+ when GNSS_ID[:Beido]
138
+ (id < 6) ? (id + 158) : (id + 27)
139
+ when GNSS_ID[:QZSS]
140
+ id + 192
141
+ when GNSS_ID[:GLONASS]
142
+ id + 64
143
+ else
144
+ nil
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GPS_PVT
4
+ VERSION = "0.1.1"
5
+ end
data/lib/gps_pvt.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gps_pvt/version"
4
+ require_relative "gps_pvt/receiver"
5
+
6
+ module GPS_PVT
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
data/sig/gps_pvt.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module GpsPvt
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gps_pvt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - fenrir(M.Naruoka)
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake-compiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: This module calculate PVT by using raw observation obtained from a GPS
42
+ receiver
43
+ email:
44
+ - fenrir.naru@gmail.com
45
+ executables: []
46
+ extensions:
47
+ - ext/gps_pvt/extconf.rb
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".rspec"
51
+ - CHANGELOG.md
52
+ - CODE_OF_CONDUCT.md
53
+ - Gemfile
54
+ - README.md
55
+ - Rakefile
56
+ - bin/console
57
+ - bin/setup
58
+ - ext/gps_pvt/Coordinate/Coordinate_wrap.cxx
59
+ - ext/gps_pvt/GPS/GPS_wrap.cxx
60
+ - ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx
61
+ - ext/gps_pvt/extconf.rb
62
+ - ext/ninja-scan-light/tool/navigation/EGM.h
63
+ - ext/ninja-scan-light/tool/navigation/GPS.h
64
+ - ext/ninja-scan-light/tool/navigation/GPS_Solver.h
65
+ - ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h
66
+ - ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h
67
+ - ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h
68
+ - ext/ninja-scan-light/tool/navigation/MagneticField.h
69
+ - ext/ninja-scan-light/tool/navigation/NTCM.h
70
+ - ext/ninja-scan-light/tool/navigation/RINEX.h
71
+ - ext/ninja-scan-light/tool/navigation/WGS84.h
72
+ - ext/ninja-scan-light/tool/navigation/coordinate.h
73
+ - ext/ninja-scan-light/tool/param/bit_array.h
74
+ - ext/ninja-scan-light/tool/param/complex.h
75
+ - ext/ninja-scan-light/tool/param/matrix.h
76
+ - ext/ninja-scan-light/tool/param/matrix_fixed.h
77
+ - ext/ninja-scan-light/tool/param/matrix_special.h
78
+ - ext/ninja-scan-light/tool/param/quaternion.h
79
+ - ext/ninja-scan-light/tool/param/vector3.h
80
+ - ext/ninja-scan-light/tool/swig/Coordinate.i
81
+ - ext/ninja-scan-light/tool/swig/GPS.i
82
+ - ext/ninja-scan-light/tool/swig/SylphideMath.i
83
+ - ext/ninja-scan-light/tool/swig/extconf.rb
84
+ - ext/ninja-scan-light/tool/swig/makefile
85
+ - ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb
86
+ - ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb
87
+ - gps_pvt.gemspec
88
+ - lib/gps_pvt.rb
89
+ - lib/gps_pvt/receiver.rb
90
+ - lib/gps_pvt/ubx.rb
91
+ - lib/gps_pvt/version.rb
92
+ - sig/gps_pvt.rbs
93
+ homepage: https://github.com/fenrir-naru/gps_pvt
94
+ licenses: []
95
+ metadata:
96
+ homepage_uri: https://github.com/fenrir-naru/gps_pvt
97
+ source_code_uri: https://github.com/fenrir-naru/gps_pvt
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 2.6.0
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.1.2
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: GPS position, velocity, and time (PVT) solver
117
+ test_files: []