gps_pvt 0.9.1 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/gps_pvt/rtcm3.rb CHANGED
@@ -13,6 +13,7 @@ class RTCM3
13
13
  end
14
14
  module Packet
15
15
  def decode(bits_list, offset = nil)
16
+ # 24 is offset of header in transport layer
16
17
  Util::BitOp::extract(self, bits_list, offset || 24)
17
18
  end
18
19
  def message_number
@@ -56,15 +57,51 @@ class RTCM3
56
57
  2 => 12,
57
58
  3 => 12,
58
59
  4 => unum_gen.call(30, Rational(1, 1000)), # [sec]
60
+ 5 => 1,
61
+ 6 => 5,
62
+ 7 => 1,
63
+ 8 => 3,
59
64
  9 => 6,
65
+ 10 => 1,
66
+ 11 => invalidate.call(unum_gen.call(24, Rational(2, 100)), 0x800000), # [m]
67
+ 12 => invalidate.call(num_gen.call(20, Rational(5, 10000)), 0x80000), # [m]
68
+ 13 => 7,
69
+ 14 => unum_gen.call(8, 299_792.458), # [m]
70
+ 15 => invalidate.call(unum_gen.call(8, Rational(1, 4)), 0), # [db-Hz],
71
+ 16 => 2,
72
+ 17 => invalidate.call(num_gen.call(14, Rational(2, 100)), 0x2000), # [m]
73
+ 18 => num_gen.call(20, Rational(5, 10000)), # [m]
74
+ 19 => 7,
75
+ 20 => invalidate.call(unum_gen.call(8, Rational(1, 4)), 0), # [db-Hz]
60
76
  21 => 6,
61
77
  22 => 1,
62
78
  23 => 1,
63
79
  24 => 1,
64
80
  25 => num_gen.call(38, Rational(1, 10000)), # [m]
65
81
  34 => unum_gen.call(27, Rational(1, 1000)), # [sec]
82
+ 35 => 5,
83
+ 36 => 1,
84
+ 37 => 3,
66
85
  38 => 6,
67
- 40 => 5,
86
+ 39 => 1,
87
+ 40 => [5, proc{|v| v - 7}],
88
+ 41 => invalidate.call(unum_gen.call(25, Rational(2, 100)), 0x1000000), # [m]
89
+ 42 => invalidate.call(num_gen.call(20, Rational(5, 10000)), 0x80000), # [m]
90
+ 43 => 7,
91
+ 44 => unum_gen.call(7, 599_584.916), # [m]
92
+ 45 => invalidate.call(unum_gen.call(8, Rational(1, 4)), 0), # [db-Hz],
93
+ 46 => 2,
94
+ 47 => invalidate.call(num_gen.call(14, Rational(2, 100)), 0x2000), # [m]
95
+ 48 => invalidate.call(num_gen.call(20, Rational(5, 10000)), 0x80000), # [m]
96
+ 49 => 7,
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,
68
105
  71 => 8,
69
106
  76 => 10,
70
107
  77 => proc{
@@ -146,11 +183,15 @@ class RTCM3
146
183
  397 => invalidate.call(unum_gen.call(8, Rational(1, 1000)), 0xFF), # [sec]
147
184
  398 => unum_gen.call(10, Rational(1, 1000 << 10)), # [sec]
148
185
  399 => invalidate.call(num_gen.call(14), 0x2000), # [m/s]
186
+ 400 => invalidate.call(num_gen.call(15, Rational(1, 1000 << 24)), 0x4000), # [sec],
187
+ 401 => invalidate.call(num_gen.call(22, Rational(1, 1000 << 29)), 0x200000), # [sec],
188
+ 402 => 4,
189
+ 403 => invalidate.call(unum_gen.call(6), 0), # [dB-Hz],
149
190
  404 => invalidate.call(num_gen.call(15, Rational(1, 10000)), 0x4000), # [m/s]
150
191
  405 => invalidate.call(num_gen.call(20, Rational(1, 1000 << 29)), 0x80000), # [sec]
151
192
  406 => invalidate.call(num_gen.call(24, Rational(1, 1000 << 31)), 0x800000), # [sec]
152
193
  407 => 10,
153
- 408 => unum_gen.call(10, Rational(1, 1 << 4)), # [dB-Hz]
194
+ 408 => invalidate.call(unum_gen.call(10, Rational(1, 1 << 4)), 0), # [dB-Hz]
154
195
  409 => 3,
155
196
  411 => 2,
156
197
  412 => 2,
@@ -171,6 +212,20 @@ class RTCM3
171
212
  src = (src.to_a rescue [src]).flatten
172
213
  (dst.to_a rescue ([dst] * src.size)).flatten.zip(src).each{|i, j| df[i] = df[j]}
173
214
  }
215
+ df.merge!({
216
+ :SBAS_prn => [6, proc{|v| v + 120}],
217
+ :SBAS_iodn => 8,
218
+ :SBAS_tod => num_gen.call(13, 1 << 4),
219
+ :SBAS_ura => df[77],
220
+ :SBAS_xy => num_gen.call(30, Rational(8, 100)),
221
+ :SBAS_z => num_gen.call(25, Rational(4, 10)),
222
+ :SBAS_dxy => num_gen.call(17, Rational(1, 1600)),
223
+ :SBAS_dz => num_gen.call(18, Rational(1, 250)),
224
+ :SBAS_ddxy => num_gen.call(10, Rational(1, 80000)),
225
+ :SBAS_ddz => num_gen.call(10, Rational(1, 16000)),
226
+ :SBAS_agf0 => num_gen.call(12, Rational(1, 1 << 31)),
227
+ :SBAS_agf1 => num_gen.call(8, Rational(1, 1 << 40)),
228
+ })
174
229
  df.define_singleton_method(:generate_prop){|idx_list|
175
230
  hash = Hash[*([:bits, :op].collect.with_index{|k, i|
176
231
  [k, idx_list.collect{|idx, *args|
@@ -180,21 +235,81 @@ class RTCM3
180
235
  [prop].flatten(1)[i]
181
236
  }]
182
237
  }.flatten(1))].merge({:df => idx_list})
183
- hash[:bits_total] = hash[:bits].inject{|a, b| a + b}
238
+ hash[:bits_total] = hash[:bits].inject{|a, b| a + b} || 0
184
239
  hash
185
240
  }
186
241
  df
187
242
  }.call
188
243
  MessageType = Hash[*({
244
+ 1001..1004 => (2..8).to_a,
189
245
  1005 => [2, 3, 21, 22, 23, 24, 141, 25, 142, [1, 1], 26, 364, 27],
246
+ 1009..1012 => [2, 3, 34, 5, 35, 36, 37],
247
+ 1013 => [2, 3, 51, 52, 53, 54],
190
248
  1019 => [2, 9, (76..79).to_a, 71, (81..103).to_a, 137].flatten, # 488 bits @see Table 3.5-21
191
249
  1020 => [2, 38, 40, (104..136).to_a].flatten, # 360 bits @see Table 3.5-21
250
+ 1043 => [2] + [:prn, :iodn, :tod, :ura,
251
+ [: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
192
253
  1044 => [2, (429..457).to_a].flatten, # 485 bits
193
- 1077 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-78
194
- 1087 => [2, 3, 416, 34, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-93
195
- 1097 => [2, 3, 248, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-98
196
- 1117 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits
197
- }.collect{|mt, df_list| [mt, DataFrame.generate_prop(df_list)]}.flatten(1))]
254
+ 1070..1229 => [2, [:uint, 12], [:uint, 30], 393], # 55 bits part of messages will be overwritten
255
+ 1071..1077 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-78
256
+ 1081..1087 => [2, 3, 416, 34, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-93
257
+ 1091..1097 => [2, 3, 248, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits @see Table 3.5-98
258
+ 1101..1107 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits
259
+ 1111..1117 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits
260
+ 1121..1127 => [2, 3, 4, 393, 409, [1, 7], 411, 412, 417, 418, 394, 395], # 169 bits
261
+ }.collect{|mt_list, df_list|
262
+ (mt_list.to_a rescue [mt_list]).collect{|mt|
263
+ [mt, DataFrame.generate_prop(df_list)]
264
+ }
265
+ }.flatten(2))]
266
+ module GPS_Observation
267
+ def ranges
268
+ res = {
269
+ :sat => select{|v, df| df == 9}.transpose[0],
270
+ :pseudo_range_rem => select{|v, df| df == 11}.transpose[0],
271
+ }
272
+ add_proc = proc{|k, df, base|
273
+ values = select{|v, df_| df_ == df}
274
+ next if values.empty?
275
+ res[k] = values.transpose[0]
276
+ res[k] = res[k].zip(res[base]).collect{|a, b| (a + b) rescue nil} if base
277
+ }
278
+ add_proc.call(:pseudo_range, 14, :pseudo_range_rem)
279
+ suffix = res[:pseudo_range] ? "" : "_rem"
280
+ base = "pseudo_range#{suffix}".to_sym
281
+ add_proc.call("phase_range#{suffix}".to_sym, 12, base)
282
+ add_proc.call(:cn, 15)
283
+ add_proc.call("pseudo_range_L2#{suffix}".to_sym, 17, base)
284
+ add_proc.call("phase_range_L2#{suffix}".to_sym, 18, base)
285
+ add_proc.call(:cn_L2, 20)
286
+ res
287
+ end
288
+ end
289
+ module GLONASS_Observation
290
+ def ranges
291
+ res = {
292
+ :sat => select{|v, df| df == 38}.transpose[0],
293
+ :freq_ch => select{|v, df| df == 40}.transpose[0],
294
+ :pseudo_range_rem => select{|v, df| df == 41}.transpose[0],
295
+ }
296
+ add_proc = proc{|k, df, base|
297
+ values = select{|v, df_| df_ == df}
298
+ next if values.empty?
299
+ res[k] = values.transpose[0]
300
+ res[k] = res[k].zip(res[base]).collect{|a, b| (a + b) rescue nil} if base
301
+ }
302
+ add_proc.call(:pseudo_range, 44, :pseudo_range_rem)
303
+ suffix = res[:pseudo_range] ? "" : "_rem"
304
+ base = "pseudo_range#{suffix}".to_sym
305
+ add_proc.call("phase_range#{suffix}".to_sym, 42, base)
306
+ add_proc.call(:cn, 45)
307
+ add_proc.call("pseudo_range_L2#{suffix}".to_sym, 47, base)
308
+ add_proc.call("phase_range_L2#{suffix}".to_sym, 48, base)
309
+ add_proc.call(:cn_L2, 50)
310
+ res
311
+ end
312
+ end
198
313
  module GPS_Ephemeris
199
314
  KEY2IDX = {:svid => 1, :WN => 2, :URA => 3, :dot_i0 => 5, :iode => 6, :t_oc => 7,
200
315
  :a_f2 => 8, :a_f1 => 9, :a_f0 => 10, :iodc => 11, :c_rs => 12, :delta_n => 13,
@@ -218,6 +333,17 @@ class RTCM3
218
333
  res
219
334
  end
220
335
  end
336
+ module SBAS_Ephemeris
337
+ KEY2IDX = {:svid => 1, :iodn => 2, :tod => 3, :URA => 4,
338
+ :x => 5, :y => 6, :z => 7,
339
+ :dx => 8, :dy => 9, :dz => 10,
340
+ :ddx => 11, :ddy => 12, :ddz => 13,
341
+ :a_Gf0 => 14, :a_Gf1 => 15}
342
+ def params
343
+ # TODO WN is required to provide
344
+ Hash[*(KEY2IDX.collect{|k, i| [k, self[i][0]]}.flatten(1))]
345
+ end
346
+ end
221
347
  module GLONASS_Ephemeris
222
348
  def params
223
349
  # TODO insufficient: :n => ?(String4); extra: :P3
@@ -259,30 +385,86 @@ class RTCM3
259
385
  def more_data?
260
386
  self.find{|v| v[1] == 393}[0] == 1
261
387
  end
388
+ def property
389
+ idx_sat = self.find_index{|v| v[1] == 394}
390
+ {
391
+ :sats => self[idx_sat][0],
392
+ :cells => self[idx_sat + 2][0], # DF396
393
+ :header_items => idx_sat + 3,
394
+ }
395
+ end
262
396
  end
263
- module MSM7
397
+ module MSM
398
+ include MSM_Header
399
+ def ranges
400
+ {:sat_sig => property[:cells]} # expect to be overriden
401
+ end
264
402
  SPEED_OF_LIGHT = 299_792_458
403
+ end
404
+ module MSM1_2_3
405
+ include MSM
265
406
  def ranges
266
- idx_sat = self.find_index{|v| v[1] == 394}
267
- sats = self[idx_sat][0]
268
- nsat = sats.size
269
- cells = self[idx_sat + 2][0] # DF396
270
- ncell = cells.size
271
- offset = idx_sat + 3
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
425
+ end
426
+ end
427
+ module MSM4_6
428
+ include MSM
429
+ def ranges
430
+ sats, cells, offset = property.values_at(:sats, :cells, :header_items)
431
+ nsat, ncell = [sats.size, cells.size]
432
+ range_rough = self[offset, nsat] # DF397
433
+ range_rough2 = self[offset + (nsat * 1), nsat] # DF398
434
+ range_fine = self[offset + (nsat * 2), ncell] # DF400/405
435
+ phase_fine = self[offset + (nsat * 2) + (ncell * 1), ncell] # DF401/406
436
+ cn = self[offset + (nsat * 2) + (ncell * 4), ncell] # DF403/408
437
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :cn].zip(
438
+ [cells] + cells.collect.with_index{|(sat, sig), i|
439
+ i2 = sats.find_index(sat)
440
+ rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
441
+ [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
442
+ (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
443
+ cn[i][0]]
444
+ }.transpose).flatten(1))]
445
+ end
446
+ end
447
+ module MSM5_7
448
+ include MSM
449
+ def ranges
450
+ sats, cells, offset = property.values_at(:sats, :cells, :header_items)
451
+ nsat, ncell = [sats.size, cells.size]
272
452
  range_rough = self[offset, nsat] # DF397
273
453
  range_rough2 = self[offset + (nsat * 2), nsat] # DF398
274
454
  delta_rough = self[offset + (nsat * 3), nsat] # DF399
275
- range_fine = self[offset + (nsat * 4), ncell] # DF405
276
- phase_fine = self[offset + (nsat * 4) + (ncell * 1), ncell] # DF406
455
+ range_fine = self[offset + (nsat * 4), ncell] # DF400/405
456
+ phase_fine = self[offset + (nsat * 4) + (ncell * 1), ncell] # DF401/406
457
+ cn = self[offset + (nsat * 4) + (ncell * 4), ncell] # DF403/408
277
458
  delta_fine = self[offset + (nsat * 4) + (ncell * 5), ncell] # DF404
278
- Hash[*([:pseudo_range, :phase_range, :phase_range_rate, :sat_sig].zip(
279
- cells.collect.with_index{|(sat, sig), i|
459
+ Hash[*([:sat_sig, :pseudo_range, :phase_range, :phase_range_rate, :cn].zip(
460
+ [cells] + cells.collect.with_index{|(sat, sig), i|
280
461
  i2 = sats.find_index(sat)
281
- rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
462
+ rough_ms = (range_rough2[i2][0] + range_rough[i2][0]) rescue nil
282
463
  [(((range_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
283
464
  (((phase_fine[i][0] + rough_ms) * SPEED_OF_LIGHT) rescue nil),
284
- ((delta_fine[i][0] + delta_rough[i2][0]) rescue nil)]
285
- }.transpose + [cells]).flatten(1))]
465
+ ((delta_fine[i][0] + delta_rough[i2][0]) rescue nil),
466
+ cn[i][0]]
467
+ }.transpose).flatten(1))]
286
468
  end
287
469
  end
288
470
  def parse
@@ -298,28 +480,77 @@ class RTCM3
298
480
  }
299
481
  add_proc.call(mt)
300
482
  case msg_num
483
+ when 1001..1004
484
+ nsat = values[4]
485
+ offset = 24 + mt[:bits_total]
486
+ add_proc.call(DataFrame.generate_prop(([{
487
+ 1001 => (9..13).to_a,
488
+ 1002 => (9..15).to_a,
489
+ 1003 => (9..13).to_a + (16..19).to_a,
490
+ 1004 => (9..20).to_a,
491
+ }[msg_num]] * nsat).flatten), offset)
492
+ attributes << GPS_Observation
493
+ when 1009..1012
494
+ nsat = values[4]
495
+ offset = 24 + mt[:bits_total]
496
+ add_proc.call(DataFrame.generate_prop(([{
497
+ 1009 => (38..43).to_a,
498
+ 1010 => (38..45).to_a,
499
+ 1011 => (38..43).to_a + (46..49).to_a,
500
+ 1012 => (38..50).to_a,
501
+ }[msg_num]] * nsat).flatten), offset)
502
+ attributes << GLONASS_Observation
503
+ when 1013
504
+ add_proc.call(DataFrame.generate_prop(
505
+ ((55..57).to_a * values[4]).flatten), 24 + mt[:bits_total])
301
506
  when 1019
302
507
  attributes << GPS_Ephemeris
303
508
  when 1020
304
509
  attributes << GLONASS_Ephemeris
510
+ when 1043
511
+ attributes << SBAS_Ephemeris
305
512
  when 1044
306
513
  attributes << QZSS_Ephemeris
307
- when 1077, 1087, 1097, 1117
308
- # 1077(GPS), 1087(GLONASS), 1097(GALILEO), 1117(QZSS)
309
- attributes << MSM7
514
+ when 1071..1077, 1081..1087, 1091..1097, 1101..1107, 1111..1117, 1121..1127
515
+ # 107X(GPS), 108X(GLONASS), 109X(GALILEO), 110X(SBAS), 111X(QZSS), 112X(Beidou)
310
516
  nsat, nsig = [-2, -1].collect{|i| values[i].size}
311
517
  offset = 24 + mt[:bits_total]
312
518
  df396 = DataFrame.generate_prop([[396, values[-2], values[-1]]])
313
519
  add_proc.call(df396, offset)
314
520
  ncell = values[-1].size
315
521
  offset += df396[:bits_total]
316
- msm7_sat = DataFrame.generate_prop(
317
- ([[397, [:uint, 4], 398, 399]] * nsat).transpose.flatten(1))
318
- add_proc.call(msm7_sat, offset)
319
- offset += msm7_sat[:bits_total]
320
- msm7_sig = DataFrame.generate_prop(
321
- ([[405, 406, 407, 420, 408, 404]] * ncell).transpose.flatten(1))
322
- add_proc.call(msm7_sig, offset)
522
+ msm_proc = proc{|sat_data, signal_data|
523
+ msm_sat = DataFrame.generate_prop(([sat_data] * nsat).transpose.flatten(1))
524
+ add_proc.call(msm_sat, offset)
525
+ offset += msm_sat[:bits_total]
526
+ msm_sig = DataFrame.generate_prop(([signal_data] * ncell).transpose.flatten(1))
527
+ add_proc.call(msm_sig, offset)
528
+ }
529
+ case msg_num % 10
530
+ when 1
531
+ attributes << MSM1_2_3
532
+ msm_proc.call([398], [400])
533
+ when 2
534
+ attributes << MSM1_2_3
535
+ msm_proc.call([398], [401, 402, 420])
536
+ when 3
537
+ attributes << MSM1_2_3
538
+ msm_proc.call([398], [400, 401, 402, 420])
539
+ when 4
540
+ attributes << MSM4_6
541
+ msm_proc.call([397, 398], [400, 401, 402, 420, 403])
542
+ when 5
543
+ attributes << MSM5_7
544
+ msm_proc.call([397, [:uint, 4], 398, 399], [400, 401, 402, 420, 403, 404])
545
+ when 6
546
+ attributes << MSM4_6
547
+ msm_proc.call([397, 398], [405, 406, 407, 420, 408])
548
+ when 7
549
+ attributes << MSM5_7
550
+ msm_proc.call([397, [:uint, 4], 398, 399], [405, 406, 407, 420, 408, 404])
551
+ else
552
+ attributes << MSM # for #range
553
+ end
323
554
  end
324
555
  attributes << MSM_Header if (1070..1229).include?(msg_num)
325
556
  res = values.zip(df_list)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.9.1"
4
+ VERSION = "0.9.3"
5
5
 
6
6
  def GPS_PVT.version_compare(a, b)
7
7
  Gem::Version::new(a) <=> Gem::Version::new(b)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gps_pvt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - fenrir(M.Naruoka)
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-27 00:00:00.000000000 Z
11
+ date: 2023-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyserial