gps_pvt 0.7.0 → 0.8.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.
@@ -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