Class: SGS::Navigate
- Inherits:
-
Object
- Object
- SGS::Navigate
- Defined in:
- lib/sgs/navigate.rb
Instance Attribute Summary collapse
-
#course ⇒ Object
readonly
Returns the value of attribute course.
-
#gps ⇒ Object
readonly
Returns the value of attribute gps.
-
#otto ⇒ Object
readonly
Returns the value of attribute otto.
-
#waypoint ⇒ Object
readonly
What is the next waypoint?.
Instance Method Summary collapse
-
#active? ⇒ Boolean
Check we’re active - basically, are there any more waypoints left?.
-
#compute_bearings(waypoints) ⇒ Object
Compute the bearing for every attractor or repellor.
-
#compute_new_course ⇒ Object
Compute a new course based on our position and other information.
-
#curpos ⇒ Object
What is our current position?.
-
#elapsed ⇒ Object
How long has the mission been active?.
-
#initialize(mission) ⇒ Navigate
constructor
Initialize the navigational parameters.
-
#mission ⇒ Object
Navigate the mission.
-
#mission_abort ⇒ Object
The mission is aborted.
-
#mission_end ⇒ Object
The mission has ended - sail to the rendezvous point.
-
#navigate ⇒ Object
Compute the best heading based on our current position and the position of the current attractor.
-
#next_waypoint! ⇒ Object
Advance to the next waypoint.
-
#olympic_course ⇒ Object
Navigate around an olympic triangle.
-
#overall_distance ⇒ Object
Compute the remaining distance from the current location.
-
#pull_gps_data ⇒ Object
Pull the latest GPS data.
-
#pull_otto_data ⇒ Object
Pull the latest Otto data.
-
#reached? ⇒ Boolean
Have we reached the waypoint? Note that even though the waypoints have a “reached” circle, we discard the last 10m on the basis that it is within the GPS error.
-
#set_position(time, loc) ⇒ Object
Set new position.
-
#set_waypoint ⇒ Object
Set the waypoint instance variable based on where we are.
-
#simulated_movement(how_long = 60) ⇒ Object
Advance the mission by a number of seconds (computing the new location in the process).
-
#status_str ⇒ Object
Return the mission status as a string.
-
#upwind_downwind_course ⇒ Object
Navigate a course up to a windward mark which is one nautical mile upwind of the start position.
Constructor Details
#initialize(mission) ⇒ Navigate
Initialize the navigational parameters
56 57 58 59 60 |
# File 'lib/sgs/navigate.rb', line 56 def initialize(mission) @mission = mission @course = nil @swing = 45 end |
Instance Attribute Details
#course ⇒ Object (readonly)
Returns the value of attribute course.
52 53 54 |
# File 'lib/sgs/navigate.rb', line 52 def course @course end |
#gps ⇒ Object (readonly)
Returns the value of attribute gps.
52 53 54 |
# File 'lib/sgs/navigate.rb', line 52 def gps @gps end |
#otto ⇒ Object (readonly)
Returns the value of attribute otto.
52 53 54 |
# File 'lib/sgs/navigate.rb', line 52 def otto @otto end |
#waypoint ⇒ Object (readonly)
What is the next waypoint?
322 323 324 |
# File 'lib/sgs/navigate.rb', line 322 def waypoint @waypoint end |
Instance Method Details
#active? ⇒ Boolean
Check we’re active - basically, are there any more waypoints left?
186 187 188 |
# File 'lib/sgs/navigate.rb', line 186 def active? @mission.status.current_waypoint < @mission.attractors.count end |
#compute_bearings(waypoints) ⇒ Object
Compute the bearing for every attractor or repellor
154 155 156 157 158 |
# File 'lib/sgs/navigate.rb', line 154 def compute_bearings(waypoints) waypoints.each do |waypt| waypt.compute_bearing(@gps.location) end end |
#compute_new_course ⇒ Object
Compute a new course based on our position and other information.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/sgs/navigate.rb', line 80 def compute_new_course # # Update our local copy of the course based on what Otto says. puts "Compute new course..." unless @course # # First time through, the current course is whichever way the boat # is pointing. @course = Course.new @course.heading = @otto.compass end # # Really it's the AWA we're interested in, not the boat heading. @course.awa = @otto.awa @course.compute_wind p @course # # First off, compute distance and bearing from our current location # to every attractor and repellor. We only look at forward attractors, # not ones behind us. compute_bearings(@mission.attractors[@mission.status.current_waypoint..-1]) compute_bearings(@mission.repellors) # # Right. Now look to see if we've achieved the current waypoint and # adjust, accordingly while active? and reached? next_waypoint! end return true unless active? puts "Angle to next waypoint: #{@waypoint.bearing.angle_d}d" puts "Adjusted distance to waypoint is #{@waypoint.distance}" # # Now, start the vector field analysis by examining headings either side # of the bearing to the waypoint. best_course = @course best_relvmg = 0.0 puts "Currently on a #{@course.tack_name} tack (heading is #{@course.heading_d} degrees)" (-@swing..@swing).each do |alpha_d| new_course = Course.new(@course.wind) new_course.heading = waypoint.bearing.angle + Bearing.dtor(alpha_d) # # Ignore head-to-wind cases, as they're pointless. When looking at # the list of waypoints to compute relative VMG, only look to the next # three or so waypoints. next if new_course.speed < 0.001 relvmg = 0.0 relvmg = new_course.relative_vmg(@mission.attractors[@mission.status.current_waypoint]) end_wpt = @mission.status.current_waypoint + 3 if end_wpt >= @mission.attractors.count end_wpt = @mission.attractors.count - 1 end @mission.attractors[@mission.status.current_waypoint..end_wpt].each do |waypt| relvmg += new_course.relative_vmg(waypt) end @mission.repellors.each do |waypt| relvmg -= new_course.relative_vmg(waypt) end relvmg *= 0.1 if new_course.tack != @course.tack if relvmg > best_relvmg best_relvmg = relvmg best_course = new_course end end puts "Best course: AWA: #{best_course.awa_d} degrees, Course: #{best_course.heading_d} degrees, Speed: #{best_course.speed} knots" p best_course if best_course.tack != @course.tack puts "TACKING!!!!" end @course = best_course return false end |
#curpos ⇒ Object
What is our current position?
316 317 318 |
# File 'lib/sgs/navigate.rb', line 316 def curpos @curpos ||= GPS.load end |
#elapsed ⇒ Object
How long has the mission been active?
180 181 182 |
# File 'lib/sgs/navigate.rb', line 180 def elapsed @time - @start_time end |
#mission ⇒ Object
Navigate the mission. This is the main “meat and potatoes” navigation. It concerns itself with finding the best route to the next mark and sailing to that
301 302 |
# File 'lib/sgs/navigate.rb', line 301 def mission end |
#mission_abort ⇒ Object
The mission is aborted. Determine what to do next
311 312 |
# File 'lib/sgs/navigate.rb', line 311 def mission_abort end |
#mission_end ⇒ Object
The mission has ended - sail to the rendezvous point
306 307 |
# File 'lib/sgs/navigate.rb', line 306 def mission_end end |
#navigate ⇒ Object
Compute the best heading based on our current position and the position of the current attractor. This is where the heavy-lifting happens. Returns TRUE if we’re done.
66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/sgs/navigate.rb', line 66 def navigate if @mission.status.current_waypoint == -1 @mission.status.current_waypoint = 0 @mission.status.distance = 0 end set_waypoint puts "Attempting to navigate to #{@waypoint}..." pull_gps_data pull_otto_data return compute_new_course end |
#next_waypoint! ⇒ Object
Advance to the next waypoint. Return TRUE if there actually is one…
215 216 217 218 219 |
# File 'lib/sgs/navigate.rb', line 215 def next_waypoint! @mission.status.current_waypoint += 1 puts "Attempting to navigate to new waypoint: #{waypoint}" set_waypoint end |
#olympic_course ⇒ Object
Navigate around an olympic triangle. Sail one nautical mile upwind of the current position, then sail to a point to the left-side of the course which is at an angle of 120 degrees to the wind. From there, sail back to the start position
294 295 |
# File 'lib/sgs/navigate.rb', line 294 def olympic_course end |
#overall_distance ⇒ Object
Compute the remaining distance from the current location
246 247 248 249 250 251 252 253 254 255 |
# File 'lib/sgs/navigate.rb', line 246 def overall_distance dist = 0.0 loc = @where @mission.attractors[@mission.status.current_waypoint..-1].each do |wpt| wpt.compute_bearing(loc) dist += wpt.bearing.distance loc = wpt.location end dist end |
#pull_gps_data ⇒ Object
Pull the latest GPS data. Failure is not an option.
259 260 261 262 263 264 265 266 267 |
# File 'lib/sgs/navigate.rb', line 259 def pull_gps_data loop do @gps = GPS.load puts "GPS: #{@gps}" break if @gps.valid? puts "Retrying GPS..." sleep 1 end end |
#pull_otto_data ⇒ Object
Pull the latest Otto data.
271 272 273 274 275 276 277 278 279 280 |
# File 'lib/sgs/navigate.rb', line 271 def pull_otto_data # # Pull the latest Otto data... @otto = Otto.load puts "OTTO:" p @otto puts "Compass: #{@otto.compass}" puts "AWA: #{@otto.awa}" puts "Wind: #{@otto.wind}" end |
#reached? ⇒ Boolean
Have we reached the waypoint? Note that even though the waypoints have a “reached” circle, we discard the last 10m on the basis that it is within the GPS error.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/sgs/navigate.rb', line 194 def reached? puts "ARE WE THERE YET? (dist=#{@waypoint.distance})" p @waypoint return true if @waypoint.distance <= 0.0054 # # Check to see if the next WPT is nearer than the current one #if current_wpt < (@mission.attractors.count - 1) # next_wpt = @mission.attractors[@current_wpt + 1] # brng = @mission.attractors[@current_wpt].location - next_wpt.location # angle = Bearing.absolute(waypoint.bearing.angle - next_wpt.bearing.angle) # return true if brng.distance > next_wpt.distance and # angle > (0.25 * Math::PI) and # angle < (0.75 * Math::PI) #end puts "... Sadly, no." return false end |
#set_position(time, loc) ⇒ Object
Set new position
162 163 164 165 166 |
# File 'lib/sgs/navigate.rb', line 162 def set_position(time, loc) @where = loc @time = time @track << TrackPoint.new(@time, @where) end |
#set_waypoint ⇒ Object
Set the waypoint instance variable based on where we are
223 224 225 |
# File 'lib/sgs/navigate.rb', line 223 def set_waypoint @waypoint = @mission.attractors[@mission.status.current_waypoint] end |
#simulated_movement(how_long = 60) ⇒ Object
Advance the mission by a number of seconds (computing the new location in the process). Fake out the speed and thus the location.
171 172 173 174 175 176 |
# File 'lib/sgs/navigate.rb', line 171 def simulated_movement(how_long = 60) puts "Advancing mission by #{how_long}s" distance = @course.speed * how_long.to_f / 3600.0 puts "Travelled #{distance * 1852.0} metres in that time." set_position(@time + how_long, @where + Bearing.new(@course.heading, distance)) end |
#status_str ⇒ Object
Return the mission status as a string
229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/sgs/navigate.rb', line 229 def status_str mins = elapsed / 60 hours = mins / 60 mins %= 60 days = hours / 24 hours %= 24 str = ">>> #{@time}, " if days < 1 str += "%dh%02dm" % [hours, mins] else str += "+%dd%%02dh%02dm" % [days, hours, mins] end str + ": My position is #{@where}" end |
#upwind_downwind_course ⇒ Object
Navigate a course up to a windward mark which is one nautical mile upwind of the start position. From there, navigate downwind to the finish position
286 287 |
# File 'lib/sgs/navigate.rb', line 286 def upwind_downwind_course end |