ruby-adsb 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 88ac5ff75d15c6af3a3d4ec0c36dc18f6b017cca
4
+ data.tar.gz: 3bd95ab7c548acf40b294edd72e96b3d6e0af95d
5
+ SHA512:
6
+ metadata.gz: 3213d314aa5ec2e8016b2021b0ee6203a857e8143e480ada24bbb8c5de5b1a9d535616aee32b9922bd479c3a8d29a992755149ab29f9f71e32907d9fc6888e8a
7
+ data.tar.gz: d696265f869695a61efecc5c9493158d35797bcbf89575e7dd81e65199c6f97f6780c5919c1a4e36803f5bb35bd46941fc3a899055c62ad37267a19bb9be095b
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby-adsb.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Laurens Boekhorst
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # ADSB
2
+
3
+ Automatic Dependent Surveillance Broadcast is a cooperative surveillance
4
+ technology in which an aircraft determines its position via satellite navigation
5
+ and periodically broadcasts it. This gem decodes automatic dependent
6
+ surveillance broadcasts.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'ruby-adsb', require: 'adsb'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install ruby-adsb
23
+
24
+ ## Usage
25
+
26
+ Create a new message:
27
+
28
+ ```ruby
29
+ message = ADSB::Message.new('8D4840D6202CC371C32CE0576098')
30
+ ```
31
+
32
+ Get the address of the sender:
33
+
34
+ ```ruby
35
+ address = message.address
36
+ ```
37
+
38
+ Get the type of message:
39
+
40
+ ```ruby
41
+ type = message.type
42
+ ```
43
+
44
+ The type of message is either `:identification`, `:position`, or `:velocity`.
45
+
46
+ ### Identification
47
+
48
+ Get the reported identification:
49
+
50
+ ```ruby
51
+ identification = message.identification
52
+ ```
53
+
54
+ ### Position
55
+
56
+ Create a new compact position report from a message of even parity and a message
57
+ of odd parity:
58
+
59
+ ```ruby
60
+ even = ADSB::Message.new('8D40621D58C382D690C8AC2863A7')
61
+ odd = ADSB::Message.new('8D40621D58C386435CC412692AD6')
62
+ report = ADSB::CPR::Report.new(even, odd)
63
+ ```
64
+
65
+ Get the reported altitude:
66
+
67
+ ```ruby
68
+ altitude = report.altitude
69
+ ```
70
+
71
+ Get the reported latitude:
72
+
73
+ ```ruby
74
+ latitude = report.latitude
75
+ ```
76
+
77
+ Get the reported longitude:
78
+
79
+ ```ruby
80
+ longitude = report.longitude
81
+ ```
82
+
83
+ ### Velocity
84
+
85
+ Get the reported heading:
86
+
87
+ ```ruby
88
+ heading = message.heading
89
+ ```
90
+
91
+ Get the reported velocity:
92
+
93
+ ```ruby
94
+ velocity = message.velocity
95
+ ```
96
+
97
+ ## Development
98
+
99
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
100
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
101
+ prompt that will allow you to experiment.
102
+
103
+ To install this gem onto your local machine, run `bundle exec rake install`. To
104
+ release a new version, update the version number in `version.rb`, and then run
105
+ `bundle exec rake release`, which will create a git tag for the version, push
106
+ git commits and tags, and push the `.gem` file to
107
+ [rubygems.org](https://rubygems.org).
108
+
109
+ ## Contributing
110
+
111
+ Bug reports and pull requests are welcome on GitHub at
112
+ https://github.com/lboekhorst/ruby-adsb.
113
+
114
+ ## License
115
+
116
+ The gem is available as open source under the terms of the
117
+ [MIT](http://opensource.org/licenses/MIT) license.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :spec
data/adsb.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'adsb/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby-adsb"
8
+ spec.version = ADSB::VERSION
9
+ spec.authors = ["Laurens Boekhorst"]
10
+ spec.email = ["laurens.boekhorst@gmail.com"]
11
+
12
+ spec.summary = %q{Decode automatic dependent surveillance broadcasts}
13
+ spec.homepage = "https://github.com/lboekhorst/ruby-adsb"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.11"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "minitest", "~> 5.0"
32
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "adsb"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,90 @@
1
+ module ADSB
2
+ module CPR
3
+ class Report
4
+
5
+ # Create a new compact position report.
6
+ #
7
+ # ==== Attributes
8
+ # * +even+ - The position message of even parity
9
+ # * +odd+ - The position message of odd parity
10
+ #
11
+ # ==== Examples
12
+ # even = ADSB::Message.new('8D40621D58C382D690C8AC2863A7')
13
+ # odd = ADSB::Message.new('8D40621D58C382D690C8AC2863A7')
14
+ # report = ADSB::CPR::Report.new(even, odd)
15
+ def initialize even, odd
16
+ @even = even
17
+ @odd = odd
18
+ @parity = even.created_at > odd.created_at ? 0 : 1
19
+ end
20
+
21
+ # Get the reported altitude in feet.
22
+ #
23
+ # ==== Examples
24
+ # even = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
25
+ # odd = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
26
+ # report = CPR::Report.new(even, odd)
27
+ # altitude = report.altitude
28
+ def altitude
29
+ message = @parity.eql?(0) ? @even : @odd
30
+ return message.altitude
31
+ end
32
+
33
+ # Get the reported latitude in decimal degrees.
34
+ #
35
+ # ==== Examples
36
+ # even = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
37
+ # odd = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
38
+ # report = CPR::Report.new(even, odd)
39
+ # latitude = report.latitude
40
+ def latitude
41
+ return @parity.eql?(0) ? even_latitude : odd_latitude
42
+ end
43
+
44
+ # Get the reported longitude in decimal degrees.
45
+ #
46
+ # ==== Examples
47
+ # even = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
48
+ # odd = CPR::Message.new('8D40621D58C382D690C8AC2863A7')
49
+ # report = CPR::Report.new(even, odd)
50
+ # longitude = report.longitude
51
+ def longitude
52
+ ni = n(latitude, @parity)
53
+ m = (@even.longitude * (transition_latitude(latitude) - 1) - @odd.longitude * transition_latitude(latitude) + 0.5).floor
54
+ longitude = @parity.eql?(0) ? 360.0 / ni * (m % ni + @even.longitude) : 360.0 / ni * (m % ni + @odd.longitude)
55
+ return longitude >= 180 ? longitude - 360 : longitude
56
+ end
57
+
58
+ private
59
+
60
+ def even_latitude
61
+ latitude = (latitude_index % 59) + @even.latitude
62
+ latitude = 360.0 / 60 * latitude
63
+ return latitude >= 270 ? latitude - 360 : latitude
64
+ end
65
+
66
+ def latitude_index
67
+ latitude_index = 59 * @even.latitude - 60 * @odd.latitude + 0.5
68
+ return latitude_index.floor
69
+ end
70
+
71
+ def odd_latitude
72
+ latitude = (latitude_index % 59) + @odd.latitude
73
+ latitude = 360.0 / 59 * latitude
74
+ return latitude >= 270 ? latitude - 360 : latitude
75
+ end
76
+
77
+ def n latitude, parity
78
+ transition_latitude = transition_latitude(latitude) - parity
79
+ return transition_latitude > 1 ? transition_latitude : 1
80
+ end
81
+
82
+ def transition_latitude latitude
83
+ a = 1 - Math.cos(Math::PI * 2 / 60)
84
+ b = Math.cos(Math::PI / 180.0 * latitude.abs) ** 2
85
+ transition_latitude = 2 * Math::PI / Math.acos(1 - a / b)
86
+ return transition_latitude.to_i
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,55 @@
1
+ module ADSB
2
+ class Message
3
+ attr_reader :created_at
4
+
5
+ # Create a new message.
6
+ #
7
+ # ==== Attributes
8
+ # * +body+ - The body of the message as a hexadecimal string
9
+ # * +created_at+ - The time at which the message was created
10
+ #
11
+ # ==== Examples
12
+ # message = ADSB::Message.new('8D4840D6202CC371C32CE0576098')
13
+ # message = ADSB::Message.new('8D4840D6202CC371C32CE0576098', Time.now)
14
+ def initialize body, created_at = Time.now
15
+ @body = body.hex.to_s(2)
16
+ @created_at = created_at
17
+ decoder = Kernel.const_get("ADSB::Messages::#{type.to_s.capitalize}")
18
+ extend(decoder)
19
+ end
20
+
21
+ # Get the address of the sender.
22
+ #
23
+ # ==== Examples
24
+ # message = ADSB::Message.new('8D4840D6202CC371C32CE0576098')
25
+ # address = message.address
26
+ def address
27
+ '%02x' % @body[8..31].to_i(2)
28
+ end
29
+
30
+ def data
31
+ @body[32..87]
32
+ end
33
+
34
+ def downlink_format
35
+ @body[0..4].to_i(2)
36
+ end
37
+
38
+ # Get type of message.
39
+ #
40
+ # ==== Examples
41
+ # message = ADSB::Message.new('8D4840D6202CC371C32CE0576098')
42
+ # type = message.type
43
+ def type
44
+ case type_code
45
+ when 1, 2, 3, 4 then :identification
46
+ when 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 then :position
47
+ when 19 then :velocity
48
+ end
49
+ end
50
+
51
+ def type_code
52
+ @body[32..36].to_i(2)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ module ADSB
2
+ module Messages
3
+ module Identification
4
+
5
+ # Get the reported identification.
6
+ #
7
+ # ==== Examples
8
+ # message = ADSB::Message.new('8D4840D6202CC371C32CE0576098')
9
+ # address = message.address
10
+ def identification
11
+ characters = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######'
12
+ data = self.data[8..-1]
13
+ identification = String.new
14
+ (0..48).step(6) { |x| identification += characters[data[x..x + 5].to_i(2)] }
15
+ return identification.gsub(/(_|#)/, String.new)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module ADSB
2
+ module Messages
3
+ module Position
4
+ def altitude
5
+ altitude = @body[40..51]
6
+ resolution = altitude.slice!(7).eql?(0) ? 100 : 25
7
+ altitude = altitude.to_i(2) * resolution - 1000
8
+ end
9
+
10
+ def latitude
11
+ @body[54..70].to_i(2).to_f / 131072
12
+ end
13
+
14
+ def longitude
15
+ @body[71..87].to_i(2).to_f / 131072
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module ADSB
2
+ module Messages
3
+ module Velocity
4
+
5
+ # Get the reported heading.
6
+ #
7
+ # ==== Examples
8
+ # message = ADSB::Message.new('8D485020994409940838175B284F')
9
+ # heading = message.heading
10
+ def heading
11
+ heading = Math.atan2(@west_east_velocity, @south_north_velocity) * 360 / (2 * Math::PI)
12
+ return heading < 0 ? heading + 360 : heading
13
+ end
14
+
15
+ def self.extended message
16
+ west_east_velocity = signed_velocity(message.data[14..23].to_i(2), message.data[13].to_i(2))
17
+ message.instance_variable_set(:@west_east_velocity, west_east_velocity)
18
+ south_north_velocity = signed_velocity(message.data[25..34].to_i(2), message.data[24].to_i(2))
19
+ message.instance_variable_set(:@south_north_velocity, south_north_velocity)
20
+ end
21
+
22
+ # Get the reported velocity.
23
+ #
24
+ # ==== Examples
25
+ # message = ADSB::Message.new('8D485020994409940838175B284F')
26
+ # velocity = message.velocity
27
+ def velocity
28
+ return Math.sqrt(@west_east_velocity ** 2 + @south_north_velocity ** 2)
29
+ end
30
+
31
+ private
32
+
33
+ def self.signed_velocity velocity, velocity_sign
34
+ return velocity_sign.eql?(0) ? velocity - 1 : -1 * (velocity - 1)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module ADSB
2
+ VERSION = "0.1.0"
3
+ end
data/lib/adsb.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "adsb/cpr/report"
2
+ require "adsb/message"
3
+ require "adsb/messages/identification"
4
+ require "adsb/messages/position"
5
+ require "adsb/messages/velocity"
6
+ require "adsb/version"
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-adsb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Laurens Boekhorst
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description:
56
+ email:
57
+ - laurens.boekhorst@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - adsb.gemspec
68
+ - bin/console
69
+ - bin/setup
70
+ - lib/adsb.rb
71
+ - lib/adsb/cpr/report.rb
72
+ - lib/adsb/message.rb
73
+ - lib/adsb/messages/identification.rb
74
+ - lib/adsb/messages/position.rb
75
+ - lib/adsb/messages/velocity.rb
76
+ - lib/adsb/version.rb
77
+ homepage: https://github.com/lboekhorst/ruby-adsb
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ allowed_push_host: https://rubygems.org
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.2.5
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Decode automatic dependent surveillance broadcasts
102
+ test_files: []