gps_pvt 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/Rakefile +18 -1
- data/exe/gps2ubx +12 -5
- data/exe/gps_pvt +7 -2
- data/gps_pvt.gemspec +3 -2
- data/lib/gps_pvt/asn1/asn1.rb +888 -0
- data/lib/gps_pvt/asn1/asn1.y +903 -0
- data/lib/gps_pvt/asn1/per.rb +182 -0
- data/lib/gps_pvt/receiver/agps.rb +31 -0
- data/lib/gps_pvt/receiver/extension.rb +94 -0
- data/lib/gps_pvt/receiver/rtcm3.rb +2 -1
- data/lib/gps_pvt/receiver.rb +23 -16
- data/lib/gps_pvt/rtcm3.rb +15 -30
- data/lib/gps_pvt/supl.rb +567 -0
- data/lib/gps_pvt/ubx.rb +15 -0
- data/lib/gps_pvt/upl/LPP-V17_5_0-Release17.asn +6441 -0
- data/lib/gps_pvt/upl/RRLP-V17_0_0-Release17.asn +2780 -0
- data/lib/gps_pvt/upl/ULP-V2_0_6-20200720-D.asn +2185 -0
- data/lib/gps_pvt/upl/upl.json.gz +0 -0
- data/lib/gps_pvt/upl/upl.rb +99 -0
- data/lib/gps_pvt/util.rb +1 -0
- data/lib/gps_pvt/version.rb +1 -1
- metadata +26 -2
@@ -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
|
@@ -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
|
@@ -195,7 +195,7 @@ class Receiver
|
|
195
195
|
when 1020
|
196
196
|
params = parsed.params
|
197
197
|
eph = GPS::Ephemeris_GLONASS::new
|
198
|
-
|
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)
|
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -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)
|
@@ -724,4 +730,5 @@ end
|
|
724
730
|
end
|
725
731
|
|
726
732
|
require_relative 'receiver/rtcm3'
|
733
|
+
require_relative 'receiver/agps'
|
727
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 =>
|
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 =>
|
160
|
-
|
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},
|
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, :
|
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] =
|
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, :
|
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, :
|
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, :
|
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, :
|
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] =
|
365
|
+
res[:fit_interval] = [self[29][0], res[:iodc], :QZSS]
|
381
366
|
res
|
382
367
|
end
|
383
368
|
end
|