gps_pvt 0.9.1 → 0.9.3

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.
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