ogn_client-ruby 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -3
- data/CHANGELOG.md +16 -0
- data/README.md +39 -19
- data/lib/ogn_client.rb +3 -2
- data/lib/ogn_client/message.rb +5 -3
- data/lib/ogn_client/messages/receiver_beacon.rb +21 -0
- data/lib/ogn_client/messages/receiver_status.rb +161 -0
- data/lib/ogn_client/messages/{sender.rb → sender_beacon.rb} +21 -21
- data/lib/ogn_client/version.rb +1 -1
- data/tasks/test.rake +42 -13
- metadata +6 -5
- data/lib/ogn_client/messages/receiver.rb +0 -159
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cac6cf6364fa78e20954308deb6258c8ae789661
|
4
|
+
data.tar.gz: b5c802a8c11071a73549abaa50d7cf8e7cf03128
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e60aabf34f6e4967e9423b732d676149fda8fb1f3470d3e02b37f791b4804ddc637dfb09538dcac92ecc4687672eb4c2cf84f802d694a031f59734394ac448b1
|
7
|
+
data.tar.gz: 1e6903681dcb6c26c5f3612f304914b867113b7598f7816f5d907c46cbade23d8392bc0a31fa43a4d61102fa08fe402e1a818bb0e9aaed06ac33c8c9d12c67df
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.4.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 0.2.0
|
2
|
+
|
3
|
+
* support for receiver versions <= 0.2.6
|
4
|
+
* renamed `Sender` to `SenderBeacon`
|
5
|
+
* devided `Receiver` to `ReceiverBeacon` and `ReceiverStatus`
|
6
|
+
* `ReceiverStatus` is *not* available for receiver versions < 0.2.6
|
7
|
+
|
8
|
+
## 0.1.3
|
9
|
+
|
10
|
+
* renamed sender and receiver attributes
|
11
|
+
* `Sender#signal` -> `Sender#signal_quality`
|
12
|
+
* `Sender#power` -> `Sender#signal_power`
|
13
|
+
* `Receiver#signal` -> `Receiver#signal_quality`
|
14
|
+
* `Receiver#senders_signal` -> `Receiver#senders_signal_quality`
|
15
|
+
* `Receiver#good_senders_signal` -> `Receiver#good_senders_signal_quality`
|
16
|
+
|
1
17
|
## 0.1.2
|
2
18
|
|
3
19
|
* support for receiver versions <= 0.2.5
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
[![Continuous Integration](https://img.shields.io/travis/svoop/ogn_client-ruby/master.svg?style=flat)](https://travis-ci.org/svoop/ogn_client-ruby)
|
3
3
|
[![Code Climate](https://img.shields.io/codeclimate/github/svoop/ogn_client-ruby.svg?style=flat)](https://codeclimate.com/github/svoop/ogn_client-ruby)
|
4
4
|
[![Gitter](https://img.shields.io/gitter/room/svoop/ogn_client-ruby.svg?style=flat)](https://gitter.im/svoop/ogn_client-ruby)
|
5
|
-
[![Donorbox](https://img.shields.io/badge/donate-
|
5
|
+
[![Donorbox](https://img.shields.io/badge/donate-on_donorbox-yellow.svg)](https://donorbox.org/bitcetera-ogn_client-ruby)
|
6
6
|
|
7
7
|
# ogn_client-ruby
|
8
8
|
|
@@ -36,8 +36,12 @@ Or install it yourself with:
|
|
36
36
|
Choose a [valid callsign](http://www.aprs-is.net/Connecting.aspx#loginrules) and [appropriate filters](http://www.aprs-is.net/javAPRSFilter.aspx), then start listening to the broadcasted raw messages:
|
37
37
|
|
38
38
|
```ruby
|
39
|
-
|
40
|
-
|
39
|
+
require 'ogn_client'
|
40
|
+
|
41
|
+
OGNClient::APRS.start(callsign: "ROCT#{rand(1000)}", filter: 'r/47/2/500') do |aprs|
|
42
|
+
while raw = aprs.gets
|
43
|
+
puts raw # do more interesting stuff here
|
44
|
+
end
|
41
45
|
end
|
42
46
|
```
|
43
47
|
|
@@ -53,11 +57,13 @@ OGNClient::Message.parse(aprs.gets)
|
|
53
57
|
|
54
58
|
:point_up: Raw APRS messages as returned by `aprs.gets` are "ASCII-8BIT" encoded and may contain tailing whitespace. The parser removes this whitespace and converts the string to "UTF-8".
|
55
59
|
|
56
|
-
The factory method `OGNClient::Message.parse` will return one an instance of `OGNClient::Sender`, `OGNClient::Receiver`, `OGNClient::
|
60
|
+
The factory method `OGNClient::Message.parse` will return one an instance of `OGNClient::Sender`, `OGNClient::Receiver`, `OGNClient::Comment` or [raise an error](#errors). When this happens, either the message is crippled, the [OGN](http://glidernet.org) specifications have changed or you have found a bug in the parser code.
|
61
|
+
|
62
|
+
In production, you may want to rescue from these errors and ignore the message. You should, however, log the offending messages messages, [file a bug](#community-support) and replay them once the bug has been fixed.
|
57
63
|
|
58
|
-
#### OGNClient::
|
64
|
+
#### OGNClient::SenderBeacon
|
59
65
|
|
60
|
-
|
66
|
+
Sender beacons are usually coming from aircraft equipped with [FLARM](https://flarm.com) (anti-collision warning system) or similar devices which broadcast position data as RF beacons.
|
61
67
|
|
62
68
|
The data is converted into the metric system since [OGN](http://glidernet.org) is primarily made for gliders which mostly use the metric system for ground speed, climb rate and so forth.
|
63
69
|
|
@@ -78,19 +84,19 @@ Attributes:
|
|
78
84
|
* **flight_level** - 100 feet QNE
|
79
85
|
* **climb_rate** - meters per second
|
80
86
|
* **turn_rate** - revolutions per minute
|
81
|
-
* **
|
82
|
-
* **
|
87
|
+
* **signal_power** - power ratio in dBm
|
88
|
+
* **signal_quality** - signal to noise ratio in decibel
|
83
89
|
* **errors** - number of CRC errors
|
84
90
|
* **frequency_offset** - kilohertz
|
85
91
|
* **gps_accuracy** - array [vertical meters, horizontal meters]
|
86
|
-
* **flarm_software_version** - version as
|
87
|
-
* **flarm_hardware_version** - version as
|
92
|
+
* **flarm_software_version** - version as "major.minor"
|
93
|
+
* **flarm_hardware_version** - version as integer
|
88
94
|
* **flarm_id** - FLARM device ID
|
89
95
|
* **proximity** - array of FLARM device ID tails
|
90
96
|
|
91
|
-
#### OGNClient::
|
97
|
+
#### OGNClient::ReceiverBeacon
|
92
98
|
|
93
|
-
Receivers are little RF boxes which pick up the RF beacons from aircraft and relay them to the OGN servers as messages. They send their own
|
99
|
+
Receivers are little RF boxes which pick up the RF beacons from aircraft and relay them to the OGN servers as messages. They send their own beacons on a regular basis.
|
94
100
|
|
95
101
|
Attributes:
|
96
102
|
* **callsign** - origin callsign
|
@@ -101,7 +107,18 @@ Attributes:
|
|
101
107
|
* **altitude** - WG84 meters above mean sea level QNH
|
102
108
|
* **heading** - degrees from 1 to 360
|
103
109
|
* **ground_speed** - kilometers per hour
|
104
|
-
|
110
|
+
|
111
|
+
Please note: These receiver beacons contained status information up until version 0.2.5.
|
112
|
+
|
113
|
+
#### OGNClient::ReceiverStatus
|
114
|
+
|
115
|
+
Receivers of version 0.2.6 and higher send status messages on a regular basis:
|
116
|
+
|
117
|
+
Attributes:
|
118
|
+
* **callsign** - origin callsign
|
119
|
+
* **receiver** - receiver callsign
|
120
|
+
* **time** - zulu/UTC time with date
|
121
|
+
* **version** - software version as "major.minor.patch"
|
105
122
|
* **platform** - e.g. :arm
|
106
123
|
* **cpu_load** - as reported by "uptime"
|
107
124
|
* **cpu_temperature** - in degrees celsius
|
@@ -116,10 +133,10 @@ Attributes:
|
|
116
133
|
* **senders** - number of senders received within the last hour
|
117
134
|
* **visible_senders** - number of visible senders withint the last hour
|
118
135
|
* **invisible_senders** - number of invisible senders ("no-track" on device or "invisible" in database)
|
119
|
-
* **
|
120
|
-
* **
|
136
|
+
* **signal_quality** - signal-to-noise ratio in decibel
|
137
|
+
* **senders_signal_quality** - average signal-to-noise ratio across all senders
|
121
138
|
* **senders_messages** - number of messages analyzed to calculate the above
|
122
|
-
* **
|
139
|
+
* **good_senders_signal_quality** - average signal-to-noise ratio in decibel of good senders (transmitting properly) within the last 24 hours
|
123
140
|
* **good_and_bad_senders** - number of good and bad senders within the last 24 hours
|
124
141
|
* **good_senders** - number of good senders (transmitting properly) within the last 24 hours
|
125
142
|
* **bad_senders** - number of bad senders (not transmitting properly) within the last 24 hours
|
@@ -142,9 +159,10 @@ They all inherit from `OGNClient::Error`. An fault-tolerant subscription could t
|
|
142
159
|
|
143
160
|
```ruby
|
144
161
|
require 'ogn_client'
|
162
|
+
require 'logger'
|
145
163
|
|
146
164
|
logger = Logger.new('/tmp/ogn_client.log')
|
147
|
-
options = { callsign:
|
165
|
+
options = { callsign: "ROCT#{rand(1000)}", filter: 'r/47/2/500' }
|
148
166
|
loop do
|
149
167
|
OGNClient::APRS.start(options) do |aprs|
|
150
168
|
while raw = aprs.gets
|
@@ -160,13 +178,15 @@ loop do
|
|
160
178
|
end
|
161
179
|
```
|
162
180
|
|
181
|
+
:point_up: Receiver versions ("major.minor.patch") are will only raise an error when the offending version has a higher major or minor version digit. Patch level differences will only trigger a warning.
|
182
|
+
|
163
183
|
## Community Support
|
164
184
|
|
165
185
|
* Look for developers and users on [Gitter](https://gitter.im/svoop/ogn_client-ruby).
|
166
186
|
* Ask your questions on [Stackoverflow](https://stackoverflow.com/questions/ask?tags=ogn_client-ruby,ruby,gem).
|
167
187
|
* Annotated source code on [omniref](https://www.omniref.com/repositories/svoop/ogn_client-ruby)
|
168
188
|
* Bug reports, feature and pull requests are welcome on [GitHub](https://github.com/svoop/ogn_client-ruby).
|
169
|
-
* [Donations
|
189
|
+
* [Donations are welcome as well](https://donorbox.org/bitcetera-ogn_client-ruby)l. If you prefer to sponsor a feature, please create an issue on [GitHub](https://github.com/svoop/ogn_client-ruby) first and state your intentions.
|
170
190
|
|
171
191
|
## Development
|
172
192
|
|
@@ -178,7 +198,7 @@ Check out the repository, install the dependencies and run the test suite:
|
|
178
198
|
$ bin/setup
|
179
199
|
$ rake
|
180
200
|
|
181
|
-
If you are on
|
201
|
+
If you are on macOS, *guard*, *guard-minitest* and *minitest-osx* are also installed and therefore you get the test results as notifications by running a guard watchdog with:
|
182
202
|
|
183
203
|
$ guard
|
184
204
|
|
data/lib/ogn_client.rb
CHANGED
@@ -5,5 +5,6 @@ require_relative 'ogn_client/errors'
|
|
5
5
|
require_relative 'ogn_client/aprs'
|
6
6
|
require_relative 'ogn_client/message'
|
7
7
|
require_relative 'ogn_client/messages/comment'
|
8
|
-
require_relative 'ogn_client/messages/
|
9
|
-
require_relative 'ogn_client/messages/
|
8
|
+
require_relative 'ogn_client/messages/receiver_beacon'
|
9
|
+
require_relative 'ogn_client/messages/receiver_status'
|
10
|
+
require_relative 'ogn_client/messages/sender_beacon'
|
data/lib/ogn_client/message.rb
CHANGED
@@ -27,8 +27,9 @@ module OGNClient
|
|
27
27
|
(?<time>\d{6})h
|
28
28
|
(?:(?<latitude>\d{4}\.\d{2}[NS]).(?<longitude>\d{5}\.\d{2}[EW]).)?
|
29
29
|
(?:(?<heading>\d{3})/(?<ground_speed>\d{3}))?
|
30
|
-
(?:/A=(?<altitude>\d{6}))?\s
|
30
|
+
(?:/A=(?<altitude>\d{6}))?\s*
|
31
31
|
(?:!W((?<latitude_enhancement>\d)(?<longitude_enhancement>\d))!)?
|
32
|
+
(?:\s|$)
|
32
33
|
)x
|
33
34
|
|
34
35
|
attr_reader :raw
|
@@ -44,8 +45,9 @@ module OGNClient
|
|
44
45
|
def self.parse(raw)
|
45
46
|
fail(OGNClient::MessageError, "raw message must be String but is #{raw.class}") unless raw.is_a? String
|
46
47
|
raw = raw.chomp.force_encoding('ASCII-8BIT').encode('UTF-8')
|
47
|
-
OGNClient::
|
48
|
-
OGNClient::
|
48
|
+
OGNClient::SenderBeacon.new.send(:parse, raw) ||
|
49
|
+
OGNClient::ReceiverStatus.new.send(:parse, raw) ||
|
50
|
+
OGNClient::ReceiverBeacon.new.send(:parse, raw) ||
|
49
51
|
OGNClient::Comment.new.send(:parse, raw) ||
|
50
52
|
fail(OGNClient::MessageError, "message payload parsing failed: `#{raw}'")
|
51
53
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module OGNClient
|
2
|
+
|
3
|
+
class ReceiverBeacon < Message
|
4
|
+
|
5
|
+
RECEIVER_BEACON_PATTERN = %r(^
|
6
|
+
.+?>APRS,(?:.+?,){1,2}
|
7
|
+
(?:GLIDERN|GIGA|NYMSERV)\d*:
|
8
|
+
)x
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def parse(raw)
|
13
|
+
raw.match RECEIVER_BEACON_PATTERN do
|
14
|
+
super unless @raw
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module OGNClient
|
2
|
+
|
3
|
+
class ReceiverStatus < Message
|
4
|
+
|
5
|
+
RECEIVER_STATUS_PATTERN = %r(
|
6
|
+
(?:
|
7
|
+
v(?<version>\d+\.\d+\.\d+)
|
8
|
+
(?:\.(?<platform>.+?))?
|
9
|
+
\s)?
|
10
|
+
CPU:(?<cpu_load>[\d.]+)\s
|
11
|
+
RAM:(?<ram_free>[\d.]+)/(?<ram_total>[\d.]+)MB\s
|
12
|
+
NTP:(?<ntp_offset>[\d.]+)ms/(?<ntp_correction>[+-][\d.]+)ppm\s
|
13
|
+
(?:(?<voltage>[\d.]+)V\s)?
|
14
|
+
(?:(?<amperage>[\d.]+)A\s)?
|
15
|
+
(?:(?<cpu_temperature>[+-][\d.]+)C\s*)?
|
16
|
+
(?:(?<visible_senders>\d+)/(?<senders>\d+)Acfts\[1h\]\s*)?
|
17
|
+
(?:RF:
|
18
|
+
(?:
|
19
|
+
(?<rf_correction_manual>[+-][\d]+)
|
20
|
+
(?<rf_correction_automatic>[+-][\d.]+)ppm/
|
21
|
+
)?
|
22
|
+
(?<signal_quality>[+-][\d.]+)dB
|
23
|
+
(?:/(?<senders_signal_quality>[+-][\d.]+)dB@10km\[(?<senders_messages>\d+)\])?
|
24
|
+
(?:/(?<good_senders_signal_quality>[+-][\d.]+)dB@10km\[(?<good_senders>\d+)/(?<good_and_bad_senders>\d+)\])?
|
25
|
+
)?
|
26
|
+
$)x
|
27
|
+
|
28
|
+
NO_WARN_RECEIVER_VERSIONS = Gem::Dependency.new('', '>= 0.2.6', '< 0.2.8')
|
29
|
+
NO_FAIL_RECEIVER_VERSIONS = Gem::Dependency.new('', '< 0.3')
|
30
|
+
|
31
|
+
attr_reader :version # software version as "major.minor.patch"
|
32
|
+
attr_reader :platform # e.g. "ARM"
|
33
|
+
attr_reader :cpu_load # as reported by "uptime"
|
34
|
+
attr_reader :cpu_temperature # degrees celsius
|
35
|
+
attr_reader :ram_free # megabytes
|
36
|
+
attr_reader :ram_total # megabytes
|
37
|
+
attr_reader :ntp_offset # milliseconds
|
38
|
+
attr_reader :ntp_correction # parts-per-million
|
39
|
+
attr_reader :voltage # board voltage in V
|
40
|
+
attr_reader :amperage # board amperage in A
|
41
|
+
attr_reader :rf_correction_manual # as per configuration
|
42
|
+
attr_reader :rf_correction_automatic # based on GSM
|
43
|
+
attr_reader :senders # number of senders within the last hour
|
44
|
+
attr_reader :visible_senders # number of visible senders within the last hour
|
45
|
+
attr_reader :signal_quality # signal-to-noise ratio in decibel
|
46
|
+
attr_reader :senders_signal_quality # average signal-to-noise ratio in decibel across all senders
|
47
|
+
attr_reader :senders_messages # number of messages analyzed to calculate the above
|
48
|
+
attr_reader :good_senders_signal_quality # average signal-to-noise ratio in decibel of good senders (transmitting properly) within the last 24 hours
|
49
|
+
attr_reader :good_and_bad_senders # number of good and bad senders within the last 24 hours
|
50
|
+
attr_reader :good_senders # number of good senders (transmitting properly) within the last 24 hours
|
51
|
+
|
52
|
+
def invisible_senders
|
53
|
+
senders - visible_senders
|
54
|
+
rescue
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def bad_senders
|
59
|
+
good_and_bad_senders - good_senders
|
60
|
+
rescue
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def parse(raw)
|
67
|
+
raw.match RECEIVER_STATUS_PATTERN do |match|
|
68
|
+
super unless @raw
|
69
|
+
%i(version platform cpu_load cpu_temperature ram_free ram_total ntp_offset ntp_correction voltage amperage rf_correction_manual rf_correction_automatic senders visible_senders signal_quality senders_signal_quality senders_messages good_senders_signal_quality good_and_bad_senders good_senders).each do |attr|
|
70
|
+
send("#{attr}=", match[attr]) if match[attr]
|
71
|
+
end
|
72
|
+
self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def version=(raw)
|
77
|
+
@version = raw
|
78
|
+
fail(OGNClient::ReceiverError, "receiver version `#{@version}'") unless NO_FAIL_RECEIVER_VERSIONS.match?('', @version)
|
79
|
+
warn("WARNING: receiver version `#{@version}'") unless NO_WARN_RECEIVER_VERSIONS.match?('', @version)
|
80
|
+
@version
|
81
|
+
end
|
82
|
+
|
83
|
+
def platform=(raw)
|
84
|
+
@platform = raw.to_sym.downcase
|
85
|
+
end
|
86
|
+
|
87
|
+
def cpu_load=(raw)
|
88
|
+
@cpu_load = raw.to_f.round(2)
|
89
|
+
end
|
90
|
+
|
91
|
+
def cpu_temperature=(raw)
|
92
|
+
@cpu_temperature = raw.to_f.round(2)
|
93
|
+
end
|
94
|
+
|
95
|
+
def ram_free=(raw)
|
96
|
+
@ram_free = raw.to_f.round(2)
|
97
|
+
end
|
98
|
+
|
99
|
+
def ram_total=(raw)
|
100
|
+
@ram_total = raw.to_f.round(2)
|
101
|
+
end
|
102
|
+
|
103
|
+
def ntp_offset=(raw)
|
104
|
+
@ntp_offset = raw.to_f.round(2)
|
105
|
+
end
|
106
|
+
|
107
|
+
def ntp_correction=(raw)
|
108
|
+
@ntp_correction = raw.to_f.round(2)
|
109
|
+
end
|
110
|
+
|
111
|
+
def voltage=(raw)
|
112
|
+
@voltage = raw.to_f.round(3)
|
113
|
+
end
|
114
|
+
|
115
|
+
def amperage=(raw)
|
116
|
+
@amperage = raw.to_f.round(3)
|
117
|
+
end
|
118
|
+
|
119
|
+
def rf_correction_manual=(raw)
|
120
|
+
@rf_correction_manual = raw.to_i
|
121
|
+
end
|
122
|
+
|
123
|
+
def rf_correction_automatic=(raw)
|
124
|
+
@rf_correction_automatic = raw.to_f.round(1)
|
125
|
+
end
|
126
|
+
|
127
|
+
def senders=(raw)
|
128
|
+
@senders = raw.to_i
|
129
|
+
end
|
130
|
+
|
131
|
+
def visible_senders=(raw)
|
132
|
+
@visible_senders = raw.to_i
|
133
|
+
end
|
134
|
+
|
135
|
+
def signal_quality=(raw)
|
136
|
+
@signal_quality = raw.to_f.round(3)
|
137
|
+
end
|
138
|
+
|
139
|
+
def senders_signal_quality=(raw)
|
140
|
+
@senders_signal_quality = raw.to_f.round(3)
|
141
|
+
end
|
142
|
+
|
143
|
+
def senders_messages=(raw)
|
144
|
+
@senders_messages = raw.to_i
|
145
|
+
end
|
146
|
+
|
147
|
+
def good_senders_signal_quality=(raw)
|
148
|
+
@good_senders_signal_quality = raw.to_f.round(3)
|
149
|
+
end
|
150
|
+
|
151
|
+
def good_and_bad_senders=(raw)
|
152
|
+
@good_and_bad_senders = raw.to_i
|
153
|
+
end
|
154
|
+
|
155
|
+
def good_senders=(raw)
|
156
|
+
@good_senders = raw.to_i
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module OGNClient
|
2
2
|
|
3
|
-
class
|
3
|
+
class SenderBeacon < Message
|
4
4
|
|
5
|
-
|
6
|
-
id(?<details>\w{2})(?<id>\w+?)\s
|
7
|
-
(?<climb_rate>[+-]\d+?)fpm\s
|
8
|
-
(?<turn_rate>[+-][\d.]+?)rot\s
|
5
|
+
SENDER_BEACON_PATTERN = %r(
|
6
|
+
id(?<details>\w{2})(?<id>\w+?)\s?
|
7
|
+
(?:(?<climb_rate>[+-]\d+?)fpm\s)?
|
8
|
+
(?:(?<turn_rate>[+-][\d.]+?)rot\s)?
|
9
9
|
(?:FL(?<flight_level>[\d.]+)\s)?
|
10
|
-
(?<
|
11
|
-
(?<errors>\d+)e\s
|
12
|
-
(?<frequency_offset>[+-][\d.]+?)kHz\s?
|
10
|
+
(?:(?<signal_quality>[\d.]+?)dB\s)?
|
11
|
+
(?:(?<errors>\d+)e\s)?
|
12
|
+
(?:(?<frequency_offset>[+-][\d.]+?)kHz\s?)?
|
13
13
|
(?:gps(?<gps_accuracy>\d+x\d+)\s?)?
|
14
14
|
(?:s(?<flarm_software_version>[\d.]+)\s?)?
|
15
15
|
(?:h(?<flarm_hardware_version>[\dA-F]{2})\s?)?
|
16
16
|
(?:r(?<flarm_id>[\dA-F]+)\s?)?
|
17
|
-
(?:(?<
|
17
|
+
(?:(?<signal_power>[+-][\d.]+)dBm\s?)?
|
18
18
|
(?:hear(?<proximity>.+))?
|
19
19
|
$)x
|
20
20
|
|
@@ -50,22 +50,22 @@ module OGNClient
|
|
50
50
|
attr_reader :flight_level # 100 feet QNE
|
51
51
|
attr_reader :climb_rate # meters per second
|
52
52
|
attr_reader :turn_rate # revolutions per minute
|
53
|
-
attr_reader :
|
54
|
-
attr_reader :
|
53
|
+
attr_reader :signal_power # power ratio in dBm
|
54
|
+
attr_reader :signal_quality # signal-to-noise ratio in decibel
|
55
55
|
attr_reader :errors # number of CRC errors
|
56
56
|
attr_reader :frequency_offset # kilohertz
|
57
57
|
attr_reader :gps_accuracy # array [vertical meters, horizontal meters]
|
58
|
-
attr_reader :flarm_software_version # version as
|
59
|
-
attr_reader :flarm_hardware_version # version as
|
58
|
+
attr_reader :flarm_software_version # version as "major.minor"
|
59
|
+
attr_reader :flarm_hardware_version # version as "major"
|
60
60
|
attr_reader :flarm_id # FLARM device ID
|
61
61
|
attr_reader :proximity # array of FLARM device ID tails
|
62
62
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def parse(raw)
|
66
|
-
raw.match
|
66
|
+
raw.match SENDER_BEACON_PATTERN do |match|
|
67
67
|
super unless @raw
|
68
|
-
%i(details id flight_level climb_rate turn_rate
|
68
|
+
%i(details id flight_level climb_rate turn_rate signal_power signal_quality errors frequency_offset gps_accuracy flarm_software_version flarm_hardware_version flarm_id proximity).each do |attr|
|
69
69
|
send("#{attr}=", match[attr]) if match[attr]
|
70
70
|
end
|
71
71
|
# NOTE: [@svoop] [ruby21] workaround necessary until support for ruby21 is removed
|
@@ -99,12 +99,12 @@ module OGNClient
|
|
99
99
|
@turn_rate = (raw.to_i / 4.0).round(1)
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
103
|
-
@
|
102
|
+
def signal_power=(raw)
|
103
|
+
@signal_power = raw.to_f.round(3)
|
104
104
|
end
|
105
105
|
|
106
|
-
def
|
107
|
-
@
|
106
|
+
def signal_quality=(raw)
|
107
|
+
@signal_quality = raw.to_f.round(1)
|
108
108
|
end
|
109
109
|
|
110
110
|
def errors=(raw)
|
@@ -120,11 +120,11 @@ module OGNClient
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def flarm_software_version=(raw)
|
123
|
-
@flarm_software_version =
|
123
|
+
@flarm_software_version = raw
|
124
124
|
end
|
125
125
|
|
126
126
|
def flarm_hardware_version=(raw)
|
127
|
-
@flarm_hardware_version =
|
127
|
+
@flarm_hardware_version = raw.to_i(16)
|
128
128
|
end
|
129
129
|
|
130
130
|
def flarm_id=(raw)
|
data/lib/ogn_client/version.rb
CHANGED
data/tasks/test.rake
CHANGED
@@ -1,28 +1,57 @@
|
|
1
1
|
require 'ogn_client'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'fileutils'
|
2
4
|
|
3
5
|
namespace :test do
|
4
6
|
|
5
|
-
desc "
|
6
|
-
task :
|
7
|
-
|
8
|
-
loop do
|
9
|
-
print aprs.gets
|
10
|
-
end
|
11
|
-
end
|
7
|
+
desc "Reset the test suite"
|
8
|
+
task :reset do
|
9
|
+
FileUtils.rm_f('spec/fixtures/messages.txt')
|
12
10
|
end
|
13
11
|
|
14
|
-
desc "
|
15
|
-
task :
|
16
|
-
callsign = "ROCT
|
12
|
+
desc "Feed live APRS messages to the parser"
|
13
|
+
task :live do
|
14
|
+
callsign = "ROCT#{rand(1000)}"
|
15
|
+
counter = 0
|
17
16
|
loop do
|
18
17
|
OGNClient::APRS.start(callsign: callsign) do |aprs|
|
19
18
|
while raw = aprs.gets do
|
20
|
-
print '.'
|
21
19
|
begin
|
22
20
|
OGNClient::Message.parse raw
|
23
21
|
rescue OGNClient::Error => error
|
24
|
-
puts
|
25
|
-
|
22
|
+
puts "ERROR: #{error.message}"
|
23
|
+
end
|
24
|
+
counter += 1
|
25
|
+
puts "INFO: captured #{counter} messages" if counter % 1000 == 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
namespace :live do
|
32
|
+
desc "Record live APRS messages"
|
33
|
+
task :record do
|
34
|
+
callsign = "ROCT#{rand(1000)}"
|
35
|
+
counter = 0
|
36
|
+
File.open("#{Dir.tmpdir}/ogn_messages.txt", "w") do |file|
|
37
|
+
OGNClient::APRS.start(callsign: callsign) do |aprs|
|
38
|
+
while raw = aprs.gets do
|
39
|
+
file.print raw
|
40
|
+
counter += 1
|
41
|
+
puts "INFO: captured #{counter} messages" if counter % 1000 == 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Feed recorded APRS messages to the parser"
|
48
|
+
task :replay do
|
49
|
+
File.open("#{Dir.tmpdir}/ogn_messages.txt") do |file|
|
50
|
+
while raw = file.gets do
|
51
|
+
begin
|
52
|
+
OGNClient::Message.parse raw
|
53
|
+
rescue OGNClient::Error => error
|
54
|
+
puts "ERROR: #{error.message}"
|
26
55
|
end
|
27
56
|
end
|
28
57
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ogn_client-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Schwyn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -121,8 +121,9 @@ files:
|
|
121
121
|
- lib/ogn_client/errors.rb
|
122
122
|
- lib/ogn_client/message.rb
|
123
123
|
- lib/ogn_client/messages/comment.rb
|
124
|
-
- lib/ogn_client/messages/
|
125
|
-
- lib/ogn_client/messages/
|
124
|
+
- lib/ogn_client/messages/receiver_beacon.rb
|
125
|
+
- lib/ogn_client/messages/receiver_status.rb
|
126
|
+
- lib/ogn_client/messages/sender_beacon.rb
|
126
127
|
- lib/ogn_client/version.rb
|
127
128
|
- ogn_client-ruby.gemspec
|
128
129
|
- tasks/test.rake
|
@@ -146,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
147
|
version: '0'
|
147
148
|
requirements: []
|
148
149
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.
|
150
|
+
rubygems_version: 2.6.11
|
150
151
|
signing_key:
|
151
152
|
specification_version: 4
|
152
153
|
summary: Subscriber and parser for APRS messages from OGN
|
@@ -1,159 +0,0 @@
|
|
1
|
-
module OGNClient
|
2
|
-
|
3
|
-
class Receiver < Message
|
4
|
-
|
5
|
-
RECEIVER_PATTERN = %r(
|
6
|
-
(?:
|
7
|
-
v(?<version>\d+\.\d+\.\d+)
|
8
|
-
(?:\.(?<platform>.+?))?
|
9
|
-
\s)?
|
10
|
-
CPU:(?<cpu_load>[\d.]+)\s
|
11
|
-
RAM:(?<ram_free>[\d.]+)/(?<ram_total>[\d.]+)MB\s
|
12
|
-
NTP:(?<ntp_offset>[\d.]+)ms/(?<ntp_correction>[+-][\d.]+)ppm\s
|
13
|
-
(?:(?<voltage>[\d.]+)V\s)?
|
14
|
-
(?:(?<amperage>[\d.]+)A\s)?
|
15
|
-
(?:(?<cpu_temperature>[+-][\d.]+)C\s*)?
|
16
|
-
(?:(?<visible_senders>\d+)/(?<senders>\d+)Acfts\[1h\]\s*)?
|
17
|
-
(?:RF:
|
18
|
-
(?:
|
19
|
-
(?<rf_correction_manual>[+-][\d]+)
|
20
|
-
(?<rf_correction_automatic>[+-][\d.]+)ppm/
|
21
|
-
)?
|
22
|
-
(?<signal>[+-][\d.]+)dB
|
23
|
-
(?:/(?<senders_signal>[+-][\d.]+)dB@10km\[(?<senders_messages>\d+)\])?
|
24
|
-
(?:/(?<good_senders_signal>[+-][\d.]+)dB@10km\[(?<good_senders>\d+)/(?<good_and_bad_senders>\d+)\])?
|
25
|
-
)?
|
26
|
-
$)x
|
27
|
-
|
28
|
-
SUPPORTED_RECEIVER_VERSION = Gem::Version.new('0.2.5')
|
29
|
-
|
30
|
-
attr_reader :version # software version as #<Gem::Version "major.minor.patch">
|
31
|
-
attr_reader :platform # e.g. "ARM"
|
32
|
-
attr_reader :cpu_load # as reported by "uptime"
|
33
|
-
attr_reader :cpu_temperature # degrees celsius
|
34
|
-
attr_reader :ram_free # megabytes
|
35
|
-
attr_reader :ram_total # megabytes
|
36
|
-
attr_reader :ntp_offset # milliseconds
|
37
|
-
attr_reader :ntp_correction # parts-per-million
|
38
|
-
attr_reader :voltage # board voltage in V
|
39
|
-
attr_reader :amperage # board amperage in A
|
40
|
-
attr_reader :rf_correction_manual # as per configuration
|
41
|
-
attr_reader :rf_correction_automatic # based on GSM
|
42
|
-
attr_reader :senders # number of senders within the last hour
|
43
|
-
attr_reader :visible_senders # number of visible senders within the last hour
|
44
|
-
attr_reader :signal # signal-to-noise ratio in decibel
|
45
|
-
attr_reader :senders_signal # average signal-to-noise ratio in decibel across all senders
|
46
|
-
attr_reader :senders_messages # number of messages analyzed to calculate the above
|
47
|
-
attr_reader :good_senders_signal # average signal-to-noise ratio in decibel of good senders (transmitting properly) within the last 24 hours
|
48
|
-
attr_reader :good_and_bad_senders # number of good and bad senders within the last 24 hours
|
49
|
-
attr_reader :good_senders # number of good senders (transmitting properly) within the last 24 hours
|
50
|
-
|
51
|
-
def invisible_senders
|
52
|
-
senders - visible_senders
|
53
|
-
rescue
|
54
|
-
nil
|
55
|
-
end
|
56
|
-
|
57
|
-
def bad_senders
|
58
|
-
good_and_bad_senders - good_senders
|
59
|
-
rescue
|
60
|
-
nil
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def parse(raw)
|
66
|
-
raw.match RECEIVER_PATTERN do |match|
|
67
|
-
super unless @raw
|
68
|
-
%i(version platform cpu_load cpu_temperature ram_free ram_total ntp_offset ntp_correction voltage amperage rf_correction_manual rf_correction_automatic senders visible_senders signal senders_signal senders_messages good_senders_signal good_and_bad_senders good_senders).each do |attr|
|
69
|
-
send("#{attr}=", match[attr]) if match[attr]
|
70
|
-
end
|
71
|
-
self
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def version=(raw)
|
76
|
-
@version = Gem::Version.new(raw)
|
77
|
-
fail(OGNClient::ReceiverError, "unsupported receiver version: `#{@raw}'") if @version > SUPPORTED_RECEIVER_VERSION
|
78
|
-
@version
|
79
|
-
end
|
80
|
-
|
81
|
-
def platform=(raw)
|
82
|
-
@platform = raw.to_sym.downcase
|
83
|
-
end
|
84
|
-
|
85
|
-
def cpu_load=(raw)
|
86
|
-
@cpu_load = raw.to_f.round(2)
|
87
|
-
end
|
88
|
-
|
89
|
-
def cpu_temperature=(raw)
|
90
|
-
@cpu_temperature = raw.to_f.round(2)
|
91
|
-
end
|
92
|
-
|
93
|
-
def ram_free=(raw)
|
94
|
-
@ram_free = raw.to_f.round(2)
|
95
|
-
end
|
96
|
-
|
97
|
-
def ram_total=(raw)
|
98
|
-
@ram_total = raw.to_f.round(2)
|
99
|
-
end
|
100
|
-
|
101
|
-
def ntp_offset=(raw)
|
102
|
-
@ntp_offset = raw.to_f.round(2)
|
103
|
-
end
|
104
|
-
|
105
|
-
def ntp_correction=(raw)
|
106
|
-
@ntp_correction = raw.to_f.round(2)
|
107
|
-
end
|
108
|
-
|
109
|
-
def voltage=(raw)
|
110
|
-
@voltage = raw.to_f.round(3)
|
111
|
-
end
|
112
|
-
|
113
|
-
def amperage=(raw)
|
114
|
-
@amperage = raw.to_f.round(3)
|
115
|
-
end
|
116
|
-
|
117
|
-
def rf_correction_manual=(raw)
|
118
|
-
@rf_correction_manual = raw.to_i
|
119
|
-
end
|
120
|
-
|
121
|
-
def rf_correction_automatic=(raw)
|
122
|
-
@rf_correction_automatic = raw.to_f.round(1)
|
123
|
-
end
|
124
|
-
|
125
|
-
def senders=(raw)
|
126
|
-
@senders = raw.to_i
|
127
|
-
end
|
128
|
-
|
129
|
-
def visible_senders=(raw)
|
130
|
-
@visible_senders = raw.to_i
|
131
|
-
end
|
132
|
-
|
133
|
-
def signal=(raw)
|
134
|
-
@signal = raw.to_f.round(3)
|
135
|
-
end
|
136
|
-
|
137
|
-
def senders_signal=(raw)
|
138
|
-
@senders_signal = raw.to_f.round(3)
|
139
|
-
end
|
140
|
-
|
141
|
-
def senders_messages=(raw)
|
142
|
-
@senders_messages = raw.to_i
|
143
|
-
end
|
144
|
-
|
145
|
-
def good_senders_signal=(raw)
|
146
|
-
@good_senders_signal = raw.to_f.round(3)
|
147
|
-
end
|
148
|
-
|
149
|
-
def good_and_bad_senders=(raw)
|
150
|
-
@good_and_bad_senders = raw.to_i
|
151
|
-
end
|
152
|
-
|
153
|
-
def good_senders=(raw)
|
154
|
-
@good_senders = raw.to_i
|
155
|
-
end
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|