gps_pvt 0.9.3 → 0.10.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +159 -3
  3. data/README.md +4 -3
  4. data/Rakefile +24 -0
  5. data/exe/gps2ubx +12 -5
  6. data/exe/gps_pvt +7 -2
  7. data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +53 -19
  8. data/ext/gps_pvt/GPS/GPS_wrap.cxx +39 -7
  9. data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +5210 -2058
  10. data/ext/gps_pvt/extconf.rb +6 -5
  11. data/ext/ninja-scan-light/tool/navigation/GLONASS_Solver.h +1 -1
  12. data/ext/ninja-scan-light/tool/navigation/GPS.h +6 -2
  13. data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +1 -1
  14. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +3 -1
  15. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +3 -0
  16. data/ext/ninja-scan-light/tool/navigation/RINEX.h +9 -9
  17. data/ext/ninja-scan-light/tool/navigation/SBAS_Solver.h +1 -1
  18. data/ext/ninja-scan-light/tool/navigation/coordinate.h +13 -6
  19. data/ext/ninja-scan-light/tool/param/matrix.h +1020 -247
  20. data/ext/ninja-scan-light/tool/param/matrix_fixed.h +26 -0
  21. data/ext/ninja-scan-light/tool/swig/GPS.i +6 -4
  22. data/ext/ninja-scan-light/tool/swig/SylphideMath.i +139 -36
  23. data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +115 -5
  24. data/gps_pvt.gemspec +3 -1
  25. data/lib/gps_pvt/asn1/asn1.rb +888 -0
  26. data/lib/gps_pvt/asn1/asn1.y +903 -0
  27. data/lib/gps_pvt/asn1/per.rb +182 -0
  28. data/lib/gps_pvt/ntrip.rb +1 -1
  29. data/lib/gps_pvt/receiver/agps.rb +31 -0
  30. data/lib/gps_pvt/receiver/extension.rb +94 -0
  31. data/lib/gps_pvt/receiver/rtcm3.rb +6 -4
  32. data/lib/gps_pvt/receiver.rb +41 -24
  33. data/lib/gps_pvt/rtcm3.rb +24 -34
  34. data/lib/gps_pvt/supl.rb +567 -0
  35. data/lib/gps_pvt/ubx.rb +15 -0
  36. data/lib/gps_pvt/upl/LPP-V17_5_0-Release17.asn +6441 -0
  37. data/lib/gps_pvt/upl/RRLP-V17_0_0-Release17.asn +2780 -0
  38. data/lib/gps_pvt/upl/ULP-V2_0_6-20200720-D.asn +2185 -0
  39. data/lib/gps_pvt/upl/upl.json.gz +0 -0
  40. data/lib/gps_pvt/upl/upl.rb +99 -0
  41. data/lib/gps_pvt/util.rb +1 -0
  42. data/lib/gps_pvt/version.rb +1 -1
  43. metadata +41 -3
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module GPS_PVT
4
+
5
+ # @see ISO/IEC 8825-2:2003(E)
6
+ # @see http://www5d.biglobe.ne.jp/~stssk/asn1/per.html
7
+ module PER
8
+ module Basic_Unaligned
9
+ module Encoder
10
+ class <<self
11
+ def non_negative_binary_integer2(v, bits) # 10.3
12
+ "%0#{bits}b" % v
13
+ end
14
+ def non_negative_binary_integer(v, align = 1) # 10.3
15
+ non_negative_binary_integer2(
16
+ v, (Math::log2(v + 1) / align).ceil * align)
17
+ end
18
+ def twos_complement_binary_integer(v) # 10.4
19
+ if v >= 0 then
20
+ bits = ((Math::log2(v + 1) + 1) / 8).ceil * 8
21
+ "%0#{bits}b" % v
22
+ else
23
+ bits = ((Math::log2(-v) + 1) / 8).ceil * 8 - 1
24
+ "1%0#{bits}b" % (v + (1 << bits))
25
+ end
26
+ end
27
+ def constrainted_whole_number2(v, v_min, v_max) # 10.5.6
28
+ non_negative_binary_integer2(
29
+ v - v_min,
30
+ Math::log2(v_max - v_min + 1).ceil)
31
+ end
32
+ def constrainted_whole_number(v, v_range) # 10.5.6
33
+ constrainted_whole_number2(v, *v_range.minmax)
34
+ end
35
+ def normally_small_non_negative_whole_number(v, *len_enc) # 10.6
36
+ if v <= 63 then
37
+ "0%06b" % v # 10.6.1
38
+ else
39
+ "1#{semi_constrained_whole_number(v, 0, *len_enc)}" # 10.6.2
40
+ end
41
+ end
42
+ def semi_constrained_whole_number(v, v_min, *len_enc) # 10.7
43
+ len_enc = :length_otherwise if len_enc.empty?
44
+ bf = non_negative_binary_integer(v - v_min, 8).scan(/.{8}/)
45
+ with_length(bf.size, *len_enc).collect{|len_str, range|
46
+ len_str + bf[range].join
47
+ }.join
48
+ end
49
+ def unconstrained_whole_number(v, *len_enc) # 10.8
50
+ len_enc = :length_otherwise if len_enc.empty?
51
+ bf = twos_complement_binary_integer(v).scan(/.{8}/)
52
+ with_length(bf.size, *len_enc).collect{|len_str, range|
53
+ len_str + bf[range].join
54
+ }.join
55
+ end
56
+ def length_constrained_whole_number(len, len_range)
57
+ if len_range.max < 65536 then # 10.9.4.1
58
+ (len_range.min == len_range.max) ?
59
+ "" :
60
+ constrainted_whole_number(len, len_range)
61
+ else
62
+ length_otherwise(len)
63
+ end
64
+ end
65
+ def length_normally_small_length(len) # 10.9.4.2 -> 10.9.3.4
66
+ if len <= 64 then
67
+ normally_small_non_negative_whole_number(len - 1)
68
+ else
69
+ len_enc, len_remain = length_otherwise(len)
70
+ len_enc = "1#{len_enc}"
71
+ len_remain ? [len_enc, len_remain] : len_enc
72
+ end
73
+ end
74
+ def length_otherwise(len) # 10.9.4.2 -> 10.9.3.5-8
75
+ if len <= 127 then # 10.9.3.6
76
+ non_negative_binary_integer2(len, 8)
77
+ elsif len < 16384 then # 10.9.3.7
78
+ "10#{non_negative_binary_integer2(len, 14)}"
79
+ else # 10.9.3.8
80
+ q, r = len.divmod(16384)
81
+ q2 = [q, 4].min
82
+ res = "11#{non_negative_binary_integer2(q2, 6)}"
83
+ ((r == 0) && (q <= 4)) ? res : [res, len - (q2 * 16384)]
84
+ end
85
+ end
86
+ def with_length(len, *len_enc)
87
+ Enumerator::new{|y|
88
+ len_str, len_remain = len_enc[0].kind_of?(Symbol) ? send(len_enc[0], len, *len_enc[1..-1]) : len_enc
89
+ loop{
90
+ if len_remain then
91
+ y << [len_str, -len..-(len_remain+1)]
92
+ else
93
+ y << [len_str, -len..-1]
94
+ break
95
+ end
96
+ len_str, len_remain = length_otherwise(len = len_remain) # fragmentation
97
+ }
98
+ }
99
+ end
100
+ end
101
+ end
102
+ module Decoder
103
+ class <<self
104
+ def non_negative_binary_integer(str, bits) # 10.3
105
+ str.slice!(0, bits).to_i(2)
106
+ end
107
+ def twos_complement_binary_integer(str, bits) # 10.4
108
+ bits -= 1
109
+ case str.slice!(0)
110
+ when '0'; 0
111
+ when '1'; -(1 << bits)
112
+ end + non_negative_binary_integer(str, bits)
113
+ end
114
+ def constrainted_whole_number2(str, v_min, v_max) # 10.5.6
115
+ non_negative_binary_integer(
116
+ str,
117
+ Math::log2(v_max - v_min + 1).ceil) + v_min
118
+ end
119
+ def constrainted_whole_number(str, v_range) # 10.5.6
120
+ constrainted_whole_number2(str, *v_range.minmax)
121
+ end
122
+ def normally_small_non_negative_whole_number(str, *len_dec) # 10.6
123
+ case str.slice!(0)
124
+ when '0'; str.slice!(0, 6).to_i(2) # 10.6.1
125
+ when '1'; semi_constrained_whole_number(str, 0, *len_dec) # 10.6.2
126
+ end
127
+ end
128
+ def semi_constrained_whole_number(str, v_min, *len_dec) # 10.7
129
+ len_dec = :length_otherwise if len_dec.empty?
130
+ v_str = with_length(str, *len_dec).collect{|len_oct|
131
+ str.slice!(0, len_oct * 8)
132
+ }.join
133
+ non_negative_binary_integer(v_str, v_str.size) + v_min
134
+ end
135
+ def unconstrained_whole_number(str, *len_dec) # 10.8
136
+ len_dec = :length_otherwise if len_dec.empty?
137
+ v_str = with_length(str, *len_dec).collect{|len_oct|
138
+ str.slice!(0, len_oct * 8)
139
+ }.join
140
+ twos_complement_binary_integer(v_str, v_str.size)
141
+ end
142
+ def length_constrained_whole_number(str, len_range)
143
+ if len_range.max < 65536 then # 10.9.4.1
144
+ (len_range.min == len_range.max) ?
145
+ len_range.min :
146
+ constrainted_whole_number(str, len_range)
147
+ else
148
+ length_otherwise(str)
149
+ end
150
+ end
151
+ def length_normally_small_length(str) # 10.9.4.2 -> 10.9.3.4
152
+ case str.slice!(0)
153
+ when '0'; str.slice!(0, 6).to_i(2) + 1
154
+ when '1'; length_otherwise(str)
155
+ end
156
+ end
157
+ def length_otherwise(str) # 10.9.4.2 -> 10.9.3.5-8
158
+ case str.slice!(0)
159
+ when '0'; non_negative_binary_integer(str, 7) # 10.9.3.6
160
+ when '1';
161
+ case str.slice!(0)
162
+ when '0'; non_negative_binary_integer(str, 14) # 10.9.3.7
163
+ when '1'; [non_negative_binary_integer(str, 6) * (1 << 14), true] # 10.9.3.8
164
+ end
165
+ end
166
+ end
167
+ def with_length(str, *len_dec)
168
+ Enumerator::new{|y|
169
+ len, cnt = len_dec[0].kind_of?(Symbol) ? send(len_dec[0], str, *len_dec[1..-1]) : len_dec
170
+ loop{
171
+ y << len
172
+ break unless cnt
173
+ len, cnt = length_otherwise(str) # fragmentation
174
+ }
175
+ }
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ end
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
@@ -0,0 +1,31 @@
1
+ =begin
2
+ AGPS handler for receiver
3
+ =end
4
+
5
+ module GPS_PVT
6
+ class Receiver
7
+ def parse_supl(src, opt = {}, &b)
8
+ $stderr.print "A-GPS (%s) "%[src]
9
+ opt = {
10
+ :interval => 60 * 10, # 10 min.
11
+ }.merge(opt)
12
+ require_relative '../supl'
13
+ src_io = Util::open(src)
14
+ while data = src_io.get_assisted_data
15
+ data.ephemeris.each{|eph|
16
+ target = case eph
17
+ when GPS::Ephemeris; @solver.gps_space_node
18
+ when GPS::Ephemeris_GLONASS; @solver.glonass_space_node
19
+ when GPS::Ephemeris_SBAS; @solver.sbas_space_node
20
+ else nil
21
+ end
22
+ critical{target.register_ephemeris(eph.svid, eph)} if target
23
+ } if data.respond_to?(:ephemeris)
24
+ critical{
25
+ @solver.gps_space_node.update_iono_utc(data.iono_utc)
26
+ } if data.respond_to?(:iono_utc)
27
+ sleep(opt[:interval])
28
+ end
29
+ end
30
+ end
31
+ end
@@ -57,4 +57,98 @@ class Receiver
57
57
  }
58
58
  end
59
59
  end
60
+
61
+ module GPS
62
+
63
+ # These ephemeris helper functions will be removed
64
+ # when native functions are available in GPS.i
65
+ class Ephemeris
66
+ URA_TABLE = [
67
+ 2.40, 3.40, 4.85, 6.85, 9.65, 13.65, 24.00, 48.00,
68
+ 96.00, 192.00, 384.00, 768.00, 1536.00, 3072.00, 6144.00]
69
+ def URA_index=(idx)
70
+ send(:URA=, (idx >= URA_TABLE.size) ? (URA_TABLE[-1] * 2) : (idx < 0 ? -1 : URA_TABLE[idx]))
71
+ end
72
+ def URA_index
73
+ ura = send(:URA)
74
+ (ura < 0) ? -1 : URA_TABLE.find_index{|v| ura <= v}
75
+ end
76
+ proc{
77
+ orig = instance_method(:fit_interval=)
78
+ define_method(:fit_interval=){|args|
79
+ args = case args
80
+ when Array
81
+ flag, iodc, sys = args
82
+ hr = case (sys ||= :GPS)
83
+ when :GPS, :gps
84
+ (flag == 0) ? 4 : case iodc
85
+ when 240..247; 8
86
+ when 248..255, 496; 14
87
+ when 497..503; 26
88
+ when 504..510; 50
89
+ when 511, 752..756; 74
90
+ when 757..763; 98
91
+ when 764..767, 1088..1010; 122
92
+ when 1011..1020; 146
93
+ else; 6
94
+ end
95
+ when :QZSS, :qzss
96
+ raise unless flag == 0 # TODO how to treat fit_interval > 2 hrs
97
+ 2
98
+ else; raise
99
+ end
100
+ hr * 60 * 60
101
+ else
102
+ args
103
+ end
104
+ orig.bind(self).call(args)
105
+ }
106
+ }.call
107
+ end
108
+ class Ephemeris_SBAS
109
+ URA_TABLE = [ # Table 2-3 in DO-229E
110
+ 2.0, 2.8, 4.0, 5.7, 8.0, 11.3, 16.0, 32.0,
111
+ 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0]
112
+ def URA_index=(idx)
113
+ send(:URA=, (idx >= URA_TABLE.size) ? (URA_TABLE[-1] * 2) : (idx < 0 ? -1 : URA_TABLE[idx]))
114
+ end
115
+ def URA_index
116
+ ura = send(:URA)
117
+ (ura < 0) ? -1 : URA_TABLE.find_index{|v| ura <= v}
118
+ end
119
+ end
120
+ class Ephemeris_GLONASS
121
+ F_T_TABLE = [ # Table 4.4 in ICD 5.1
122
+ 1, 2, 2.5, 4, 5, 7, 10, 12, 14, 16, 32, 64, 128, 256, 512, 1024]
123
+ def F_T_index=(idx)
124
+ send(:F_T=, (idx >= F_T_TABLE.size) ? (F_T_TABLE[-1] * 2) : (idx < 0 ? -1 : F_T_TABLE[idx]))
125
+ end
126
+ def F_T_index
127
+ f_t = send(:F_T)
128
+ (f_t < 0) ? -1 : F_T_TABLE.find_index{|v| f_t <= v}
129
+ end
130
+ def NA
131
+ # based on TimeProperties::date2raw
132
+ self.day_of_year + [1, 367, 732, 1097][(self.year - 1996) % 4]
133
+ end
134
+ end
135
+
136
+ [
137
+ Ionospheric_UTC_Parameters,
138
+ Ephemeris, Ephemeris_SBAS, Ephemeris_GLONASS,
139
+ ].each{|cls|
140
+ cls.class_eval{
141
+ proc{|func_list|
142
+ func_list.select!{|func|
143
+ (/=$/ !~ func.to_s) && func_list.include?("#{func}=".to_sym)
144
+ }
145
+ define_method(:to_hash){
146
+ Hash[*(func_list.collect{|func|
147
+ [func, send(func)]
148
+ }.flatten(1))]
149
+ }
150
+ }.call(instance_methods(false))
151
+ }
152
+ }
153
+ end
60
154
  end
@@ -81,7 +81,7 @@ class Receiver
81
81
  eph = sn.ephemeris(svid)
82
82
  cache[[sys, svid]] = [if eph.valid?(t) then
83
83
  sv_pos, clk_err = eph.constellation(t).values_at(0, 2)
84
- sv_pos.dist(ref_pos) - (clk_err * c_1ms * 1E3)
84
+ sv_pos.distance(ref_pos) - (clk_err * c_1ms * 1E3)
85
85
  end, t]
86
86
  }
87
87
  }
@@ -195,7 +195,7 @@ class Receiver
195
195
  when 1020
196
196
  params = parsed.params
197
197
  eph = GPS::Ephemeris_GLONASS::new
198
- params[:F_T] ||= 10 # [m]
198
+ eph.F_T = 10 # [m], default to be overwritten
199
199
  params.each{|k, v|
200
200
  next if [:P3, :NA, :N_4].include?(k)
201
201
  eph.send("#{k}=".to_sym, v)
@@ -205,6 +205,7 @@ class Receiver
205
205
  ? date_src \
206
206
  : [(ref_time + 3 * 60 * 60).c_tm(leap_sec)])) # UTC -> Moscow time
207
207
  }.call([:N_4, :NA].collect{|k| params[k]})
208
+ eph.N_T = params[:NA] || eph.NA unless params[:N_T] # N_T is available only for GLONASS-M
208
209
  eph.rehash(leap_sec)
209
210
  critical{
210
211
  @solver.glonass_space_node.register_ephemeris(eph.svid, eph)
@@ -259,9 +260,9 @@ class Receiver
259
260
  sys_svid_list = ranges[:sat_sig].collect{|sat, sig| [sys, (sat + svid_offset) & 0xFF]}
260
261
  restore_ranges.call(t_meas2, sys_svid_list, ranges)
261
262
  item_size = sys_svid_list.size
262
- [:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn].collect{|k|
263
+ [:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn, :halfc_amb].collect{|k|
263
264
  ranges[k] || ([nil] * item_size)
264
- }.transpose.each{|(svid, sig), pr, cpr, dr, cn|
265
+ }.transpose.each{|(svid, sig), pr, cpr, dr, cn, amb|
265
266
  prefix, len = sig_list[sig]
266
267
  next unless prefix
267
268
  proc{
@@ -274,6 +275,7 @@ class Receiver
274
275
  meas2 << [svid, "#{prefix}_RANGE_RATE".to_sym, dr] if dr
275
276
  meas2 << [svid, "#{prefix}_CARRIER_PHASE".to_sym, cpr / len] if cpr && len
276
277
  meas2 << [svid, "#{prefix}_SIGNAL_STRENGTH_dBHz".to_sym, cn] if cn
278
+ meas2 << [svid, "#{prefix}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5] if amb && (amb == 1)
277
279
  }
278
280
  else
279
281
  #p({msg_num => parsed})
@@ -135,7 +135,7 @@ class Receiver
135
135
  opt[:satellites].collect{|prn, label|
136
136
  pr, rate, doppler, freq = keys.collect{|k| meas_hash[prn][k] rescue nil}
137
137
  freq ||= GPS::SpaceNode.L1_Frequency
138
- [pr, rate || ((doppler * GPS::SpaceNode::light_speed / freq) rescue nil)]
138
+ [pr, rate || ((-doppler * GPS::SpaceNode::light_speed / freq) rescue nil)]
139
139
  }
140
140
  }
141
141
  ]]
@@ -324,25 +324,31 @@ class Receiver
324
324
  end
325
325
 
326
326
  GPS::Measurement.class_eval{
327
- proc{
328
- key2sym = []
329
- GPS::Measurement.constants.each{|k|
330
- i = GPS::Measurement.const_get(k)
331
- key2sym[i] = k if i.kind_of?(Integer)
332
- }
333
- define_method(:to_a2){
334
- to_a.collect{|prn, k, v| [prn, key2sym[k] || k, v]}
335
- }
336
- define_method(:to_hash2){
337
- Hash[*(to_hash.collect{|prn, k_v|
338
- [prn, Hash[*(k_v.collect{|k, v| [key2sym[k] || k, v]}.flatten(1))]]
339
- }.flatten(1))]
340
- }
341
- }.call
342
327
  add_orig = instance_method(:add)
343
328
  define_method(:add){|prn, key, value|
344
329
  add_orig.bind(self).call(prn, key.kind_of?(Symbol) ? GPS::Measurement.const_get(key) : key, value)
345
330
  }
331
+ key2sym = GPS::Measurement.constants.inject([]){|res, k|
332
+ res[GPS::Measurement.const_get(k)] = k if /^L\d/ =~ k.to_s
333
+ res
334
+ }
335
+ define_method(:to_a2){
336
+ collect{|prn, k, v| [prn, key2sym[k] || k, v]}
337
+ }
338
+ cl_hash2 = Class::new(Hash){
339
+ define_method(:to_meas){
340
+ GPS::Measurement::new.tap{|res|
341
+ each{|prn, k_v|
342
+ k_v.each{|k, v| res.add(prn, k, v)}
343
+ }
344
+ }
345
+ }
346
+ }
347
+ define_method(:to_hash2){
348
+ cl_hash2::new.tap{|res|
349
+ each{|prn, k, v| (res[prn] ||= {})[key2sym[k] || k] = v}
350
+ }
351
+ }
346
352
  }
347
353
 
348
354
  def run(meas, t_meas, ref_pos = @base_station)
@@ -509,10 +515,12 @@ class Receiver
509
515
  }.each{|k, prop|
510
516
  meas.add(prn, k, loader.call(*prop))
511
517
  }
518
+ lli = packet[6 + 31 + (i * 24)]
512
519
  # bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
513
520
  # between previous and current observation, which maps negative lock seconds
514
- meas.add(prn, :L1_LOCK_SEC,
515
- (packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
521
+ meas.add(prn, :L1_LOCK_SEC, (lli & 0x01 == 0x01) ? -1 : 0)
522
+ # set bit 1 of LLI represents possibility of half cycle ambiguity
523
+ meas.add(prn, :L1_CARRIER_PHASE_AMBIGUITY_SCALE, 0.5) if (lli & 0x02 == 0x02)
516
524
  }
517
525
  after_run.call(run(meas, t_meas), [meas, t_meas])
518
526
  when [0x02, 0x15] # RXM-RAWX
@@ -553,7 +561,13 @@ class Receiver
553
561
  }],
554
562
  :DOPPLER => [32, 4, "e"],
555
563
  :DOPPLER_SIGMA => [45, 1, nil, proc{|v| 2E-3 * (1 << (v[0] & 0xF))}],
556
- :CARRIER_PHASE => [24, 8, "E", proc{|v| (trk_stat & 0x2 == 0x2) ? v : nil}],
564
+ :CARRIER_PHASE => [24, 8, "E", proc{|v|
565
+ case (trk_stat & 0x6)
566
+ when 0x6; (trk_stat & 0x8 == 0x8) ? (v + 0.5) : v
567
+ when 0x2; meas.add(svid, "#{sigid}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5); v
568
+ else; nil
569
+ end
570
+ }],
557
571
  :CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
558
572
  (trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
559
573
  }],
@@ -629,7 +643,7 @@ class Receiver
629
643
  'S' => :SIGNAL_STRENGTH_dBHz,
630
644
  }[type_[0]]]
631
645
  next nil unless sig_obs_type.all?
632
- [i, sig_obs_type.join('_').to_sym]
646
+ [i, sig_obs_type.join('_').to_sym, *sig_obs_type]
633
647
  }.compact]
634
648
  }.flatten(1))]
635
649
 
@@ -644,8 +658,7 @@ class Receiver
644
658
  }.call(item[:header]["GLONASS SLOT / FRQ #"])
645
659
 
646
660
  meas = GPS::Measurement::new
647
- item[:meas].each{|k, v|
648
- sys, prn = k
661
+ item[:meas].each{|(sys, prn), v|
649
662
  case sys
650
663
  when 'G', ' '
651
664
  when 'S'; prn += 100
@@ -662,8 +675,11 @@ class Receiver
662
675
  else; next
663
676
  end
664
677
  types[sys] = (types[' '] || []) unless types[sys]
665
- types[sys].each{|i, type_|
666
- meas.add(prn, type_, v[i][0]) if v[i]
678
+ types[sys].each{|i, type_, sig_type, obs_type|
679
+ next unless v[i]
680
+ meas.add(prn, type_, v[i][0])
681
+ meas.add(prn, "#{sig_type}_CARRIER_PHASE_AMBIGUITY_SCALE".to_sym, 0.5) \
682
+ if (obs_type == :CARRIER_PHASE) && (v[i][1] & 0x2 == 0x2)
667
683
  }
668
684
  }
669
685
  after_run.call(run(meas, t_meas), [meas, t_meas])
@@ -714,4 +730,5 @@ end
714
730
  end
715
731
 
716
732
  require_relative 'receiver/rtcm3'
733
+ require_relative 'receiver/agps'
717
734
  require_relative 'receiver/extension'
data/lib/gps_pvt/rtcm3.rb CHANGED
@@ -104,12 +104,7 @@ class RTCM3
104
104
  57 => 16,
105
105
  71 => 8,
106
106
  76 => 10,
107
- 77 => proc{
108
- idx2meter = [
109
- 2.40, 3.40, 4.85, 6.85, 9.65, 13.65, 24.00, 48.00,
110
- 96.00, 192.00, 384.00, 768.00, 1536.00, 3072.00, 6144.00]
111
- [4, proc{|v| (v >= idx2meter.size) ? (idx2meter[-1] * 2) : idx2meter[v]}]
112
- }.call, # [m]
107
+ 77 => 4,
113
108
  78 => 2,
114
109
  79 => num_gen.call(14, Rational(sc2rad, 1 << 43)), # [rad/s]
115
110
  81 => unum_gen.call(16, 1 << 4), # [sec]
@@ -156,15 +151,13 @@ class RTCM3
156
151
  125 => num_sign_gen.call(5, Rational(1, 1 << 30)), # [sec], (M)
157
152
  126 => 5, # [day]
158
153
  127 => 1, # (M)
159
- 128 => [4, proc{|v|
160
- [1, 2, 2.5, 4, 5, 7, 10, 12, 14, 16, 32, 64, 128, 256, 512, 1024][v]
161
- }], # [m] (M)
162
- 129 => 11, # [day]
154
+ 128 => 4, # (M)
155
+ 129 => invalidate.call(unum_gen.call(11), 0), # [day]
163
156
  130 => 2, # 1 => GLONASS-M, (M) fields are active
164
157
  131 => 1,
165
- 132 => 11, # [day]
158
+ 132 => invalidate.call(unum_gen.call(11), 0), # [day]
166
159
  133 => num_sign_gen.call(32, Rational(1, 1 << 31)), # [sec]
167
- 134 => 5, # [4year], (M)
160
+ 134 => invalidate.call(unum_gen.call(5), 0), # [4year], (M)
168
161
  135 => num_sign_gen.call(22, Rational(1, 1 << 30)), # [sec], (M)
169
162
  136 => 1, # (M)
170
163
  137 => 1,
@@ -247,9 +240,11 @@ class RTCM3
247
240
  1013 => [2, 3, 51, 52, 53, 54],
248
241
  1019 => [2, 9, (76..79).to_a, 71, (81..103).to_a, 137].flatten, # 488 bits @see Table 3.5-21
249
242
  1020 => [2, 38, 40, (104..136).to_a].flatten, # 360 bits @see Table 3.5-21
243
+ # @see BNC Ntrip client DecodeSBASEphemeris() of RTCM3Decorder.cpp
244
+ # https://software.rtcm-ntrip.org/browser/ntrip/trunk/BNC/src/RTCM3/RTCM3Decoder.cpp
250
245
  1043 => [2] + [:prn, :iodn, :tod, :ura,
251
246
  [:xy] * 2, :z, [:dxy] * 2, :dz, [:ddxy] * 2, :ddz,
252
- :agf0, :agf1].flatten.collect{|k| "SBAS_#{k}".to_sym}, # @see BNC Ntrip client RTCM3Decorder.cpp
247
+ :agf0, :agf1].flatten.collect{|k| "SBAS_#{k}".to_sym},
253
248
  1044 => [2, (429..457).to_a].flatten, # 485 bits
254
249
  1070..1229 => [2, [:uint, 12], [:uint, 30], 393], # 55 bits part of messages will be overwritten
255
250
  1071..1077 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-78
@@ -311,7 +306,7 @@ class RTCM3
311
306
  end
312
307
  end
313
308
  module GPS_Ephemeris
314
- KEY2IDX = {:svid => 1, :WN => 2, :URA => 3, :dot_i0 => 5, :iode => 6, :t_oc => 7,
309
+ KEY2IDX = {:svid => 1, :WN => 2, :URA_index => 3, :dot_i0 => 5, :iode => 6, :t_oc => 7,
315
310
  :a_f2 => 8, :a_f1 => 9, :a_f0 => 10, :iodc => 11, :c_rs => 12, :delta_n => 13,
316
311
  :M0 => 14, :c_uc => 15, :e => 16, :c_us => 17, :sqrt_A => 18, :t_oe => 19, :c_ic => 20,
317
312
  :Omega0 => 21, :c_is => 22, :i0 => 23, :c_rc => 24, :omega => 25, :dot_Omega0 => 26,
@@ -319,22 +314,12 @@ class RTCM3
319
314
  def params
320
315
  # TODO WN is truncated to 0-1023
321
316
  res = Hash[*(KEY2IDX.collect{|k, i| [k, self[i][0]]}.flatten(1))]
322
- res[:fit_interval] = ((self[29] == 0) ? 4 : case res[:iodc]
323
- when 240..247; 8
324
- when 248..255, 496; 14
325
- when 497..503; 26
326
- when 504..510; 50
327
- when 511, 752..756; 74
328
- when 757..763; 98
329
- when 764..767, 1088..1010; 122
330
- when 1011..1020; 146
331
- else; 6
332
- end) * 60 * 60
317
+ res[:fit_interval] = [self[29][0], res[:iodc]]
333
318
  res
334
319
  end
335
320
  end
336
321
  module SBAS_Ephemeris
337
- KEY2IDX = {:svid => 1, :iodn => 2, :tod => 3, :URA => 4,
322
+ KEY2IDX = {:svid => 1, :iodn => 2, :tod => 3, :URA_index => 4,
338
323
  :x => 5, :y => 6, :z => 7,
339
324
  :dx => 8, :dy => 9, :dz => 10,
340
325
  :ddx => 11, :ddy => 12, :ddz => 13,
@@ -354,13 +339,13 @@ class RTCM3
354
339
  :yn_dot => 13, :yn => 14, :yn_ddot => 15,
355
340
  :zn_dot => 16, :zn => 17, :zn_ddot => 18,
356
341
  :P3 => 19, :gamma_n => 20, :p => 21, :tau_n => 23, :delta_tau_n => 24, :E_n => 25,
357
- :P4 => 26, :F_T => 27, :N_T => 28, :M => 29}
342
+ :P4 => 26, :F_T_index => 27, :N_T => 28, :M => 29}
358
343
  k_i.merge!({:NA => 31, :tau_c => 32, :N_4 => 33, :tau_GPS => 34}) if self[30][0] == 1 # check DF131
359
344
  res = Hash[*(k_i.collect{|k, i| [k, self[i][0]]}.flatten(1))]
360
345
  res.reject!{|k, v|
361
346
  case k
362
347
  when :N_T; v == 0
363
- when :p, :delta_tau_n, :P4, :F_T, :N_4, :tau_GPS; true # TODO sometimes delta_tau_n is valid?
348
+ when :p, :delta_tau_n, :P4, :F_T_index, :N_4, :tau_GPS; true # TODO sometimes delta_tau_n is valid?
364
349
  else; false
365
350
  end
366
351
  } if (res[:M] != 1) # check DF130
@@ -372,12 +357,12 @@ class RTCM3
372
357
  :iode => 6, :c_rs => 7, :delta_n => 8, :M0 => 9, :c_uc => 10, :e => 11,
373
358
  :c_us => 12, :sqrt_A => 13, :t_oe => 14, :c_ic => 15, :Omega0 => 16,
374
359
  :c_is => 17, :i0 => 18, :c_rc => 19, :omega => 20, :dot_Omega0 => 21,
375
- :dot_i0 => 22, :WN => 24, :URA => 25, :SV_health => 26,
360
+ :dot_i0 => 22, :WN => 24, :URA_index => 25, :SV_health => 26,
376
361
  :t_GD => 27, :iodc => 28}
377
362
  def params
378
363
  # TODO PRN = svid + 192, WN is truncated to 0-1023
379
364
  res = Hash[*(KEY2IDX.collect{|k, i| [k, self[i][0]]}.flatten(1))]
380
- res[:fit_interval] = (self[29] == 0) ? 2 * 60 * 60 : nil # TODO how to treat fit_interval > 2 hrs
365
+ res[:fit_interval] = [self[29][0], res[:iodc], :QZSS]
381
366
  res
382
367
  end
383
368
  end
@@ -421,6 +406,7 @@ class RTCM3
421
406
  }
422
407
  add_proc.call(0)
423
408
  add_proc.call(1)
409
+ res[:halfc_amb] = self[-ncell, ncell].transpose[0] if self[-1][1] == 420
424
410
  res
425
411
  end
426
412
  end
@@ -433,14 +419,16 @@ class RTCM3
433
419
  range_rough2 = self[offset + (nsat * 1), nsat] # DF398
434
420
  range_fine = self[offset + (nsat * 2), ncell] # DF400/405
435
421
  phase_fine = self[offset + (nsat * 2) + (ncell * 1), ncell] # DF401/406
422
+ halfc_amb = self[offset + (nsat * 2) + (ncell * 3), ncell] # DF420
436
423
  cn = self[offset + (nsat * 2) + (ncell * 4), ncell] # DF403/408
437
- Hash[*([:sat_sig, :pseudo_range, :phase_range, :cn].zip(
424
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :cn, :halfc_amb].zip(
438
425
  [cells] + cells.collect.with_index{|(sat, sig), i|
439
426
  i2 = sats.find_index(sat)
440
427
  rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
441
428
  [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
442
429
  (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
443
- cn[i][0]]
430
+ cn[i][0],
431
+ halfc_amb[i][0]]
444
432
  }.transpose).flatten(1))]
445
433
  end
446
434
  end
@@ -454,16 +442,18 @@ class RTCM3
454
442
  delta_rough = self[offset + (nsat * 3), nsat] # DF399
455
443
  range_fine = self[offset + (nsat * 4), ncell] # DF400/405
456
444
  phase_fine = self[offset + (nsat * 4) + (ncell * 1), ncell] # DF401/406
445
+ halfc_amb = self[offset + (nsat * 4) + (ncell * 3), ncell] # DF420
457
446
  cn = self[offset + (nsat * 4) + (ncell * 4), ncell] # DF403/408
458
447
  delta_fine = self[offset + (nsat * 4) + (ncell * 5), ncell] # DF404
459
- Hash[*([:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn].zip(
448
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn, :halfc_amb].zip(
460
449
  [cells] + cells.collect.with_index{|(sat, sig), i|
461
450
  i2 = sats.find_index(sat)
462
451
  rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
463
452
  [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
464
453
  (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
465
454
  ((delta_fine[i][0] + delta_rough[i2][0]) rescue nil),
466
- cn[i][0]]
455
+ cn[i][0],
456
+ halfc_amb[i][0]]
467
457
  }.transpose).flatten(1))]
468
458
  end
469
459
  end