sgslib 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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sgs/alarm.rb +103 -0
- data/lib/sgs/command.rb +167 -0
- data/lib/sgs/gps.rb +53 -0
- data/lib/sgs/location.rb +328 -0
- data/lib/sgs/logger.rb +96 -0
- data/lib/sgs/mission.rb +362 -0
- data/lib/sgs/navigate.rb +141 -0
- data/lib/sgs/nmea.rb +138 -0
- data/lib/sgs/otto.rb +163 -0
- data/lib/sgs/polar.rb +86 -0
- data/lib/sgs/redis_base.rb +251 -0
- data/lib/sgs/setup.rb +40 -0
- data/lib/sgs/timing.rb +45 -0
- data/lib/sgs/version.rb +3 -0
- data/lib/sgs/waypoint.rb +115 -0
- data/lib/sgslib.rb +46 -0
- data/sgslib.gemspec +35 -0
- metadata +129 -0
data/lib/sgs/otto.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
3
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
4
|
+
# GNU General Public License as published by the Free Software Foundation;
|
5
|
+
# either version 2, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
8
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
10
|
+
# for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License along
|
13
|
+
# with this product; see the file COPYING. If not, write to the Free
|
14
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
17
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
19
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
20
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
22
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
23
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# Routines for interfacing with the low-level microcontroller.
|
30
|
+
#
|
31
|
+
module SGS
|
32
|
+
class Otto < RedisBase
|
33
|
+
attr_reader :rudder, :sail, :compass, :twa
|
34
|
+
attr_accessor :bvcal, :bical, :btcal, :svcal
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@bvcal = 0.0
|
38
|
+
@bical = 0.0
|
39
|
+
@btcal = 0.0
|
40
|
+
@svcal = 0.0
|
41
|
+
@rudder = read_rudder
|
42
|
+
@sail = read_sail
|
43
|
+
@compass = read_compass
|
44
|
+
@twa = read_twa
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Set the required rudder angle. Input values range from +/- 40 degrees
|
50
|
+
def rudder=(val)
|
51
|
+
val = -39.9 if val < -39.9
|
52
|
+
val = 39.9 if val > 39.9
|
53
|
+
return if @rudder == val
|
54
|
+
@rudder = val
|
55
|
+
intval = (@rudder * 120.0 / 40.0).to_int + 128
|
56
|
+
puts "New rudder value: #{intval} (#{@rudder} degrees)"
|
57
|
+
send_command(SET_RUDDER, intval)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Set the required sail angle. Input values range from 0 -> 90 degrees.
|
62
|
+
def sail=(val)
|
63
|
+
val = 0.0 if val < 0.0
|
64
|
+
val = 90.0 if val > 90.0
|
65
|
+
return if @sail == val
|
66
|
+
@sail = val
|
67
|
+
intval = (@sail * 256.0 / 90.0).to_int
|
68
|
+
puts "New sail angle: #{intval} (#{@sail} degrees)"
|
69
|
+
send_command(SET_SAIL, intval)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Set the required compass reading. Input values range from 0 -> 359 degrees
|
74
|
+
def compass=(val)
|
75
|
+
while val < 0.0
|
76
|
+
val += 360.0
|
77
|
+
end
|
78
|
+
val %= 360.0
|
79
|
+
return if @compass == val
|
80
|
+
@compass = val
|
81
|
+
intval = (@compass * 256.0 / 360.0).to_int
|
82
|
+
puts "New compass heading: #{intval} (#{@compass} degrees)"
|
83
|
+
send_command(SET_COMPASS, intval)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Set the required true wind angle. Input values range from +/- 180 degrees
|
88
|
+
def twa=(val)
|
89
|
+
val = -179.9 if val < -179.9
|
90
|
+
val = 179.9 if val > 179.9
|
91
|
+
return if @twa == val
|
92
|
+
@twa = val
|
93
|
+
val = 360.0 + val if val < 0.0
|
94
|
+
intval = (val * 256.0 / 360.0).to_int
|
95
|
+
puts "New TWA: #{intval} (#{@twa} degrees)"
|
96
|
+
send_command(SET_TWA, intval)
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Read the uptime clock
|
101
|
+
def read_uptime
|
102
|
+
intval = send_command(READ_UPTIME)
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Read the battery voltage
|
107
|
+
def read_battery_volts
|
108
|
+
intval = send_command(READ_BATTERY_VOLTAGE)
|
109
|
+
intval.to_f * @bvcal / 1024.0
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Read the battery current
|
114
|
+
def read_battery_current
|
115
|
+
intval = send_command(READ_BATTERY_CURRENT)
|
116
|
+
intval.to_f * @bical / 1024.0
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Read the boat temperature
|
121
|
+
def read_boat_temperature
|
122
|
+
intval = send_command(READ_BOAT_TEMPERATURE)
|
123
|
+
intval.to_f * @btcal / 1024.0
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Read the solar voltage
|
128
|
+
def read_solar_volts
|
129
|
+
intval = send_command(READ_SOLAR_VOLTAGE)
|
130
|
+
intval.to_f * @svcal / 1024.0
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Read the actual compass value
|
135
|
+
def read_compass
|
136
|
+
intval = send_command(GET_COMPASS)
|
137
|
+
intval.to_f * 360.0 / 256.0
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Read the actual TWA value
|
142
|
+
def read_twa
|
143
|
+
intval = send_command(GET_TWA)
|
144
|
+
val = intval.to_f * 180.0 / 128.0
|
145
|
+
val = val - 360.0 if val > 180.0
|
146
|
+
val
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Read the actual boat pitch
|
151
|
+
def read_pitch
|
152
|
+
intval = send_command(GET_PITCH)
|
153
|
+
intval.to_f
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Read the actual boat heel
|
158
|
+
def read_heel
|
159
|
+
intval = send_command(GET_HEEL)
|
160
|
+
intval.to_f
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
data/lib/sgs/polar.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
3
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
4
|
+
# GNU General Public License as published by the Free Software Foundation;
|
5
|
+
# either version 2, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
8
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
10
|
+
# for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License along
|
13
|
+
# with this product; see the file COPYING. If not, write to the Free
|
14
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
17
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
19
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
20
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
22
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
23
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# Routines for handling sailboat navigation and route planning.
|
30
|
+
#
|
31
|
+
# The code on this page was derived from formulae on the Movable Type site:
|
32
|
+
# http://www.movable-type.co.uk/scripts/latlong.html
|
33
|
+
#
|
34
|
+
|
35
|
+
require 'date'
|
36
|
+
|
37
|
+
module SGS
|
38
|
+
##
|
39
|
+
#
|
40
|
+
# A class to handle boat polar calculations. It takes a range of polars
|
41
|
+
# as polynomials and then applies them.
|
42
|
+
class Polar
|
43
|
+
# Right now, we have one polar - from a Catalina 22.
|
44
|
+
# Note that the speed is the same, regardless of the tack.
|
45
|
+
STANDARD = [
|
46
|
+
-3.15994,
|
47
|
+
23.8741,
|
48
|
+
-27.4595,
|
49
|
+
16.4868,
|
50
|
+
-5.15663,
|
51
|
+
0.743936,
|
52
|
+
-0.0344716
|
53
|
+
].freeze
|
54
|
+
|
55
|
+
#
|
56
|
+
# set up the default values
|
57
|
+
def initialize
|
58
|
+
@curve = STANDARD
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Compute the hull speed from the polar
|
63
|
+
# :awa: is the apparent wind angle (in radians)
|
64
|
+
# :wspeed: is the wind speed (in knots)
|
65
|
+
def speed(awa, wspeed = 0.0)
|
66
|
+
awa = awa.to_f.abs
|
67
|
+
ap = 1.0
|
68
|
+
@speed = 0.0
|
69
|
+
@curve.each do |poly_val|
|
70
|
+
@speed += poly_val * ap
|
71
|
+
ap *= awa
|
72
|
+
end
|
73
|
+
@speed /= 1.529955
|
74
|
+
if @speed < 0.0
|
75
|
+
@speed = 0.0
|
76
|
+
end
|
77
|
+
@speed
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Calculate the VMG from the angle to the mark.
|
82
|
+
def vmg(alpha)
|
83
|
+
Math::cos(alpha) * @speed
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
3
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
4
|
+
# GNU General Public License as published by the Free Software Foundation;
|
5
|
+
# either version 2, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
8
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
10
|
+
# for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License along
|
13
|
+
# with this product; see the file COPYING. If not, write to the Free
|
14
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
17
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
19
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
20
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
22
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
23
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# Routines for manipulating data in Redis.
|
30
|
+
#
|
31
|
+
require 'redis'
|
32
|
+
|
33
|
+
module SGS
|
34
|
+
class RedisBase
|
35
|
+
##
|
36
|
+
# The base (inherited) class for dealing with Redis data for
|
37
|
+
# the navigation system. Each model class inherits this parent,
|
38
|
+
# and gets an update count for free.
|
39
|
+
|
40
|
+
#
|
41
|
+
# Initialize the base class.
|
42
|
+
def initialize
|
43
|
+
$redis = Redis.new unless $redis
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Initialize the (sub-)class variables in Redis.
|
48
|
+
def self.setup
|
49
|
+
cls = new
|
50
|
+
cls.instance_variables.each do |var|
|
51
|
+
val = cls.instance_variable_get var
|
52
|
+
if val.kind_of? Array
|
53
|
+
#
|
54
|
+
# Arrays are handled separately. We instead
|
55
|
+
# use the index to create a series of 'fooN'
|
56
|
+
# variables.
|
57
|
+
val.size.times do |idx|
|
58
|
+
var_init var, val, idx
|
59
|
+
end
|
60
|
+
else
|
61
|
+
var_init var, val
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Initialize a Redis variable.
|
68
|
+
def self.var_init(var, val, idx = nil)
|
69
|
+
cls = new
|
70
|
+
$redis.setnx cls.make_redis_name(var, :idx => idx), self.to_redis(var, val, idx)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Load the instance variables for the class.
|
75
|
+
def self.load()
|
76
|
+
cls = new
|
77
|
+
cls.load
|
78
|
+
cls
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Load the instance variables for the class.
|
83
|
+
def load
|
84
|
+
instance_variables.each do |var|
|
85
|
+
lval = instance_variable_get var
|
86
|
+
if lval.kind_of? Array
|
87
|
+
#
|
88
|
+
# It's an array - iterate and read the values.
|
89
|
+
lval.size.times do |idx|
|
90
|
+
idx_val = lval[idx]
|
91
|
+
lval[idx] = redis_read_var var, idx_val.class, :idx => idx
|
92
|
+
end
|
93
|
+
elsif lval.kind_of? Location
|
94
|
+
#
|
95
|
+
# ::FIXME:: Yes. this is a hack.
|
96
|
+
# This belongs in the Location class itself. It's arguable that a lot
|
97
|
+
# of the stuff belongs in the parent class. Perhaps the thing to do
|
98
|
+
# is ask the class to return a hash of names and values, and then
|
99
|
+
# set them accordingly.
|
100
|
+
lval.latitude = redis_read_var var, Float, :name => 'latitude'
|
101
|
+
lval.longitude = redis_read_var var, Float, :name => 'longitude'
|
102
|
+
else
|
103
|
+
lval = redis_read_var var, lval.class
|
104
|
+
end
|
105
|
+
instance_variable_set var, lval
|
106
|
+
end
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Write the instance to Redis. IWe produce a Hash of keys and values. From
|
112
|
+
# this and inside a Redis "multi" block, we set all the values and finally
|
113
|
+
# increment the count. @count is actually an instance variable of redis_base
|
114
|
+
def save
|
115
|
+
#
|
116
|
+
# Get the Hash of settable values (including count).
|
117
|
+
var_list = {}
|
118
|
+
self.instance_variables.each do |var|
|
119
|
+
lval = self.instance_variable_get var
|
120
|
+
if lval.kind_of? Array
|
121
|
+
lval.size.times do |idx|
|
122
|
+
var_list[make_redis_name(var, :idx => idx)] = self.class.to_redis var, lval, idx
|
123
|
+
end
|
124
|
+
elsif lval.kind_of? Location
|
125
|
+
#
|
126
|
+
# ::FIXME:: Yes. this is a hack. see 'load' above.
|
127
|
+
var_list[make_redis_name(var, :name => 'latitude')] = lval.latitude
|
128
|
+
var_list[make_redis_name(var, :name => 'longitude')] = lval.longitude
|
129
|
+
else
|
130
|
+
var_list[make_redis_name(var)] = self.class.to_redis var, lval
|
131
|
+
end
|
132
|
+
end
|
133
|
+
#
|
134
|
+
# Inside a multi-block, set all the variables and increment
|
135
|
+
# the count.
|
136
|
+
$redis.multi do
|
137
|
+
var_list.each do |key, value|
|
138
|
+
$redis.set key, value
|
139
|
+
end
|
140
|
+
$redis.incr count_name
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Publish the count onto a Redis pub/sub channel. The trick to subscribing
|
146
|
+
# to a channel is that whenever there's a publish, the new count is
|
147
|
+
# published as a string. If you subscribe to the channel (usually the
|
148
|
+
# class name), you can remember the last received count and decide if
|
149
|
+
# there is fresh data. Or, you can just act anyway.
|
150
|
+
def publish
|
151
|
+
$redis.publish self.class.redis_handle, count.to_s
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Subscribe to messages from this particular channel. Each count is sent
|
156
|
+
# to the code block. It's up to the called code block to decide if the
|
157
|
+
# count has changed and if so, to read the data from Redis.
|
158
|
+
def self.subscribe
|
159
|
+
redis = Redis.new
|
160
|
+
redis.subscribe(redis_handle) do |on|
|
161
|
+
on.message do |channel, count|
|
162
|
+
yield count.to_i
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Combined save and publish
|
169
|
+
def save_and_publish
|
170
|
+
save && publish
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# Retrieve the count
|
175
|
+
def count
|
176
|
+
$redis.get count_name
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# What is the official name of the count instance variable
|
181
|
+
def count_name
|
182
|
+
make_redis_name "@count"
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Get an instance variable value from a Redis value.
|
187
|
+
def redis_read_var(var, klass, opts = {})
|
188
|
+
redis_name = make_redis_name var, opts
|
189
|
+
redis_val = $redis.get redis_name
|
190
|
+
redis_val = nil if redis_val == ""
|
191
|
+
if redis_val
|
192
|
+
if not klass or klass == NilClass
|
193
|
+
redis_val = true if redis_val == "true"
|
194
|
+
redis_val = false if redis_val == "false"
|
195
|
+
klass = Float if redis_val =~ /[0-9+-\.]+/
|
196
|
+
end
|
197
|
+
case
|
198
|
+
when klass == Time
|
199
|
+
redis_val = Time.at(redis_val.to_f).gmtime
|
200
|
+
when klass == Fixnum
|
201
|
+
redis_val = redis_val.to_i
|
202
|
+
when klass == Float
|
203
|
+
redis_val = redis_val.to_f
|
204
|
+
when klass == FalseClass
|
205
|
+
redis_val = false
|
206
|
+
when klass == TrueClass
|
207
|
+
redis_val = true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
redis_val
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
# Set a variable - convert from Ruby format to Redis format.
|
215
|
+
# As of now, we only convert times. Floats and integers are
|
216
|
+
# dealt with by Redis (converted to strings, unfortunately).
|
217
|
+
def self.to_redis(var, local_val, idx = nil)
|
218
|
+
if local_val
|
219
|
+
local_val = local_val[idx] if idx
|
220
|
+
if local_val.class == Time
|
221
|
+
local_val = local_val.to_f
|
222
|
+
end
|
223
|
+
end
|
224
|
+
local_val
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Translate an instance variable into a Redis key name.
|
229
|
+
# This is simply the class name, a dot and the instance
|
230
|
+
# variable. A bit of jiggery-pokery to convert the
|
231
|
+
# instance variable into a proper name. Probably an easier
|
232
|
+
# way to do this, but...
|
233
|
+
#
|
234
|
+
# Instance method for above
|
235
|
+
def make_redis_name(var, opts = {})
|
236
|
+
var_name = opts[:name] || var.to_s.gsub(/^@/, '')
|
237
|
+
prefix = opts[:prefix] || self.class.redis_handle
|
238
|
+
if opts[:idx]
|
239
|
+
"#{prefix}.#{var_name}#{opts[:idx] + 1}"
|
240
|
+
else
|
241
|
+
"#{prefix}.#{var_name}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# Convert the class name into something suitable for Redis
|
247
|
+
def self.redis_handle
|
248
|
+
self.name.downcase.gsub(/^sgs::/, 'sgs_')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
data/lib/sgs/setup.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
4
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
5
|
+
# GNU General Public License as published by the Free Software Foundation;
|
6
|
+
# either version 2, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License along
|
14
|
+
# with this product; see the file COPYING. If not, write to the Free
|
15
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
18
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
19
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
20
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
21
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
22
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
24
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
25
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#
|
28
|
+
$: << '../sgslib'
|
29
|
+
|
30
|
+
require 'sgslib'
|
31
|
+
|
32
|
+
##
|
33
|
+
# Initialise the REDIS system.
|
34
|
+
#
|
35
|
+
SGS::Alarm.setup
|
36
|
+
SGS::GPS.setup
|
37
|
+
SGS::Otto.setup
|
38
|
+
SGS::Timing.setup
|
39
|
+
SGS::Waypoint.setup
|
40
|
+
#SGS::Mission.setup
|
data/lib/sgs/timing.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
3
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
4
|
+
# GNU General Public License as published by the Free Software Foundation;
|
5
|
+
# either version 2, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
8
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
10
|
+
# for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License along
|
13
|
+
# with this product; see the file COPYING. If not, write to the Free
|
14
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
17
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
19
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
20
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
22
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
23
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# Routines for handling sailboat navigation and route planning.
|
30
|
+
#
|
31
|
+
module SGS
|
32
|
+
class Timing < RedisBase
|
33
|
+
attr_accessor :status, :boot, :briefing, :m_start, :m_abort, :m_complete
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@status = nil
|
37
|
+
@boot = Time.at(0)
|
38
|
+
@briefing = nil
|
39
|
+
@m_start = nil
|
40
|
+
@m_abort = nil
|
41
|
+
@m_complete = nil
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/sgs/version.rb
ADDED
data/lib/sgs/waypoint.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013, Kalopa Research. All rights reserved. This is free
|
3
|
+
# software; you can redistribute it and/or modify it under the terms of the
|
4
|
+
# GNU General Public License as published by the Free Software Foundation;
|
5
|
+
# either version 2, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# It is distributed in the hope that it will be useful, but WITHOUT
|
8
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
10
|
+
# for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License along
|
13
|
+
# with this product; see the file COPYING. If not, write to the Free
|
14
|
+
# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY KALOPA RESEARCH "AS IS" AND ANY EXPRESS OR
|
17
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
19
|
+
# IN NO EVENT SHALL KALOPA RESEARCH BE LIABLE FOR ANY DIRECT, INDIRECT,
|
20
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
22
|
+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
23
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# Routines for handling sailboat navigation and route planning.
|
30
|
+
#
|
31
|
+
require 'date'
|
32
|
+
|
33
|
+
module SGS
|
34
|
+
#
|
35
|
+
# Waypoint, Attractor, and Repellor definitions
|
36
|
+
class Waypoint < RedisBase
|
37
|
+
attr_accessor :location, :chord, :type, :name, :bearing, :distance
|
38
|
+
|
39
|
+
#
|
40
|
+
# Types of waypoints
|
41
|
+
START_LINE = 0
|
42
|
+
FINISH_LINE = 1
|
43
|
+
WAYPOINT = 2
|
44
|
+
REPELLOR = 3
|
45
|
+
|
46
|
+
def initialize(location = nil, chord = nil, name = "", type = WAYPOINT)
|
47
|
+
@location = location || Location.new
|
48
|
+
@chord = chord
|
49
|
+
@type = type
|
50
|
+
@name = name
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Calculate the back-vector from the waypoint to the specified position
|
55
|
+
# Calculate the adjusted distance between the mark and the set location.
|
56
|
+
# Check to see if our back-bearing from the waypoint to our location is
|
57
|
+
# inside the chord of the waypoint. If so, reduce the distance to the
|
58
|
+
# waypoint by the length of the chord.
|
59
|
+
def compute_bearing(loc)
|
60
|
+
@bearing = Bearing.calculate(loc, @location)
|
61
|
+
@distance = @bearing.distance
|
62
|
+
d = Bearing.new(@bearing.back_angle - @chord.angle, @distance)
|
63
|
+
# A chord angle of 0 gives a semicircle from 180 to 360 degrees
|
64
|
+
# (inclusive). If our approach angle is within range, then reduce our
|
65
|
+
# distance to the mark by the chord distance (radius).
|
66
|
+
unless d.angle > 0.0 and d.angle < Math::PI
|
67
|
+
@distance -= @chord.distance
|
68
|
+
end
|
69
|
+
@distance
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Is the waypoint in scope? In other words, is our angle inside the chord.
|
74
|
+
def in_scope?
|
75
|
+
puts "In-scope distance is %f..." % @distance
|
76
|
+
@distance <= 0.0
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Pretty version of the waypoint.
|
81
|
+
def to_s
|
82
|
+
"'#{@name}' at #{@location} => #{chord}"
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Display a string for a KML file
|
87
|
+
def to_kml
|
88
|
+
c2 = @chord.clone
|
89
|
+
c2.angle += Math::PI
|
90
|
+
pos1 = @location.calculate(@chord)
|
91
|
+
pos2 = @location.calculate(c2)
|
92
|
+
"#{pos1.to_kml(',')} #{pos2.to_kml(',')}"
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Show the axis line for the waypoint (as a KML)
|
97
|
+
def to_axis_kml
|
98
|
+
c2 = @chord.clone
|
99
|
+
c2.angle += 1.5 * Math::PI
|
100
|
+
pos1 = @location.calculate(c2)
|
101
|
+
"#{@location.to_kml(',')} #{pos1.to_kml(',')}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Store the track over the ground.
|
107
|
+
class Track
|
108
|
+
attr_accessor :time, :location
|
109
|
+
|
110
|
+
def initialize(time, location)
|
111
|
+
@time = time
|
112
|
+
@location = location
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|