gps_pvt 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,6 +19,7 @@ class Receiver
19
19
  opt = {
20
20
  :system => [[:GPS, 1..32]],
21
21
  :satellites => (1..32).to_a,
22
+ :FDE => true,
22
23
  }.merge(opt)
23
24
  [[
24
25
  [:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec_rcv_UTC],
@@ -106,7 +107,7 @@ class Receiver
106
107
  el_deg = [4, 6].collect{|i| pvt.elevation[fd[i]] / Math::PI * 180}
107
108
  fd[0..4] + [el_deg[0]] + fd[5..6] + [el_deg[1]]
108
109
  }
109
- ]] + [[
110
+ ]] + (opt[:FDE] ? [[
110
111
  [:wssr_FDE_min, :wssr_FDE_min_PRN, :wssr_FDE_2nd, :wssr_FDE_2nd_PRN],
111
112
  proc{|pvt|
112
113
  [:fde_min, :fde_2nd].collect{|f|
@@ -115,7 +116,7 @@ class Receiver
115
116
  [info[0], info[-3]]
116
117
  }.flatten
117
118
  }
118
- ]]
119
+ ]] : [])
119
120
  end
120
121
 
121
122
  def self.meas_items(opt = {})
@@ -149,11 +150,11 @@ class Receiver
149
150
 
150
151
  def initialize(options = {})
151
152
  @solver = GPS::Solver::new
152
- @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
153
- rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
154
- rel_prop
153
+ @solver.options = {
154
+ :skip_exclusion => true, # default is to skip fault exclusion calculation
155
155
  }
156
156
  @debug = {}
157
+ @semaphore = Mutex::new
157
158
  solver_opts = [:gps_options, :sbas_options, :glonass_options].collect{|target|
158
159
  @solver.send(target)
159
160
  }
@@ -165,8 +166,10 @@ class Receiver
165
166
  output_options = {
166
167
  :system => [[:GPS, 1..32], [:QZSS, 193..202]],
167
168
  :satellites => (1..32).to_a + (193..202).to_a, # [idx, ...] or [[idx, label], ...] is acceptable
169
+ :FDE => false,
168
170
  }
169
171
  options = options.reject{|k, v|
172
+ def v.to_b; !(self =~ /^(?:false|0|f|off)$/i); end
170
173
  case k
171
174
  when :debug
172
175
  v = v.split(/,/)
@@ -174,7 +177,7 @@ class Receiver
174
177
  next true
175
178
  when :weight
176
179
  case v.to_sym
177
- when :elevation # (same as underneath C++ library)
180
+ when :elevation # (same as underneath C++ library except for ignoring broadcasted/calculated URA)
178
181
  @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
179
182
  if rel_prop[0] > 0 then
180
183
  elv = Coordinate::ENU::relative_rel(
@@ -184,7 +187,11 @@ class Receiver
184
187
  rel_prop
185
188
  }
186
189
  next true
187
- when :identical # same as default
190
+ when :identical # treat each satellite range having same accuracy
191
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
192
+ rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
193
+ rel_prop
194
+ }
188
195
  next true
189
196
  end
190
197
  when :elevation_mask_deg
@@ -280,6 +287,9 @@ class Receiver
280
287
  $stderr.puts "#{mode.capitalize} satellite: #{[sys, svid].compact.join(':')}"
281
288
  }
282
289
  next true
290
+ when :fault_exclusion
291
+ @solver.options = {:skip_exclusion => !(output_options[:FDE] = v.to_b)}
292
+ next true
283
293
  end
284
294
  false
285
295
  }
@@ -289,6 +299,24 @@ class Receiver
289
299
  :meas => Receiver::meas_items(output_options),
290
300
  }
291
301
  end
302
+
303
+ def critical(&b)
304
+ begin
305
+ @semaphore.synchronize{b.call}
306
+ rescue ThreadError # recovery from deadlock
307
+ b.call
308
+ end
309
+ end
310
+
311
+ class << self
312
+ def make_critical(fname)
313
+ f_orig = instance_method(fname)
314
+ define_method(fname){|*args, &b|
315
+ critical{f_orig.bind(self).call(*args, &b)}
316
+ }
317
+ end
318
+ private :make_critical
319
+ end
292
320
 
293
321
  GPS::Measurement.class_eval{
294
322
  proc{
@@ -322,7 +350,7 @@ class Receiver
322
350
  =end
323
351
 
324
352
  #@solver.gps_space_node.update_all_ephemeris(t_meas) # internally called in the following solver.solve
325
- pvt = @solver.solve(meas, t_meas)
353
+ pvt = critical{@solver.solve(meas, t_meas)}
326
354
  pvt.define_singleton_method(:rel_ENU){
327
355
  Coordinate::ENU::relative(xyz, ref_pos)
328
356
  } if (ref_pos && pvt.position_solved?)
@@ -369,60 +397,59 @@ class Receiver
369
397
  }
370
398
  }
371
399
 
372
- proc{
373
- eph_list = Hash[*((1..32).to_a + (193..202).to_a).collect{|prn|
400
+ def register_ephemeris(t_meas, sys, prn, bcast_data, *options)
401
+ @eph_list ||= Hash[*((1..32).to_a + (193..202).to_a).collect{|prn|
374
402
  eph = GPS::Ephemeris::new
375
403
  eph.svid = prn
376
404
  [prn, eph]
377
405
  }.flatten(1)]
378
- eph_glonass_list = Hash[*(1..24).collect{|num|
406
+ @eph_glonass_list ||= Hash[*(1..24).collect{|num|
379
407
  eph = GPS::Ephemeris_GLONASS::new
380
408
  eph.svid = num
381
409
  [num, eph]
382
410
  }.flatten(1)]
383
- define_method(:register_ephemeris){|t_meas, sys, prn, bcast_data, *options|
384
- opt = options[0] || {}
385
- case sys
386
- when :GPS, :QZSS
387
- next unless eph = eph_list[prn]
388
- sn = @solver.gps_space_node
389
- subframe, iodc_or_iode = eph.parse(bcast_data)
390
- if iodc_or_iode < 0 then
391
- begin
392
- sn.update_iono_utc(
393
- GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
394
- [:alpha, :beta].each{|k|
395
- $stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
396
- } if false
397
- rescue
398
- end
399
- next
411
+ opt = options[0] || {}
412
+ case sys
413
+ when :GPS, :QZSS
414
+ return unless eph = @eph_list[prn]
415
+ sn = @solver.gps_space_node
416
+ subframe, iodc_or_iode = eph.parse(bcast_data)
417
+ if iodc_or_iode < 0 then
418
+ begin
419
+ sn.update_iono_utc(
420
+ GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
421
+ [:alpha, :beta].each{|k|
422
+ $stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
423
+ } if false
424
+ rescue
400
425
  end
401
- if t_meas and eph.consistent? then
402
- eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
403
- sn.register_ephemeris(prn, eph)
404
- eph.invalidate
405
- end
406
- when :SBAS
407
- case @solver.sbas_space_node.decode_message(bcast_data[0..7], prn, t_meas)
408
- when 26
409
- ['', "IGP broadcasted by PRN#{prn} @ #{Time::utc(*t_meas.c_tm)}",
410
- @solver.sbas_space_node.ionospheric_grid_points(prn)].each{|str|
411
- $stderr.puts str
412
- } if @debug[:SBAS_IGP]
413
- end if t_meas
414
- when :GLONASS
415
- next unless eph = eph_glonass_list[prn]
416
- leap_sec = @solver.gps_space_node.is_valid_utc ?
417
- @solver.gps_space_node.iono_utc.delta_t_LS :
418
- GPS::Time::guess_leap_seconds(t_meas)
419
- next unless eph.parse(bcast_data[0..3], leap_sec)
420
- eph.freq_ch = opt[:freq_ch] || 0
421
- @solver.glonass_space_node.register_ephemeris(prn, eph)
426
+ return
427
+ end
428
+ if t_meas and eph.consistent? then
429
+ eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
430
+ sn.register_ephemeris(prn, eph)
422
431
  eph.invalidate
423
432
  end
424
- }
425
- }.call
433
+ when :SBAS
434
+ case @solver.sbas_space_node.decode_message(bcast_data[0..7], prn, t_meas)
435
+ when 26
436
+ ['', "IGP broadcasted by PRN#{prn} @ #{Time::utc(*t_meas.c_tm)}",
437
+ @solver.sbas_space_node.ionospheric_grid_points(prn)].each{|str|
438
+ $stderr.puts str
439
+ } if @debug[:SBAS_IGP]
440
+ end if t_meas
441
+ when :GLONASS
442
+ return unless eph = @eph_glonass_list[prn]
443
+ leap_sec = @solver.gps_space_node.is_valid_utc ?
444
+ @solver.gps_space_node.iono_utc.delta_t_LS :
445
+ GPS::Time::guess_leap_seconds(t_meas)
446
+ return unless eph.parse(bcast_data[0..3], leap_sec)
447
+ eph.freq_ch = opt[:freq_ch] || 0
448
+ @solver.glonass_space_node.register_ephemeris(prn, eph)
449
+ eph.invalidate
450
+ end
451
+ end
452
+ make_critical :register_ephemeris
426
453
 
427
454
  def parse_ubx(ubx_fname, &b)
428
455
  $stderr.print "Reading UBX file (%s) "%[ubx_fname]
@@ -555,7 +582,7 @@ class Receiver
555
582
  @solver.sbas_space_node,
556
583
  @solver.glonass_space_node,
557
584
  ].inject(0){|res, sn|
558
- loaded_items = sn.send(:read, fname)
585
+ loaded_items = critical{sn.send(:read, fname)}
559
586
  raise "Format error! (Not RINEX) #{src}" if loaded_items < 0
560
587
  res + loaded_items
561
588
  }
@@ -639,7 +666,7 @@ class Receiver
639
666
  next unless /^SYS_(?!SYSTEMS)(.*)/ =~ sys.to_s
640
667
  idx, sys_name = [@sp3.class.const_get(sys), $1]
641
668
  next unless sats[idx] > 0
642
- next unless @sp3.push(@solver, idx)
669
+ next unless critical{@sp3.push(@solver, idx)}
643
670
  $stderr.puts "Change ephemeris source of #{sys_name} to SP3"
644
671
  }
645
672
  end
@@ -647,7 +674,7 @@ class Receiver
647
674
  def attach_antex(src)
648
675
  fname = Util::get_txt(src)
649
676
  raise "Specify SP3 before ANTEX application!" unless @sp3
650
- applied_items = @sp3.apply_antex(fname)
677
+ applied_items = critical{@sp3.apply_antex(fname)}
651
678
  raise "Format error! (Not ANTEX) #{src}" unless applied_items >= 0
652
679
  $stderr.puts "SP3 correction with ANTEX file (%s): %d items have been processed."%[src, applied_items]
653
680
  end
@@ -663,7 +690,7 @@ class Receiver
663
690
  next unless /^SYS_(?!SYSTEMS)(.*)/ =~ sys.to_s
664
691
  idx, sys_name = [@clk.class.const_get(sys), $1]
665
692
  next unless sats[idx] > 0
666
- next unless @clk.push(@solver, idx)
693
+ next unless critical{@clk.push(@solver, idx)}
667
694
  $stderr.puts "Change clock error source of #{sys_name} to RINEX clock"
668
695
  }
669
696
  end
data/lib/gps_pvt/util.rb CHANGED
@@ -1,7 +1,39 @@
1
- require 'open-uri'
2
1
  require 'tempfile'
3
2
  require 'uri'
4
3
 
4
+ proc{
5
+ # port[:baudrate], baudrate default is 115200
6
+ Serial.class_eval{
7
+ const_set(:SPEC,
8
+ if RubySerial::ON_WINDOWS then
9
+ %r{^(?:\\\\.\\)?(COM\d+)(?::(\d+))?$}
10
+ elsif RubySerial::ON_LINUX then
11
+ %r{^(/dev/tty[^:]+)(?::(\d+))?$}
12
+ else
13
+ nil
14
+ end)
15
+ }
16
+ Serial.class_eval{
17
+ read_orig = instance_method(:read)
18
+ define_method(:read){|len|
19
+ buf = ''
20
+ f = read_orig.bind(self)
21
+ buf += f.call(len - buf.size) while buf.size < len
22
+ buf
23
+ }
24
+ def eof?; false; end
25
+ }
26
+ Kernel.instance_eval{
27
+ open_orig = method(:open)
28
+ define_method(:open){|*args, &b|
29
+ return open_orig.call(*args, &b) unless Serial::SPEC =~ args[0]
30
+ Serial::new($1, $2 ? $2.to_i : 115200)
31
+ }
32
+ }
33
+ }.call if require 'rubyserial'
34
+
35
+ require 'open-uri'
36
+
5
37
  module GPS_PVT
6
38
  module Util
7
39
  class << self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gps_pvt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
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: 2022-08-24 00:00:00.000000000 Z
11
+ date: 2022-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyserial
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement