gps_pvt 0.1.5 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,115 +7,148 @@ require_relative 'GPS'
7
7
 
8
8
  module GPS_PVT
9
9
  class Receiver
10
- OUTPUT_PVT_ITEMS = [[
11
- [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec],
12
- proc{|pvt|
13
- [:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
14
- }
15
- ]] + [[
16
- [:receiver_clock_error_meter, :longitude, :latitude, :height],
17
- proc{|pvt|
18
- next [nil] * 4 unless pvt.position_solved?
10
+ def self.pvt_items(opt = {})
11
+ opt = {
12
+ :system => [[:GPS, 1..32]],
13
+ :satellites => (1..32).to_a,
14
+ }.merge(opt)
15
+ [[
16
+ [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec],
17
+ proc{|pvt|
18
+ [:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
19
+ }
20
+ ]] + [[
21
+ [:receiver_clock_error_meter, :longitude, :latitude, :height, :rel_E, :rel_N, :rel_U],
22
+ proc{|pvt|
23
+ next [nil] * 7 unless pvt.position_solved?
24
+ [
25
+ pvt.receiver_error,
26
+ pvt.llh.lng / Math::PI * 180,
27
+ pvt.llh.lat / Math::PI * 180,
28
+ pvt.llh.alt,
29
+ ] + (pvt.rel_ENU.to_a rescue [nil] * 3)
30
+ }
31
+ ]] + [proc{
32
+ labels = [:g, :p, :h, :v, :t].collect{|k| "#{k}dop".to_sym}
19
33
  [
20
- pvt.receiver_error,
21
- pvt.llh.lng / Math::PI * 180,
22
- pvt.llh.lat / Math::PI * 180,
23
- pvt.llh.alt,
34
+ labels,
35
+ proc{|pvt|
36
+ next [nil] * 5 unless pvt.position_solved?
37
+ labels.collect{|k| pvt.send(k)}
38
+ }
24
39
  ]
25
- }
26
- ]] + [proc{
27
- labels = [:g, :p, :h, :v, :t].collect{|k| "#{k}dop".to_sym}
28
- [
29
- labels,
40
+ }.call] + [[
41
+ [:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms],
30
42
  proc{|pvt|
31
- next [nil] * 5 unless pvt.position_solved?
32
- labels.collect{|k| pvt.send(k)}
43
+ next [nil] * 4 unless pvt.velocity_solved?
44
+ [:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
45
+ + [pvt.receiver_error_rate]
33
46
  }
34
- ]
35
- }.call] + [[
36
- [:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms],
37
- proc{|pvt|
38
- next [nil] * 4 unless pvt.velocity_solved?
39
- [:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
40
- + [pvt.receiver_error_rate]
41
- }
42
- ]] + [
43
- [:used_satellites, proc{|pvt| pvt.used_satellites}],
44
- [:PRN, proc{|pvt|
45
- ("%32s"%[pvt.used_satellite_list.collect{|i|
46
- 1 << (i - 1)
47
- }.inject(0){|res, v| res | v}.to_s(2)]).scan(/.{8}/).collect{|str|
48
- str.gsub(' ', '0')
49
- }.join('_')
50
- }],
51
- ] + [[
52
- (1..32).collect{|prn|
53
- [:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str| "#{str}(#{prn})"}
54
- }.flatten,
55
- proc{|pvt|
56
- next ([nil] * 6 * 32) unless pvt.position_solved?
57
- sats = pvt.used_satellite_list
58
- r, w = [:delta_r, :W].collect{|f| pvt.send(f)}
59
- (1..32).collect{|i|
60
- next ([nil] * 6) unless i2 = sats.index(i)
61
- [r[i2, 0], w[i2, i2]] +
62
- [:azimuth, :elevation].collect{|f|
63
- pvt.send(f)[i] / Math::PI * 180
64
- } + [pvt.slopeH[i], pvt.slopeV[i]]
65
- }.flatten
66
- },
67
- ]] + [[
68
- [:wssr, :wssr_sf, :weight_max,
69
- :slopeH_max, :slopeH_max_PRN, :slopeH_max_elevation,
70
- :slopeV_max, :slopeV_max_PRN, :slopeV_max_elevation],
71
- proc{|pvt|
72
- next [nil] * 9 unless fd = pvt.fd
73
- el_deg = [4, 6].collect{|i| pvt.elevation[fd[i]] / Math::PI * 180}
74
- fd[0..4] + [el_deg[0]] + fd[5..6] + [el_deg[1]]
75
- }
76
- ]] + [[
77
- [:wssr_FDE_min, :wssr_FDE_min_PRN, :wssr_FDE_2nd, :wssr_FDE_2nd_PRN],
78
- proc{|pvt|
79
- [:fde_min, :fde_2nd].collect{|f|
80
- info = pvt.send(f)
81
- next ([nil] * 2) if (!info) || info.empty?
82
- [info[0], info[-3]]
83
- }.flatten
84
- }
85
- ]]
47
+ ]] + [
48
+ [:used_satellites, proc{|pvt| pvt.used_satellites}],
49
+ ] + opt[:system].collect{|sys, range|
50
+ bit_flip = if range.kind_of?(Array) then
51
+ proc{|res, i|
52
+ res[i] = "1" if i = range.index(i)
53
+ res
54
+ }
55
+ else # expect Range
56
+ base_prn = range.min
57
+ proc{|res, i|
58
+ res[i - base_prn] = "1" if range.include?(i)
59
+ res
60
+ }
61
+ end
62
+ ["#{sys}_PRN", proc{|pvt|
63
+ pvt.used_satellite_list.inject("0" * range.size, &bit_flip) \
64
+ .scan(/.{1,8}/).join('_').reverse
65
+ }]
66
+ } + [[
67
+ opt[:satellites].collect{|prn, label|
68
+ [:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str|
69
+ "#{str}(#{label || prn})"
70
+ }
71
+ }.flatten,
72
+ proc{|pvt|
73
+ next ([nil] * 6 * opt[:satellites].size) unless pvt.position_solved?
74
+ sats = pvt.used_satellite_list
75
+ r, w = [:delta_r, :W].collect{|f| pvt.send(f)}
76
+ opt[:satellites].collect{|i|
77
+ next ([nil] * 6) unless i2 = sats.index(i)
78
+ [r[i2, 0], w[i2, i2]] +
79
+ [:azimuth, :elevation].collect{|f|
80
+ pvt.send(f)[i] / Math::PI * 180
81
+ } + [pvt.slopeH[i], pvt.slopeV[i]]
82
+ }.flatten
83
+ },
84
+ ]] + [[
85
+ [:wssr, :wssr_sf, :weight_max,
86
+ :slopeH_max, :slopeH_max_PRN, :slopeH_max_elevation,
87
+ :slopeV_max, :slopeV_max_PRN, :slopeV_max_elevation],
88
+ proc{|pvt|
89
+ next [nil] * 9 unless fd = pvt.fd
90
+ el_deg = [4, 6].collect{|i| pvt.elevation[fd[i]] / Math::PI * 180}
91
+ fd[0..4] + [el_deg[0]] + fd[5..6] + [el_deg[1]]
92
+ }
93
+ ]] + [[
94
+ [:wssr_FDE_min, :wssr_FDE_min_PRN, :wssr_FDE_2nd, :wssr_FDE_2nd_PRN],
95
+ proc{|pvt|
96
+ [:fde_min, :fde_2nd].collect{|f|
97
+ info = pvt.send(f)
98
+ next ([nil] * 2) if (!info) || info.empty?
99
+ [info[0], info[-3]]
100
+ }.flatten
101
+ }
102
+ ]]
103
+ end
86
104
 
87
- OUTPUT_MEAS_ITEMS = [[
88
- (1..32).collect{|prn|
89
- [:L1_range, :L1_rate].collect{|str| "#{str}(#{prn})"}
90
- }.flatten,
91
- proc{|meas|
92
- meas_hash = Hash[*(meas.collect{|prn, k, v| [[prn, k], v]}.flatten(1))]
93
- (1..32).collect{|prn|
94
- [:L1_PSEUDORANGE, [:L1_DOPPLER, GPS::SpaceNode.L1_WaveLength]].collect{|k, sf|
95
- meas_hash[[prn, GPS::Measurement.const_get(k)]] * (sf || 1) rescue nil
105
+ def self.meas_items(opt = {})
106
+ opt = {
107
+ :satellites => (1..32).to_a,
108
+ }.merge(opt)
109
+ [[
110
+ opt[:satellites].collect{|prn, label|
111
+ [:L1_range, :L1_rate].collect{|str| "#{str}(#{label || prn})"}
112
+ }.flatten,
113
+ proc{|meas|
114
+ meas_hash = Hash[*(meas.collect{|prn, k, v| [[prn, k], v]}.flatten(1))]
115
+ opt[:satellites].collect{|prn|
116
+ [:L1_PSEUDORANGE, [:L1_DOPPLER, GPS::SpaceNode.L1_WaveLength]].collect{|k, sf|
117
+ meas_hash[[prn, GPS::Measurement.const_get(k)]] * (sf || 1) rescue nil
118
+ }
96
119
  }
97
120
  }
98
- }
99
- ]]
100
-
101
- def self.header
102
- (OUTPUT_PVT_ITEMS + OUTPUT_MEAS_ITEMS).transpose[0].flatten.join(',')
121
+ ]]
122
+ end
123
+
124
+ def header
125
+ (@output[:pvt] + @output[:meas]).transpose[0].flatten.join(',')
103
126
  end
104
127
 
105
128
  attr_accessor :solver
129
+ attr_accessor :base_station
106
130
 
107
131
  def initialize(options = {})
108
132
  @solver = GPS::Solver::new
109
- @solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
133
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
110
134
  rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
111
135
  rel_prop
112
136
  }
137
+ @debug = {}
138
+ output_options = {
139
+ :system => [[:GPS, 1..32], [:QZSS, 193..202]],
140
+ :satellites => (1..32).to_a + (193..202).to_a, # [idx, ...] or [[idx, label], ...] is acceptable
141
+ }
113
142
  options = options.reject{|k, v|
114
143
  case k
144
+ when :debug
145
+ v = v.split(/,/)
146
+ @debug[v[0].upcase.to_sym] = v[1..-1]
147
+ next true
115
148
  when :weight
116
149
  case v.to_sym
117
150
  when :elevation # (same as underneath C++ library)
118
- @solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
151
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
119
152
  if rel_prop[0] > 0 then
120
153
  elv = Coordinate::ENU::relative_rel(
121
154
  Coordinate::XYZ::new(*rel_prop[4..6]), usr_pos).elevation
@@ -127,6 +160,77 @@ class Receiver
127
160
  when :identical # same as default
128
161
  next true
129
162
  end
163
+ when :base_station
164
+ crd, sys = v.split(/ *, */).collect.with_index{|item, i|
165
+ case item
166
+ when /^([\+-]?\d+\.?\d*)([XYZNEDU]?)$/ # ex) meter[X], degree[N]
167
+ [$1.to_f, ($2 + "XY?"[i])[0]]
168
+ when /^([\+-]?\d+)_(?:(\d+)_(\d+\.?\d*)|(\d+\.?\d*))([NE])$/ # ex) deg_min_secN
169
+ [$1.to_f + ($2 || $4).to_f / 60 + ($3 || 0).to_f / 3600, $5]
170
+ else
171
+ raise "Unknown coordinate spec.: #{item}"
172
+ end
173
+ }.transpose
174
+ raise "Unknown base station: #{v}" if crd.size != 3
175
+ @base_station = case (sys = sys.join.to_sym)
176
+ when :XYZ, :XY?
177
+ Coordinate::XYZ::new(*crd)
178
+ when :NED, :ENU, :NE?, :EN? # :NE? => :NEU, :EN? => :ENU
179
+ (0..1).each{|i| crd[i] *= (Math::PI / 180)}
180
+ ([:NED, :NE?].include?(sys) ?
181
+ Coordinate::LLH::new(crd[0], crd[1], crd[2] * (:NED == sys ? -1 : 1)) :
182
+ Coordinate::LLH::new(crd[1], crd[0], crd[2])).xyz
183
+ else
184
+ raise "Unknown coordinate system: #{sys}"
185
+ end
186
+ $stderr.puts "Base station (LLH): #{
187
+ llh = @base_station.llh.to_a
188
+ llh[0..1].collect{|rad| rad / Math::PI * 180} + [llh[2]]
189
+ }"
190
+ next true
191
+ when :with, :without
192
+ [v].flatten.each{|spec| # array is acceptable
193
+ sys, svid = case spec
194
+ when Integer
195
+ [nil, spec]
196
+ when /([a-zA-Z]+)(?::(-?\d+))?/
197
+ [$1.upcase.to_sym, (Integre($2) rescue nil)]
198
+ when /-?\d+/
199
+ [nil, $&.to_i]
200
+ else
201
+ next false
202
+ end
203
+ mode = if svid && (svid < 0) then
204
+ svid *= -1
205
+ (k == :with) ? :exclude : :include
206
+ else
207
+ (k == :with) ? :include : :exclude
208
+ end
209
+ if (sys == :GPS) || (sys == :QZSS) \
210
+ || (svid && ((1..32).include?(svid) || (193..202).include?(svid))) then
211
+ [svid || ((1..32).to_a + (193..202).to_a)].flatten.each{
212
+ @solver.gps_options.send(mode, svid)
213
+ }
214
+ elsif (sys == :SBAS) || (svid && (120..158).include?(svid)) then
215
+ prns = [svid || (120..158).to_a].flatten
216
+ unless (i = output_options[:system].index{|sys, range| sys == :SBAS}) then
217
+ i = -1
218
+ output_options[:system] << [:SBAS, []]
219
+ else
220
+ output_options[:system][i].reject!{|prn| prns.include?(prn)}
221
+ end
222
+ output_options[:satellites].reject!{|prn, label| prns.include?(prn)}
223
+ if mode == :include then
224
+ output_options[:system][i][1] += prns
225
+ output_options[:satellites] += prns
226
+ end
227
+ prns.each{|prn| @solver.sbas_options.send(mode, prn)}
228
+ else
229
+ next false
230
+ end
231
+ $stderr.puts "#{mode.capitalize} satellite: #{[sys, svid].compact.join(':')}"
232
+ }
233
+ next true
130
234
  end
131
235
  false
132
236
  }
@@ -135,9 +239,35 @@ class Receiver
135
239
  opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg
136
240
  opt.residual_mask = 1E4 # 10 km
137
241
  }.call(@solver.gps_options)
242
+ @output = {
243
+ :pvt => Receiver::pvt_items(output_options),
244
+ :meas => Receiver::meas_items(output_options),
245
+ }
138
246
  end
139
247
 
140
- def run(meas, t_meas)
248
+ GPS::Measurement.class_eval{
249
+ proc{
250
+ key2sym = []
251
+ GPS::Measurement.constants.each{|k|
252
+ i = GPS::Measurement.const_get(k)
253
+ key2sym[i] = k if i.kind_of?(Integer)
254
+ }
255
+ define_method(:to_a2){
256
+ to_a.collect{|prn, k, v| [prn, key2sym[k] || k, v]}
257
+ }
258
+ define_method(:to_hash2){
259
+ Hash[*(to_hash.collect{|prn, k_v|
260
+ [prn, Hash[*(k_v.collect{|k, v| [key2sym[k] || k, v]}.flatten(1))]]
261
+ }.flatten(1))]
262
+ }
263
+ }.call
264
+ alias_method(:add_orig, :add)
265
+ define_method(:add){|prn, key, value|
266
+ add_orig(prn, key.kind_of?(Symbol) ? GPS::Measurement.const_get(key) : key, value)
267
+ }
268
+ }
269
+
270
+ def run(meas, t_meas, ref_pos = @base_station)
141
271
  =begin
142
272
  $stderr.puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %d:%d:%d UTC"%[*t_meas.c_tm]})"
143
273
  meas.to_a.collect{|prn, k, v| prn}.uniq.each{|prn|
@@ -148,10 +278,14 @@ class Receiver
148
278
 
149
279
  #@solver.gps_space_node.update_all_ephemeris(t_meas) # internally called in the following solver.solve
150
280
  pvt = @solver.solve(meas, t_meas)
281
+ pvt.define_singleton_method(:rel_ENU){
282
+ Coordinate::ENU::relative(xyz, ref_pos)
283
+ } if (ref_pos && pvt.position_solved?)
284
+ output = @output
151
285
  pvt.define_singleton_method(:to_s){
152
- (OUTPUT_PVT_ITEMS.transpose[1].collect{|task|
286
+ (output[:pvt].transpose[1].collect{|task|
153
287
  task.call(pvt)
154
- } + OUTPUT_MEAS_ITEMS.transpose[1].collect{|task|
288
+ } + output[:meas].transpose[1].collect{|task|
155
289
  task.call(meas)
156
290
  }).flatten.join(',')
157
291
  }
@@ -172,7 +306,7 @@ class Receiver
172
306
  self.instance_variable_set(k, Hash[*(sats.zip(values).flatten(1))])
173
307
  }
174
308
  [:@slopeH, :@slopeV] \
175
- .zip((self.slope_HV_enu.to_a.transpose rescue [nil, nil])) \
309
+ .zip((self.fd ? self.slope_HV_enu.to_a.transpose : [nil, nil])) \
176
310
  .each{|k, values|
177
311
  self.instance_variable_set(k,
178
312
  Hash[*(values ? sats.zip(values).flatten(1) : [])])
@@ -185,30 +319,41 @@ class Receiver
185
319
  }
186
320
 
187
321
  proc{
188
- eph_list = Hash[*(1..32).collect{|prn|
322
+ eph_list = Hash[*((1..32).to_a + (193..202).to_a).collect{|prn|
189
323
  eph = GPS::Ephemeris::new
190
324
  eph.svid = prn
191
325
  [prn, eph]
192
326
  }.flatten(1)]
193
- define_method(:register_ephemeris){|t_meas, prn, bcast_data|
194
- next unless eph = eph_list[prn]
195
- sn = @solver.gps_space_node
196
- subframe, iodc_or_iode = eph.parse(bcast_data)
197
- if iodc_or_iode < 0 then
198
- begin
199
- sn.update_iono_utc(
200
- GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
201
- [:alpha, :beta].each{|k|
202
- $stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
203
- } if false
204
- rescue
327
+ define_method(:register_ephemeris){|t_meas, sys, prn, bcast_data|
328
+ case sys
329
+ when :GPS, :QZSS
330
+ next unless eph = eph_list[prn]
331
+ sn = @solver.gps_space_node
332
+ subframe, iodc_or_iode = eph.parse(bcast_data)
333
+ if iodc_or_iode < 0 then
334
+ begin
335
+ sn.update_iono_utc(
336
+ GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
337
+ [:alpha, :beta].each{|k|
338
+ $stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
339
+ } if false
340
+ rescue
341
+ end
342
+ next
343
+ end
344
+ if t_meas and eph.consistent? then
345
+ eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
346
+ sn.register_ephemeris(prn, eph)
347
+ eph.invalidate
348
+ end
349
+ when :SBAS
350
+ case @solver.sbas_space_node.decode_message(bcast_data[0..7], prn, t_meas)
351
+ when 26
352
+ ['', "IGP broadcasted by PRN#{prn} @ #{Time::utc(*t_meas.c_tm)}",
353
+ @solver.sbas_space_node.ionospheric_grid_points(prn)].each{|str|
354
+ $stderr.puts str
355
+ } if @debug[:SBAS_IGP]
205
356
  end
206
- next
207
- end
208
- if t_meas and eph.consistent? then
209
- eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
210
- sn.register_ephemeris(prn, eph)
211
- eph.invalidate
212
357
  end
213
358
  }
214
359
  }.call
@@ -222,6 +367,24 @@ class Receiver
222
367
 
223
368
  after_run = b || proc{|pvt| puts pvt.to_s}
224
369
 
370
+ gnss_serial = proc{|svid, sys|
371
+ if sys then # new numbering
372
+ sys = [:GPS, :SBAS, :Galileo, :BeiDou, :IMES, :QZSS, :GLONASS][sys] if sys.kind_of?(Integer)
373
+ case sys
374
+ when :QZSS; svid += 192
375
+ end
376
+ else # old numbering
377
+ sys = case svid
378
+ when 1..32; :GPS
379
+ when 120..158; :SBAS
380
+ when 193..202; :QZSS
381
+ when 65..96; svid -= 64; :GLONASS
382
+ when 255; :GLONASS
383
+ end
384
+ end
385
+ [sys, svid]
386
+ }
387
+
225
388
  t_meas = nil
226
389
  ubx.each_packet.with_index(1){|packet, i|
227
390
  $stderr.print '.' if i % 1000 == 0
@@ -245,11 +408,11 @@ class Receiver
245
408
  :L1_CARRIER_PHASE => [8, 8, "E"],
246
409
  :L1_SIGNAL_STRENGTH_dBHz => [30, 1, "c"],
247
410
  }.each{|k, prop|
248
- meas.add(prn, GPS::Measurement.const_get(k), loader.call(*prop))
411
+ meas.add(prn, k, loader.call(*prop))
249
412
  }
250
413
  # bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
251
414
  # between previous and current observation, which maps negative lock seconds
252
- meas.add(prn, GPS::Measurement::L1_LOCK_SEC,
415
+ meas.add(prn, :L1_LOCK_SEC,
253
416
  (packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
254
417
  }
255
418
  after_run.call(run(meas, t_meas), [meas, t_meas])
@@ -266,8 +429,11 @@ class Receiver
266
429
  v = post.call(v) if post
267
430
  v
268
431
  }
269
- next unless (gnss = loader.call(36, 1)[0]) == 0
270
- svid = loader.call(37, 1)[0]
432
+ sys, svid = gnss_serial.call(*loader.call(36, 2).reverse)
433
+ case sys
434
+ when :GPS, :QZSS;
435
+ else; next
436
+ end
271
437
  trk_stat = loader.call(46, 1)[0]
272
438
  {
273
439
  :L1_PSEUDORANGE => [16, 8, "E", proc{|v| (trk_stat & 0x1 == 0x1) ? v : nil}],
@@ -280,26 +446,28 @@ class Receiver
280
446
  :L1_CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
281
447
  (trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
282
448
  }],
283
- :L1_SIGNAL_STRENGTH_dBHz => [42, 1],
449
+ :L1_SIGNAL_STRENGTH_dBHz => [42, 1, "C"],
284
450
  :L1_LOCK_SEC => [40, 2, "v", proc{|v| 1E-3 * v}],
285
451
  }.each{|k, prop|
286
452
  next unless v = loader.call(*prop)
287
- meas.add(svid, GPS::Measurement.const_get(k), v)
453
+ meas.add(svid, k, v)
288
454
  }
289
455
  }
290
456
  after_run.call(run(meas, t_meas), [meas, t_meas])
291
457
  when [0x02, 0x11] # RXM-SFRB
458
+ sys, svid = gnss_serial.call(packet[6 + 1])
292
459
  register_ephemeris(
293
460
  t_meas,
294
- packet[6 + 1],
461
+ sys, svid,
295
462
  packet.slice(6 + 2, 40).each_slice(4).collect{|v|
296
- (v.pack("C*").unpack("V")[0] & 0xFFFFFF) << 6
463
+ res = v.pack("C*").unpack("V")[0]
464
+ (sys == :GPS) ? ((res & 0xFFFFFF) << 6) : res
297
465
  })
298
466
  when [0x02, 0x13] # RXM-SFRBX
299
- next unless (gnss = packet[6]) == 0
467
+ sys, svid = gnss_serial.call(packet[6 + 1], packet[6])
300
468
  register_ephemeris(
301
469
  t_meas,
302
- packet[6 + 1],
470
+ sys, svid,
303
471
  packet.slice(6 + 8, 4 * packet[6 + 4]).each_slice(4).collect{|v|
304
472
  v.pack("C*").unpack("V")[0]
305
473
  })
@@ -309,8 +477,15 @@ class Receiver
309
477
  end
310
478
 
311
479
  def parse_rinex_nav(fname)
312
- $stderr.puts "Read RINEX NAV file (%s): %d items."%[
313
- fname, @solver.gps_space_node.read(fname)]
480
+ items = [
481
+ @solver.gps_space_node,
482
+ @solver.sbas_space_node,
483
+ ].inject(0){|res, sn|
484
+ loaded_items = sn.send(:read, fname)
485
+ raise "Format error! (Not RINEX) #{fname}" if loaded_items < 0
486
+ res + loaded_items
487
+ }
488
+ $stderr.puts "Read RINEX NAV file (%s): %d items."%[fname, items]
314
489
  end
315
490
 
316
491
  def parse_rinex_obs(fname, &b)
@@ -321,30 +496,37 @@ class Receiver
321
496
  GPS::RINEX_Observation::read(fname){|item|
322
497
  $stderr.print '.' if (count += 1) % 1000 == 0
323
498
  t_meas = item[:time]
324
-
499
+
500
+ types ||= Hash[*(item[:meas_types].collect{|sys, values|
501
+ [sys, values.collect.with_index{|type_, i|
502
+ case type_
503
+ when "C1", "C1C"
504
+ [i, :L1_PSEUDORANGE]
505
+ when "L1", "L1C"
506
+ [i, :L1_CARRIER_PHASE]
507
+ when "D1", "D1C"
508
+ [i, :L1_DOPPLER]
509
+ when "S1", "S1C"
510
+ [i, :L1_SIGNAL_STRENGTH_dBHz]
511
+ else
512
+ nil
513
+ end
514
+ }.compact]
515
+ }.flatten(1))]
516
+
325
517
  meas = GPS::Measurement::new
326
- types ||= (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
327
- case type_
328
- when "C1", "C1C"
329
- [i, GPS::Measurement::L1_PSEUDORANGE]
330
- when "L1", "L1C"
331
- [i, GPS::Measurement::L1_CARRIER_PHASE]
332
- when "D1", "D1C"
333
- [i, GPS::Measurement::L1_DOPPLER]
334
- when "S1", "S1C"
335
- [i, GPS::Measurement::L1_SIGNAL_STRENGTH_dBHz]
336
- else
337
- nil
338
- end
339
- }.compact
340
518
  item[:meas].each{|k, v|
341
519
  sys, prn = k
342
- next unless sys == 'G' # GPS only
343
- types.each{|i, type_|
520
+ case sys
521
+ when 'G', ' '
522
+ when 'J'; prn += 192
523
+ else; next
524
+ end
525
+ types[sys] = (types[' '] || []) unless types[sys]
526
+ types[sys].each{|i, type_|
344
527
  meas.add(prn, type_, v[i][0]) if v[i]
345
528
  }
346
529
  }
347
- p meas.to_hash
348
530
  after_run.call(run(meas, t_meas), [meas, t_meas])
349
531
  }
350
532
  $stderr.puts ", %d epochs."%[count]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.1"
5
5
  end
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.1.5
4
+ version: 0.2.1
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: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -69,7 +69,10 @@ files:
69
69
  - ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h
70
70
  - ext/ninja-scan-light/tool/navigation/MagneticField.h
71
71
  - ext/ninja-scan-light/tool/navigation/NTCM.h
72
+ - ext/ninja-scan-light/tool/navigation/QZSS.h
72
73
  - ext/ninja-scan-light/tool/navigation/RINEX.h
74
+ - ext/ninja-scan-light/tool/navigation/SBAS.h
75
+ - ext/ninja-scan-light/tool/navigation/SBAS_Solver.h
73
76
  - ext/ninja-scan-light/tool/navigation/WGS84.h
74
77
  - ext/ninja-scan-light/tool/navigation/coordinate.h
75
78
  - ext/ninja-scan-light/tool/param/bit_array.h
@@ -86,7 +89,6 @@ files:
86
89
  - ext/ninja-scan-light/tool/swig/makefile
87
90
  - ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb
88
91
  - ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb
89
- - gps_pvt.gemspec
90
92
  - lib/gps_pvt.rb
91
93
  - lib/gps_pvt/receiver.rb
92
94
  - lib/gps_pvt/ubx.rb
@@ -97,7 +99,7 @@ licenses: []
97
99
  metadata:
98
100
  homepage_uri: https://github.com/fenrir-naru/gps_pvt
99
101
  source_code_uri: https://github.com/fenrir-naru/gps_pvt
100
- post_install_message:
102
+ post_install_message:
101
103
  rdoc_options: []
102
104
  require_paths:
103
105
  - lib
@@ -112,8 +114,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
114
  - !ruby/object:Gem::Version
113
115
  version: '0'
114
116
  requirements: []
115
- rubygems_version: 3.1.2
116
- signing_key:
117
+ rubygems_version: 3.0.3
118
+ signing_key:
117
119
  specification_version: 4
118
120
  summary: GPS position, velocity, and time (PVT) solver
119
121
  test_files: []