gps_pvt 0.9.2 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,8 @@ require 'SylphideMath.so'
6
6
  require 'matrix'
7
7
 
8
8
  shared_examples 'Matrix' do
9
+ let!(:tolerance){SylphideMath::tolerance}
10
+ after{SylphideMath::tolerance = tolerance}
9
11
  let(:params){{
10
12
  :rc => [8, 8],
11
13
  :acceptable_delta => 1E-10,
@@ -53,7 +55,7 @@ shared_examples 'Matrix' do
53
55
  [:row_size, :column_size].each{|f|
54
56
  a = a_gen.call
55
57
  a.define_singleton_method(f){-1}
56
- expect{ mat_type::new(a) }.to raise_error(RuntimeError)
58
+ expect{ mat_type::new(a) }.to raise_error(ArgumentError)
57
59
  }
58
60
  end
59
61
  it 'is invoked with I, identity, unit' do
@@ -70,12 +72,12 @@ shared_examples 'Matrix' do
70
72
  end
71
73
  it 'sets its elements with [], [[]], Matrix' do
72
74
  expect( mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten).to_a ).to eq(compare_with)
73
- expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten[0..-2]) }.to raise_error(RuntimeError)
75
+ expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten[0..-2]) }.to raise_error(ArgumentError)
74
76
  expect( mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten + [gen_elm.call]).to_a ).to eq(compare_with)
75
- expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten[0..-2] + [nil]) }.to raise_error(RuntimeError)
77
+ expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with.flatten[0..-2] + [nil]) }.to raise_error(ArgumentError)
76
78
 
77
79
  expect( mat_type::new(params[:rc][0], params[:rc][1], compare_with).to_a ).to eq(compare_with)
78
- expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with[0..-2]) }.to raise_error(RuntimeError)
80
+ expect{ mat_type::new(params[:rc][0], params[:rc][1], compare_with[0..-2]) }.to raise_error(ArgumentError)
79
81
  expect( mat_type::new(params[:rc][0], params[:rc][1], compare_with + [params[:rc][1].times.map{gen_elm.call}]).to_a ).to eq(compare_with)
80
82
  expect( mat_type::new(compare_with).to_a ).to eq(compare_with)
81
83
 
@@ -83,7 +85,7 @@ shared_examples 'Matrix' do
83
85
  end
84
86
  it 'sets its elements with {}' do
85
87
  expect( mat_type::new(*params[:rc]){|i, j| compare_with[i][j]}.to_a ).to eq(compare_with)
86
- expect{ mat_type::new(*params[:rc]){nil}.to_a }.to raise_error(RuntimeError)
88
+ expect{ mat_type::new(*params[:rc]){nil}.to_a }.to raise_error(ArgumentError)
87
89
  expect{ mat_type::new(compare_with){|i, j| compare_with[i][j]} }.to raise_error(ArgumentError)
88
90
  expect{ mat_type::new(Matrix[*compare_with]){|i, j| compare_with[i][j]} }.to raise_error(ArgumentError)
89
91
 
@@ -92,17 +94,25 @@ shared_examples 'Matrix' do
92
94
  end
93
95
 
94
96
  describe 'property' do
95
- let(:mat){{
96
- :square => proc{
97
+ let(:mat_gen){{
98
+ :square => proc{|r| # example: [[1, 3, 6], [2, 5, 8], [4, 7, 9]]
97
99
  k = 0
98
- mat_type::new(params[:rc][0], params[:rc][0]){|i, j| k += 1}
99
- }.call,
100
+ res = mat_type::new(r, r)
101
+ (r * 2 - 1).times{|ij|
102
+ (([ij - r + 1, 0].max)..([ij, r - 1].min)).each{|i| res[ij - i, i] = (k += 1)}
103
+ }
104
+ res
105
+ }
106
+ }}
107
+ let(:mat){{
108
+ :square => mat_gen[:square].call(params[:rc][0]),
100
109
  :not_square => proc{
101
110
  k = 0
102
111
  mat_type::new(params[:rc][0], params[:rc][0] * 2){|i, j| k += 1}
103
112
  }.call,
104
113
  :diagonal => mat_type::new(params[:rc][0], params[:rc][0]){|i, j| i == j ? 1 : 0},
105
114
  :symmetric => mat_type::new(params[:rc][0], params[:rc][0]){|i, j| i + j},
115
+ :unit => mat_type::I(params[:rc][0]),
106
116
  }}
107
117
  describe 'is checked with' do
108
118
  it 'square?' do
@@ -117,12 +127,52 @@ shared_examples 'Matrix' do
117
127
  expect(mat[:diagonal].diagonal?) .to eq(true)
118
128
  expect(mat[:symmetric].diagonal?) .to eq(false)
119
129
  end
130
+ it 'lower_triangular?' do
131
+ expect(mat[:square].lower_triangular?) .to eq(false)
132
+ expect(mat[:not_square].lower_triangular?) .to eq(false)
133
+ expect(mat[:diagonal].lower_triangular?) .to eq(true)
134
+ expect(mat[:symmetric].lower_triangular?) .to eq(false)
135
+ end
136
+ it 'upper_triangular?' do
137
+ expect(mat[:square].upper_triangular?) .to eq(false)
138
+ expect(mat[:not_square].upper_triangular?) .to eq(false)
139
+ expect(mat[:diagonal].upper_triangular?) .to eq(true)
140
+ expect(mat[:symmetric].upper_triangular?) .to eq(false)
141
+ end
120
142
  it 'symmetric?' do
121
143
  expect(mat[:square].symmetric?) .to eq(false)
122
144
  expect(mat[:not_square].symmetric?) .to eq(false)
123
145
  expect(mat[:diagonal].symmetric?) .to eq(true)
124
146
  expect(mat[:symmetric].symmetric?) .to eq(true)
125
147
  end
148
+ it 'hermitian?' do
149
+ expect(mat[:square].hermitian?) .to eq(false)
150
+ expect(mat[:not_square].hermitian?) .to eq(false)
151
+ expect(mat[:diagonal].hermitian?) .to eq(true)
152
+ expect(mat[:symmetric].hermitian?) .to eq(true)
153
+ end
154
+ it 'skew_symmetric?' do
155
+ expect(mat[:square].skew_symmetric?) .to eq(false)
156
+ expect(mat[:not_square].skew_symmetric?) .to eq(false)
157
+ expect(mat[:diagonal].skew_symmetric?) .to eq(true)
158
+ expect(mat[:symmetric].skew_symmetric?) .to eq(false)
159
+ end
160
+ it 'normal?' do
161
+ expect(mat[:square].normal?) .to eq(false)
162
+ expect(mat[:not_square].normal?) .to eq(false)
163
+ expect(mat[:diagonal].normal?) .to eq(true)
164
+ expect(mat[:symmetric].normal?) .to eq(true)
165
+ end
166
+ it 'orthogonal?' do
167
+ expect(mat[:square].orthogonal?) .to eq(false)
168
+ expect(mat[:not_square].orthogonal?).to eq(false)
169
+ expect(mat[:unit].orthogonal?) .to eq(true)
170
+ end
171
+ it 'unitary?' do
172
+ expect(mat[:square].unitary?) .to eq(false)
173
+ expect(mat[:not_square].unitary?) .to eq(false)
174
+ expect(mat[:unit].unitary?) .to eq(true)
175
+ end
126
176
  it 'different_size?' do
127
177
  mat.keys.combination(2).each{|mat1, mat2|
128
178
  expect(mat[mat1].different_size?(mat[mat2])).to eq([mat1, mat2].include?(:not_square))
@@ -142,10 +192,40 @@ shared_examples 'Matrix' do
142
192
  end
143
193
  it 'determinant, det' do
144
194
  [:determinant, :det].each{|f|
145
- #expect(mat[:square].send(f)).to eq(Matrix[*mat[:square].to_a].det)
195
+ expect(mat[:square].send(f)).to eq(Matrix[*mat[:square].to_a].det)
146
196
  expect{mat[:not_square].send(f)}.to raise_error(RuntimeError)
147
197
  }
148
198
  end
199
+ it 'rank' do
200
+ (5..8).each{|n|
201
+ orig = mat_gen[:square].call(n)
202
+ expect(orig.rank).to eq(Matrix[*orig.to_a].rank)
203
+ }
204
+ expect(mat[:symmetric].rank).to eq(Matrix[*mat[:symmetric].to_a].rank)
205
+ expect{mat[:not_square].rank}.to raise_error(RuntimeError)
206
+ end
207
+ it 'cofactor' do
208
+ SylphideMath::tolerance = 1E-10
209
+ (5..8).each{|n|
210
+ orig = mat_gen[:square].call(n)
211
+ cmp = Matrix[*orig.to_a]
212
+ orig.rows.times{|i|
213
+ orig.columns.times{|j|
214
+ a, b = [orig, cmp].collect{|item| item.cofactor(i, j)} #rescue next
215
+ expect((a - b).abs).to be < params[:acceptable_delta]
216
+ }
217
+ }
218
+ }
219
+ end
220
+ it 'adjugate' do
221
+ SylphideMath::tolerance = 1E-10
222
+ (5..8).each{|n|
223
+ orig = mat_gen[:square].call(n)
224
+ (Matrix[*orig.adjugate.to_a] - Matrix[*orig.to_a].adjugate).each{|v|
225
+ expect(v.abs).to be < params[:acceptable_delta]
226
+ }
227
+ }
228
+ end
149
229
  end
150
230
  end
151
231
 
@@ -257,6 +337,8 @@ shared_examples 'Matrix' do
257
337
  expect(mat.adjoint.to_a).to eq(Matrix[*compare_with].conj.t.to_a)
258
338
  end
259
339
  it 'supports submatrix with partial' do
340
+ expect(mat.partial(params[:rc][0] - 1, params[:rc][1] - 1).to_a) \
341
+ .to eq(Matrix[*compare_with[0..-2].collect{|values| values[0..-2]}].to_a)
260
342
  expect(mat.partial(params[:rc][0] - 1, params[:rc][1] - 1, 1, 1).to_a) \
261
343
  .to eq(Matrix[*compare_with[1..-1].collect{|values| values[1..-1]}].to_a)
262
344
  end
@@ -280,6 +362,14 @@ shared_examples 'Matrix' do
280
362
  }
281
363
  }
282
364
  end
365
+ it 'generates minor matrix with first_minor' do
366
+ params[:rc][0].times{|i|
367
+ params[:rc][1].times{|j|
368
+ expect(mat.first_minor(i, j).to_a) \
369
+ .to eq(Matrix[*compare_with].first_minor(i, j).to_a)
370
+ }
371
+ }
372
+ end
283
373
  end
284
374
 
285
375
  describe 'iterator' do
@@ -310,6 +400,19 @@ shared_examples 'Matrix' do
310
400
  }
311
401
  }
312
402
  end
403
+ it 'supports index, find_index' do
404
+ cnd = proc{|v| v.abs >= 0.5}
405
+ [:index, :find_index].each{|func|
406
+ opt.each{|k, indices|
407
+ expect(mat.send(*[func, k].compact, &cnd)).to eq(
408
+ indices.select{|i, j| cnd.call(compare_with[i][j])}.first)
409
+ indices.each{|i, j|
410
+ expect(mat.send(*[func, compare_with[i][j], k].compact)).to eq([i, j])
411
+ }
412
+ expect(mat.send(*[func, 1, k].compact)).to be(nil)
413
+ }
414
+ }
415
+ end
313
416
  it 'supports map, collect, map_with_index, collect_with_index' do
314
417
  [:map, :collect, :map_with_index, :collect_with_index].each{|func|
315
418
  opt.each{|k, indices|
@@ -372,7 +475,7 @@ shared_examples 'Matrix' do
372
475
  end
373
476
  it 'have +(scalar)' do
374
477
  expect((mat[0] + 1).to_a).to eq((Matrix[*compare_with[0]] + Matrix::unit(params[:rc][0])).to_a)
375
- expect{mat[2] + 1}.to raise_error(RuntimeError)
478
+ expect{mat[2] + 1}.to raise_error(ArgumentError)
376
479
  end
377
480
  it 'have +(mat)' do
378
481
  [[0, 1], [2, 3]].each{|i, j|
@@ -381,7 +484,7 @@ shared_examples 'Matrix' do
381
484
  end
382
485
  it 'have -(scalar)' do
383
486
  expect((mat[0] - 1).to_a).to eq((Matrix[*compare_with[0]] - Matrix::unit(params[:rc][0])).to_a)
384
- expect{mat[2] - 1}.to raise_error(RuntimeError)
487
+ expect{mat[2] - 1}.to raise_error(ArgumentError)
385
488
  end
386
489
  it 'have -(mat)' do
387
490
  [[0, 1], [2, 3]].each{|i, j|
@@ -395,9 +498,16 @@ shared_examples 'Matrix' do
395
498
  end
396
499
  it 'have *(mat)' do
397
500
  expect((mat[0] * mat[1]).to_a).to eq((Matrix[*compare_with[0]] * Matrix[*compare_with[1]]).to_a)
398
- expect{mat[2] * mat[3]}.to raise_error(RuntimeError)
501
+ expect{mat[2] * mat[3]}.to raise_error(ArgumentError)
399
502
  expect((mat[2] * mat[3].t).to_a).to eq((Matrix[*compare_with[2]] * Matrix[*compare_with[3]].t).to_a)
400
503
  end
504
+ it 'have entrywise_product(mat), a.k.a. .*(mat)' do
505
+ [:entrywise_product, :hadamard_product].each{|func|
506
+ [[0, 1], [2, 3]].each{|i, j|
507
+ expect((mat[i].send(func, mat[j])).to_a).to eq((Matrix[*compare_with[i]].send(func, Matrix[*compare_with[j]])).to_a)
508
+ }
509
+ } if Gem::Version::create(RUBY_VERSION) >= Gem::Version::create("2.5.0")
510
+ end
401
511
  it 'have /(scalar)' do
402
512
  expect((mat[0] / 2).to_a).to eq((Matrix[*compare_with[0]] / 2).to_a)
403
513
  expect((mat[2] / 2).to_a).to eq((Matrix[*compare_with[2]] / 2).to_a)
data/gps_pvt.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = spec.homepage
20
- #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
+ #spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
21
21
 
22
22
  spec.extensions = ["ext/gps_pvt/extconf.rb"]
23
23
 
@@ -63,6 +63,7 @@ Gem::Specification.new do |spec|
63
63
  spec.add_development_dependency "rake-compiler"
64
64
  spec.add_development_dependency "rspec", "~> 3.0"
65
65
  spec.add_development_dependency "matrix" if GPS_PVT::version_compare(RUBY_VERSION, "3.1") >= 0
66
+ spec.add_development_dependency "github_changelog_generator"
66
67
 
67
68
  # For more information and examples about making a new gem, checkout our
68
69
  # guide at: https://bundler.io/guides/creating_gem.html
data/lib/gps_pvt/ntrip.rb CHANGED
@@ -54,7 +54,7 @@ class Ntrip < Net::HTTP
54
54
  llh0 = Coordinate::LLH::new(D2R * lat_deg, D2R * lng_deg, 0)
55
55
  collect{|pt, prop|
56
56
  llh = Coordinate::LLH::new(*([:latitude, :longitude].collect{|k| D2R * prop[k].to_f} + [0]))
57
- [llh0.xyz.dist(llh.xyz), prop]
57
+ [llh0.xyz.distance(llh.xyz), prop]
58
58
  }.sort{|a, b| a[0] <=> b[0]} # return [distance, property]
59
59
  end
60
60
  end
@@ -18,6 +18,13 @@ class Receiver
18
18
  else; raise "reference time (#{ref_time}) should be GPS::Time or Time"
19
19
  end
20
20
  leap_sec = ref_time.leap_seconds
21
+ ref_pos = opt[:ref_pos] || if src_io.respond_to?(:property) then
22
+ Coordinate::LLH::new(*(src_io.property.values_at(:latitude, :longitude).collect{|v|
23
+ v.to_f / 180 * Math::PI
24
+ } + [0])).xyz
25
+ else
26
+ defined?(@base_station) ? @base_station : nil
27
+ end
21
28
  after_run = b || proc{|pvt| puts pvt.to_s if pvt}
22
29
  t_meas, meas = [nil, {}]
23
30
  # meas := {msg_num => [[], ...]} due to duplicated observation such as 1074 and 1077
@@ -26,7 +33,9 @@ class Receiver
26
33
  meas.sort.each{|k, values| # larger msg_num entries have higher priority
27
34
  values.each{|prn_k_v| meas_.add(*prn_k_v)}
28
35
  }
29
- after_run.call(run(meas_, t_meas), [meas_, ref_time = t_meas]) if t_meas
36
+ pvt = nil
37
+ after_run.call(pvt = run(meas_, t_meas), [meas_, ref_time = t_meas]) if t_meas
38
+ ref_pos = pvt.xyz if pvt && pvt.position_solved?
30
39
  t_meas, meas = [nil, {}]
31
40
  }
32
41
  dt_threshold = GPS::Time::Seconds_week / 2
@@ -53,6 +62,63 @@ class Receiver
53
62
  GPS::Time::new(ref_time.week, 0) + tod + 60 * 60 * 24 * ref_dow
54
63
  end
55
64
  }
65
+ restore_ranges = proc{
66
+ c_1ms = 299_792.458
67
+ threshold = c_1ms / 10 # 100 us =~ 30 km
68
+ cache = {} # {[sys, svid] => [range, t], ...}
69
+ get_rough = proc{|t, sys_svid_list|
70
+ sn_list = sys_svid_list.collect{|sys, svid|
71
+ case sys
72
+ when :GPS, :QZSS; @solver.gps_space_node
73
+ when :SBAS; @solver.sbas_space_node
74
+ when :GLONASS; @solver.glonass_space_node
75
+ end
76
+ }
77
+ critical{
78
+ sn_list.uniq.compact{|sn| sn.update_all_ephemeris(t)}
79
+ sys_svid_list.zip(sn_list).each{|(sys, svid), sn|
80
+ next unless sn
81
+ eph = sn.ephemeris(svid)
82
+ cache[[sys, svid]] = [if eph.valid?(t) then
83
+ sv_pos, clk_err = eph.constellation(t).values_at(0, 2)
84
+ sv_pos.distance(ref_pos) - (clk_err * c_1ms * 1E3)
85
+ end, t]
86
+ }
87
+ }
88
+ }
89
+ per_kind = proc{|t, sys_svid_list, ranges_rem|
90
+ get_rough.call(t, sys_svid_list.uniq.reject{|sys, svid|
91
+ next true unless sys
92
+ range, t2 = cache[[sys, svid]]
93
+ range && ((t2 - t).abs <= 60)
94
+ })
95
+ ranges_rem.zip(sys_svid_list).collect{|rem_in, (sys, svid)|
96
+ range_ref, t2 = cache[[sys, svid]]
97
+ next nil unless range_ref
98
+ q, rem_ref = range_ref.divmod(c_1ms)
99
+ delta = rem_in - rem_ref
100
+ res = if delta.abs <= threshold then
101
+ q * c_1ms + rem_in
102
+ elsif -delta + c_1ms <= threshold
103
+ (q - 1) * c_1ms + rem_in
104
+ elsif delta + c_1ms <= threshold
105
+ (q + 1) * c_1ms + rem_in
106
+ end
107
+ #p [sys, svid, q, rem_in, rem_ref, res]
108
+ (cache[[sys, svid]] = [res, t])[0]
109
+ }
110
+ }
111
+ proc{|t, sys_svid_list, ranges|
112
+ [
113
+ :pseudo_range, # for MT 1001/3/9/11, MSM1/3
114
+ :phase_range, # for MT 1003/11, MSM2/3
115
+ ].each{|k|
116
+ next if ranges[k]
117
+ k_rem = "#{k}_rem".to_sym
118
+ ranges[k] = per_kind.call(t, sys_svid_list, ranges[k_rem]) if ranges[k_rem]
119
+ }
120
+ }
121
+ }.call
56
122
 
57
123
  while packet = rtcm3.read_packet
58
124
  msg_num = packet.message_number
@@ -66,15 +132,19 @@ class Receiver
66
132
  when 1001..1004
67
133
  t_meas2 = tow2t.call(parsed[2][0]) # DF004
68
134
  ranges = parsed.ranges
69
- item_size = ranges[:sat].size
70
- [:sat, :pseudo_range, :phase_range, :cn].collect{|k|
71
- ranges[k] || ([nil] * item_size)
72
- }.transpose.each{|svid, pr, cpr, cn|
135
+ sys_svid_list = ranges[:sat].collect{|svid|
73
136
  case svid
74
- when 1..32; # GPS
75
- when 40..58; svid += 80 # SBAS
76
- else; next
137
+ when 1..32; [:GPS, svid]
138
+ when 40..58; [:SBAS, svid + 80]
139
+ else; [nil, svid]
77
140
  end
141
+ }
142
+ restore_ranges.call(t_meas2, sys_svid_list, ranges)
143
+ item_size = sys_svid_list.size
144
+ ([sys_svid_list] + [:pseudo_range, :phase_range, :cn].collect{|k|
145
+ ranges[k] || ([nil] * item_size)
146
+ }).transpose.each{|(sys, svid), pr, cpr, cn|
147
+ next unless sys
78
148
  meas2 << [svid, :L1_PSEUDORANGE, pr] if pr
79
149
  meas2 << [svid, :L1_CARRIER_PHASE, cpr / GPS::SpaceNode.L1_WaveLength] if cpr
80
150
  meas2 << [svid, :L1_SIGNAL_STRENGTH_dBHz, cn] if cn
@@ -82,25 +152,34 @@ class Receiver
82
152
  when 1009..1012
83
153
  t_meas2 = utc2t.call(parsed[2][0] - 60 * 60 * 3) # DF034 UTC(SU)+3hr, time of day[sec]
84
154
  ranges = parsed.ranges
85
- item_size = ranges[:sat].size
86
- [:sat, :freq_ch, :pseudo_range, :phase_range, :cn].collect{|k|
87
- ranges[k] || ([nil] * item_size)
88
- }.transpose.each{|svid, freq_ch, pr, cpr, cn|
155
+ sys_svid_list = ranges[:sat].collect{|svid|
89
156
  case svid
90
- when 1..24 # GLONASS
157
+ when 1..24; [:GLONASS, svid]
158
+ when 40..58; [:SBAS, svid + 80]
159
+ else; [nil, svid]
160
+ end
161
+ }
162
+ restore_ranges.call(t_meas2, sys_svid_list, ranges)
163
+ item_size = sys_svid_list.size
164
+ ([sys_svid_list] + [:freq_ch, :pseudo_range, :phase_range, :cn].collect{|k|
165
+ ranges[k] || ([nil] * item_size)
166
+ }).transpose.each{|(sys, svid), freq_ch, pr, cpr, cn|
167
+ case sys
168
+ when :GLONASS
91
169
  svid += 0x100
92
170
  freq = GPS::SpaceNode_GLONASS::L1_frequency(freq_ch)
93
171
  len = GPS::SpaceNode_GLONASS.light_speed / freq
94
172
  meas2 << [svid, :L1_FREQUENCY, freq]
95
173
  meas2 << [svid, :L1_CARRIER_PHASE, cpr / len] if cpr
96
- when 40..58
97
- svid += 80 # SBAS
174
+ when :SBAS
98
175
  meas2 << [svid, :L1_CARRIER_PHASE, cpr / GPS::SpaceNode.L1_WaveLength] if cpr
99
176
  else; next
100
177
  end
101
178
  meas2 << [svid, :L1_PSEUDORANGE, pr] if pr
102
179
  meas2 << [svid, :L1_SIGNAL_STRENGTH_dBHz, cn] if cn
103
180
  }
181
+ when 1013
182
+ leap_sec = parsed[5][0]
104
183
  when 1019, 1044
105
184
  params = parsed.params
106
185
  if msg_num == 1044
@@ -162,25 +241,27 @@ class Receiver
162
241
  }.compact.flatten(1))]
163
242
  }
164
243
  end
165
- sig_list, svid_offset = case msg_num / 10
244
+ sig_list, sys, svid_offset = case msg_num / 10
166
245
  when 107 # GPS
167
246
  [{2 => [:L1, GPS::SpaceNode.L1_WaveLength],
168
247
  15 => [:L2CM, GPS::SpaceNode.L2_WaveLength],
169
- 16 => [:L2CL, GPS::SpaceNode.L2_WaveLength]}, 0]
248
+ 16 => [:L2CL, GPS::SpaceNode.L2_WaveLength]}, :GPS, 0]
170
249
  when 108 # GLONASS
171
- [{2 => [:L1, nil]}, 0x100]
250
+ [{2 => [:L1, nil]}, :GLONASS, 0x100]
172
251
  when 110 # SBAS
173
- [{2 => [:L1, GPS::SpaceNode.L1_WaveLength]}, 120]
252
+ [{2 => [:L1, GPS::SpaceNode.L1_WaveLength]}, :SBAS, 120]
174
253
  when 111 # QZSS
175
254
  [{2 => [:L1, GPS::SpaceNode.L1_WaveLength],
176
255
  15 => [:L2CM, GPS::SpaceNode.L2_WaveLength],
177
- 16 => [:L2CL, GPS::SpaceNode.L2_WaveLength]}, 192]
178
- else; [{}, 0]
256
+ 16 => [:L2CL, GPS::SpaceNode.L2_WaveLength]}, :QZSS, 192]
257
+ else; [{}, nil, 0]
179
258
  end
180
- item_size = ranges[:sat_sig].size
181
- [:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn].collect{|k|
259
+ sys_svid_list = ranges[:sat_sig].collect{|sat, sig| [sys, (sat + svid_offset) & 0xFF]}
260
+ restore_ranges.call(t_meas2, sys_svid_list, ranges)
261
+ item_size = sys_svid_list.size
262
+ [:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn, :halfc_amb].collect{|k|
182
263
  ranges[k] || ([nil] * item_size)
183
- }.transpose.each{|(svid, sig), pr, cpr, dr, cn|
264
+ }.transpose.each{|(svid, sig), pr, cpr, dr, cn, amb|
184
265
  prefix, len = sig_list[sig]
185
266
  next unless prefix
186
267
  proc{
@@ -193,6 +274,7 @@ class Receiver
193
274
  meas2 << [svid, "#{prefix}_RANGE_RATE".to_sym, dr] if dr
194
275
  meas2 << [svid, "#{prefix}_CARRIER_PHASE".to_sym, cpr / len] if cpr && len
195
276
  meas2 << [svid, "#{prefix}_SIGNAL_STRENGTH_dBHz".to_sym, cn] if cn
277
+ meas2 << [svid, "#{prefix}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5] if amb && (amb == 1)
196
278
  }
197
279
  else
198
280
  #p({msg_num => parsed})
@@ -416,6 +416,7 @@ class Receiver
416
416
  opt = options[0] || {}
417
417
  case sys
418
418
  when :GPS, :QZSS
419
+ return unless bcast_data.size == 10 # 8 for QZSS(SAIF)
419
420
  return unless eph = @eph_list[prn]
420
421
  sn = @solver.gps_space_node
421
422
  subframe, iodc_or_iode = eph.parse(bcast_data)
@@ -508,10 +509,12 @@ class Receiver
508
509
  }.each{|k, prop|
509
510
  meas.add(prn, k, loader.call(*prop))
510
511
  }
512
+ lli = packet[6 + 31 + (i * 24)]
511
513
  # bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
512
514
  # between previous and current observation, which maps negative lock seconds
513
- meas.add(prn, :L1_LOCK_SEC,
514
- (packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
515
+ meas.add(prn, :L1_LOCK_SEC, (lli & 0x01 == 0x01) ? -1 : 0)
516
+ # set bit 1 of LLI represents possibility of half cycle ambiguity
517
+ meas.add(prn, :L1_CARRIER_PHASE_AMBIGUITY_SCALE, 0.5) if (lli & 0x02 == 0x02)
515
518
  }
516
519
  after_run.call(run(meas, t_meas), [meas, t_meas])
517
520
  when [0x02, 0x15] # RXM-RAWX
@@ -552,7 +555,13 @@ class Receiver
552
555
  }],
553
556
  :DOPPLER => [32, 4, "e"],
554
557
  :DOPPLER_SIGMA => [45, 1, nil, proc{|v| 2E-3 * (1 << (v[0] & 0xF))}],
555
- :CARRIER_PHASE => [24, 8, "E", proc{|v| (trk_stat & 0x2 == 0x2) ? v : nil}],
558
+ :CARRIER_PHASE => [24, 8, "E", proc{|v|
559
+ case (trk_stat & 0x6)
560
+ when 0x6; (trk_stat & 0x8 == 0x8) ? (v + 0.5) : v
561
+ when 0x2; meas.add(svid, "#{sigid}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5); v
562
+ else; nil
563
+ end
564
+ }],
556
565
  :CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
557
566
  (trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
558
567
  }],
@@ -628,7 +637,7 @@ class Receiver
628
637
  'S' => :SIGNAL_STRENGTH_dBHz,
629
638
  }[type_[0]]]
630
639
  next nil unless sig_obs_type.all?
631
- [i, sig_obs_type.join('_').to_sym]
640
+ [i, sig_obs_type.join('_').to_sym, *sig_obs_type]
632
641
  }.compact]
633
642
  }.flatten(1))]
634
643
 
@@ -643,8 +652,7 @@ class Receiver
643
652
  }.call(item[:header]["GLONASS SLOT / FRQ #"])
644
653
 
645
654
  meas = GPS::Measurement::new
646
- item[:meas].each{|k, v|
647
- sys, prn = k
655
+ item[:meas].each{|(sys, prn), v|
648
656
  case sys
649
657
  when 'G', ' '
650
658
  when 'S'; prn += 100
@@ -661,8 +669,11 @@ class Receiver
661
669
  else; next
662
670
  end
663
671
  types[sys] = (types[' '] || []) unless types[sys]
664
- types[sys].each{|i, type_|
665
- meas.add(prn, type_, v[i][0]) if v[i]
672
+ types[sys].each{|i, type_, sig_type, obs_type|
673
+ next unless v[i]
674
+ meas.add(prn, type_, v[i][0])
675
+ meas.add(prn, "#{sig_type}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5) \
676
+ if (obs_type == :CARRIER_PHASE) && (v[i][1] & 0x2 == 0x2)
666
677
  }
667
678
  }
668
679
  after_run.call(run(meas, t_meas), [meas, t_meas])
data/lib/gps_pvt/rtcm3.rb CHANGED
@@ -95,6 +95,13 @@ class RTCM3
95
95
  48 => invalidate.call(num_gen.call(20, Rational(5, 10000)), 0x80000), # [m]
96
96
  49 => 7,
97
97
  50 => invalidate.call(unum_gen.call(8, Rational(1, 4)), 0), # [db-Hz]
98
+ 51 => 16,
99
+ 52 => 17,
100
+ 53 => 5,
101
+ 54 => 8,
102
+ 55 => 12,
103
+ 56 => 1,
104
+ 57 => 16,
98
105
  71 => 8,
99
106
  76 => 10,
100
107
  77 => proc{
@@ -237,12 +244,14 @@ class RTCM3
237
244
  1001..1004 => (2..8).to_a,
238
245
  1005 => [2, 3, 21, 22, 23, 24, 141, 25, 142, [1, 1], 26, 364, 27],
239
246
  1009..1012 => [2, 3, 34, 5, 35, 36, 37],
247
+ 1013 => [2, 3, 51, 52, 53, 54],
240
248
  1019 => [2, 9, (76..79).to_a, 71, (81..103).to_a, 137].flatten, # 488 bits @see Table 3.5-21
241
249
  1020 => [2, 38, 40, (104..136).to_a].flatten, # 360 bits @see Table 3.5-21
242
250
  1043 => [2] + [:prn, :iodn, :tod, :ura,
243
251
  [:xy] * 2, :z, [:dxy] * 2, :dz, [:ddxy] * 2, :ddz,
244
252
  :agf0, :agf1].flatten.collect{|k| "SBAS_#{k}".to_sym}, # @see BNC Ntrip client RTCM3Decorder.cpp
245
253
  1044 => [2, (429..457).to_a].flatten, # 485 bits
254
+ 1070..1229 => [2, [:uint, 12], [:uint, 30], 393], # 55 bits part of messages will be overwritten
246
255
  1071..1077 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-78
247
256
  1081..1087 => [2, 3, 416, 34, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-93
248
257
  1091..1097 => [2, 3, 248, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-98
@@ -392,6 +401,30 @@ class RTCM3
392
401
  end
393
402
  SPEED_OF_LIGHT = 299_792_458
394
403
  end
404
+ module MSM1_2_3
405
+ include MSM
406
+ def ranges
407
+ sats, cells, offset = property.values_at(:sats, :cells, :header_items)
408
+ nsat, ncell = [sats.size, cells.size]
409
+ res = {:sat_sig => cells}
410
+ range_rough = cells.collect{|sat, sig| # DF398
411
+ self[offset + sats.find_index(sat)][0]
412
+ }
413
+ add_proc = proc{|idx_cell|
414
+ values = self[offset + (nsat * 1) + (ncell * idx_cell), ncell]
415
+ next if values.empty?
416
+ k = {400 => :pseudo_range_rem, 401 => :phase_range_rem}[values[0][1]]
417
+ next unless k
418
+ res[k] = values.zip(range_rough).collect{|(v, df), v_base|
419
+ ((v_base + v) * SPEED_OF_LIGHT) rescue nil
420
+ }
421
+ }
422
+ add_proc.call(0)
423
+ add_proc.call(1)
424
+ res[:halfc_amb] = self[-ncell, ncell].transpose[0] if self[-1][1] == 420
425
+ res
426
+ end
427
+ end
395
428
  module MSM4_6
396
429
  include MSM
397
430
  def ranges
@@ -401,14 +434,16 @@ class RTCM3
401
434
  range_rough2 = self[offset + (nsat * 1), nsat] # DF398
402
435
  range_fine = self[offset + (nsat * 2), ncell] # DF400/405
403
436
  phase_fine = self[offset + (nsat * 2) + (ncell * 1), ncell] # DF401/406
437
+ halfc_amb = self[offset + (nsat * 2) + (ncell * 3), ncell] # DF420
404
438
  cn = self[offset + (nsat * 2) + (ncell * 4), ncell] # DF403/408
405
- Hash[*([:sat_sig, :pseudo_range, :phase_range, :cn].zip(
439
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :cn, :halfc_amb].zip(
406
440
  [cells] + cells.collect.with_index{|(sat, sig), i|
407
441
  i2 = sats.find_index(sat)
408
442
  rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
409
443
  [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
410
444
  (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
411
- cn[i][0]]
445
+ cn[i][0],
446
+ halfc_amb[i][0]]
412
447
  }.transpose).flatten(1))]
413
448
  end
414
449
  end
@@ -422,16 +457,18 @@ class RTCM3
422
457
  delta_rough = self[offset + (nsat * 3), nsat] # DF399
423
458
  range_fine = self[offset + (nsat * 4), ncell] # DF400/405
424
459
  phase_fine = self[offset + (nsat * 4) + (ncell * 1), ncell] # DF401/406
460
+ halfc_amb = self[offset + (nsat * 4) + (ncell * 3), ncell] # DF420
425
461
  cn = self[offset + (nsat * 4) + (ncell * 4), ncell] # DF403/408
426
462
  delta_fine = self[offset + (nsat * 4) + (ncell * 5), ncell] # DF404
427
- Hash[*([:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn].zip(
463
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn, :halfc_amb].zip(
428
464
  [cells] + cells.collect.with_index{|(sat, sig), i|
429
465
  i2 = sats.find_index(sat)
430
466
  rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
431
467
  [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
432
468
  (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
433
469
  ((delta_fine[i][0] + delta_rough[i2][0]) rescue nil),
434
- cn[i][0]]
470
+ cn[i][0],
471
+ halfc_amb[i][0]]
435
472
  }.transpose).flatten(1))]
436
473
  end
437
474
  end
@@ -468,6 +505,9 @@ class RTCM3
468
505
  1012 => (38..50).to_a,
469
506
  }[msg_num]] * nsat).flatten), offset)
470
507
  attributes << GLONASS_Observation
508
+ when 1013
509
+ add_proc.call(DataFrame.generate_prop(
510
+ ((55..57).to_a * values[4]).flatten), 24 + mt[:bits_total])
471
511
  when 1019
472
512
  attributes << GPS_Ephemeris
473
513
  when 1020
@@ -492,6 +532,15 @@ class RTCM3
492
532
  add_proc.call(msm_sig, offset)
493
533
  }
494
534
  case msg_num % 10
535
+ when 1
536
+ attributes << MSM1_2_3
537
+ msm_proc.call([398], [400])
538
+ when 2
539
+ attributes << MSM1_2_3
540
+ msm_proc.call([398], [401, 402, 420])
541
+ when 3
542
+ attributes << MSM1_2_3
543
+ msm_proc.call([398], [400, 401, 402, 420])
495
544
  when 4
496
545
  attributes << MSM4_6
497
546
  msm_proc.call([397, 398], [400, 401, 402, 420, 403])
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.9.2"
4
+ VERSION = "0.9.4"
5
5
 
6
6
  def GPS_PVT.version_compare(a, b)
7
7
  Gem::Version::new(a) <=> Gem::Version::new(b)