sgslib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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