Class: SGS::Navigate

Inherits:
Object
  • Object
show all
Defined in:
lib/sgs/navigate.rb

Instance Method Summary collapse

Constructor Details

#initialize(mission) ⇒ Navigate

Initialize the navigational parameters



54
55
56
57
# File 'lib/sgs/navigate.rb', line 54

def initialize(mission)
  @mission = mission
  @swing = 45
end

Instance Method Details

#active?Boolean

Check we’re active - basically, are there any more waypoints left?

Returns:

  • (Boolean)


185
186
187
# File 'lib/sgs/navigate.rb', line 185

def active?
  @mission.status.current_waypoint < @mission.attractors.count
end

#compute_bearings(waypoints) ⇒ Object

Compute the bearing for every attractor or repellor



153
154
155
156
157
# File 'lib/sgs/navigate.rb', line 153

def compute_bearings(waypoints)
  waypoints.each do |waypt|
    waypt.compute_bearing(@gps.location)
  end
end

#compute_new_courseObject

Compute a new course based on our position and other information.



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
# File 'lib/sgs/navigate.rb', line 96

def compute_new_course
  puts "Compute new 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 nil 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
  if best_course.tack != @course.tack
    puts "TACKING!!!!"
  end
  best_course
end

#curposObject

What is our current position?



290
291
292
# File 'lib/sgs/navigate.rb', line 290

def curpos
  @curpos ||= GPS.load
end

#elapsedObject

How long has the mission been active?



179
180
181
# File 'lib/sgs/navigate.rb', line 179

def elapsed
  @time - @start_time
end

#missionObject

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



275
276
# File 'lib/sgs/navigate.rb', line 275

def mission
end

#mission_abortObject

The mission is aborted. Determine what to do next



285
286
# File 'lib/sgs/navigate.rb', line 285

def mission_abort
end

#mission_endObject

The mission has ended - sail to the rendezvous point



280
281
# File 'lib/sgs/navigate.rb', line 280

def mission_end
end

Compute the best heading based on our current position and the position of the current attractor. This is where the heavy-lifting happens



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/sgs/navigate.rb', line 62

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 the latest GPS data...
  @gps = GPS.load
  puts "GPS: #{@gps}"
  return unless @gps.valid?
  #
  # Pull the latest Otto data...
  @otto = Otto.load
  puts "OTTO:"
  p @otto
  puts "Compass: #{@otto.compass}"
  puts "AWA: #{@otto.awa}"
  puts "Wind: #{@otto.wind}"
  #
  # Update our local copy of the course based on what Otto says.
  puts "Course:"
  @course = Course.new
  @course.heading = @otto.compass
  @course.awa = @otto.awa
  @course.compute_wind
  #
  # Compute a new course from the parameter set
  compute_new_course
end

#next_waypoint!Object

Advance to the next waypoint. Return TRUE if there actually is one…



214
215
216
217
218
# File 'lib/sgs/navigate.rb', line 214

def next_waypoint!
  @mission.status.current_waypoint += 1
  puts "Attempting to navigate to new waypoint: #{waypoint}"
  set_waypoint
end

#olympic_courseObject

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



268
269
# File 'lib/sgs/navigate.rb', line 268

def olympic_course
end

#overall_distanceObject

Compute the remaining distance from the current location



245
246
247
248
249
250
251
252
253
254
# File 'lib/sgs/navigate.rb', line 245

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

#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.

Returns:

  • (Boolean)


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/sgs/navigate.rb', line 193

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



161
162
163
164
165
# File 'lib/sgs/navigate.rb', line 161

def set_position(time, loc)
  @where = loc
  @time = time
  @track << TrackPoint.new(@time, @where)
end

#set_waypointObject

Set the waypoint instance variable based on where we are



222
223
224
# File 'lib/sgs/navigate.rb', line 222

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.



170
171
172
173
174
175
# File 'lib/sgs/navigate.rb', line 170

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_strObject

Return the mission status as a string



228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/sgs/navigate.rb', line 228

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_courseObject

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



260
261
# File 'lib/sgs/navigate.rb', line 260

def upwind_downwind_course
end

#waypointObject

What is the next waypoint?



296
297
298
# File 'lib/sgs/navigate.rb', line 296

def waypoint
  @waypoint ||= Waypoint.load
end