gps_pvt 0.1.1 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +117 -86
- data/Rakefile +85 -85
- data/exe/gps_pvt +46 -0
- data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -6613
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +16029 -16019
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -21050
- data/ext/gps_pvt/extconf.rb +70 -70
- data/ext/ninja-scan-light/tool/swig/GPS.i +20 -9
- data/gps_pvt.gemspec +57 -57
- data/lib/gps_pvt/receiver.rb +353 -375
- data/lib/gps_pvt/ubx.rb +147 -147
- data/lib/gps_pvt/version.rb +5 -5
- data/lib/gps_pvt.rb +9 -9
- data/sig/gps_pvt.rbs +4 -4
- metadata +6 -4
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -1,375 +1,353 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
pvt.
|
23
|
-
pvt.llh.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
}
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
]
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
}
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
(
|
48
|
-
|
49
|
-
}.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
[
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
info
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
next true
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
task.call(
|
156
|
-
}
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
eph
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
eph.
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
:
|
246
|
-
:
|
247
|
-
}.each{|k, prop|
|
248
|
-
meas.add(prn, GPS::Measurement.const_get(k), loader.call(*prop))
|
249
|
-
}
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
:
|
274
|
-
:
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
$stderr.
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
item[:
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
}
|
355
|
-
|
356
|
-
rcv = GPS_PVT::Receiver::new(options)
|
357
|
-
|
358
|
-
puts GPS_PVT::Receiver::header
|
359
|
-
|
360
|
-
# parse RINEX NAV
|
361
|
-
ARGV.reject!{|arg|
|
362
|
-
next false unless arg =~ /\.\d{2}n$/
|
363
|
-
rcv.parse_rinex_nav(arg)
|
364
|
-
}
|
365
|
-
|
366
|
-
# other files
|
367
|
-
ARGV.each{|arg|
|
368
|
-
case arg
|
369
|
-
when /\.ubx$/
|
370
|
-
rcv.parse_ubx(arg)
|
371
|
-
when /\.\d{2}o$/
|
372
|
-
rcv.parse_rinex_obs(arg)
|
373
|
-
end
|
374
|
-
}
|
375
|
-
end
|
1
|
+
=begin
|
2
|
+
Receiver class to be an top level interface to a user
|
3
|
+
(The origin is ninja-scan-light/tool/misc/receiver_debug.rb)
|
4
|
+
=end
|
5
|
+
|
6
|
+
require_relative 'GPS'
|
7
|
+
|
8
|
+
module GPS_PVT
|
9
|
+
class Receiver
|
10
|
+
OUTPUT_PVT_ITEMS = [[
|
11
|
+
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec],
|
12
|
+
proc{|pvt|
|
13
|
+
[:week, :seconds, :c_tm].collect{|f| pvt.receiver_time.send(f)}.flatten
|
14
|
+
}
|
15
|
+
]] + [[
|
16
|
+
[:receiver_clock_error_meter, :longitude, :latitude, :height],
|
17
|
+
proc{|pvt|
|
18
|
+
next [nil] * 4 unless pvt.position_solved?
|
19
|
+
[
|
20
|
+
pvt.receiver_error,
|
21
|
+
pvt.llh.lng / Math::PI * 180,
|
22
|
+
pvt.llh.lat / Math::PI * 180,
|
23
|
+
pvt.llh.alt,
|
24
|
+
]
|
25
|
+
}
|
26
|
+
]] + [proc{
|
27
|
+
labels = [:g, :p, :h, :v, :t].collect{|k| "#{k}dop".to_sym}
|
28
|
+
[
|
29
|
+
labels,
|
30
|
+
proc{|pvt|
|
31
|
+
next [nil] * 5 unless pvt.position_solved?
|
32
|
+
labels.collect{|k| pvt.send(k)}
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}.call] + [[
|
36
|
+
[:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms],
|
37
|
+
proc{|pvt|
|
38
|
+
next [nil] * 4 unless pvt.velocity_solved?
|
39
|
+
[:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
|
40
|
+
+ [pvt.receiver_error_rate]
|
41
|
+
}
|
42
|
+
]] + [
|
43
|
+
[:used_satellites, proc{|pvt| pvt.used_satellites}],
|
44
|
+
[:PRN, proc{|pvt|
|
45
|
+
("%32s"%[pvt.used_satellite_list.collect{|i|
|
46
|
+
1 << (i - 1)
|
47
|
+
}.inject(0){|res, v| res | v}.to_s(2)]).scan(/.{8}/).collect{|str|
|
48
|
+
str.gsub(' ', '0')
|
49
|
+
}.join('_')
|
50
|
+
}],
|
51
|
+
] + [[
|
52
|
+
(1..32).collect{|prn|
|
53
|
+
[:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str| "#{str}(#{prn})"}
|
54
|
+
}.flatten,
|
55
|
+
proc{|pvt|
|
56
|
+
next ([nil] * 6 * 32) unless pvt.position_solved?
|
57
|
+
sats = pvt.used_satellite_list
|
58
|
+
r, w = [:delta_r, :W].collect{|f| pvt.send(f)}
|
59
|
+
(1..32).collect{|i|
|
60
|
+
next ([nil] * 6) unless i2 = sats.index(i)
|
61
|
+
[r[i2, 0], w[i2, i2]] +
|
62
|
+
[:azimuth, :elevation].collect{|f|
|
63
|
+
pvt.send(f)[i] / Math::PI * 180
|
64
|
+
} + [pvt.slopeH[i], pvt.slopeV[i]]
|
65
|
+
}.flatten
|
66
|
+
},
|
67
|
+
]] + [[
|
68
|
+
[:wssr, :wssr_sf, :weight_max,
|
69
|
+
:slopeH_max, :slopeH_max_PRN, :slopeH_max_elevation,
|
70
|
+
:slopeV_max, :slopeV_max_PRN, :slopeV_max_elevation],
|
71
|
+
proc{|pvt|
|
72
|
+
next [nil] * 9 unless fd = pvt.fd
|
73
|
+
el_deg = [4, 6].collect{|i| pvt.elevation[fd[i]] / Math::PI * 180}
|
74
|
+
fd[0..4] + [el_deg[0]] + fd[5..6] + [el_deg[1]]
|
75
|
+
}
|
76
|
+
]] + [[
|
77
|
+
[:wssr_FDE_min, :wssr_FDE_min_PRN, :wssr_FDE_2nd, :wssr_FDE_2nd_PRN],
|
78
|
+
proc{|pvt|
|
79
|
+
[:fde_min, :fde_2nd].collect{|f|
|
80
|
+
info = pvt.send(f)
|
81
|
+
next ([nil] * 2) if (!info) || info.empty?
|
82
|
+
[info[0], info[-3]]
|
83
|
+
}.flatten
|
84
|
+
}
|
85
|
+
]]
|
86
|
+
|
87
|
+
OUTPUT_MEAS_ITEMS = [[
|
88
|
+
(1..32).collect{|prn|
|
89
|
+
[:L1_range, :L1_rate].collect{|str| "#{str}(#{prn})"}
|
90
|
+
}.flatten,
|
91
|
+
proc{|meas|
|
92
|
+
meas_hash = Hash[*(meas.collect{|prn, k, v| [[prn, k], v]}.flatten(1))]
|
93
|
+
(1..32).collect{|prn|
|
94
|
+
[:L1_PSEUDORANGE, [:L1_DOPPLER, GPS::SpaceNode.L1_WaveLength]].collect{|k, sf|
|
95
|
+
meas_hash[[prn, GPS::Measurement.const_get(k)]] * (sf || 1) rescue nil
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
]]
|
100
|
+
|
101
|
+
def self.header
|
102
|
+
(OUTPUT_PVT_ITEMS + OUTPUT_MEAS_ITEMS).transpose[0].flatten.join(',')
|
103
|
+
end
|
104
|
+
|
105
|
+
attr_accessor :solver
|
106
|
+
|
107
|
+
def initialize(options = {})
|
108
|
+
@solver = GPS::Solver::new
|
109
|
+
@solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
|
110
|
+
rel_prop[0] = 1 if rel_prop[0] > 0 # weight = 1
|
111
|
+
rel_prop
|
112
|
+
}
|
113
|
+
options = options.reject{|k, v|
|
114
|
+
case k
|
115
|
+
when :weight
|
116
|
+
case v.to_sym
|
117
|
+
when :elevation # (same as underneath C++ library)
|
118
|
+
@solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
|
119
|
+
if rel_prop[0] > 0 then
|
120
|
+
elv = Coordinate::ENU::relative_rel(
|
121
|
+
Coordinate::XYZ::new(*rel_prop[4..6]), usr_pos).elevation
|
122
|
+
rel_prop[0] = (Math::sin(elv)/0.8)**2
|
123
|
+
end
|
124
|
+
rel_prop
|
125
|
+
}
|
126
|
+
next true
|
127
|
+
when :identical # same as default
|
128
|
+
next true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
false
|
132
|
+
}
|
133
|
+
raise "Unknown receiver options: #{options.inspect}" unless options.empty?
|
134
|
+
proc{|opt|
|
135
|
+
opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg
|
136
|
+
opt.residual_mask = 1E4 # 10 km
|
137
|
+
}.call(@solver.gps_options)
|
138
|
+
end
|
139
|
+
|
140
|
+
def run(meas, t_meas)
|
141
|
+
=begin
|
142
|
+
$stderr.puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %d:%d:%d UTC"%[*t_meas.c_tm]})"
|
143
|
+
meas.to_a.collect{|prn, k, v| prn}.uniq.each{|prn|
|
144
|
+
eph = @solver.gps_space_node.ephemeris(prn)
|
145
|
+
$stderr.puts "XYZ(PRN:#{prn}): #{eph.constellation(t_meas)[0].to_a} (iodc: #{eph.iodc}, iode: #{eph.iode})"
|
146
|
+
}
|
147
|
+
=end
|
148
|
+
|
149
|
+
#@solver.gps_space_node.update_all_ephemeris(t_meas) # internally called in the following solver.solve
|
150
|
+
pvt = @solver.solve(meas, t_meas)
|
151
|
+
pvt.define_singleton_method(:to_s){
|
152
|
+
(OUTPUT_PVT_ITEMS.transpose[1].collect{|task|
|
153
|
+
task.call(pvt)
|
154
|
+
} + OUTPUT_MEAS_ITEMS.transpose[1].collect{|task|
|
155
|
+
task.call(meas)
|
156
|
+
}).flatten.join(',')
|
157
|
+
}
|
158
|
+
pvt
|
159
|
+
end
|
160
|
+
|
161
|
+
GPS::PVT.class_eval{
|
162
|
+
define_method(:post_solution){|target|
|
163
|
+
sats, az, el = proc{|g|
|
164
|
+
self.used_satellite_list.collect.with_index{|prn, i|
|
165
|
+
# G_enu is measured in the direction from satellite to user positions
|
166
|
+
[prn,
|
167
|
+
Math::atan2(-g[i, 0], -g[i, 1]),
|
168
|
+
Math::asin(-g[i, 2])]
|
169
|
+
}.transpose
|
170
|
+
}.call(self.G_enu) rescue [[], [], []]
|
171
|
+
[[:@azimuth, az], [:@elevation, el]].each{|k, values|
|
172
|
+
self.instance_variable_set(k, Hash[*(sats.zip(values).flatten(1))])
|
173
|
+
}
|
174
|
+
[:@slopeH, :@slopeV] \
|
175
|
+
.zip((self.slope_HV_enu.to_a.transpose rescue [nil, nil])) \
|
176
|
+
.each{|k, values|
|
177
|
+
self.instance_variable_set(k,
|
178
|
+
Hash[*(values ? sats.zip(values).flatten(1) : [])])
|
179
|
+
}
|
180
|
+
instance_variable_get(target)
|
181
|
+
}
|
182
|
+
[:azimuth, :elevation, :slopeH, :slopeV].each{|k|
|
183
|
+
eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
proc{
|
188
|
+
eph_list = Hash[*(1..32).collect{|prn|
|
189
|
+
eph = GPS::Ephemeris::new
|
190
|
+
eph.svid = prn
|
191
|
+
[prn, eph]
|
192
|
+
}.flatten(1)]
|
193
|
+
define_method(:register_ephemeris){|t_meas, prn, bcast_data|
|
194
|
+
next unless eph = eph_list[prn]
|
195
|
+
sn = @solver.gps_space_node
|
196
|
+
subframe, iodc_or_iode = eph.parse(bcast_data)
|
197
|
+
if iodc_or_iode < 0 then
|
198
|
+
begin
|
199
|
+
sn.update_iono_utc(
|
200
|
+
GPS::Ionospheric_UTC_Parameters::parse(bcast_data))
|
201
|
+
[:alpha, :beta].each{|k|
|
202
|
+
$stderr.puts "Iono #{k}: #{sn.iono_utc.send(k)}"
|
203
|
+
} if false
|
204
|
+
rescue
|
205
|
+
end
|
206
|
+
next
|
207
|
+
end
|
208
|
+
if t_meas and eph.consistent? then
|
209
|
+
eph.WN = ((t_meas.week / 1024).to_i * 1024) + (eph.WN % 1024)
|
210
|
+
sn.register_ephemeris(prn, eph)
|
211
|
+
eph.invalidate
|
212
|
+
end
|
213
|
+
}
|
214
|
+
}.call
|
215
|
+
|
216
|
+
def parse_ubx(ubx_fname, &b)
|
217
|
+
$stderr.print "Reading UBX file (%s) "%[ubx_fname]
|
218
|
+
require_relative 'ubx'
|
219
|
+
|
220
|
+
ubx = UBX::new(open(ubx_fname))
|
221
|
+
ubx_kind = Hash::new(0)
|
222
|
+
|
223
|
+
after_run = b || proc{|pvt| puts pvt.to_s}
|
224
|
+
|
225
|
+
t_meas = nil
|
226
|
+
ubx.each_packet.with_index(1){|packet, i|
|
227
|
+
$stderr.print '.' if i % 1000 == 0
|
228
|
+
ubx_kind[packet[2..3]] += 1
|
229
|
+
case packet[2..3]
|
230
|
+
when [0x02, 0x10] # RXM-RAW
|
231
|
+
msec, week = [[0, 4, "V"], [4, 2, "v"]].collect{|offset, len, str|
|
232
|
+
packet.slice(6 + offset, len).pack("C*").unpack(str)[0]
|
233
|
+
}
|
234
|
+
t_meas = GPS::Time::new(week, msec.to_f / 1000)
|
235
|
+
meas = GPS::Measurement::new
|
236
|
+
packet[6 + 6].times{|i|
|
237
|
+
loader = proc{|offset, len, str|
|
238
|
+
ary = packet.slice(6 + offset + (i * 24), len)
|
239
|
+
str ? ary.pack("C*").unpack(str)[0] : ary
|
240
|
+
}
|
241
|
+
prn = loader.call(28, 1)[0]
|
242
|
+
{
|
243
|
+
:L1_PSEUDORANGE => [16, 8, "E"],
|
244
|
+
:L1_DOPPLER => [24, 4, "e"],
|
245
|
+
:L1_CARRIER_PHASE => [8, 8, "E"],
|
246
|
+
:L1_SIGNAL_STRENGTH_dBHz => [30, 1, "c"],
|
247
|
+
}.each{|k, prop|
|
248
|
+
meas.add(prn, GPS::Measurement.const_get(k), loader.call(*prop))
|
249
|
+
}
|
250
|
+
# bit 0 of RINEX LLI (loss of lock indicator) shows lost lock
|
251
|
+
# between previous and current observation, which maps negative lock seconds
|
252
|
+
meas.add(prn, GPS::Measurement::L1_LOCK_SEC,
|
253
|
+
(packet[6 + 31 + (i * 24)] & 0x01 == 0x01) ? -1 : 0)
|
254
|
+
}
|
255
|
+
after_run.call(run(meas, t_meas), [meas, t_meas])
|
256
|
+
when [0x02, 0x15] # RXM-RAWX
|
257
|
+
sec, week = [[0, 8, "E"], [8, 2, "v"]].collect{|offset, len, str|
|
258
|
+
packet.slice(6 + offset, len).pack("C*").unpack(str)[0]
|
259
|
+
}
|
260
|
+
t_meas = GPS::Time::new(week, sec)
|
261
|
+
meas = GPS::Measurement::new
|
262
|
+
packet[6 + 11].times{|i|
|
263
|
+
loader = proc{|offset, len, str, post|
|
264
|
+
v = packet.slice(6 + offset + (i * 32), len)
|
265
|
+
v = str ? v.pack("C*").unpack(str)[0] : v
|
266
|
+
v = post.call(v) if post
|
267
|
+
v
|
268
|
+
}
|
269
|
+
next unless (gnss = loader.call(36, 1)[0]) == 0
|
270
|
+
svid = loader.call(37, 1)[0]
|
271
|
+
trk_stat = loader.call(46, 1)[0]
|
272
|
+
{
|
273
|
+
:L1_PSEUDORANGE => [16, 8, "E", proc{|v| (trk_stat & 0x1 == 0x1) ? v : nil}],
|
274
|
+
:L1_PSEUDORANGE_SIGMA => [43, 1, nil, proc{|v|
|
275
|
+
(trk_stat & 0x1 == 0x1) ? (1E-2 * (v[0] & 0xF)) : nil
|
276
|
+
}],
|
277
|
+
:L1_DOPPLER => [32, 4, "e"],
|
278
|
+
:L1_DOPPLER_SIGMA => [45, 1, nil, proc{|v| 2E-3 * (v[0] & 0xF)}],
|
279
|
+
:L1_CARRIER_PHASE => [24, 8, "E", proc{|v| (trk_stat & 0x2 == 0x2) ? v : nil}],
|
280
|
+
:L1_CARRIER_PHASE_SIGMA => [44, 1, nil, proc{|v|
|
281
|
+
(trk_stat & 0x2 == 0x2) ? (0.004 * (v[0] & 0xF)) : nil
|
282
|
+
}],
|
283
|
+
:L1_SIGNAL_STRENGTH_dBHz => [42, 1],
|
284
|
+
:L1_LOCK_SEC => [40, 2, "v", proc{|v| 1E-3 * v}],
|
285
|
+
}.each{|k, prop|
|
286
|
+
next unless v = loader.call(*prop)
|
287
|
+
meas.add(svid, GPS::Measurement.const_get(k), v)
|
288
|
+
}
|
289
|
+
}
|
290
|
+
after_run.call(run(meas, t_meas), [meas, t_meas])
|
291
|
+
when [0x02, 0x11] # RXM-SFRB
|
292
|
+
register_ephemeris(
|
293
|
+
t_meas,
|
294
|
+
packet[6 + 1],
|
295
|
+
packet.slice(6 + 2, 40).each_slice(4).collect{|v|
|
296
|
+
(v.pack("C*").unpack("V")[0] & 0xFFFFFF) << 6
|
297
|
+
})
|
298
|
+
when [0x02, 0x13] # RXM-SFRBX
|
299
|
+
next unless (gnss = packet[6]) == 0
|
300
|
+
register_ephemeris(
|
301
|
+
t_meas,
|
302
|
+
packet[6 + 1],
|
303
|
+
packet.slice(6 + 8, 4 * packet[6 + 4]).each_slice(4).collect{|v|
|
304
|
+
v.pack("C*").unpack("V")[0]
|
305
|
+
})
|
306
|
+
end
|
307
|
+
}
|
308
|
+
$stderr.puts ", found packets are %s"%[ubx_kind.inspect]
|
309
|
+
end
|
310
|
+
|
311
|
+
def parse_rinex_nav(fname)
|
312
|
+
$stderr.puts "Read RINEX NAV file (%s): %d items."%[
|
313
|
+
fname, @solver.gps_space_node.read(fname)]
|
314
|
+
end
|
315
|
+
|
316
|
+
def parse_rinex_obs(fname, &b)
|
317
|
+
after_run = b || proc{|pvt| puts pvt.to_s}
|
318
|
+
$stderr.print "Reading RINEX observation file (%s)"%[fname]
|
319
|
+
types = nil
|
320
|
+
count = 0
|
321
|
+
GPS::RINEX_Observation::read(fname){|item|
|
322
|
+
$stderr.print '.' if (count += 1) % 1000 == 0
|
323
|
+
t_meas = item[:time]
|
324
|
+
|
325
|
+
meas = GPS::Measurement::new
|
326
|
+
types ||= (item[:meas_types]['G'] || item[:meas_types][' ']).collect.with_index{|type_, i|
|
327
|
+
case type_
|
328
|
+
when "C1", "C1C"
|
329
|
+
[i, GPS::Measurement::L1_PSEUDORANGE]
|
330
|
+
when "L1", "L1C"
|
331
|
+
[i, GPS::Measurement::L1_CARRIER_PHASE]
|
332
|
+
when "D1", "D1C"
|
333
|
+
[i, GPS::Measurement::L1_DOPPLER]
|
334
|
+
when "S1", "S1C"
|
335
|
+
[i, GPS::Measurement::L1_SIGNAL_STRENGTH_dBHz]
|
336
|
+
else
|
337
|
+
nil
|
338
|
+
end
|
339
|
+
}.compact
|
340
|
+
item[:meas].each{|k, v|
|
341
|
+
sys, prn = k
|
342
|
+
next unless sys == 'G' # GPS only
|
343
|
+
types.each{|i, type_|
|
344
|
+
meas.add(prn, type_, v[i][0]) if v[i]
|
345
|
+
}
|
346
|
+
}
|
347
|
+
p meas.to_hash
|
348
|
+
after_run.call(run(meas, t_meas), [meas, t_meas])
|
349
|
+
}
|
350
|
+
$stderr.puts ", %d epochs."%[count]
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|