gps_pvt 0.8.1 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb3abd803cfe1b0b5199c4187fe17c2595eb603ab27e09f71b66fde11eeafce0
4
- data.tar.gz: 351d46d74583346750aec426c087c33bc13f9e9e9adff746567e40df7eb1e39e
3
+ metadata.gz: e2f23806309c4244499a91877edba1a5dfacb1a9108596527dffad650c9cd9ee
4
+ data.tar.gz: dc33e31c739a79dc7e7c28ad0d6f404b65788499dfa2f53f9b93edae7b589e7e
5
5
  SHA512:
6
- metadata.gz: 0fb78d49675fbbcb3fcfeb80debd7ca15f351be478ed5716f94a61b23f26af7ff957ee446a5ab6c4cc2e67abf2f251cf62845a347ea3810282987da04fa9a2e8
7
- data.tar.gz: ac74ba61920cad427d6e848f4621227d150bfe9a30a2f1465bd6d87681cfdd935e20e8e141575306b87c770b485f8dad1555766b7281980ba1f412adddff4e7c
6
+ metadata.gz: 2a3ec484712ccbd06b99a08a672ef20980d68e5f1670b07b093f1d6e4d0480dce974cbe8a181ecb0e8d6074cc60c1c122778113e962625f57b41a7a85b61fc4a
7
+ data.tar.gz: 5368619df05a35b6295702e3ad64958aab84e92652b01747f31b5bb98974243ac56a5b4fc25bde1075109bd4d586eac14bf64e1c2ac6b5a31d9bc990ece92fd7
data/README.md CHANGED
@@ -27,7 +27,7 @@ For Windows users, this gem requires Devkit because of native compilation.
27
27
 
28
28
  ## Usage
29
29
 
30
- ### For user who just generate PVT solution
30
+ ### For user who just wants to generate PVT solution
31
31
  An attached executable is useful. After installation, type
32
32
 
33
33
  $ gps_pvt file_or_URI(s)
@@ -36,12 +36,12 @@ The format of file is automatically determined with its extension, such as .ubx
36
36
 
37
37
  | specification | recoginized as |
38
38
  ----|----
39
- | --rinex_nav=file_or_URI | [RINEX](https://www.igs.org/wg/rinex/#documents-formats) navigation file |
40
- | --rinex_obs=file_or_URI | [RINEX](https://www.igs.org/wg/rinex/#documents-formats) observation file |
41
- | --ubx=file_or_URI | [U-blox](https://www.u-blox.com/) dedicated format |
39
+ | <a name=opt_rinex_nav>--rinex_nav=file_or_URI</a> | [RINEX](https://www.igs.org/wg/rinex/#documents-formats) navigation file |
40
+ | <a name=opt_rinex_obs>--rinex_obs=file_or_URI</a> | [RINEX](https://www.igs.org/wg/rinex/#documents-formats) observation file |
41
+ | <a name=opt_ubx>--ubx=file_or_URI</a> | [U-blox](https://www.u-blox.com/) dedicated format |
42
42
  | --sp3=file_or_URI | [Standard Product 3 Orbit Format](https://files.igs.org/pub/data/format/sp3c.txt) (supported gps_pvt version >= 0.6.0) |
43
43
  | --antex=file_or_URI | [Antenna Exchange Format](https://igs.org/wg/antenna#files) (supported gps_pvt version >= 0.6.0) |
44
- | --rinex_clk=file_or_URI | [RINEX clock](https://files.igs.org/pub/data/format/rinex_clock304.txt) file (supported gps_pvt version >= 0.7.0) |
44
+ | --rinex_clk=file_or_URI | [RINEX clock](https://files.igs.org/pub/data/format/rinex_clock304.txt) file (supported gps_pvt version >= 0.7.0) |
45
45
 
46
46
  Since version 0.2.0, SBAS and QZSS are supported in addition to GPS. Since version 0.4.0, GLONASS is also available. QZSS ranging is activated in default, however, SBAS is just utilized for ionospheric correction. GLONASS is also turned off by default. If you want to activate SBAS or GLONASS ranging, "--with=(system or PRN)" options are used with gps_pvt executable like
47
47
 
@@ -49,13 +49,13 @@ Since version 0.2.0, SBAS and QZSS are supported in addition to GPS. Since versi
49
49
 
50
50
  Additionally, the following command options *--key=value* are available.
51
51
 
52
- | key | value | comment | version |
52
+ | key | value | comment | since |
53
53
  ----|----|----|----
54
54
  | base_station | 3 \* (numeric+coordinate) | base position used for relative ENU position calculation. XYZ, NEU formats are acceptable. *ex1) --base_station=0X,0Y,0Z*, *ex2) --base_station=12.34N,56.789E,0U* | v0.1.7 |
55
55
  | elevation_mask_deg | numeric | satellite elevation mask specified in degrees. *ex) --elevation_mask_deg=10* | v0.3.0 |
56
56
  | start_time | time string | start time to perform solution. GPS, UTC and other formats are supported. *ex1) --start_time=1234:5678* represents 5678 seconds in 1234 GPS week, *ex2) --start_time="2000-01-01 00:00:00 UTC"* is in UTC format. | v0.3.3 |
57
57
  | end_time | time string | end time to perform solution. Its format is the same as start_time. | v0.3.3 |
58
- | online_ephemeris | | based on observation, automatically load ephemeris which is previously broadcasted from satellite and currently published online | v0.5.0 |
58
+ | <a name=opt_online_ephemeris>online_ephemeris</a> | URL string | based on observation, ephemeris which is previously broadcasted from satellite and currently published online will automatically be loaded. If value is not given, the default source "ftp://gssc.esa.int/gnss/data/daily/%Y/brdc/BRDC00IGS_R_%Y%j0000_01D_MN.rnx.gz" is used. The value string is converted with [strftime](https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html) before actual use. | v0.8.1 |
59
59
 
60
60
  ### For developer
61
61
 
@@ -148,6 +148,21 @@ receiver.solver.correction = { # provide by using a Hash
148
148
  }
149
149
  ```
150
150
 
151
+ ## Additional utilities
152
+
153
+ ### [to_ubx](exe/to_ubx)
154
+
155
+ Utility to convert observation into u-blox ubx format and dump standard input. After installation of gps_pvt, to type
156
+
157
+ $ to_ubx file_or_URI(s) (options) > out.ubx
158
+
159
+ saves resultant into out.ubx by using redirection. The shared options with gps_pvt executable are [rinex_obs](#opt_rinex_obs), [rinex_nav](#opt_rinex_nav), [ubx](#opt_ubx), and [online_ephemeris](#opt_online_ephemeris). In addition, the following options are available.
160
+
161
+ | key | value | comment | since |
162
+ ----|----|----|----
163
+ | ubx_rawx | | Change output packet types to UBX-RAWX from its default UBX-RAW. | v0.8.1 |
164
+ | broadcast_data | | In addition to observation, ephemeris is inserted by using UBX-SFRB packets. If ubx_rawx option is specified, UBX-SFRBX is used instead of UBX-SFRB. | v0.8.1 |
165
+
151
166
  ## Development
152
167
 
153
168
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to build library and run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/exe/gps_pvt CHANGED
@@ -59,7 +59,7 @@ options.reject!{|opt|
59
59
  misc_options[opt[0]] = t
60
60
  true
61
61
  when :online_ephemeris
62
- misc_options[opt[0]] = opt[1]
62
+ (misc_options[opt[0]] ||= []) << opt[1]
63
63
  true
64
64
  else
65
65
  false
data/exe/to_ubx CHANGED
@@ -40,6 +40,7 @@ files.collect!{|fname, ftype|
40
40
  ftype ||= case fname
41
41
  when /\.\d{2}[nhqg](?:\.gz)?$/; :rinex_nav
42
42
  when /\.\d{2}o(?:\.gz)?$/; :rinex_obs
43
+ when /\.ubx$/; :ubx
43
44
  else
44
45
  raise "Format cannot be guessed, use --(format, ex. rinex_obs)=#{fname}"
45
46
  end
@@ -55,9 +56,12 @@ files.collect!{|fname, ftype|
55
56
 
56
57
  options.reject!{|opt|
57
58
  case opt[0]
58
- when :ubx_rawx, :online_ephemeris, :broadcast_data, :eph_interval
59
+ when :ubx_rawx, :broadcast_data, :eph_interval
59
60
  misc_options[opt[0]] = opt[1]
60
61
  true
62
+ when :online_ephemeris
63
+ (misc_options[opt[0]] ||= []) << opt[1]
64
+ true
61
65
  else
62
66
  false
63
67
  end
@@ -74,6 +78,17 @@ proc{|src|
74
78
  rcv.attach_online_ephemeris(src) if src
75
79
  }.call(misc_options[:online_ephemeris])
76
80
 
81
+ proc{
82
+ cache = nil
83
+ rcv.define_singleton_method(:leap_seconds){|t_meas|
84
+ cache ||= if self.solver.gps_space_node.is_valid_utc then
85
+ self.solver.gps_space_node.iono_utc.delta_t_LS
86
+ else
87
+ t_meas.leap_seconds
88
+ end
89
+ }
90
+ }.call
91
+
77
92
  # parse RINEX NAV
78
93
  files.each{|fname, ftype|
79
94
  case ftype
@@ -84,14 +99,37 @@ files.each{|fname, ftype|
84
99
  # other files
85
100
  files.each{|fname, ftype|
86
101
  case ftype
102
+ when :ubx; rcv.parse_ubx(fname){}
87
103
  when :rinex_obs; rcv.parse_rinex_obs(fname){}
88
104
  end
89
105
  }
90
106
 
91
107
  obs.sort!{|a, b| a[0] <=> b[0]} # Sort by measurement time
92
108
 
93
- # Add dummy before actual first observation in order to notify time to solver
94
- obs.unshift([obs[0][0] - 1, GPS_PVT::GPS::Measurement::new])
109
+ # Time packet for solution of leap seconds
110
+ gen_gpstime = proc{
111
+ tmpl = [0xB5, 0x62, 0x01, 0x20, 16, 0]
112
+ gpst = GPS_PVT::GPS::Time
113
+ t_next = gpst::new(*gpst::leap_second_events.find{|wn, sec, leap| leap == 1}[0..1])
114
+ proc{|t_meas, meas|
115
+ next nil if t_meas < t_next
116
+ t_next = t_meas + 60 # 1 min. interval
117
+ ubx = tmpl.clone
118
+ t_sec = t_meas.seconds
119
+ t_msec = (t_sec * 1E3).round
120
+ t_nsec = ((t_sec * 1E3 - t_msec) * 1E6).round
121
+ leap = rcv.leap_seconds(t_meas)
122
+ ubx += [
123
+ t_msec, # ITOW ms GPS Millisecond time of Week
124
+ t_nsec, # Frac ns Nanoseconds remainder of rounded ms above, range -500000 .. 500000
125
+ t_meas.week, # week - GPS week (GPS time)
126
+ leap || 0, # LeapS s Leap Seconds (GPS-UTC)
127
+ leap ? 0x07 : 0x03, # validity bit field (0x01=ToW, 0x02=WN, 0x04=UTC)
128
+ 10000, # TAcc ns Time Accuracy Estimate
129
+ ].pack("Vl<vcCV").unpack("C*")
130
+ GPS_PVT::UBX::update(ubx + [0, 0]).pack("C*")
131
+ }
132
+ }.call
95
133
 
96
134
  gen_sfrb, gen_sfrbx = proc{
97
135
  cache = {}
@@ -236,13 +274,14 @@ gen_rawx = proc{|t_meas, meas| # Convert to RXM-RAWX(0x15)
236
274
  ubx = [0xB5, 0x62, 0x02, 0x15, 0, 0]
237
275
  ubx += [t_meas.seconds, t_meas.week].pack("Ev").unpack("C*")
238
276
  ubx += [0] * 6
239
- meas_ubx = meas.collect{|sat, items|
277
+ gen_packet = proc{|sys, svid, sig, items|
240
278
  res = [0] * 32
241
279
  setter = proc{|value, offset, len, str, pre_proc|
242
280
  array = case value
243
281
  when Array; value
244
282
  when Symbol
245
- [items[GPS_PVT::GPS::Measurement.const_get(value)]]
283
+ k = [sig, value].join('_').to_sym
284
+ [items[GPS_PVT::GPS::Measurement.const_get(k)]]
246
285
  else
247
286
  next nil
248
287
  end
@@ -251,55 +290,79 @@ gen_rawx = proc{|t_meas, meas| # Convert to RXM-RAWX(0x15)
251
290
  array = array.pack(str).unpack("C*") if str
252
291
  res[offset - 16, len] = array
253
292
  }
254
- sys, svid = case sat
255
- when 1..32 # GPS
256
- [0, sat]
257
- when 120..158 # SBAS
258
- [1, sat]
259
- when 193..202 # QZSS
260
- [5, sat]
261
- when (0x100 + 1)..(0x100 + 32) # GLONASS
262
- setter.call(:L1_FREQUENCY,
263
- 39, 1, nil, proc{|v| v.replace([(v[0] ? glonass_freq_ch.call(v[0]) : 0) + 7])} )
264
- [6, sat - 0x100]
265
- else
266
- next nil # TODO Galileo, Beidou, ...
267
- end
268
- setter.call([sys, svid], 36, 2)
269
293
 
294
+ setter.call([sys, svid], 36, 2)
295
+
270
296
  trk_stat = 0
271
- setter.call(:L1_PSEUDORANGE,
297
+ setter.call(:PSEUDORANGE,
272
298
  16, 8, "E", proc{|v| v[0] ? (trk_stat |= 0x1) : v.clear})
273
- setter.call(:L1_PSEUDORANGE_SIGMA,
299
+ setter.call(:PSEUDORANGE_SIGMA,
274
300
  43, 1, nil, proc{|v|
275
301
  b = (Math::log2(v[0] / 1E-2).to_i & 0xF) rescue 0x8
276
302
  v.replace((trk_stat & 0x1 == 0x1) ? [b] : [])
277
303
  })
278
- setter.call(:L1_DOPPLER, 32, 4, "e") rescue next nil
279
- setter.call(:L1_DOPPLER_SIGMA,
304
+ setter.call(:DOPPLER, 32, 4, "e") rescue next nil
305
+ setter.call(:DOPPLER_SIGMA,
280
306
  45, 1, nil, proc{|v| v.replace(v[0] ? [Math::log2(v[0] / 2E-3).to_i & 0xF] : [0x8])})
281
- setter.call(:L1_CARRIER_PHASE,
307
+ setter.call(:CARRIER_PHASE,
282
308
  24, 8, "E", proc{|v| v[0] ? (trk_stat |= 0x2) : v.clear})
283
- setter.call(:L1_CARRIER_PHASE_SIGMA,
309
+ setter.call(:CARRIER_PHASE_SIGMA,
284
310
  44, 1, nil, proc{|v|
285
311
  b = ((v[0] / 0.004).to_i & 0xF) rescue 0x8
286
312
  v.replace((trk_stat & 0x2 == 0x2) ? [b] : [])
287
313
  })
288
- setter.call(:L1_SIGNAL_STRENGTH_dBHz,
314
+ setter.call(:SIGNAL_STRENGTH_dBHz,
289
315
  42, 1, nil, proc{|v| v.replace(v[0] ? [v[0].to_i] : [])})
290
- setter.call(:L1_LOCK_SEC,
316
+ setter.call(:LOCK_SEC,
291
317
  40, 2, "v", proc{|v| v.replace(v[0] ? [(v[0] / 1E-3).to_i] : [])})
292
318
  setter.call([trk_stat], 46, 1)
319
+ res.define_singleton_method(:set, &setter)
293
320
 
294
321
  res
322
+ }
323
+ meas_ubx = meas.inject([]){|packets, (sat, items)|
324
+ case sat
325
+ when 1..32 # GPS
326
+ packets << gen_packet.call(0, sat, :L1, items)
327
+ packets += {:L2CL => 3, :L2CM => 4}.collect{|sig, sigid|
328
+ next nil unless packet = gen_packet.call(0, sat, sig, items)
329
+ packet[38 - 16] = sigid
330
+ packet
331
+ }
332
+ when 120..158 # SBAS
333
+ packets << gen_packet.call(1, sat, :L1, items)
334
+ when 193..202 # QZSS
335
+ packets << gen_packet.call(5, sat, :L1, items)
336
+ packets += {:L2CL => 5, :L2CM => 4}.collect{|sig, sigid|
337
+ next nil unless packet = gen_packet.call(5, sat, sig, items)
338
+ packet[38 - 16] = sigid
339
+ packet
340
+ }
341
+ when (0x100 + 1)..(0x100 + 32) # GLONASS
342
+ packet = gen_packet.call(6, sat - 0x100, :L1, items)
343
+ packet.set(:FREQUENCY,
344
+ 39, 1, nil,
345
+ proc{|v| v.replace([(v[0] ? glonass_freq_ch.call(v[0]) : 0) + 7])} ) if packet
346
+ packets << packet
347
+ else
348
+ # TODO Galileo, Beidou, ...
349
+ end
295
350
  }.compact
351
+
352
+ proc{|ls| # leap seconds
353
+ next unless ls
354
+ ubx[6 + 10] = ls
355
+ ubx[6 + 12] |= 0x01
356
+ }.call(rcv.leap_seconds(t_meas))
296
357
  ubx[6 + 11] = meas_ubx.size
297
- ubx += meas_ubx.flatten(1)
358
+ ubx[6 + 13] = 1 # version
359
+ ubx += meas_ubx.flatten
298
360
  ubx += [0, 0]
299
361
  GPS_PVT::UBX::update(ubx).pack("C*")
300
362
  }
301
363
 
302
364
  gen_list = []
365
+ gen_list << gen_gpstime unless misc_options[:ubx_rawx]
303
366
  gen_list << (misc_options[:ubx_rawx] ? gen_sfrbx : gen_sfrb) if misc_options[:broadcast_data]
304
367
  gen_list << (misc_options[:ubx_rawx] ? gen_rawx : gen_raw)
305
368
  STDOUT.binmode