gps_pvt 0.9.4 → 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.
@@ -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
- 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)
@@ -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 => 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