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.
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
@@ -0,0 +1,3 @@
1
+ module SGS
2
+ VERSION = "0.1.0"
3
+ end
@@ -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