gps_pvt 0.6.4 → 0.7.2

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.
@@ -658,6 +658,73 @@ __ANTEX_TEXT__
658
658
  # https://files.igs.org/pub/station/general/igs14.atx
659
659
  f.path
660
660
  },
661
+ :rinex_clk => Tempfile::open{|f|
662
+ f.write(<<-__RINEX_CLK_TEXT__)
663
+ 3.00 C RINEX VERSION / TYPE
664
+ CCLOCK IGSACC @ NOAA NGS PGM / RUN BY / DATE
665
+ GPS week: 1849 Day: 2 MJD: 57189 COMMENT
666
+ THE COMBINED CLOCKS ARE A WEIGHTED AVERAGE OF: COMMENT
667
+ cod emr esa gfz grg jpl COMMENT
668
+ THE FOLLOWING REFERENCE CLOCKS WERE USED BY ACs: COMMENT
669
+ WSRT AMC2 BRUX HRAO COMMENT
670
+ THE COMBINED CLOCKS ARE ALIGNED TO GPS TIME COMMENT
671
+ USING THE SATELLITE BROADCAST EPHEMERIDES COMMENT
672
+ All clocks have been re-aligned to the IGS time scale: IGST COMMENT
673
+ 16 LEAP SECONDS
674
+ 1 AS # / TYPES OF DATA
675
+ IGS IGSACC @ NOAA NGS ANALYSIS CENTER
676
+ 6 # OF SOLN SATS
677
+ G12 G18 G24 G25 G29 G31 PRN LIST
678
+ G igs08_1848.atx SYS / PCVS APPLIED
679
+ END OF HEADER
680
+ AS G12 2015 06 15 23 45 0.000000 2 3.017937472687e-04 1.069824072610e-11
681
+ AS G18 2015 06 15 23 45 0.000000 2 4.096815344517e-04 9.225410060960e-12
682
+ AS G24 2015 06 15 23 45 0.000000 2 -4.998574751545e-05 2.372704308220e-11
683
+ AS G25 2015 06 15 23 45 0.000000 2 -2.290169594092e-06 1.683218228880e-11
684
+ AS G29 2015 06 15 23 45 0.000000 2 6.168866864097e-04 1.206217360840e-11
685
+ AS G31 2015 06 15 23 45 0.000000 2 3.128244102077e-04 2.173867579920e-11
686
+ AS G12 2015 06 15 23 50 0.000000 2 3.017948525918e-04 1.300769315700e-11
687
+ AS G18 2015 06 15 23 50 0.000000 2 4.096823340968e-04 8.242414325510e-12
688
+ AS G24 2015 06 15 23 50 0.000000 2 -4.998578937856e-05 3.095238106380e-11
689
+ AS G25 2015 06 15 23 50 0.000000 2 -2.291410963946e-06 2.242880773880e-11
690
+ AS G29 2015 06 15 23 50 0.000000 2 6.168873857128e-04 1.695910016600e-11
691
+ AS G31 2015 06 15 23 50 0.000000 2 3.128241216228e-04 1.904362802980e-11
692
+ AS G12 2015 06 15 23 55 0.000000 2 3.017958412577e-04 1.171495359430e-11
693
+ AS G18 2015 06 15 23 55 0.000000 2 4.096833491457e-04 1.008770038860e-11
694
+ AS G24 2015 06 15 23 55 0.000000 2 -4.998596967628e-05 2.600074192880e-11
695
+ AS G25 2015 06 15 23 55 0.000000 2 -2.292650290468e-06 2.671557682290e-11
696
+ AS G29 2015 06 15 23 55 0.000000 2 6.168880208367e-04 1.729095211710e-11
697
+ AS G31 2015 06 15 23 55 0.000000 2 3.128236493567e-04 1.807462018720e-11
698
+ AS G12 2015 06 16 00 00 0.000000 2 3.017967728708e-04 1.000801049580e-11
699
+ AS G18 2015 06 16 00 00 0.000000 2 4.096840539308e-04 1.041670195750e-11
700
+ AS G24 2015 06 16 00 00 0.000000 2 -4.998614775959e-05 1.699628132880e-11
701
+ AS G25 2015 06 16 00 00 0.000000 2 -2.293760646361e-06 1.805118466830e-11
702
+ AS G29 2015 06 16 00 00 0.000000 2 6.168886213338e-04 2.220055594770e-11
703
+ AS G31 2015 06 16 00 00 0.000000 2 3.128233311248e-04 1.215284180360e-11
704
+ AS G12 2015 06 16 00 05 0.000000 2 3.017980078016e-04 1.152529430320e-11
705
+ AS G18 2015 06 16 00 05 0.000000 2 4.096851745816e-04 1.517949585220e-11
706
+ AS G24 2015 06 16 00 05 0.000000 2 -4.998569129850e-05 1.987308774570e-11
707
+ AS G25 2015 06 16 00 05 0.000000 2 -2.295014987154e-06 1.568891027900e-11
708
+ AS G29 2015 06 16 00 05 0.000000 2 6.168894672506e-04 1.906237292480e-11
709
+ AS G31 2015 06 16 00 05 0.000000 2 3.128231387506e-04 1.007829428500e-11
710
+ AS G12 2015 06 16 00 10 0.000000 2 3.017987334831e-04 8.726251168980e-12
711
+ AS G18 2015 06 16 00 10 0.000000 2 4.096859333261e-04 1.403166740100e-11
712
+ AS G24 2015 06 16 00 10 0.000000 2 -4.998597422529e-05 1.727695888430e-11
713
+ AS G25 2015 06 16 00 10 0.000000 2 -2.296262879345e-06 1.460867818340e-11
714
+ AS G29 2015 06 16 00 10 0.000000 2 6.168900483001e-04 1.556536485070e-11
715
+ AS G31 2015 06 16 00 10 0.000000 2 3.128226535741e-04 1.258498073020e-11
716
+ AS G12 2015 06 16 00 15 0.000000 2 3.017999301583e-04 1.006627990480e-11
717
+ AS G18 2015 06 16 00 15 0.000000 2 4.096864636193e-04 1.460541676660e-11
718
+ AS G24 2015 06 16 00 15 0.000000 2 -4.998605241609e-05 1.970841363000e-11
719
+ AS G25 2015 06 16 00 15 0.000000 2 -2.297477664413e-06 1.598459869060e-11
720
+ AS G29 2015 06 16 00 15 0.000000 2 6.168906691983e-04 1.729396319140e-11
721
+ AS G31 2015 06 16 00 15 0.000000 2 3.128222195893e-04 1.358710059900e-11
722
+ __RINEX_CLK_TEXT__
723
+ # modified version. original data are
724
+ # https://cddis.nasa.gov/archive/gnss/products/1849/igs1849[12].clk.Z
725
+ # mirrored: ftp://garner.ucsd.edu/pub/products/1849/igs1849[12].clk.Z
726
+ f.path
727
+ },
661
728
  }}
662
729
  let(:solver){
663
730
  res = GPS::Solver::new
@@ -733,20 +800,20 @@ __ANTEX_TEXT__
733
800
  expect(pvt.position_solved?).to be(true)
734
801
  expect(pvt.receiver_time.to_a).to eq([1849, 172413])
735
802
  expect(pvt.llh.to_a).to eq([:lat, :lng, :alt].collect{|k| pvt.llh.send(k)})
736
- expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-9).of(35.6992591268) # latitude
737
- expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-9).of(139.541502292) # longitude
738
- expect(pvt.llh.alt) .to be_within(1E-4).of(104.279402455) # altitude
739
- expect(pvt.receiver_error).to be_within(1E-4).of(1259087.83603)
740
- expect(pvt.gdop).to be_within(1E-10).of(3.83282723293)
741
- expect(pvt.pdop).to be_within(1E-10).of(3.30873220653)
742
- expect(pvt.hdop).to be_within(1E-10).of(2.05428293774)
743
- expect(pvt.vdop).to be_within(1E-10).of(2.59376761222)
744
- expect(pvt.tdop).to be_within(1E-10).of(1.9346461648)
803
+ expect(pvt.llh.lat / Math::PI * 180).to be_within(1E-3).of(35.7) # latitude
804
+ expect(pvt.llh.lng / Math::PI * 180).to be_within(1E-3).of(139.542) # longitude
805
+ expect(pvt.llh.alt) .to be_within(1E-1).of(104.3) # altitude
806
+ expect(pvt.receiver_error).to be_within(1E-1).of(1259087.8)
807
+ expect(pvt.gdop).to be_within(1E-2).of(2.42)
808
+ expect(pvt.pdop).to be_within(1E-2).of(2.17)
809
+ expect(pvt.hdop).to be_within(1E-2).of(1.11)
810
+ expect(pvt.vdop).to be_within(1E-2).of(1.87)
811
+ expect(pvt.tdop).to be_within(1E-2).of(1.08)
745
812
  expect(pvt.velocity.to_a).to eq([:e, :n, :u].collect{|k| pvt.velocity.send(k)})
746
- expect(pvt.velocity.north).to be_within(1E-7).of(-0.839546227836) # north
747
- expect(pvt.velocity.east) .to be_within(1E-7).of(-1.05805616381) # east
748
- expect(pvt.velocity.down) .to be_within(1E-7).of(-0.12355474006) # down
749
- expect(pvt.receiver_error_rate).to be_within(1E-7).of(-1061.92654151)
813
+ expect(pvt.velocity.north).to be_within(1E-2).of(-0.86) # north
814
+ expect(pvt.velocity.east) .to be_within(1E-2).of(-1.10) # east
815
+ expect(pvt.velocity.down) .to be_within(1E-2).of(-0.22) # down
816
+ expect(pvt.receiver_error_rate).to be_within(1E-2).of(-1061.86)
750
817
  expect(pvt.G.rows).to eq(6)
751
818
  expect(pvt.W.rows).to eq(6)
752
819
  expect(pvt.delta_r.rows).to eq(6)
@@ -778,6 +845,10 @@ __ANTEX_TEXT__
778
845
 
779
846
  it 'can be modified through hooks' do
780
847
  sn = solver.gps_space_node
848
+ expect(solver.options).to be_a_kind_of(Hash)
849
+ expect(solver.options.keys).to include(:skip_exclusion)
850
+ expect{solver.options = {:skip_exclusion => true}}.not_to raise_error
851
+ expect(solver.options[:skip_exclusion]).to eq(true)
781
852
  expect(solver.correction[:gps_ionospheric]).to include(:klobuchar)
782
853
  expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
783
854
  expect{solver.correction = nil}.to raise_error(RuntimeError)
@@ -937,5 +1008,55 @@ __ANTEX_TEXT__
937
1008
  puts ([:lat, :lng].collect{|f| pvt.llh.send(f) / Math::PI * 180} + [pvt.llh.alt]).inspect
938
1009
  }
939
1010
  end
1011
+ it 'calculates satellite clock error based on RINEX clock' do
1012
+ clk, sn = [GPS::RINEX_Clock::new, solver.gps_space_node]
1013
+ expect(clk.read(input[:rinex_clk])).to eq(6 * 7)
1014
+ proc{|sats|
1015
+ expect(sats.kind_of?(Array)).to eq(true)
1016
+ expect(sats[clk.class::SYS_GPS]).to eq(6)
1017
+ }.call(clk.satellites)
1018
+ sn.read(input[:rinex_nav])
1019
+ t0 = GPS::Time::new(1849, 172800)
1020
+ sn.update_all_ephemeris(t0)
1021
+ (-5..5).step(1){|dt_min|
1022
+ t = t0 + (dt_min * 60)
1023
+ [12, 18, 24, 25, 29, 31].each{|sat_id|
1024
+ eph = sn.ephemeris(sat_id).constellation(t)
1025
+ expect(clk.clock_error(sat_id, t)).to be_within(1E-7).of(eph[2]) # 100 ns
1026
+ expect(clk.clock_error_dot(sat_id, t)).to be_within(1E-10).of(eph[3]) # 100 ps
1027
+ }
1028
+ }
1029
+ end
1030
+ it 'calculates position without any error with RINEX NAV and CLK' do
1031
+ sn = solver.gps_space_node
1032
+ sn.read(input[:rinex_nav])
1033
+ clk = GPS::RINEX_Clock::new
1034
+ clk.read(input[:rinex_clk])
1035
+ expect(clk.push(solver, clk.class::SYS_GPS)).to eq(true)
1036
+ GPS::RINEX_Observation::read(input[:rinex_obs]){|item|
1037
+ t_meas = item[:time]
1038
+ sn.update_all_ephemeris(t_meas)
1039
+ meas = GPS::Measurement::new
1040
+ types = (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
1041
+ type_ = {
1042
+ "C1" => :L1_PSEUDORANGE,
1043
+ "D1" => :L1_RANGE_RATE,
1044
+ }[type_]
1045
+ type_ && [i, GPS::Measurement::const_get(type_)]
1046
+ }.compact
1047
+ item[:meas].each{|k, v|
1048
+ sys, prn = k
1049
+ next unless sys == 'G' # GPS only
1050
+ types.each{|i, type_|
1051
+ meas.add(prn, type_, v[i][0]) if v[i]
1052
+ }
1053
+ }
1054
+ pvt = solver.solve(meas, t_meas)
1055
+ expect(pvt.position_solved?).to eq(true)
1056
+ [-3952590.4754, 3360273.8926, 3697987.2632].zip(pvt.xyz.to_a).each{|a, b|
1057
+ expect(a).to be_within(1E+2).of(b) # 10 m
1058
+ }
1059
+ }
1060
+ end
940
1061
  end
941
1062
  end
@@ -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,9 +150,8 @@ 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
157
  solver_opts = [:gps_options, :sbas_options, :glonass_options].collect{|target|
@@ -165,8 +165,10 @@ class Receiver
165
165
  output_options = {
166
166
  :system => [[:GPS, 1..32], [:QZSS, 193..202]],
167
167
  :satellites => (1..32).to_a + (193..202).to_a, # [idx, ...] or [[idx, label], ...] is acceptable
168
+ :FDE => false,
168
169
  }
169
170
  options = options.reject{|k, v|
171
+ def v.to_b; !(self =~ /^(?:false|0|f|off)$/i); end
170
172
  case k
171
173
  when :debug
172
174
  v = v.split(/,/)
@@ -174,7 +176,7 @@ class Receiver
174
176
  next true
175
177
  when :weight
176
178
  case v.to_sym
177
- when :elevation # (same as underneath C++ library)
179
+ when :elevation # (same as underneath C++ library except for ignoring broadcasted/calculated URA)
178
180
  @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
179
181
  if rel_prop[0] > 0 then
180
182
  elv = Coordinate::ENU::relative_rel(
@@ -184,7 +186,11 @@ class Receiver
184
186
  rel_prop
185
187
  }
186
188
  next true
187
- when :identical # same as default
189
+ when :identical # treat each satellite range having same accuracy
190
+ @solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
191
+ rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
192
+ rel_prop
193
+ }
188
194
  next true
189
195
  end
190
196
  when :elevation_mask_deg
@@ -280,6 +286,9 @@ class Receiver
280
286
  $stderr.puts "#{mode.capitalize} satellite: #{[sys, svid].compact.join(':')}"
281
287
  }
282
288
  next true
289
+ when :fault_exclusion
290
+ @solver.options = {:skip_exclusion => !(output_options[:FDE] = v.to_b)}
291
+ next true
283
292
  end
284
293
  false
285
294
  }
@@ -651,5 +660,21 @@ class Receiver
651
660
  raise "Format error! (Not ANTEX) #{src}" unless applied_items >= 0
652
661
  $stderr.puts "SP3 correction with ANTEX file (%s): %d items have been processed."%[src, applied_items]
653
662
  end
663
+
664
+ def attach_rinex_clk(src)
665
+ fname = Util::get_txt(src)
666
+ @clk ||= GPS::RINEX_Clock::new
667
+ read_items = @clk.read(fname)
668
+ raise "Format error! (Not RINEX clock) #{src}" if read_items < 0
669
+ $stderr.puts "Read RINEX clock file (%s): %d items."%[src, read_items]
670
+ sats = @clk.satellites
671
+ @clk.class.constants.each{|sys|
672
+ next unless /^SYS_(?!SYSTEMS)(.*)/ =~ sys.to_s
673
+ idx, sys_name = [@clk.class.const_get(sys), $1]
674
+ next unless sats[idx] > 0
675
+ next unless @clk.push(@solver, idx)
676
+ $stderr.puts "Change clock error source of #{sys_name} to RINEX clock"
677
+ }
678
+ end
654
679
  end
655
680
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.6.4"
4
+ VERSION = "0.7.2"
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.6.4
4
+ version: 0.7.2
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-19 00:00:00.000000000 Z
11
+ date: 2022-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -78,6 +78,7 @@ files:
78
78
  - ext/ninja-scan-light/tool/navigation/NTCM.h
79
79
  - ext/ninja-scan-light/tool/navigation/QZSS.h
80
80
  - ext/ninja-scan-light/tool/navigation/RINEX.h
81
+ - ext/ninja-scan-light/tool/navigation/RINEX_Clock.h
81
82
  - ext/ninja-scan-light/tool/navigation/SBAS.h
82
83
  - ext/ninja-scan-light/tool/navigation/SBAS_Solver.h
83
84
  - ext/ninja-scan-light/tool/navigation/SP3.h