gps_pvt 0.1.1 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db616707d74b61691f2d968fe2034c513333d300257f72a6968df7c6a538f07b
4
- data.tar.gz: 875638b4fb9378dc1928ba532e868138373174a1bc8316f7852a88867f52b43d
3
+ metadata.gz: 8dda091173f9531e0afccd9bab371f7fab0f6c6b82c1f7332fb99cbae81bb3cb
4
+ data.tar.gz: 01173b782d6848634840b6cb2946fd4cf9874ba191cb8acb0f8e072b4e17d67b
5
5
  SHA512:
6
- metadata.gz: 77e3567bbc1a124faa29b78f9e2f745c6713d5b81f5b7204432ba64134d1914fd93bed6b11e6e047e6dd50dd42372df8deba379b7940a74b65dedddc2aac3156
7
- data.tar.gz: 171c292e462856f0f4f566954311d393063dd1b2864b15dbeab887c4eafb84875a95b7e242010de17652a281665c0e7bffaa1833a4e89971d8d55a5997138a37
6
+ metadata.gz: d756b85034c18bcc2069d2da6b06e1949ff868f33fd57c3c72f8a8a4e4eafa49185128cf1f5a10e35bafbef3d11d130386d3ce1996b21487bc61fb445eba2df0
7
+ data.tar.gz: 26c00d4f6743a07327a57354d83ac8c565f20548748c718855a6476924a7959a3e7fac3c1b7707f7cb1cf1a324bbefa7ab8a68479b7988ae1c87a6627aca93b3
data/README.md CHANGED
@@ -1,86 +1,117 @@
1
- # GPS_PVT
2
-
3
- GPS_PVT is a Ruby GPS (Global positioning system) PVT (position, velocity and time) solver. It accepts RINEX NAV and OBS files in addition to u-blox ubx format. Its significant features are easy to use with highly flexibility to customize internal solver behavior such as weight for each available satellite.
4
-
5
- The PVT solution is obtained with a stand alone positioning (i.e. neither differential nor kinematic) with lease square. Its main internal codes are derived from ones of [ninja-scan-light](https://github.com/fenrir-naru/ninja-scan-light) having capability to calculate tightly-coupled GNSS/INS integrated solution. These codes are written by C++, and wrapped by [SWIG](http://www.swig.org/).
6
-
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'gps_pvt'
13
- ```
14
-
15
- And then execute:
16
-
17
- $ bundle install
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install gps_pvt
22
-
23
- ## Usage
24
-
25
- ```ruby
26
- require 'gps_pvt'
27
-
28
- receiver = GPS_PVT::Receiver::new
29
- receiver.parse_rinex_nav(rinex_nav_file)
30
-
31
- # For generate solution in CSV format
32
- puts GPS_PVT::Receiver::header
33
- receiver.parse_rinex_obs(rinex_obs_file)
34
-
35
- # receiver.parse_ubx(ubx_file) # same as above for ubx file including RXM-RAW(X) and RXM-SFRB(X)
36
-
37
- # Or precise control of outputs
38
- receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
39
- meas # => measurement, array of [prn, key, value]; key is represented by GPS_PVT::GPS::Measurement::L1_PSEUDORANGE
40
- pvt # => PVT solution, all properties are shown by pvt.methods
41
- # for example
42
- if(pvt.position_solved?){
43
- pvt.receiver_time # receiver time; .to_a => [GPS week, seconds], .c_tm => [year, month, day, hour, min, sec] without leap second consideration
44
- [:lat, :lng, :alt].collect{|f| pvt.llh.send(f)} # latitude[rad], longitude[rad], WGS-84 altitude[m]
45
- pvt.receiver_error # receiver clock error in meter
46
- [:g, :p, :h, :v, :t].collect{|k| pvt.send("#{k}dop".to_sym)} # various DOP, dilution of precision
47
- if(pvt.velocity_solved?){
48
- [:north, east, :down].collect{|dir| pvt.velocity.send(dir)} # speed in north/east/down [m/s]
49
- pvt.receiver_error_rate # clock error rate in m/s
50
- }
51
- pvt.used_satellite_list # array of used, i.e., visible and weight>0, satellite
52
- pvt.G # design matrix in Earth-centered-Earth-fixed (ECEF); .to_a returns double array converted from matrix. its row corresponds to one of used_satellite_list
53
- pvt.G_enu # design matrix in East-North-Up (ENU)
54
- pvt.W # weight for each satellite
55
- pvt.delta_r # residual of pseudo range
56
- pvt.S # (delta position) = S * (residual) in last iteration in ECEF
57
- }
58
- }
59
-
60
- # Customize solution
61
- receiver.solver.options.exclude(prn) # Exclude satellite; the default is to use every satellite if visible
62
- receiver.solver.options.include(prn) # Discard previous setting of exclusion
63
- receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
64
- # control weight per satellite per iteration
65
- weight, range_c, range_r, rate_rel_neg, *los_neg = rel_prop # relative property
66
- weight = 1 # default; same weight
67
- # or weight based on elevation
68
- # elv = GPS_PVT::Coordinate::ENU::relative_rel(GPS_PVT::Coordinate::XYZ::new(*los_neg), usr_pos).elevation
69
- # weight = (Math::sin(elv)/0.8)**2
70
- [weight, range_c, range_r, rate_rel_neg] + los_neg
71
- }
72
- ```
73
-
74
- ## Development
75
-
76
- 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.
77
-
78
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
79
-
80
- ## Contributing
81
-
82
- Bug reports and pull requests are welcome on GitHub at https://github.com/fenrir-naru/gps_pvt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/fenrir-naru/gps_pvt/blob/master/CODE_OF_CONDUCT.md).
83
-
84
- ## Code of Conduct
85
-
86
- Everyone interacting in the GPS_PVT project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fenrir-naru/gps_pvt/blob/master/CODE_OF_CONDUCT.md).
1
+ # GPS_PVT
2
+
3
+ GPS_PVT is a Ruby GPS (Global positioning system) PVT (position, velocity and time) solver. It accepts RINEX NAV and OBS files in addition to u-blox ubx format. Its significant features are easy to use with highly flexibility to customize internal solver behavior such as weight for each available satellite.
4
+
5
+ The PVT solution is obtained with a stand alone positioning (i.e. neither differential nor kinematic) with application of least square to each snapshot. Its main internal codes are derived from ones of [ninja-scan-light](https://github.com/fenrir-naru/ninja-scan-light) having capability to calculate tightly-coupled GNSS/INS integrated solution. These codes are written by C++, and wrapped by [SWIG](http://www.swig.org/).
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/gps_pvt.svg)](https://badge.fury.io/rb/gps_pvt)
8
+ [![Ruby](https://github.com/fenrir-naru/gps_pvt/actions/workflows/main.yml/badge.svg)](https://github.com/fenrir-naru/gps_pvt/actions/workflows/main.yml)
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'gps_pvt'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle install
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install gps_pvt
25
+
26
+ For Windows users, this gem requires Devkit because of native compilation.
27
+
28
+ ## Usage
29
+
30
+ For user who just generate PVT solution, an attached executable is useful. After installation, type
31
+
32
+ $ gps_pvt RINEX_or_UBX_file(s)
33
+
34
+ For developer, this library will be used in the following:
35
+
36
+ ```ruby
37
+ require 'gps_pvt'
38
+
39
+ receiver = GPS_PVT::Receiver::new
40
+ receiver.parse_rinex_nav(rinex_nav_file) # This is required before parsing RINEX obs file (For ubx, skippable)
41
+
42
+ # For generate solution in CSV format
43
+ puts GPS_PVT::Receiver::header
44
+ receiver.parse_rinex_obs(rinex_obs_file)
45
+ # receiver.parse_ubx(ubx_file) # same as above for ubx file including RXM-RAW(X) and RXM-SFRB(X)
46
+
47
+ # Or precise control of outputs
48
+ receiver.parse_rinex_obs(rinex_obs_file){|pvt, meas| # per epoch
49
+ meas.to_a # => measurement, array of [prn, key, value]; key is represented by GPS_PVT::GPS::Measurement::L1_PSEUDORANGE; instead of .to_a, .to_hash returns {prn => {key => value, ...}, ...}
50
+
51
+ pvt # => PVT solution, all properties are shown by pvt.methods
52
+ # for example
53
+ if(pvt.position_solved?){
54
+ pvt.receiver_time # receiver time; .to_a => [GPS week, seconds], .c_tm => [year, month, day, hour, min, sec] without leap second consideration
55
+ [:lat, :lng, :alt].collect{|f| pvt.llh.send(f)} # latitude[rad], longitude[rad], WGS-84 altitude[m]
56
+ pvt.receiver_error # receiver clock error in meter
57
+ [:g, :p, :h, :v, :t].collect{|k| pvt.send("#{k}dop".to_sym)} # various DOP, dilution of precision
58
+ if(pvt.velocity_solved?){
59
+ [:north, east, :down].collect{|dir| pvt.velocity.send(dir)} # speed in north/east/down [m/s]
60
+ pvt.receiver_error_rate # clock error rate in m/s
61
+ }
62
+ pvt.used_satellite_list # array of used, i.e., visible and weight>0, satellite
63
+ pvt.azimuth # azimuth angle [rad] to used satellites in Hash {prn => value, ...}
64
+ pvt.elevation # elevation angle [rad]
65
+
66
+ pvt.G # design matrix in Earth-centered-Earth-fixed (ECEF); .to_a returns double array converted from matrix. its row corresponds to one of used_satellite_list
67
+ pvt.G_enu # design matrix in East-North-Up (ENU)
68
+ pvt.W # weight for each satellite
69
+ pvt.delta_r # residual of pseudo range
70
+ pvt.S # (delta position) = S * (residual) in last iteration in ECEF
71
+ }
72
+ }
73
+
74
+ # Customize solution
75
+ receiver.solver.options.exclude(prn) # Exclude satellite; the default is to use every satellite if visible
76
+ receiver.solver.options.include(prn) # Discard previous setting of exclusion
77
+ receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
78
+ # control weight per satellite per iteration
79
+ weight, range_c, range_r, rate_rel_neg, *los_neg = rel_prop # relative property
80
+ # rcv_e, t_arv, usr_pos, usr_vel are temporary solution of
81
+ # receiver clock error [m], time of arrival [s], user position and velocity in ECEF, respectively.
82
+
83
+ weight = 1 # same as default; identical weight for each visible satellite
84
+ # or weight based on elevation
85
+ # elv = GPS_PVT::Coordinate::ENU::relative_rel(GPS_PVT::Coordinate::XYZ::new(*los_neg), usr_pos).elevation
86
+ # weight = (Math::sin(elv)/0.8)**2
87
+
88
+ [weight, range_c, range_r, rate_rel_neg] + los_neg # must return relative property
89
+ }
90
+
91
+ # Dynamic customization of weight for each epoch
92
+ (class << receiver; self; end).instance_eval{ # do before parse_XXX
93
+ alias_method(:run_orig, :run)
94
+ define_method(:run){|meas, t_meas, &b|
95
+ meas # observation, same as the 2nd argument of parse_XXX
96
+ receiver.solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
97
+ # Do something based on meas, t_meas.
98
+ rel_prop
99
+ }
100
+ run_orig(meas, t_meas, &b)
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Development
106
+
107
+ 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.
108
+
109
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
110
+
111
+ ## Contributing
112
+
113
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fenrir-naru/gps_pvt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/fenrir-naru/gps_pvt/blob/master/CODE_OF_CONDUCT.md).
114
+
115
+ ## Code of Conduct
116
+
117
+ Everyone interacting in the GPS_PVT project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fenrir-naru/gps_pvt/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,86 +1,86 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rake/extensiontask"
9
-
10
- Rake::ExtensionTask.new("gps_pvt") do |ext|
11
- ext.lib_dir = "lib/gps_pvt"
12
- end
13
-
14
- namespace :git do
15
- namespace :submodules do
16
- desc "Initialize git submodules"
17
- task :init do
18
- sh "git submodule init"
19
- # for sparse-checkout; @see https://stackoverflow.com/a/59521050/15992898
20
- `git config --file .gitmodules --name-only --get-regexp path`.lines.each{|str|
21
- # list submodule; @see https://stackoverflow.com/a/23490756/15992898
22
- next unless str =~ /submodule\.(.+)\.path/
23
- repo_dir = $1
24
- sh "git clone -n #{`git config submodule.#{repo_dir}.url`.chomp} #{repo_dir}"
25
- }
26
- {
27
- 'ext/ninja-scan-light' => [
28
- "sparse-checkout init", # same as "git -C #{repo} config core.sparseCheckout true"
29
- # same as #{repo}/.git/info/sparse-checkout
30
- "sparse-checkout set" + (<<-__SPARSE_PATTERNS__).lines.collect{|str| str.chomp.gsub(/^ */, ' ')}.join,
31
- /tool/param/
32
- /tool/navigation/GPS*
33
- /tool/navigation/coordinate.h
34
- /tool/navigation/EGM.h
35
- /tool/navigation/MagneticField.h
36
- /tool/navigation/NTCM.h
37
- /tool/navigation/RINEX.h
38
- /tool/navigation/WGS84.h
39
- /tool/swig/SylphideMath.i
40
- /tool/swig/GPS.i
41
- /tool/swig/Coordinate.i
42
- /tool/swig/makefile
43
- /tool/swig/extconf.rb
44
- /tool/swig/spec/GPS_spec.rb
45
- /tool/swig/spec/SylphideMath_spec.rb
46
- __SPARSE_PATTERNS__
47
- ]
48
- }.each{|repo, commands|
49
- commands.each{|str| sh "git -C #{repo} #{str}"}
50
- }
51
- sh "git submodule absorbgitdirs" # Move #{repo}/.git to .git/modules/#{repo}/.git
52
- sh "git submodule update"
53
- # if already checked out, then git -C #{repo} read-tree -mu HEAD
54
- end
55
- end
56
- end
57
-
58
-
59
- desc "Generate SWIG wrapper codes"
60
- task :swig do
61
- swig_dir = File::join(File::dirname(__FILE__), 'ext', 'ninja-scan-light', 'tool', 'swig')
62
- out_base_dir = File::join(File::dirname(__FILE__), 'ext', 'gps_pvt')
63
- Dir::chdir(swig_dir){
64
- Dir::glob("*.i"){|src|
65
- mod_name = File::basename(src, '.*')
66
- out_dir = File::join(out_base_dir, mod_name)
67
- sh "mkdir -p #{out_dir}"
68
- wrapper = File::join(out_dir, "#{mod_name}_wrap.cxx")
69
- sh [:make, :clean, wrapper,
70
- "BUILD_DIR=#{out_dir}",
71
- "SWIGFLAGS='-c++ -ruby -prefix \"GPS_PVT::\"#{" -D__MINGW__" if ENV["MSYSTEM"]}'"].join(' ')
72
- lines = open(wrapper, 'r').read.lines.collect{|line|
73
- line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
74
- "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
75
- }
76
- }
77
- open(wrapper, 'w').write(lines.join)
78
- }
79
- }
80
- end
81
-
82
- file "ext/ninja-scan-light/tool" do |t|
83
- Rake::Task["git:submodules:init"].invoke
84
- end
85
-
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rake/extensiontask"
9
+
10
+ Rake::ExtensionTask.new("gps_pvt") do |ext|
11
+ ext.lib_dir = "lib/gps_pvt"
12
+ end
13
+
14
+ namespace :git do
15
+ namespace :submodules do
16
+ desc "Initialize git submodules"
17
+ task :init do
18
+ sh "git submodule init"
19
+ # for sparse-checkout; @see https://stackoverflow.com/a/59521050/15992898
20
+ `git config --file .gitmodules --name-only --get-regexp path`.lines.each{|str|
21
+ # list submodule; @see https://stackoverflow.com/a/23490756/15992898
22
+ next unless str =~ /submodule\.(.+)\.path/
23
+ repo_dir = $1
24
+ sh "git clone -n #{`git config submodule.#{repo_dir}.url`.chomp} #{repo_dir}"
25
+ }
26
+ {
27
+ 'ext/ninja-scan-light' => [
28
+ "sparse-checkout init", # same as "git -C #{repo} config core.sparseCheckout true"
29
+ # same as #{repo}/.git/info/sparse-checkout
30
+ "sparse-checkout set" + (<<-__SPARSE_PATTERNS__).lines.collect{|str| str.chomp.gsub(/^ */, ' ')}.join,
31
+ /tool/param/
32
+ /tool/navigation/GPS*
33
+ /tool/navigation/coordinate.h
34
+ /tool/navigation/EGM.h
35
+ /tool/navigation/MagneticField.h
36
+ /tool/navigation/NTCM.h
37
+ /tool/navigation/RINEX.h
38
+ /tool/navigation/WGS84.h
39
+ /tool/swig/SylphideMath.i
40
+ /tool/swig/GPS.i
41
+ /tool/swig/Coordinate.i
42
+ /tool/swig/makefile
43
+ /tool/swig/extconf.rb
44
+ /tool/swig/spec/GPS_spec.rb
45
+ /tool/swig/spec/SylphideMath_spec.rb
46
+ __SPARSE_PATTERNS__
47
+ ]
48
+ }.each{|repo, commands|
49
+ commands.each{|str| sh "git -C #{repo} #{str}"}
50
+ }
51
+ sh "git submodule absorbgitdirs" # Move #{repo}/.git to .git/modules/#{repo}/.git
52
+ sh "git submodule update"
53
+ # if already checked out, then git -C #{repo} read-tree -mu HEAD
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ desc "Generate SWIG wrapper codes"
60
+ task :swig do
61
+ swig_dir = File::join(File::dirname(__FILE__), 'ext', 'ninja-scan-light', 'tool', 'swig')
62
+ out_base_dir = File::join(File::dirname(__FILE__), 'ext', 'gps_pvt')
63
+ Dir::chdir(swig_dir){
64
+ Dir::glob("*.i"){|src|
65
+ mod_name = File::basename(src, '.*')
66
+ out_dir = File::join(out_base_dir, mod_name)
67
+ sh "mkdir -p #{out_dir}"
68
+ wrapper = File::join(out_dir, "#{mod_name}_wrap.cxx")
69
+ sh [:make, :clean, wrapper,
70
+ "BUILD_DIR=#{out_dir}",
71
+ "SWIGFLAGS='-c++ -ruby -prefix \"GPS_PVT::\"#{" -D__MINGW__" if ENV["MSYSTEM"]}'"].join(' ')
72
+ lines = open(wrapper, 'r').read.lines.collect{|line|
73
+ line.sub(/rb_require\(\"([^\"]+)\"\)/){ # from camel to underscore downcase style
74
+ "rb_require(\"#{$1.sub('GPS_PVT', 'gps_pvt')}\")"
75
+ }
76
+ }
77
+ open(wrapper, 'w').write(lines.join)
78
+ }
79
+ }
80
+ end
81
+
82
+ file "ext/ninja-scan-light/tool" do |t|
83
+ Rake::Task["git:submodules:init"].invoke
84
+ end
85
+
86
86
  task :default => ["ext/ninja-scan-light/tool", :compile, :spec]
data/exe/gps_pvt ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gps_pvt'
4
+
5
+ # runnable quick example to solve PVT by using RINEX NAV/OBS or u-blox ubx
6
+
7
+ $stderr.puts <<__STRING__
8
+ Usage: #{__FILE__} GPS_file1 GPS_file2 ...
9
+ As GPS_file, rinex_nav(*.YYn), rinex_obs(*.YYo), and ubx(*.ubx) format are currently supported.
10
+ File format is automatically determined based on its extention described in above parentheses.
11
+ Note: YY = last two digit of year.
12
+ __STRING__
13
+
14
+ options = {}
15
+
16
+ # check options
17
+ ARGV.reject!{|arg|
18
+ next false unless arg =~ /^--([^=]+)=?/
19
+ options[$1.to_sym] = $'
20
+ true
21
+ }
22
+
23
+ # Check file existence
24
+ ARGV.each{|arg|
25
+ raise "File not found: #{arg}" unless File::exist?(arg)
26
+ }
27
+
28
+ rcv = GPS_PVT::Receiver::new(options)
29
+
30
+ puts GPS_PVT::Receiver::header
31
+
32
+ # parse RINEX NAV
33
+ ARGV.reject!{|arg|
34
+ next false unless arg =~ /\.\d{2}n$/
35
+ rcv.parse_rinex_nav(arg)
36
+ }
37
+
38
+ # other files
39
+ ARGV.each{|arg|
40
+ case arg
41
+ when /\.ubx$/
42
+ rcv.parse_ubx(arg)
43
+ when /\.\d{2}o$/
44
+ rcv.parse_rinex_obs(arg)
45
+ end
46
+ }