ogn_client-ruby 0.1.2 → 0.2.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 +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
|
[](https://travis-ci.org/svoop/ogn_client-ruby)
|
3
3
|
[](https://codeclimate.com/github/svoop/ogn_client-ruby)
|
4
4
|
[](https://gitter.im/svoop/ogn_client-ruby)
|
5
|
-
[](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
|