gps_pvt 0.10.2 → 0.10.4

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: 291357170b42fbbecccdac288a218cd8a1b0730db5b93e9adb43c4635674a386
4
- data.tar.gz: 2b7da00981f6a55db29dbfc88083eb6806ea744bbc0cc0b1a97fd2939bc95496
3
+ metadata.gz: 60413c3add439fa66e0ff908f43f0049ad306fcefca50e2c485bc28790597143
4
+ data.tar.gz: 788f430151ca35a95abd4f96e4f30239e9f53768014af9ddc5897305b25b7fb9
5
5
  SHA512:
6
- metadata.gz: bc697b14d15e9ebb4d445e076284c97bf349df98c4f146f4ac96ff1875f1a6c9cac328e26af533a8abca89ae792b6ad70e1ce1172f0e97117c5dc28518105d35
7
- data.tar.gz: 0536ce452ad85fe1e460e7c6e1dd577c63148505e275a4fa5bcb749d457c7fb80315833151d2aab54bd8f5a92b4b59941d7818d903de676823501d16d0d2a930
6
+ metadata.gz: 9c0ddc7d6a4f589cf3b3692eacde62405c78890f20cadf5abde07d189ae35bda83fd0b9e905728e4fecaf111aeee14bfb615166ef72d355fcac1a3368330ee89
7
+ data.tar.gz: 457d23f7e4032549961e499638403b3d01d681a8051231616ee2dde5183967c119028bd2a4475f2fe1c1e3ae2ffda61729162ced5d30b33113bfe21c161c188e
data/README.md CHANGED
@@ -43,7 +43,7 @@ The format of file is automatically determined with its extension, such as .ubx
43
43
  | --antex=file_or_URI | [Antenna Exchange Format](https://igs.org/wg/antenna#files) (supported gps_pvt version >= 0.6.0) |
44
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
  | <a name=opt_rtcm3>--rtcm3=file_or_URI</a> | [RTCM 10403.x](https://rtcm.myshopify.com/collections/differential-global-navigation-satellite-dgnss-standards). (supported gps_pvt version >= 0.9.0) The latest version uses message type Observation(GPS: 1001..1004; GLONASS: 1009..1012), Epehemris(GPS: 1019; GLOANSS: 1020; SBAS: 1043; QZSS: 1044), MSM(GPS: 1071..1077; GLONASS: 1081..1087; SBAS: 1101..1107; QZSS: 1111..1117) |
46
- | <a name=opt_supl>--supl=URI</a> | [SUPL, secure user plane location](https://www.openmobilealliance.org/release/SUPL/). (supported gps_pvt version >= 0.10.0) Both [LPP](https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3710)(default) and [RRLP](https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2688) are internally used, which can be manually selected by adding ```?protocol=lpp_or_rrlp``` URI query string. |
46
+ | <a name=opt_supl>--supl=URI</a> | [SUPL, secure user plane location](https://www.openmobilealliance.org/release/SUPL/). (supported gps_pvt version >= 0.10.0) Whether [LPP(default)](https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3710) or [RRLP](https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2688) are internally used, which can be manually selected by adding ```?protocol=lpp_or_rrlp``` URI query string. |
47
47
 
48
48
  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
49
49
 
data/Rakefile CHANGED
@@ -11,6 +11,14 @@ Rake::ExtensionTask.new("gps_pvt") do |ext|
11
11
  ext.lib_dir = "lib/gps_pvt"
12
12
  end
13
13
 
14
+ require "yard"
15
+
16
+ YARD::Rake::YardocTask.new do |t|
17
+ #t.files = ['lib/**/*.rb', 'ext/**/*_wrap.cxx']
18
+ t.options = ['--embed-mixins']
19
+ #t.stats_options = ['--list-undoc']
20
+ end
21
+
14
22
  namespace :git do
15
23
  task :version do
16
24
  @git_version ||= proc{
@@ -83,25 +91,28 @@ end
83
91
 
84
92
  desc "Generate SWIG wrapper codes"
85
93
  task :swig do
86
- swig_dir = File::join(File::dirname(__FILE__), 'ext', 'ninja-scan-light', 'tool', 'swig')
87
94
  out_base_dir = File::join(File::dirname(__FILE__), 'ext', 'gps_pvt')
88
- Dir::chdir(swig_dir){
89
- Dir::glob("*.i"){|src|
90
- mod_name = File::basename(src, '.*')
91
- out_dir = File::join(out_base_dir, mod_name)
92
- sh "mkdir -p #{out_dir}"
93
- wrapper = File::join(out_dir, "#{mod_name}_wrap.cxx")
94
- sh [:make, :clean, wrapper,
95
- "BUILD_DIR=#{out_dir}",
96
- "SWIGFLAGS='-c++ -ruby -prefix \"GPS_PVT::\"#{" -D__MINGW__" if ENV["MSYSTEM"]}'"].join(' ')
97
- open(wrapper, 'r+'){|io|
98
- lines = io.read.lines.collect{|line|
99
- line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
100
- "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
95
+ [
96
+ File::join(File::dirname(__FILE__), 'ext', 'ninja-scan-light', 'tool', 'swig'),
97
+ ].each{|swig_dir|
98
+ Dir::chdir(swig_dir){
99
+ Dir::glob("*.i"){|src|
100
+ mod_name = File::basename(src, '.*')
101
+ out_dir = File::join(out_base_dir, mod_name)
102
+ sh "mkdir -p #{out_dir}"
103
+ wrapper = File::join(out_dir, "#{mod_name}_wrap.cxx")
104
+ sh [:make, :clean, wrapper,
105
+ "BUILD_DIR=#{out_dir}",
106
+ "SWIGFLAGS='-c++ -ruby -prefix \"GPS_PVT::\"#{" -D__MINGW__" if ENV["MSYSTEM"]}'"].join(' ')
107
+ open(wrapper, 'r+'){|io|
108
+ lines = io.read.lines.collect{|line|
109
+ line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
110
+ "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
111
+ }
101
112
  }
113
+ io.rewind
114
+ io.write(lines.join)
102
115
  }
103
- io.rewind
104
- io.write(lines.join)
105
116
  }
106
117
  }
107
118
  }
data/exe/gps2ubx CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'gps_pvt'
4
4
  require 'uri'
5
5
  require 'gps_pvt/ubx'
6
+ require 'gps_pvt/pvt'
6
7
 
7
8
  # Convert file(s) to ubx format
8
9
  # TODO currently only RINEX observation file is supported.
@@ -25,6 +26,7 @@ options = []
25
26
  misc_options = {
26
27
  :broadcast_data => false,
27
28
  :ubx_rawx => false,
29
+ :ubx_nav => false,
28
30
  :eph_interval => 60 * 5,
29
31
  }
30
32
 
@@ -32,7 +34,7 @@ misc_options = {
32
34
  files = ARGV.collect{|arg|
33
35
  next [arg, nil] unless arg =~ /^--([^=]+)=?/
34
36
  k, v = [$1.downcase.to_sym, $']
35
- next [v, k] if [:rinex_nav, :rinex_obs, :ubx, :rtcm3].include?(k) # file type
37
+ next [v, k] if [:rinex_nav, :rinex_obs, :ubx, :rtcm3, :pvt_csv].include?(k) # file type
36
38
  options << [$1.to_sym, $']
37
39
  nil
38
40
  }.compact
@@ -43,6 +45,7 @@ files.collect!{|fname, ftype|
43
45
  when /\.\d{2}[nhqg](?:\.gz)?$/; :rinex_nav
44
46
  when /\.\d{2}o(?:\.gz)?$/; :rinex_obs
45
47
  when /\.ubx$/; :ubx
48
+ when /\.csv$/; misc_options[:ubx_nav] = true; :pvt_csv
46
49
  end
47
50
  if (!(uri = URI::parse(fname)).instance_of?(URI::Generic) rescue false) then
48
51
  ftype ||= case uri
@@ -57,7 +60,7 @@ files.collect!{|fname, ftype|
57
60
 
58
61
  options.reject!{|opt|
59
62
  case opt[0]
60
- when :ubx_rawx, :broadcast_data, :eph_interval
63
+ when :ubx_rawx, :ubx_nav, :broadcast_data, :eph_interval
61
64
  misc_options[opt[0]] = opt[1]
62
65
  true
63
66
  when :online_ephemeris
@@ -70,10 +73,13 @@ options.reject!{|opt|
70
73
 
71
74
  rcv = GPS_PVT::Receiver::new(options)
72
75
 
73
- obs = Queue::new
74
- rcv.define_singleton_method(:run){|meas, t_meas, *args|
75
- obs << [t_meas, meas]
76
- nil
76
+ outputs = Queue::new # [[time, item1, item2, ...], ...]
77
+ rcv.instance_eval{
78
+ nav_task = misc_options[:ubx_nav] ? method(:run) : proc{}
79
+ define_singleton_method(:run){|meas, t_meas, *args|
80
+ outputs << [t_meas, meas, nav_task.call(meas, t_meas, *args)].compact
81
+ nil
82
+ }
77
83
  }
78
84
 
79
85
  proc{|src|
@@ -106,6 +112,9 @@ threads = files.collect{|fname, ftype|
106
112
  when :rtcm3; proc{rcv.parse_rtcm3(fname){}}
107
113
  when :supl; proc{rcv.parse_supl(fname)}
108
114
  when :rinex_nav; proc{}
115
+ when :pvt_csv; proc{GPS_PVT::GPS::PVT_minimal::parse_csv(fname){|t, pvt|
116
+ outputs << [t, pvt]
117
+ }}
109
118
  end
110
119
  case fname
111
120
  when URI::Ntrip, URI::Supl; Thread::new(&task)
@@ -113,9 +122,9 @@ threads = files.collect{|fname, ftype|
113
122
  end
114
123
  }.compact
115
124
 
116
- obs = proc{
125
+ outputs = proc{
117
126
  tmp = []
118
- tmp << obs.pop until obs.empty?
127
+ tmp << outputs.pop until outputs.empty?
119
128
  tmp
120
129
  }.call.sort!{|a, b| b[0] <=> a[0]} if threads.empty? # Sort by measurement time
121
130
 
@@ -124,7 +133,7 @@ gen_gpstime = proc{
124
133
  tmpl = [0xB5, 0x62, 0x01, 0x20, 16, 0]
125
134
  gpst = GPS_PVT::GPS::Time
126
135
  t_next = gpst::new(*gpst::leap_second_events.find{|wn, sec, leap| leap == 1}[0..1])
127
- proc{|t_meas, meas|
136
+ proc{|t_meas|
128
137
  next nil if t_meas < t_next
129
138
  t_next = t_meas + 60 # 1 min. interval
130
139
  ubx = tmpl.clone
@@ -374,17 +383,102 @@ gen_rawx = proc{|t_meas, meas| # Convert to RXM-RAWX(0x15)
374
383
  GPS_PVT::UBX::update(ubx).pack("C*")
375
384
  }
376
385
 
377
- gen_list = []
378
- gen_list << gen_gpstime unless misc_options[:ubx_rawx]
379
- gen_list << (misc_options[:ubx_rawx] ? gen_sfrbx : gen_sfrb) if misc_options[:broadcast_data]
380
- gen_list << (misc_options[:ubx_rawx] ? gen_rawx : gen_raw)
386
+ gen_nav = proc{|t_meas, pvt|
387
+ t_msec = (t_meas.seconds * 1E3).round
388
+ packet = []
389
+
390
+ proc{ # Convert to NAV-SOL (0x01-0x06)
391
+ ubx_sol = [0xB5, 0x62, 0x01, 0x06, 52, 0]
392
+ ubx_sol += [
393
+ t_msec,
394
+ 0, # frac
395
+ t_meas.week, # week
396
+ if (pvt.position_solved? and pvt.velocity_solved?) then
397
+ [
398
+ 0x03, # 3D-Fix
399
+ 0x0D, # GPSfixOK, WKNSET, TOWSET
400
+ pvt.xyz.to_a.collect{|v| (v * 1E2).to_i}, # ECEF_XYZ [cm]
401
+ (Math::sqrt((pvt.hsigma ** 2) + (pvt.vsigma ** 2)) * 1E2).to_i, # 3D pos accuracy [cm]
402
+ (pvt.velocity.absolute(pvt.xyz) - pvt.xyz).to_a.collect{|v| (v * 1E2).to_i}, # ECEF_VXYZ [cm/s]
403
+ (pvt.vel_sigma * 1E2).to_i, # Speed accuracy [cm/s]
404
+ (pvt.pdop * 1E2).to_i, # PDOP [0.01]
405
+ ]
406
+ else
407
+ [
408
+ 0, # 3D-Fix
409
+ 0x08, # TOWSET
410
+ [0] * 3, # ECEF_XYZ [cm]
411
+ 0, # 3D pos accuracy [cm]
412
+ [0] * 3, # ECEF_VXYZ [cm/s]
413
+ 0, # Speed accuracy [cm/s]
414
+ 0, # PDOP
415
+ ]
416
+ end,
417
+ 0,
418
+ pvt.used_satellites,
419
+ 0].flatten.pack('V2vc2l<3Vl<3VvC2V').unpack('C*')
420
+ packet += GPS_PVT::UBX::update(ubx_sol + [0, 0])
421
+ }.call
422
+
423
+ # Convert to NAV-POSLLH (0x01-0x02)
424
+ if pvt.position_solved? then
425
+ llh = pvt.llh
426
+ ubx_posllh = [0xB5, 0x62, 0x01, 0x02, 28, 0]
427
+ ubx_posllh += [
428
+ t_msec,
429
+ (llh.lng / Math::PI * 180 * 1E7).to_i, # Longitude [1E-7 deg]
430
+ (llh.lat / Math::PI * 180 * 1E7).to_i, # Latitude [1E-7 deg]
431
+ (llh.alt * 1E3).to_i, # WGS-84 altitude [mm]
432
+ (llh.alt * 1E3).to_i, # mean sea level TODO fix
433
+ (pvt.hsigma * 1E3).to_i, # HAcc [mm]
434
+ (pvt.vsigma * 1E3).to_i, # VAcc [mm]
435
+ ].pack('V*').unpack("C*")
436
+ packet += GPS_PVT::UBX::update(ubx_posllh + [0, 0])
437
+ end
438
+
439
+ # Convert to NAV-VELNED (0x01-0x12)
440
+ if pvt.velocity_solved? then
441
+ vel = pvt.velocity
442
+ ubx_velned = [0xB5, 0x62, 0x01, 0x12, 36, 0]
443
+ ubx_velned += [
444
+ t_msec,
445
+ (vel.n * 1E2).to_i, # N speed [cm/s]
446
+ (vel.e * 1E2).to_i, # E speed [cm/s]
447
+ (vel.d * 1E2).to_i, # D speed [cm/s]
448
+ (vel.distance * 1E2).to_i, # 3D speed [cm/s]
449
+ (vel.horizontal * 1E2).to_i, # 2D speed [cm/s]
450
+ (vel.azimuth / Math::PI * 180 * 1E5).to_i, # Heading [1E-5 deg]
451
+ (pvt.vel_sigma * 1E2).to_i, # sAcc speed accuracy [cm/s]
452
+ (2 * 1E5).to_i, # cAcc heading accuracy [1E-5 deg] TODO
453
+ ].pack('V*').unpack("C*")
454
+ packet += GPS_PVT::UBX::update(ubx_velned + [0, 0])
455
+ end
456
+
457
+ (gen_gpstime.call(t_meas) || '') + packet.pack('C*')
458
+ }
459
+
460
+ gen_list = Hash[*({
461
+ :Measurement => proc{
462
+ gen_ary = []
463
+ gen_ary << gen_gpstime unless misc_options[:ubx_rawx]
464
+ gen_ary << (misc_options[:ubx_rawx] ? gen_sfrbx : gen_sfrb) if misc_options[:broadcast_data]
465
+ gen_ary << (misc_options[:ubx_rawx] ? gen_rawx : gen_raw)
466
+ proc{|t_meas, meas|
467
+ meas = meas.to_hash
468
+ gen_ary.collect{|gen| gen.call(t_meas, meas)}.join
469
+ }
470
+ }.call,
471
+ :PVT => gen_nav,
472
+ :PVT_minimal => gen_nav,
473
+ }.collect{|k, v| [GPS_PVT::GPS.const_get(k), v]}.flatten(1))]
381
474
  STDOUT.binmode
382
475
 
383
476
  task_dump = proc{
384
- until obs.empty? do
385
- t_meas, meas = obs.pop
386
- meas2 = meas.to_hash
387
- gen_list.each{|gen| print gen.call(t_meas, meas2)}
477
+ until outputs.empty? do
478
+ t_meas, *items = outputs.pop
479
+ items.each{|item|
480
+ print gen_list[item.class].call(t_meas, item)
481
+ }
388
482
  end
389
483
  }
390
484
  if threads.empty? then
@@ -8495,7 +8495,7 @@ _wrap_Matrix_FrozenD_each__SWIG_0(int argc, VALUE *argv, VALUE self) {
8495
8495
  (&_e)->regenerate();
8496
8496
  SWIG_fail;
8497
8497
  }
8498
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_double_Array2D_DenseT_double_t_MatrixViewBaseT_t_t, 0 | 0 );
8498
+ vresult = self;
8499
8499
  return vresult;
8500
8500
  fail:
8501
8501
  return Qnil;
@@ -8531,7 +8531,7 @@ _wrap_Matrix_FrozenD_each__SWIG_1(int argc, VALUE *argv, VALUE self) {
8531
8531
  (&_e)->regenerate();
8532
8532
  SWIG_fail;
8533
8533
  }
8534
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_double_Array2D_DenseT_double_t_MatrixViewBaseT_t_t, 0 | 0 );
8534
+ vresult = self;
8535
8535
  return vresult;
8536
8536
  fail:
8537
8537
  return Qnil;
@@ -11940,7 +11940,7 @@ _wrap_Matrix_FrozenD_f_each__SWIG_0(int argc, VALUE *argv, VALUE self) {
11940
11940
  (&_e)->regenerate();
11941
11941
  SWIG_fail;
11942
11942
  }
11943
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_double_Array2D_DenseT_double_t_MatrixViewFilterT_MatrixViewBaseT_t_t_t, 0 | 0 );
11943
+ vresult = self;
11944
11944
  return vresult;
11945
11945
  fail:
11946
11946
  return Qnil;
@@ -11976,7 +11976,7 @@ _wrap_Matrix_FrozenD_f_each__SWIG_1(int argc, VALUE *argv, VALUE self) {
11976
11976
  (&_e)->regenerate();
11977
11977
  SWIG_fail;
11978
11978
  }
11979
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_double_Array2D_DenseT_double_t_MatrixViewFilterT_MatrixViewBaseT_t_t_t, 0 | 0 );
11979
+ vresult = self;
11980
11980
  return vresult;
11981
11981
  fail:
11982
11982
  return Qnil;
@@ -13956,7 +13956,7 @@ _wrap_MatrixD_resizeN___(int argc, VALUE *argv, VALUE self) {
13956
13956
  }
13957
13957
  }
13958
13958
  result = (Matrix< double,Array2D_Dense< double > > *) &Matrix_Sl_double_Sc_Array2D_Dense_Sl_double_Sg__Sg__resize(arg1,(unsigned int const *)arg2,(unsigned int const *)arg3);
13959
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_MatrixT_double_Array2D_DenseT_double_t_MatrixViewBaseT_t_t, 0 | 0 );
13959
+ vresult = self;
13960
13960
  return vresult;
13961
13961
  fail:
13962
13962
  return Qnil;
@@ -16448,7 +16448,7 @@ _wrap_Matrix_FrozenComplexD_each__SWIG_0(int argc, VALUE *argv, VALUE self) {
16448
16448
  (&_e)->regenerate();
16449
16449
  SWIG_fail;
16450
16450
  }
16451
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_ComplexT_double_t_Array2D_DenseT_ComplexT_double_t_t_MatrixViewBaseT_t_t, 0 | 0 );
16451
+ vresult = self;
16452
16452
  return vresult;
16453
16453
  fail:
16454
16454
  return Qnil;
@@ -16484,7 +16484,7 @@ _wrap_Matrix_FrozenComplexD_each__SWIG_1(int argc, VALUE *argv, VALUE self) {
16484
16484
  (&_e)->regenerate();
16485
16485
  SWIG_fail;
16486
16486
  }
16487
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_ComplexT_double_t_Array2D_DenseT_ComplexT_double_t_t_MatrixViewBaseT_t_t, 0 | 0 );
16487
+ vresult = self;
16488
16488
  return vresult;
16489
16489
  fail:
16490
16490
  return Qnil;
@@ -19891,7 +19891,7 @@ _wrap_Matrix_FrozenComplexD_f_each__SWIG_0(int argc, VALUE *argv, VALUE self) {
19891
19891
  (&_e)->regenerate();
19892
19892
  SWIG_fail;
19893
19893
  }
19894
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_ComplexT_double_t_Array2D_DenseT_ComplexT_double_t_t_MatrixViewFilterT_MatrixViewBaseT_t_t_t, 0 | 0 );
19894
+ vresult = self;
19895
19895
  return vresult;
19896
19896
  fail:
19897
19897
  return Qnil;
@@ -19927,7 +19927,7 @@ _wrap_Matrix_FrozenComplexD_f_each__SWIG_1(int argc, VALUE *argv, VALUE self) {
19927
19927
  (&_e)->regenerate();
19928
19928
  SWIG_fail;
19929
19929
  }
19930
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Matrix_FrozenT_ComplexT_double_t_Array2D_DenseT_ComplexT_double_t_t_MatrixViewFilterT_MatrixViewBaseT_t_t_t, 0 | 0 );
19930
+ vresult = self;
19931
19931
  return vresult;
19932
19932
  fail:
19933
19933
  return Qnil;
@@ -21903,7 +21903,7 @@ _wrap_MatrixComplexD_resizeN___(int argc, VALUE *argv, VALUE self) {
21903
21903
  }
21904
21904
  }
21905
21905
  result = (Matrix< Complex< double >,Array2D_Dense< Complex< double > > > *) &Matrix_Sl_Complex_Sl_double_Sg__Sc_Array2D_Dense_Sl_Complex_Sl_double_Sg__Sg__Sg__resize(arg1,(unsigned int const *)arg2,(unsigned int const *)arg3);
21906
- vresult = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_MatrixT_ComplexT_double_t_Array2D_DenseT_ComplexT_double_t_t_MatrixViewBaseT_t_t, 0 | 0 );
21906
+ vresult = self;
21907
21907
  return vresult;
21908
21908
  fail:
21909
21909
  return Qnil;
@@ -95,6 +95,11 @@ class GPS_Signal {
95
95
  PRN::content <<= 1;
96
96
  PRN::content[0] = tmp;
97
97
  }
98
+ void previous(){
99
+ bool tmp(PRN::content[3] ^ PRN::content[0]);
100
+ PRN::content >>= 1;
101
+ PRN::content[9] = tmp;
102
+ }
98
103
  };
99
104
 
100
105
  class G2 : public PRN {
@@ -114,6 +119,16 @@ class GPS_Signal {
114
119
  PRN::content <<= 1;
115
120
  PRN::content[0] = tmp;
116
121
  }
122
+ void previous(){
123
+ bool tmp(PRN::content[2]
124
+ ^ PRN::content[3]
125
+ ^ PRN::content[6]
126
+ ^ PRN::content[8]
127
+ ^ PRN::content[9]
128
+ ^ PRN::content[0]);
129
+ PRN::content >>= 1;
130
+ PRN::content[9] = tmp;
131
+ }
117
132
  static G2 get_G2(const int &prn){
118
133
  switch(prn){
119
134
  case 1: return G2(1, 5);
@@ -160,9 +175,9 @@ class GPS_Signal {
160
175
  class CA_Code {
161
176
  public:
162
177
  typedef FloatT float_t;
163
- static const float_t FREQENCY;
178
+ static const float_t FREQUENCY;
164
179
  static const float_t length_1chip() {
165
- static const float_t res(1. / FREQENCY);
180
+ static const float_t res(1. / FREQUENCY);
166
181
  return res;
167
182
  }
168
183
  protected:
@@ -177,11 +192,15 @@ class GPS_Signal {
177
192
  g1.next();
178
193
  g2.next();
179
194
  }
195
+ void previous(){
196
+ g1.previous();
197
+ g2.previous();
198
+ }
180
199
  };
181
200
  };
182
201
 
183
202
  template <class FloatT>
184
- const typename GPS_Signal<FloatT>::float_t GPS_Signal<FloatT>::CA_Code::FREQENCY = 1.023E6;
203
+ const typename GPS_Signal<FloatT>::float_t GPS_Signal<FloatT>::CA_Code::FREQUENCY = 1.023E6;
185
204
 
186
205
  template <class FloatT = double>
187
206
  struct GPS_Time {
@@ -73,6 +73,8 @@ struct native_exception : public std::exception {
73
73
  values[i] = (*self)[i];
74
74
  }
75
75
  }
76
+ %typemap(in) FloatT values[3];
77
+ %typemap(argout) FloatT values[3];
76
78
  #endif
77
79
  %typemap(in,numinputs=0) const void *check_block {
78
80
  #ifdef SWIGRUBY
@@ -98,6 +100,7 @@ struct native_exception : public std::exception {
98
100
  #endif
99
101
  }
100
102
  }
103
+ %clear const void *check_block;
101
104
  };
102
105
  #ifdef SWIGRUBY
103
106
  %mixin System_3D "Enumerable";
@@ -170,6 +173,9 @@ struct native_exception : public std::exception {
170
173
  %template(XYZ) System_XYZ<type, WGS84>;
171
174
  %template(LLH) System_LLH<type, WGS84>;
172
175
  %template(ENU) System_ENU<type, WGS84>;
176
+ %typemap(out) FloatT &;
177
+ %typemap(in) (type (&res)[3][3]);
178
+ %typemap(argout) (type (&res)[3][3]);
173
179
  %enddef
174
180
 
175
181
  CONCRETIZE(double);
@@ -960,6 +960,7 @@ struct MatrixUtil {
960
960
  std::string("Unknown enumerate direction: ").append(inspect_str($1)).c_str());
961
961
  }
962
962
  }
963
+ %typemap(out) const Matrix_Frozen<T, Array2D_Type, ViewType> & "$result = self;"
963
964
  %catches(native_exception) each;
964
965
  const Matrix_Frozen<T, Array2D_Type, ViewType> &each(
965
966
  void (*each_func)(
@@ -970,6 +971,7 @@ struct MatrixUtil {
970
971
  return *$self;
971
972
  }
972
973
  %alias each "each_with_index";
974
+ %typemap(out) const Matrix_Frozen<T, Array2D_Type, ViewType> &;
973
975
 
974
976
  %catches(native_exception, std::invalid_argument) map;
975
977
  Matrix<T, Array2D_Dense<T> > map(
@@ -1005,6 +1007,7 @@ struct MatrixUtil {
1005
1007
  (RTEST(idx_selector) ? 1 : 0), &idx_selector,
1006
1008
  (rb_block_call_func_t)rb_equal, value);
1007
1009
  }
1010
+ %typemap(check) VALUE idx_selector;
1008
1011
  %alias index "find_index";
1009
1012
 
1010
1013
  SWIG_Object to_a() const {
@@ -1020,6 +1023,20 @@ struct MatrixUtil {
1020
1023
  return res;
1021
1024
  }
1022
1025
  #endif
1026
+ %typemap(in)
1027
+ Matrix<T, Array2D_Dense<T> > &output_L,
1028
+ Matrix<T, Array2D_Dense<T> > &output_U,
1029
+ Matrix<T, Array2D_Dense<T> > &output_P,
1030
+ Matrix<T, Array2D_Dense<T> > &output_D,
1031
+ Matrix<T, Array2D_Dense<T> > &output_Q,
1032
+ Matrix<T, Array2D_Dense<T> > &output_R;
1033
+ %typemap(argout)
1034
+ Matrix<T, Array2D_Dense<T> > &output_L,
1035
+ Matrix<T, Array2D_Dense<T> > &output_U,
1036
+ Matrix<T, Array2D_Dense<T> > &output_P,
1037
+ Matrix<T, Array2D_Dense<T> > &output_D,
1038
+ Matrix<T, Array2D_Dense<T> > &output_Q,
1039
+ Matrix<T, Array2D_Dense<T> > &output_R;
1023
1040
  };
1024
1041
 
1025
1042
  #if defined(SWIGRUBY)
@@ -1171,6 +1188,15 @@ MAKE_TO_S(Matrix_Frozen)
1171
1188
  %rename("map!") map_bang;
1172
1189
  %alias map_bang "collect!,map_with_index!,collect_with_index!";
1173
1190
  #endif
1191
+
1192
+ %typemap(in)
1193
+ void (*each_func)(const T &src, T *dst, const unsigned int &i, const unsigned int &j);
1194
+ %typemap(typecheck) const typename MatrixUtil::each_which_t each_which;
1195
+ %typemap(in) const typename MatrixUtil::each_which_t each_which;
1196
+ %typemap(typecheck) const void *replacer;
1197
+ %typemap(in) const void *replacer;
1198
+ %typemap(in) self_t *self_p;
1199
+ %typemap(argout) self_t *self_p;
1174
1200
  };
1175
1201
 
1176
1202
  %define INSTANTIATE_MATRIX_TRANSPOSE(type, storage, view_from, view_to)
@@ -1251,6 +1277,12 @@ MAKE_TO_S(Matrix_Frozen)
1251
1277
  }
1252
1278
  output_D = D;
1253
1279
  }
1280
+ %typemap(in)
1281
+ Matrix<ctype, Array2D_Dense<ctype > > &output_D,
1282
+ Matrix<ctype, Array2D_Dense<ctype > > &output_V;
1283
+ %typemap(argout)
1284
+ Matrix<ctype, Array2D_Dense<ctype > > &output_D,
1285
+ Matrix<ctype, Array2D_Dense<ctype > > &output_V;
1254
1286
  };
1255
1287
  %enddef
1256
1288
  %define INSTANTIATE_MATRIX_EIGEN(type, ctype)
@@ -1366,6 +1398,7 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
1366
1398
  #endif
1367
1399
  else{SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");}
1368
1400
  }
1401
+ %typemap(out) Matrix<type, Array2D_Dense<type > > & "$result = self;"
1369
1402
  Matrix<type, Array2D_Dense<type > > &resize(
1370
1403
  const unsigned int *r_p, const unsigned int *c_p){
1371
1404
  unsigned int r(r_p ? *r_p : $self->rows()), c(c_p ? *c_p : $self->columns());
@@ -1376,6 +1409,10 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
1376
1409
  mat_new.partial(r_min, c_min).replace($self->partial(r_min, c_min), false);
1377
1410
  return (*($self) = mat_new);
1378
1411
  }
1412
+ %clear Matrix<type, Array2D_Dense<type > > &;
1413
+
1414
+ //%typemap(in) unsigned int *r_p, unsigned int *c_p; // NG; remove custom typemap before Matrix::resize! generation
1415
+ %clear unsigned int *r_p, unsigned int *c_p; // OK, work around version
1379
1416
  };
1380
1417
 
1381
1418
  %template(Matrix ## suffix) Matrix<type, Array2D_Dense<type > >;
@@ -286,7 +286,7 @@ shared_examples 'Matrix' do
286
286
  ].each{|arg|
287
287
  mat_orig = mat[0].copy
288
288
  mat_replaced = arg.kind_of?(Proc) ? mat_orig.send(:replace!, &arg) : mat_orig.send(:replace!, arg)
289
- expect(mat_replaced).to equal(mat_orig)
289
+ expect(mat_replaced).to be(mat_orig)
290
290
  expect(mat_replaced).not_to equal(mat[1])
291
291
  expect(mat_replaced.to_a).to eq(mat[1].to_a)
292
292
  }
@@ -306,7 +306,7 @@ shared_examples 'Matrix' do
306
306
  idxs[a], idxs[b] = [b, a]
307
307
  mat_builtin = Matrix::columns(mat_builtin.column_vectors.values_at(*idxs))
308
308
  end
309
- expect(mat_mod).to equal(mat[0])
309
+ expect(mat_mod).to be(mat[0])
310
310
  expect(mat[0].to_a).to eq(mat_builtin.to_a)
311
311
  }
312
312
  }
@@ -391,11 +391,12 @@ shared_examples 'Matrix' do
391
391
  candidates = (func.to_s =~ /with_index$/) \
392
392
  ? indices.collect{|i, j| [compare_with[i][j], i, j]} \
393
393
  : indices.collect{|i, j| [compare_with[i][j]]}
394
- mat.send(*[func, k].compact){|*v|
394
+ mat2 = mat.send(*[func, k].compact){|*v|
395
395
  i = candidates.find_index(v)
396
396
  expect(i).not_to be(nil)
397
397
  candidates.delete_at(i)
398
398
  }
399
+ expect(mat2).to be(mat)
399
400
  expect(candidates.empty?).to be(true)
400
401
  }
401
402
  }
@@ -449,7 +450,7 @@ shared_examples 'Matrix' do
449
450
  v[0] * 2
450
451
  }
451
452
  expect(candidates.empty?).to be(true)
452
- expect(mat2.to_a).to eq(mat.to_a)
453
+ expect(mat2).to be(mat)
453
454
  expect(mat2.to_a).to eq(compare_with.collect.with_index{|values, i|
454
455
  values.collect.with_index{|v, j|
455
456
  indices.include?([i, j]) ? (v * 2) : v
@@ -541,6 +542,7 @@ shared_examples 'Matrix' do
541
542
  }
542
543
  mat_orig = mat[0].to_a
543
544
  r, c = [:rows, :columns].collect{|f| mat[0].send(f)}
545
+ expect(mat[0].resize!(r, c)).to be(mat[0])
544
546
  expect(mat[0].resize!(r, c).to_a).to eq(mat_orig)
545
547
  expect(mat[0].resize!(r, nil).to_a).to eq(mat_orig)
546
548
  expect(mat[0].resize!(nil, c).to_a).to eq(mat_orig)
data/gps_pvt.gemspec CHANGED
@@ -64,6 +64,7 @@ Gem::Specification.new do |spec|
64
64
  spec.add_development_dependency "rspec", "~> 3.0"
65
65
  spec.add_development_dependency "matrix" if GPS_PVT::version_compare(RUBY_VERSION, "3.1") >= 0
66
66
  spec.add_development_dependency "racc"
67
+ spec.add_development_dependency "yard"
67
68
  spec.add_development_dependency "github_changelog_generator" unless ((Gem::Platform.local.os =~ /mingw/) && (GPS_PVT::version_compare(RUBY_VERSION, "3.1") >= 0))
68
69
 
69
70
  # For more information and examples about making a new gem, checkout our
data/lib/gps_pvt/ntrip.rb CHANGED
@@ -129,13 +129,16 @@ OpenURI.class_eval{
129
129
  def OpenURI.open_ntrip(buf, target, proxy, options) # :nodoc:
130
130
  GPS_PVT::Ntrip.start(target.host, target.port){|ntrip|
131
131
  # get source table
132
- tbl = ntrip.get_source_table(options){|str| str}
132
+ tbl_str = ntrip.get_source_table(options){|str| str}
133
133
  if target.root? then
134
- buf << tbl
134
+ buf << tbl_str
135
135
  buf.io.rewind
136
+ buf.io.define_singleton_method(:source_table){
137
+ GPS_PVT::Ntrip::parse_source_table(tbl_str)
138
+ }
136
139
  next
137
140
  end
138
- tbl = GPS_PVT::Ntrip::parse_source_table(tbl)
141
+ tbl = GPS_PVT::Ntrip::parse_source_table(tbl_str)
139
142
 
140
143
  # check mount point
141
144
  mnt_pt = target.mount_point
@@ -0,0 +1,83 @@
1
+ require 'gps_pvt/GPS'
2
+ require 'gps_pvt/Coordinate'
3
+
4
+ module GPS_PVT
5
+ class GPS::PVT_minimal
6
+ def initialize
7
+ # GLOBAL POSITIONING SYSTEM STANDARD POSITIONING SERVICE
8
+ # PERFORMANCE STANDARD 5th edition (Apr. 2020) 3.8
9
+ @hsigma = 8.0 # 95%
10
+ @vsigma = 13.0 # 95%
11
+ @pdop = 6.0 # 98% global
12
+ @vel_sigma = 0.2 # 85%
13
+ @use_satellites = 0 # unknown
14
+ end
15
+ def position_solved?; @xyz || @llh; end
16
+ def velocity_solved?; @velocity; end
17
+ def xyz; @xyz || @llh.xyz; end
18
+ def llh; @xyz ? @xyz.llh : @llh; end
19
+ def xyz=(args); @xyz = Coordinate::XYZ::new(*args); end
20
+ def llh=(args); @llh = Coordinate::LLH::new(*args); end
21
+ def velocity=(args); @velocity = Coordinate::ENU::new(*args); end
22
+ attr_reader :velocity
23
+ attr_accessor :hsigma, :vsigma, :pdop, :vel_sigma, :used_satellites
24
+ class <<self
25
+ def parse_csv(csv_fname, &b)
26
+ require_relative 'util'
27
+ $stderr.puts "Reading CSV file (%s) "%[csv_fname]
28
+ io = open(Util::get_txt(csv_fname), 'r')
29
+
30
+ header = io.readline.chomp.split(/ *, */)
31
+ idx_table = [
32
+ :week, [:tow, /^i?t(?:ime_)?o(?:f_)?w(?:eek)?/],
33
+ :year, :month, :mday, :hour, :min, [:sec, /^sec/],
34
+ [:lng, /^(?:long(?:itude)?|lng)/], [:lat, /^lat(?:itude)/],
35
+ [:alt, /^(?:alt(?:itude)?|h(?:$|eight|_))/], # assumption: [deg], [deg], [m]
36
+ :x, :y, :z, # ECEF xyz
37
+ :hsigma, :vsigma, :pdop,
38
+ [:vn, /^v(?:el)?_?n(?:orth)?/], [:ve, /^v(?:el)?_?e(?:ast)?/],
39
+ [:vd, /^v(?:el)?_?d(?:own)?/], [:vu, /^v(?:el)?_?u(?:p)?/],
40
+ :vx, :vy, :vz, # ECEF xyz
41
+ [:vel_sigma, /^v(?:el)?_sigma/],
42
+ [:used_satellites, /^(?:used_)?sat(?:ellite)?s/],
43
+ ].collect{|k, re|
44
+ re ||= k.to_s
45
+ idx = header.find_index{|str| re === str}
46
+ idx && [k, idx]
47
+ }.compact
48
+ enum = Enumerator::new{|y|
49
+ io.each_line{|line|
50
+ values = line.chomp.split(/ *, */)
51
+ items = Hash[*(idx_table.collect{|k, idx|
52
+ v = values[idx]
53
+ (v == '') ? nil : [k, (Integer(v) rescue Float(v))]
54
+ }.compact.flatten(1))]
55
+ if items.include?(:week) then
56
+ t = GPS::Time::new(items[:week], items[:tow])
57
+ else
58
+ # UTC assumption, thus leap seconds must be added.
59
+ t = GPS::Time::new([:year, :month, :mday, :hour, :min, :sec].collect{|k| items[k]})
60
+ t += GPS::Time::guess_leap_seconds(t)
61
+ end
62
+ pvt = GPS::PVT_minimal::new
63
+ if items.include?(:lat) then
64
+ pvt.llh = ([:lat, :lng].collect{|k| Math::PI / 180 * items[k]} + [items[:alt]])
65
+ elsif items.include?(:xyz) then
66
+ pvt.xyz = [:x, :y, :z].collect{|k| items[k]}
67
+ end
68
+ if items.include?(:vn) then
69
+ pvt.velocity = ([:vn, :ve].collect{|k| items[k]} + [items[:vu] || -items[:vd]])
70
+ elsif items.include?(:vx) then
71
+ pvt.velocity = Coordinate::ENU::relative_rel(
72
+ Coordinate::XYZ::new(*([:vx, :vy, :vz].collect{|k| items[k]})),
73
+ pvt.llh)
74
+ end
75
+ [:pdop, :hsigma, :vsigma, :vel_sigma, :used_satellites].each{|k| pvt.send("#{k}=", items[k])}
76
+ y.yield(t, pvt)
77
+ }
78
+ }
79
+ b ? enum.each{|*res| b.call(*res)} : enum
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,137 @@
1
+ =begin
2
+ Additional Almanac handler for receiver
3
+ =end
4
+
5
+ module GPS_PVT
6
+ class Receiver
7
+ def correct_week_sem_yuma_almanac(src, week_rem = 0)
8
+ t_ref = case src.to_s
9
+ when /www\.navcen\.uscg\.gov\/.*\/(\d{4})\//
10
+ # ex) https://www.navcen.uscg.gov/sites/default/files/gps/almanac/20XX/(Sem|Yuma)/003.(al3|alm)
11
+ GPS_PVT::GPS::Time::new(Time::new($1.to_i).to_a.slice(0, 6).reverse)
12
+ when /www\.navcen\.uscg\.gov\/.*\/current_(sem|yuma)/
13
+ GPS_PVT::GPS::Time::now
14
+ else
15
+ raise
16
+ end
17
+ q, rem = t_ref.week.divmod(1024)
18
+ delta = rem - (week_rem % 1024)
19
+ if delta <= -512 then
20
+ q -= 1
21
+ elsif delta > 512 then
22
+ q += 1
23
+ end
24
+ q * 1024 + week_rem
25
+ end
26
+
27
+ def parse_sem_almanac(src)
28
+ src_io = open(Util::get_txt(src))
29
+ raise unless src_io.readline =~ /(\d+)\s+(\S+)/ # line 1
30
+ num, name = [$1.to_i, $2]
31
+ raise unless src_io.readline =~ /(\d+)\s+(\d+)/ # line 2
32
+ week, t_oa = [$1.to_i, $2.to_i]
33
+ week = correct_week_sem_yuma_almanac(src, week)
34
+ src_io.readline # line 3
35
+
36
+ num.times.each{
37
+ eph = GPS::Ephemeris::new
38
+ 9.times{|i| # line R-1..9
39
+ case i
40
+ when 0, 1, 2, 6, 7
41
+ # N/A items; 1 => SV reference number, 7 => configuration code
42
+ k = {0 => :svid, 2 => :URA_index, 6 => :SV_health}[i]
43
+ v = Integer(src_io.readline)
44
+ eph.send("#{k}=".to_sym, v) if k
45
+ when 3..5
46
+ res = src_io.readline.scan(/[+-]?\d+(?:\.\d+)?(?:E[+-]\d+)?/).collect{|s| Float(s)}
47
+ raise unless res.size == 3
48
+ res.zip({
49
+ 3 => [:e, [:i0, GPS::GPS_SC2RAD], [:dot_Omega0, GPS::GPS_SC2RAD]],
50
+ 4 => [:sqrt_A, [:Omega0, GPS::GPS_SC2RAD], [:omega, GPS::GPS_SC2RAD]],
51
+ 5 => [[:M0, GPS::GPS_SC2RAD], :a_f0, :a_f1],
52
+ }[i]).each{|v, (k, sf)|
53
+ eph.send("#{k}=".to_sym, sf ? (sf * v) : v)
54
+ }
55
+ when 8
56
+ src_io.readline
57
+ end
58
+ }
59
+ eph.i0 = GPS::GPS_SC2RAD * 0.3 + eph.i0
60
+ eph.WN = week
61
+ eph.t_oc = eph.t_oe = t_oa
62
+ [:iodc, :t_GD, :a_f2, :iode, :c_rs, :delta_n,
63
+ :c_uc, :c_us, :c_ic, :c_is, :c_rc, :dot_i0, :iode_subframe3].each{|k|
64
+ eph.send("#{k}=", 0)
65
+ }
66
+ critical{@solver.gps_space_node.register_ephemeris(eph.svid, eph)}
67
+ }
68
+
69
+ $stderr.puts "Read SEM Almanac file (%s): %d items."%[src, num]
70
+ end
71
+
72
+ YUMA_ITEMS = [
73
+ [proc{|s| s.to_i}, {
74
+ :ID => :svid,
75
+ :Health => :SV_health,
76
+ :week => :WN,
77
+ }],
78
+ [proc{|s| Float(s)}, {
79
+ :Eccentricity => :e,
80
+ "Time of Applicability" => [:t_oc, :t_oe],
81
+ "Orbital Inclination" => :i0,
82
+ "Rate of Right Ascen" => :dot_Omega0,
83
+ 'SQRT\(A\)' => :sqrt_A,
84
+ "Right Ascen at Week" => :Omega0,
85
+ "Argument of Perigee" => :omega,
86
+ "Mean Anom" => :M0,
87
+ "Af0" => :a_f0,
88
+ "Af1" => :a_f1,
89
+ }],
90
+ ].collect{|cnv, key_list|
91
+ key_list.collect{|k1, k2_list|
92
+ [/#{k1}[^:]*:/, cnv,
93
+ *([k2_list].flatten(1).collect{|k2|
94
+ "#{k2}=".to_sym
95
+ })]
96
+ }
97
+ }.flatten(1)
98
+
99
+ def parse_yuma_almanac(src)
100
+ src_io = open(Util::get_txt(src))
101
+ num = 0
102
+
103
+ idx_line = -1
104
+ eph, items = nil
105
+ while !src_io.eof?
106
+ line = src_io.readline.chomp
107
+ if idx_line < 0 then
108
+ if line =~ /^\*{8}/ then
109
+ eph = GPS::Ephemeris::new
110
+ items = YUMA_ITEMS.clone
111
+ idx_line = 0
112
+ end
113
+ next
114
+ end
115
+ raise unless i = items.index{|re, cnv, *k_list|
116
+ next false unless re =~ line
117
+ v = cnv.call($')
118
+ k_list.each{|k| eph.send(k, v)}
119
+ true
120
+ }
121
+ items.delete_at(i)
122
+ next unless items.empty?
123
+
124
+ [:iodc, :t_GD, :a_f2, :iode, :c_rs, :delta_n,
125
+ :c_uc, :c_us, :c_ic, :c_is, :c_rc, :dot_i0, :iode_subframe3].each{|k|
126
+ eph.send("#{k}=", 0)
127
+ }
128
+ eph.WN = correct_week_sem_yuma_almanac(src, eph.WN)
129
+ critical{@solver.gps_space_node.register_ephemeris(eph.svid, eph)}
130
+ num += 1
131
+ idx_line = -1
132
+ end
133
+
134
+ $stderr.puts "Read YUMA Almanac file (%s): %d items."%[src, num]
135
+ end
136
+ end
137
+ end
@@ -58,13 +58,11 @@ class Receiver
58
58
  end
59
59
  end
60
60
 
61
- module GPS
62
-
63
61
  [
64
- Ionospheric_UTC_Parameters,
65
- Ephemeris, Ephemeris_SBAS, Ephemeris_GLONASS,
62
+ :Ionospheric_UTC_Parameters,
63
+ :Ephemeris, :Ephemeris_SBAS, :Ephemeris_GLONASS,
66
64
  ].each{|cls|
67
- cls.class_eval{
65
+ GPS.const_get(cls).class_eval{
68
66
  proc{|func_list|
69
67
  func_list.select!{|func|
70
68
  (/=$/ !~ func.to_s) && func_list.include?("#{func}=".to_sym)
@@ -78,4 +76,3 @@ module GPS
78
76
  }
79
77
  }
80
78
  end
81
- end
@@ -50,7 +50,7 @@ class Receiver
50
50
  }.call] + [[
51
51
  [:v_north, :v_east, :v_down, :receiver_clock_error_dot_ms, :vel_sigma],
52
52
  proc{|pvt|
53
- next [nil] * 4 unless pvt.velocity_solved?
53
+ next [nil] * 5 unless pvt.velocity_solved?
54
54
  [:north, :east, :down].collect{|k| pvt.velocity.send(k)} \
55
55
  + [pvt.receiver_error_rate, pvt.vel_sigma]
56
56
  }
@@ -732,4 +732,5 @@ end
732
732
 
733
733
  require_relative 'receiver/rtcm3'
734
734
  require_relative 'receiver/agps'
735
+ require_relative 'receiver/almanac'
735
736
  require_relative 'receiver/extension'
data/lib/gps_pvt/supl.rb CHANGED
@@ -16,6 +16,7 @@ class SUPL_Client
16
16
  :port => 7275,
17
17
  :debug => 0,
18
18
  :protocol => [:lpp, :rrlp],
19
+ :req_data => [:ephemeris], # :almanac
19
20
  }.merge(opts)
20
21
  end
21
22
 
@@ -152,6 +153,16 @@ class SUPL_Client
152
153
  cmd[:sessionID] = @session_id
153
154
  proc{|posinit|
154
155
  posinit[:sETCapabilities] = @capability
156
+ req_data = [
157
+ @opts[:req_data].find_index(:almanac) && :almanacRequested,
158
+ :utcModelRequested,
159
+ :ionosphericModelRequested,
160
+ :referenceLocationRequested,
161
+ :referenceTimeRequested,
162
+ :acquisitionAssistanceRequested,
163
+ :realTimeIntegrityRequested,
164
+ @opts[:req_data].find_index(:ephemeris) && :navigationModelRequested,
165
+ ].compact
155
166
  posinit[:requestedAssistData] = Hash[*([
156
167
  :almanacRequested,
157
168
  :utcModelRequested,
@@ -163,15 +174,7 @@ class SUPL_Client
163
174
  :realTimeIntegrityRequested,
164
175
  :navigationModelRequested,
165
176
  ].collect{|k|
166
- [k, [
167
- :utcModelRequested,
168
- :ionosphericModelRequested,
169
- :referenceLocationRequested,
170
- :referenceTimeRequested,
171
- :acquisitionAssistanceRequested,
172
- :realTimeIntegrityRequested,
173
- :navigationModelRequested,
174
- ].include?(k)]
177
+ [k, req_data.include?(k)]
175
178
  }.flatten(1))]
176
179
  posinit[:requestedAssistData][:"ver2-RequestedAssistData-extension"] = {
177
180
  :ganssRequestedCommonAssistanceDataList => {
@@ -185,18 +188,21 @@ class SUPL_Client
185
188
  :ganssRequestedGenericAssistanceDataList => [
186
189
  # SBAS
187
190
  {:ganssId => 1, :ganssSBASid => [0, 1, 0], # MSAS
188
- :ganssRealTimeIntegrity => true, :ganssAlmanac => false,
189
- :ganssNavigationModelData => {:ganssWeek => 0, :ganssToe => 0, :"t-toeLimit" => 0},
191
+ :ganssRealTimeIntegrity => true,
190
192
  :ganssReferenceMeasurementInfo => false, :ganssUTCModel => true, :ganssAuxiliaryInformation => false},
191
193
  # QZSS
192
- {:ganssId => 3, :ganssRealTimeIntegrity => true, :ganssAlmanac => false,
193
- :ganssNavigationModelData => {:ganssWeek => 0, :ganssToe => 0, :"t-toeLimit" => 0},
194
+ {:ganssId => 3, :ganssRealTimeIntegrity => true,
194
195
  :ganssReferenceMeasurementInfo => false, :ganssUTCModel => true, :ganssAuxiliaryInformation => false},
195
196
  # GLONASS
196
- {:ganssId => 4, :ganssRealTimeIntegrity => true, :ganssAlmanac => false,
197
- :ganssNavigationModelData => {:ganssWeek => 0, :ganssToe => 0, :"t-toeLimit" => 0},
197
+ {:ganssId => 4, :ganssRealTimeIntegrity => true,
198
198
  :ganssReferenceMeasurementInfo => false, :ganssUTCModel => true, :ganssAuxiliaryInformation => false},
199
- ],
199
+ ].collect{|items|
200
+ items[:ganssAlmanac] = @opts[:req_data].include?(:almanac)
201
+ items[:ganssNavigationModelData] = {
202
+ :ganssWeek => 0, :ganssToe => 0, :"t-toeLimit" => 0
203
+ } if @opts[:req_data].include?(:ephemeris)
204
+ items
205
+ },
200
206
  }
201
207
  posinit[:requestedAssistData][:navigationModelData] = {
202
208
  :gpsWeek => 0,
@@ -302,7 +308,25 @@ class SUPL_Client
302
308
  :ephemL2Pflag
303
309
  :ephemAODA
304
310
  =end
305
-
311
+
312
+ ALM_KEY_TBL_RRLP = Hash[*({
313
+ :e => [:almanacE, -21],
314
+ :i0 => [:almanacKsii, -19, true],
315
+ :dot_Omega0 => [:almanacOmegaDot, -38, true],
316
+ :SV_health => :almanacSVhealth,
317
+ :sqrt_A => [:almanacAPowerHalf, -11],
318
+ :Omega0 => [:almanacOmega0, -23, true],
319
+ :omega => [:almanacW, -23, true],
320
+ :M0 => [:almanacM0, -23, true],
321
+ :a_f0 => [:almanacAF0, -20],
322
+ :a_f1 => [:almanacAF1, -38],
323
+ }.collect{|dst_k, (src_k, sf_pow2, sc2rad)|
324
+ sf_pow2 ||= 0
325
+ sf = sf_pow2 < 0 ? Rational(1, 1 << -sf_pow2) : (1 << sf_pow2)
326
+ sf = sf.to_f * GPS::GPS_SC2RAD if sc2rad
327
+ ["#{dst_k}=".to_sym, [src_k, sf]]
328
+ }.flatten(1))]
329
+
306
330
  def attach_rrlp(msg)
307
331
  t_gps = proc{
308
332
  week_rem, sec008 = [:gpsWeek, :gpsTOW23b].collect{|k| msg[:referenceTime][:gpsTime][k]}
@@ -337,10 +361,11 @@ class SUPL_Client
337
361
  params
338
362
  }
339
363
  msg.define_singleton_method(:ephemeris){
340
- self[:navigationModel][:navModelList].collect{|v|
364
+ next [] unless (model = self[:navigationModel])
365
+ model[:navModelList].collect{|sat|
341
366
  eph = GPS::Ephemeris::new
342
- eph.svid = v[:satelliteID] + 1
343
- eph_src = v[:satStatus][:newSatelliteAndModelUC]
367
+ eph.svid = sat[:satelliteID] + 1
368
+ eph_src = sat[:satStatus][:newSatelliteAndModelUC]
344
369
  EPH_KEY_TBL_RRLP.each{|dst_k, (src_k, sf)|
345
370
  v = sf * eph_src[src_k]
346
371
  eph.send(dst_k, v.kind_of?(Rational) ? v.to_f : v)
@@ -357,6 +382,30 @@ class SUPL_Client
357
382
  eph
358
383
  }
359
384
  }
385
+ msg.define_singleton_method(:almanac){
386
+ next [] unless (model = self[:almanac])
387
+ week = self[:almanac][:alamanacWNa]
388
+ week += (t_gps.week >> 8) << 8
389
+ model[:almanacList].collect{|sat|
390
+ eph = GPS::Ephemeris::new
391
+ eph.svid = sat[:satelliteID] + 1
392
+ ALM_KEY_TBL_RRLP.each{|dst_k, (src_k, sf)|
393
+ v = sf * sat[src_k]
394
+ eph.send(dst_k, v.kind_of?(Rational) ? v.to_f : v)
395
+ }
396
+ eph.i0 = GPS::GPS_SC2RAD * 0.3 + eph.i0
397
+ eph.WN = week
398
+ eph.t_oc = eph.t_oe = sat[:alamanacToa] << 12
399
+ [:iodc, :t_GD, :a_f2, :iode, :c_rs, :delta_n,
400
+ :c_uc, :c_us, :c_ic, :c_is, :c_rc, :dot_i0, :iode_subframe3].each{|k|
401
+ eph.send("#{k}=", 0)
402
+ }
403
+ eph.URA_index = -1
404
+ #eph.fit_interval
405
+ eph
406
+ }
407
+ }
408
+ :alamanacToa # typo in TS
360
409
  msg
361
410
  end
362
411
 
@@ -412,6 +461,25 @@ class SUPL_Client
412
461
  :N_T=, :p=, :delta_tau_n=, :P4=,
413
462
  :tau_GPS=, :tau_c=, :day_of_year=, :year=, :n=, :freq_ch=
414
463
  =end
464
+
465
+ ALM_KEY_TBL_LPP = Hash[*({
466
+ :e => [:E, -21],
467
+ :i0 => [:DeltaI, -19, true],
468
+ :dot_Omega0 => [:OMEGADOT, -38, true],
469
+ :SV_health => :SVHealth,
470
+ :sqrt_A => [:SqrtA, -11],
471
+ :Omega0 => [:OMEGAo, -23, true],
472
+ :omega => [:Omega, -23, true],
473
+ :M0 => [:Mo, -23, true],
474
+ :a_f0 => [:af0, -20],
475
+ :a_f1 => [:af1, -38],
476
+ }.collect{|dst_k, (src_k, sf_pow2, sc2rad)|
477
+ sf_pow2 ||= 0
478
+ sf = sf_pow2 < 0 ? Rational(1, 1 << -sf_pow2) : (1 << sf_pow2)
479
+ sf = sf.to_f * GPS::GPS_SC2RAD if sc2rad
480
+ ["#{dst_k}=".to_sym,
481
+ [(src_k.kind_of?(Symbol) ? "navAlm#{src_k}" : src_k).to_sym, sf]]
482
+ }.flatten(1))]
415
483
 
416
484
  def attach_lpp(msg)
417
485
  t_gps = proc{|data|
@@ -460,9 +528,9 @@ class SUPL_Client
460
528
  }.call
461
529
  leap_seconds = iono_utc.delta_t_LS rescue t_gps.leap_seconds
462
530
  msg.define_singleton_method(:iono_utc){iono_utc}
463
- extract_gps_ephemeris = proc{|sat_list, sys|
531
+ extract_gps_ephemeris = proc{|model, sys|
464
532
  offset = {:gps => 1, :qzss => 193}[sys]
465
- sat_list.collect{|v|
533
+ model[:"gnss-SatelliteList"].collect{|v|
466
534
  eph = GPS::Ephemeris::new
467
535
  eph.svid = v[:svID][:"satellite-id"] + offset
468
536
  eph_src = v[:"gnss-ClockModel"][:"nav-ClockModel"].merge(v[:"gnss-OrbitModel"][:"nav-KeplerianSet"])
@@ -489,42 +557,81 @@ class SUPL_Client
489
557
  [:"provideAssistanceData-r9"][:"a-gnss-ProvideAssistanceData"] \
490
558
  [:"gnss-GenericAssistData"]
491
559
  res = [:gps, :qzss].collect{|k|
492
- extract_gps_ephemeris.call(
493
- assist_data.select{|v| v[:"gnss-ID"][:"gnss-id"] == k}[0] \
494
- [:"gnss-NavigationModel"][:"gnss-SatelliteList"], k)
560
+ model = assist_data.find{|v| v[:"gnss-ID"][:"gnss-id"] == k}[:"gnss-NavigationModel"] rescue nil
561
+ next [] unless model
562
+ extract_gps_ephemeris.call(model, k)
495
563
  }.flatten(1)
496
- assist_data_glo = assist_data.select{|v| v[:"gnss-ID"][:"gnss-id"] == :glonass}[0]
497
- utc_params_glo = {
498
- :tau_c= => [:tauC, Rational(1, 1 << 31)],
499
- }.collect{|dst_k, (src_k, sf)|
500
- [dst_k, sf * assist_data_glo[:"gnss-UTC-Model"][:utcModel3][src_k]]
501
- }
502
- res += assist_data_glo[:"gnss-NavigationModel"][:"gnss-SatelliteList"].collect{|sat|
503
- eph = GPS::Ephemeris_GLONASS::new
504
- eph.svid = sat[:svID][:"satellite-id"] + 1
505
- eph_src = sat[:"gnss-ClockModel"][:"glonass-ClockModel"].merge(
506
- sat[:"gnss-OrbitModel"][:"glonass-ECEF"])
507
- (EPH_KEY_TBL_LPP_GLO.collect{|dst_k, (src_k, sf)|
508
- v = eph_src[src_k]
509
- [dst_k, sf.send(sf.kind_of?(Proc) ? :call : :*, case v
510
- when Array; Integer(v.join, 2)
511
- when true; 1
512
- when false; 0
513
- else; v
514
- end)]
515
- } + utc_params_glo).each{|dst_k, v|
516
- eph.send(dst_k, v.kind_of?(Rational) ? v.to_f : v)
564
+ proc{|assist_data_glo|
565
+ next unless assist_data_glo
566
+ next unless nav_model = assist_data_glo[:"gnss-NavigationModel"]
567
+ utc_params_glo = {
568
+ :tau_c= => [:tauC, Rational(1, 1 << 31)],
569
+ }.collect{|dst_k, (src_k, sf)|
570
+ [dst_k, sf * assist_data_glo[:"gnss-UTC-Model"][:utcModel3][src_k]]
517
571
  }
518
- eph.B_n = sat[:svHealth][0]
519
- eph.F_T_index = Integer(sat[:svHealth][1..4].join, 2)
520
- eph.t_b = Integer(sat[:iod][4..-1].join, 2) * 15 * 60
521
- eph.set_date((t_gps + 3 * 60 * 60).c_tm(leap_seconds)) # UTC -> Moscow time
522
- eph.N_T = eph.NA
523
- eph.rehash(leap_seconds)
524
- eph
525
- }
572
+ res += nav_model[:"gnss-SatelliteList"].collect{|sat|
573
+ eph = GPS::Ephemeris_GLONASS::new
574
+ eph.svid = sat[:svID][:"satellite-id"] + 1
575
+ eph_src = sat[:"gnss-ClockModel"][:"glonass-ClockModel"].merge(
576
+ sat[:"gnss-OrbitModel"][:"glonass-ECEF"])
577
+ (EPH_KEY_TBL_LPP_GLO.collect{|dst_k, (src_k, sf)|
578
+ v = eph_src[src_k]
579
+ [dst_k, sf.send(sf.kind_of?(Proc) ? :call : :*, case v
580
+ when Array; Integer(v.join, 2)
581
+ when true; 1
582
+ when false; 0
583
+ else; v
584
+ end)]
585
+ } + utc_params_glo).each{|dst_k, v|
586
+ eph.send(dst_k, v.kind_of?(Rational) ? v.to_f : v)
587
+ }
588
+ eph.B_n = sat[:svHealth][0]
589
+ eph.F_T_index = Integer(sat[:svHealth][1..4].join, 2)
590
+ eph.t_b = Integer(sat[:iod][4..-1].join, 2) * 15 * 60
591
+ eph.set_date((t_gps + 3 * 60 * 60).c_tm(leap_seconds)) # UTC -> Moscow time
592
+ eph.N_T = eph.NA
593
+ eph.rehash(leap_seconds)
594
+ eph
595
+ }
596
+ }.call(assist_data.find{|v| v[:"gnss-ID"][:"gnss-id"] == :glonass})
526
597
  res
527
598
  }
599
+ extract_gps_almanac = proc{|alm, sys|
600
+ next [] unless alm
601
+ offset = {:gps => 1, :qzss => 193}[sys]
602
+ week = alm[:weekNumber] # optional but required for non-GLONASS
603
+ week += (t_gps.week >> 8) << 8
604
+ t_oa = alm[:toa] << 12 # optional but required for non-GLONASS
605
+ alm[:"gnss-AlmanacList"].collect{|v|
606
+ next unless v = v[:"keplerianNAV-Almanac"]
607
+ eph = GPS::Ephemeris::new
608
+ eph.svid = v[:svID][:"satellite-id"] + offset
609
+ ALM_KEY_TBL_LPP.each{|dst_k, (src_k, sf)|
610
+ v2 = sf * v[src_k]
611
+ eph.send(dst_k, v2.kind_of?(Rational) ? v2.to_f : v2)
612
+ }
613
+ eph.i0 = GPS::GPS_SC2RAD * 0.3 + eph.i0
614
+ eph.WN = week
615
+ eph.t_oc = eph.t_oe = t_oa
616
+ [:iodc, :t_GD, :a_f2, :iode, :c_rs, :delta_n,
617
+ :c_uc, :c_us, :c_ic, :c_is, :c_rc, :dot_i0, :iode_subframe3].each{|k|
618
+ eph.send("#{k}=", 0)
619
+ }
620
+ eph.URA_index = -1
621
+ #eph.fit_interval
622
+ eph
623
+ }.compact
624
+ }
625
+ msg.define_singleton_method(:almanac){
626
+ assist_data = self[:c1][:provideAssistanceData][:criticalExtensions][:c1] \
627
+ [:"provideAssistanceData-r9"][:"a-gnss-ProvideAssistanceData"] \
628
+ [:"gnss-GenericAssistData"]
629
+ [:gps, :qzss].collect{|k|
630
+ model = assist_data.find{|v| v[:"gnss-ID"][:"gnss-id"] == k}[:"gnss-Almanac"] rescue nil
631
+ next [] unless model
632
+ extract_gps_almanac.call(model, k)
633
+ }.flatten(1)
634
+ }
528
635
  msg
529
636
  end
530
637
  end
@@ -537,7 +644,7 @@ OpenURI.class_eval{
537
644
  options[:port] = target.port
538
645
  URI.decode_www_form(target.query || "").each{|k, v|
539
646
  case k = k.to_sym
540
- when :protocol
647
+ when :protocol, :req_data
541
648
  (options[k] ||= []) << v.to_sym
542
649
  end
543
650
  }
data/lib/gps_pvt/util.rb CHANGED
@@ -88,9 +88,15 @@ module Util
88
88
  end
89
89
  nil
90
90
  }.call
91
- is_file = src.kind_of?(File) || src.kind_of?(Tempfile)
92
91
 
93
- return src.path if ((!compressed) and is_file)
92
+ case src
93
+ when File
94
+ next src.path
95
+ when Tempfile
96
+ # Preserve tempfile after leaving open-uri block
97
+ src.define_singleton_method(:close!){close(false)}
98
+ next src # Kernel.open(obj) redirects to obj.open if obj responds to :open
99
+ end unless compressed
94
100
 
95
101
  Tempfile::open(File::basename($0, '.*')){|dst|
96
102
  dst.binmode
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GPS_PVT
4
- VERSION = "0.10.2"
4
+ VERSION = "0.10.4"
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.10.2
4
+ version: 0.10.4
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: 2024-04-02 00:00:00.000000000 Z
11
+ date: 2024-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyserial
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: github_changelog_generator
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -166,8 +180,10 @@ files:
166
180
  - lib/gps_pvt/asn1/asn1.y
167
181
  - lib/gps_pvt/asn1/per.rb
168
182
  - lib/gps_pvt/ntrip.rb
183
+ - lib/gps_pvt/pvt.rb
169
184
  - lib/gps_pvt/receiver.rb
170
185
  - lib/gps_pvt/receiver/agps.rb
186
+ - lib/gps_pvt/receiver/almanac.rb
171
187
  - lib/gps_pvt/receiver/extension.rb
172
188
  - lib/gps_pvt/receiver/rtcm3.rb
173
189
  - lib/gps_pvt/rtcm3.rb