sgslib 0.3.1 → 1.5.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/LICENSE +24 -17
- data/README.md +43 -7
- data/Rakefile +2 -2
- data/bin/console +34 -1
- data/exe/sgs_alarm +41 -0
- data/exe/sgs_diag +41 -0
- data/exe/sgs_gpsread +42 -0
- data/exe/sgs_logger +45 -0
- data/exe/sgs_mission +45 -0
- data/exe/sgs_nav +43 -0
- data/exe/sgs_otto +46 -0
- data/exe/sgs_report +44 -0
- data/lib/sgs/alarm.rb +55 -23
- data/lib/sgs/bearing.rb +150 -0
- data/lib/sgs/config.rb +35 -24
- data/lib/sgs/course.rb +29 -24
- data/lib/sgs/diagnostics.rb +49 -0
- data/lib/sgs/gps.rb +37 -21
- data/lib/sgs/location.rb +58 -159
- data/lib/sgs/logger.rb +35 -82
- data/lib/sgs/mission.rb +96 -173
- data/lib/sgs/mission_status.rb +128 -0
- data/lib/sgs/navigate.rb +37 -21
- data/lib/sgs/nmea.rb +32 -22
- data/lib/sgs/otto.rb +37 -21
- data/lib/sgs/redis_base.rb +32 -24
- data/lib/sgs/report.rb +49 -0
- data/lib/sgs/rpc.rb +29 -21
- data/lib/sgs/timing.rb +32 -24
- data/lib/sgs/version.rb +35 -1
- data/lib/sgs/waypoint.rb +69 -33
- data/lib/sgslib.rb +40 -24
- data/sgslib.gemspec +15 -5
- metadata +61 -19
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.travis.yml +0 -4
- data/CODE_OF_CONDUCT.md +0 -49
- data/bin/setup +0 -8
data/lib/sgs/location.rb
CHANGED
@@ -1,34 +1,41 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2013, Kalopa
|
3
|
-
#
|
4
|
-
# GNU General Public License as published by the Free Software Foundation;
|
5
|
-
# either version 2, or (at your option) any later version.
|
2
|
+
# Copyright (c) 2013-2023, Kalopa Robotics Limited. All rights
|
3
|
+
# reserved.
|
6
4
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
5
|
+
# This program is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2 of
|
8
|
+
# the License, or (at your option) any later version.
|
11
9
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
15
14
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18
|
+
# 02110-1301, USA.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA ROBOTICS LIMITED "AS IS" AND
|
21
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
22
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
23
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALOPA
|
24
|
+
# ROBOTICS LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
25
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
26
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
27
|
+
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
28
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
29
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
30
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
31
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
|
+
#
|
33
|
+
# ABSTRACT
|
26
34
|
#
|
27
35
|
|
28
36
|
##
|
29
|
-
# Routines for handling sailboat location
|
37
|
+
# Routines for handling sailboat location.
|
30
38
|
#
|
31
|
-
require 'date'
|
32
39
|
require 'json'
|
33
40
|
|
34
41
|
module SGS
|
@@ -97,35 +104,19 @@ module SGS
|
|
97
104
|
end
|
98
105
|
|
99
106
|
#
|
100
|
-
# Create a new location from a
|
101
|
-
# Uses the instance method to parse.
|
102
|
-
def self.parse_str(str)
|
103
|
-
loc = new
|
104
|
-
loc.parse_str(str)
|
105
|
-
loc
|
106
|
-
end
|
107
|
-
|
108
|
-
#
|
109
|
-
# Create a new location from a lat/long string pair
|
107
|
+
# Create a new location from a lat/long hash.
|
110
108
|
# Uses the instance method to parse.
|
111
|
-
def self.parse(
|
109
|
+
def self.parse(data)
|
112
110
|
loc = new
|
113
|
-
loc.parse(
|
111
|
+
loc.parse(data)
|
114
112
|
loc
|
115
113
|
end
|
116
114
|
|
117
115
|
#
|
118
|
-
#
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
#
|
125
|
-
# Parse a lat/long value pair (in degrees)
|
126
|
-
def parse(latstr, longstr)
|
127
|
-
@latitude = ll_parse(latstr.split, "NS")
|
128
|
-
@longitude = ll_parse(longstr.split, "EW")
|
116
|
+
# Set the lat/long from a hash.
|
117
|
+
def parse(data)
|
118
|
+
@latitude = ll_parse(data["latitude"], "NS")
|
119
|
+
@longitude = ll_parse(data["longitude"], "EW")
|
129
120
|
end
|
130
121
|
|
131
122
|
#
|
@@ -134,6 +125,13 @@ module SGS
|
|
134
125
|
@latitude and @longitude
|
135
126
|
end
|
136
127
|
|
128
|
+
#
|
129
|
+
# Convert the lat/long to a hash.
|
130
|
+
def to_hash
|
131
|
+
{"latitude" => ll_to_s(latitude, "NS"),
|
132
|
+
"longitude" => ll_to_s(longitude, "EW")}
|
133
|
+
end
|
134
|
+
|
137
135
|
#
|
138
136
|
# Display the lat/long as a useful string (in degrees).
|
139
137
|
def to_s
|
@@ -186,8 +184,11 @@ module SGS
|
|
186
184
|
|
187
185
|
private
|
188
186
|
#
|
189
|
-
# Parse a string into a lat or long.
|
190
|
-
|
187
|
+
# Parse a string into a lat or long. The latitude or longitude string
|
188
|
+
# should be of the format dd mm ss.sssssC (where C is NS or EW). Note
|
189
|
+
# that latitude and longitude are saved internally in radians.
|
190
|
+
def ll_parse(value, nsew)
|
191
|
+
args = value.split
|
191
192
|
dir = args[-1].gsub(/[\d\. ]+/, '').upcase
|
192
193
|
args.map! {|val| val.to_f}
|
193
194
|
val = args.shift
|
@@ -197,15 +198,22 @@ module SGS
|
|
197
198
|
end
|
198
199
|
|
199
200
|
#
|
200
|
-
#
|
201
|
+
# Convert a latitude or longitude into an ASCII string of the form:
|
202
|
+
# dd mm ss.ssssssC (where C is NS or EW). The value should be in
|
203
|
+
# radians.
|
201
204
|
def ll_to_s(val, str)
|
205
|
+
val = Bearing.rtod val
|
202
206
|
if val < 0.0
|
203
207
|
chr = str[1]
|
204
208
|
val = -val
|
205
209
|
else
|
206
210
|
chr = str[0]
|
207
211
|
end
|
208
|
-
|
212
|
+
deg = val.to_i
|
213
|
+
val = (val - deg.to_f) * 60.0
|
214
|
+
min = val.to_i
|
215
|
+
sec = (val - min.to_f) * 60.0
|
216
|
+
"%d %d %8.6f%c" % [deg, min, sec, chr]
|
209
217
|
end
|
210
218
|
|
211
219
|
#
|
@@ -223,113 +231,4 @@ module SGS
|
|
223
231
|
[fmt % [deg, val], ne.chr]
|
224
232
|
end
|
225
233
|
end
|
226
|
-
|
227
|
-
##
|
228
|
-
# Class for dealing with the angle/distance vector.
|
229
|
-
#
|
230
|
-
# Note that for convenience, we retain the angle in Radians. The
|
231
|
-
# distance is in nautical miles.
|
232
|
-
class Bearing
|
233
|
-
attr_accessor :distance
|
234
|
-
|
235
|
-
#
|
236
|
-
# Create the Bearing instance.
|
237
|
-
def initialize(angle = 0.0, distance = 0.0)
|
238
|
-
self.angle = angle.to_f
|
239
|
-
self.distance = distance.to_f
|
240
|
-
end
|
241
|
-
|
242
|
-
#
|
243
|
-
# Create a bearing from an angle in degrees.
|
244
|
-
def self.degrees(angle, distance)
|
245
|
-
new(Bearing.dtor(angle), distance)
|
246
|
-
end
|
247
|
-
|
248
|
-
#
|
249
|
-
# Handy function to translate degrees to radians
|
250
|
-
def self.dtor(deg)
|
251
|
-
deg.to_f * Math::PI / 180.0
|
252
|
-
end
|
253
|
-
|
254
|
-
#
|
255
|
-
# Handy function to translate radians to degrees
|
256
|
-
def self.rtod(rad)
|
257
|
-
rad.to_f * 180.0 / Math::PI
|
258
|
-
end
|
259
|
-
|
260
|
-
#
|
261
|
-
# Handy function to re-adjust an angle away from negative
|
262
|
-
def self.absolute(angle)
|
263
|
-
(angle + 2.0 * Math::PI) % (2.0 * Math::PI)
|
264
|
-
end
|
265
|
-
|
266
|
-
#
|
267
|
-
# Another handy function to re-adjust an angle (in degrees) away from
|
268
|
-
# negative.
|
269
|
-
def self.absolute_d(angle)
|
270
|
-
(angle + 360) % 360
|
271
|
-
end
|
272
|
-
|
273
|
-
#
|
274
|
-
# Haversine formula for calculating distance and angle, given two
|
275
|
-
# locations.
|
276
|
-
#
|
277
|
-
# To calculate an angle and distance from two positions:
|
278
|
-
#
|
279
|
-
# This code was derived from formulae on the Movable Type site:
|
280
|
-
# http://www.movable-type.co.uk/scripts/latlong.html
|
281
|
-
#
|
282
|
-
# var d = Math.acos(Math.sin(lat1)*Math.sin(lat2) +
|
283
|
-
# Math.cos(lat1)*Math.cos(lat2) *
|
284
|
-
# Math.cos(lon2-lon1)) * R;
|
285
|
-
# var y = Math.sin(dLon) * Math.cos(lat2);
|
286
|
-
# var x = Math.cos(lat1)*Math.sin(lat2) -
|
287
|
-
# Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
|
288
|
-
# var angle = Math.atan2(y, x).toDeg();
|
289
|
-
def self.compute(loc1, loc2)
|
290
|
-
bearing = new
|
291
|
-
sin_lat1 = Math.sin(loc1.latitude)
|
292
|
-
sin_lat2 = Math.sin(loc2.latitude)
|
293
|
-
cos_lat1 = Math.cos(loc1.latitude)
|
294
|
-
cos_lat2 = Math.cos(loc2.latitude)
|
295
|
-
sin_dlon = Math.sin(loc2.longitude - loc1.longitude)
|
296
|
-
cos_dlon = Math.cos(loc2.longitude - loc1.longitude)
|
297
|
-
bearing.distance = Math.acos(sin_lat1*sin_lat2 + cos_lat1*cos_lat2*cos_dlon) *
|
298
|
-
SGS::EARTH_RADIUS
|
299
|
-
y = sin_dlon * cos_lat2
|
300
|
-
x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon
|
301
|
-
bearing.angle = Math.atan2(y, x)
|
302
|
-
bearing
|
303
|
-
end
|
304
|
-
|
305
|
-
#
|
306
|
-
# Set the angle
|
307
|
-
def angle=(angle)
|
308
|
-
@angle = Bearing.absolute(angle)
|
309
|
-
end
|
310
|
-
|
311
|
-
#
|
312
|
-
# Get the angle
|
313
|
-
def angle
|
314
|
-
@angle
|
315
|
-
end
|
316
|
-
|
317
|
-
#
|
318
|
-
# Return the angle (in degrees)
|
319
|
-
def angle_d
|
320
|
-
Bearing.rtod(@angle).to_i
|
321
|
-
end
|
322
|
-
|
323
|
-
#
|
324
|
-
# Get the back-angle (the angle viewed from the opposite end of the line)
|
325
|
-
def back_angle
|
326
|
-
Bearing.absolute(@angle - Math::PI)
|
327
|
-
end
|
328
|
-
|
329
|
-
#
|
330
|
-
# Convert to a string
|
331
|
-
def to_s
|
332
|
-
"BRNG %03dd,%.3fnm" % [angle_d, @distance]
|
333
|
-
end
|
334
|
-
end
|
335
234
|
end
|
data/lib/sgs/logger.rb
CHANGED
@@ -1,96 +1,49 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2013, Kalopa
|
3
|
-
#
|
4
|
-
# GNU General Public License as published by the Free Software Foundation;
|
5
|
-
# either version 2, or (at your option) any later version.
|
2
|
+
# Copyright (c) 2013-2023, Kalopa Robotics Limited. All rights
|
3
|
+
# reserved.
|
6
4
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
5
|
+
# This program is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2 of
|
8
|
+
# the License, or (at your option) any later version.
|
11
9
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
15
14
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18
|
+
# 02110-1301, USA.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA ROBOTICS LIMITED "AS IS" AND
|
21
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
22
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
23
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALOPA
|
24
|
+
# ROBOTICS LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
25
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
26
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
27
|
+
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
28
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
29
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
30
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
31
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
|
+
#
|
33
|
+
# ABSTRACT
|
26
34
|
#
|
27
35
|
|
28
36
|
##
|
29
|
-
# Routines for handling sailboat
|
37
|
+
# Routines for handling sailboat navigation and route planning.
|
30
38
|
#
|
31
|
-
require 'date'
|
32
|
-
|
33
39
|
module SGS
|
34
|
-
#
|
35
|
-
# Waypoint, Attractor, and Repellor definitions
|
36
40
|
class Logger < RedisBase
|
37
|
-
attr_accessor :watch
|
38
|
-
|
39
|
-
#
|
40
|
-
# Watch names and definitions. The first watch is from 8PM until
|
41
|
-
# midnight (local time), the middle watch is from midnight until
|
42
|
-
# 4AM. The morning watch is from 4AM until 8AM. The forenoon watch
|
43
|
-
# runs from 8AM until noon and the afternoon watch runs from noon
|
44
|
-
# until 4PM. The dog watches run from 4PM until 8PM and around we
|
45
|
-
# go again.
|
46
|
-
#
|
47
|
-
# Logs are sent back to base every four hours (6 per day) starting
|
48
|
-
# at midnight UTC. However, some of the reporting is based on
|
49
|
-
# the watch system rather than UTC. For example, reporting battery
|
50
|
-
# voltage is most useful at the start of the forenoon watch, because
|
51
|
-
# this represents the lowest voltage point after driving the boat all
|
52
|
-
# night. As a result, the watch ID is computed based on the longitude,
|
53
|
-
# which is a rough approximation of the timezone.
|
54
|
-
FIRST_WATCH = 0
|
55
|
-
MIDDLE_WATCH = 1
|
56
|
-
MORNING_WATCH = 2
|
57
|
-
FORENOON_WATCH = 3
|
58
|
-
AFTERNOON_WATCH = 4
|
59
|
-
DOG_WATCH = 5
|
60
|
-
ALARM_REPORT = 7
|
61
|
-
|
62
|
-
WATCH_NAMES = [
|
63
|
-
"First Watch", "Middle Watch", "Morning Watch",
|
64
|
-
"Forenoon Watch", "Afternoon Watch", "Dog Watch",
|
65
|
-
"", "** ALARM REPORT **"
|
66
|
-
].freeze
|
67
|
-
|
68
|
-
def initialize()
|
69
|
-
@watch = FIRST_WATCH
|
70
|
-
end
|
71
|
-
|
72
|
-
#
|
73
|
-
# Convert the watch ID to a name
|
74
|
-
def watch_name
|
75
|
-
WATCH_NAMES[@watch]
|
76
|
-
end
|
77
|
-
|
78
41
|
#
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# reporting from the Middle Watch.
|
85
|
-
def determine_watch(longitude)
|
86
|
-
utc = Time.now
|
87
|
-
local_hour = utc.hour + longitude * 12.0 / Math::PI
|
88
|
-
p utc
|
89
|
-
p local_hour
|
90
|
-
local_hour += 24.0 if local_hour < 0.0
|
91
|
-
local_hour -= 24.0 if local_hour >= 24.0
|
92
|
-
@watch = ((local_hour / 4.0) + 0.25).to_i
|
93
|
-
@watch = FIRST_WATCH if @watch == 6
|
42
|
+
# Main daemon function (called from executable)
|
43
|
+
def self.daemon
|
44
|
+
loop do
|
45
|
+
sleep 300
|
46
|
+
end
|
94
47
|
end
|
95
48
|
end
|
96
49
|
end
|