3 XCSoar Glide Computer - http://www.xcsoar.org/
4 Copyright (C) 2000-2013 The XCSoar Project
5 A detailed list of copyright holders can be found in the file "AUTHORS".
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "Engine/GlideSolvers/GlidePolar.hpp"
24 #include "Engine/Task/TaskEvents.hpp"
25 #include "Engine/Task/Ordered/OrderedTaskBehaviour.hpp"
26 #include "Engine/Task/Ordered/OrderedTask.hpp"
27 #include "Engine/Task/Ordered/Points/StartPoint.hpp"
28 #include "Engine/Task/Ordered/Points/FinishPoint.hpp"
29 #include "Engine/Task/Ordered/Points/ASTPoint.hpp"
30 #include "Engine/Task/ObservationZones/LineSectorZone.hpp"
38 #include "TestUtil.hpp"
40 static TaskBehaviour task_behaviour
;
41 static OrderedTaskBehaviour ordered_task_behaviour
;
42 static GlidePolar
glide_polar(fixed(0));
45 MakeGeoPoint(double longitude
, double latitude
)
47 return GeoPoint(Angle::Degrees(longitude
),
48 Angle::Degrees(latitude
));
52 MakeWaypoint(Waypoint wp
, double altitude
)
54 wp
.elevation
= fixed(altitude
);
59 MakeWaypoint(double longitude
, double latitude
, double altitude
)
61 return MakeWaypoint(Waypoint(MakeGeoPoint(longitude
, latitude
)), altitude
);
64 static const Waypoint wp1
= MakeWaypoint(0, 45, 50);
65 static const Waypoint wp2
= MakeWaypoint(0, 45.3, 50);
66 static const Waypoint wp3
= MakeWaypoint(0, 46, 50);
67 static const Waypoint wp4
= MakeWaypoint(1, 46, 50);
68 static const Waypoint wp5
= MakeWaypoint(0.3, 46, 50);
71 GetSafetyHeight(const TaskPoint
&tp
)
73 return task_behaviour
.safety_height_arrival
;
77 CheckLeg(const TaskWaypoint
&tp
, const AircraftState
&aircraft
,
78 const TaskStats
&stats
)
80 const GeoPoint destination
= tp
.GetWaypoint().location
;
81 const fixed safety_height
= GetSafetyHeight(tp
);
82 const fixed min_arrival_alt
= tp
.GetWaypoint().elevation
+ safety_height
;
83 const GeoVector vector
= aircraft
.location
.DistanceBearing(destination
);
84 const fixed ld
= glide_polar
.GetBestLD();
85 const fixed height_above_min
= aircraft
.altitude
- min_arrival_alt
;
86 const fixed height_consumption
= vector
.distance
/ ld
;
87 const ElementStat
&leg
= stats
.current_leg
;
88 const GlideResult
&solution_remaining
= leg
.solution_remaining
;
90 ok1(leg
.vector_remaining
.IsValid());
91 ok1(equals(leg
.vector_remaining
.distance
, vector
.distance
));
92 ok1(equals(leg
.vector_remaining
.bearing
, vector
.bearing
));
94 ok1(solution_remaining
.IsOk());
95 ok1(solution_remaining
.vector
.IsValid());
96 ok1(equals(solution_remaining
.vector
.distance
, vector
.distance
));
97 ok1(equals(solution_remaining
.vector
.bearing
, vector
.bearing
));
98 ok1(equals(solution_remaining
.height_glide
, height_consumption
));
99 ok1(equals(solution_remaining
.altitude_difference
,
100 height_above_min
- height_consumption
));
101 ok1(equals(solution_remaining
.GetRequiredAltitudeWithDrift(),
102 min_arrival_alt
+ height_consumption
));
104 if (height_above_min
>= height_consumption
) {
106 ok1(equals(solution_remaining
.height_climb
, 0));
107 } else if (positive(glide_polar
.GetMC())) {
109 ok1(equals(solution_remaining
.height_climb
,
110 height_consumption
- height_above_min
));
112 /* climb required, but not possible (MC=0) */
113 ok1(equals(solution_remaining
.height_climb
, 0));
118 CheckTotal(const AircraftState
&aircraft
, const TaskStats
&stats
,
119 const TaskWaypoint
&start
, const TaskWaypoint
&tp1
,
120 const TaskWaypoint
&finish
)
122 const fixed min_arrival_alt1
= tp1
.GetWaypoint().elevation
+
123 task_behaviour
.safety_height_arrival
;
124 const fixed min_arrival_alt2
= finish
.GetWaypoint().elevation
+
125 task_behaviour
.safety_height_arrival
;
126 const GeoVector vector0
=
127 start
.GetWaypoint().location
.DistanceBearing(tp1
.GetWaypoint().location
);
128 const GeoVector vector1
=
129 aircraft
.location
.DistanceBearing(tp1
.GetWaypoint().location
);
130 const GeoVector vector2
=
131 tp1
.GetWaypoint().location
.DistanceBearing(finish
.GetWaypoint().location
);
132 const fixed ld
= glide_polar
.GetBestLD();
133 const fixed height_consumption1
= vector1
.distance
/ ld
;
135 const fixed height_consumption2
= vector2
.distance
/ ld
;
137 const ElementStat
&total
= stats
.total
;
138 const GlideResult
&solution_remaining
= total
.solution_remaining
;
139 const fixed distance_nominal
= vector0
.distance
+ vector2
.distance
;
140 const fixed distance_ahead
= vector1
.distance
+ vector2
.distance
;
142 ok1(equals(stats
.distance_nominal
, distance_nominal
));
143 ok1(equals(stats
.distance_min
, distance_nominal
));
144 ok1(equals(stats
.distance_max
, distance_nominal
));
146 ok1(!total
.vector_remaining
.IsValid());
147 ok1(solution_remaining
.IsOk());
149 ok1(equals(solution_remaining
.vector
.distance
, distance_ahead
));
150 ok1(equals(solution_remaining
.height_glide
, distance_ahead
/ ld
));
152 fixed alt_required_at_1
= std::max(min_arrival_alt1
,
153 min_arrival_alt2
+ height_consumption2
);
154 fixed alt_required_at_aircraft
= alt_required_at_1
+ height_consumption1
;
155 ok1(equals(solution_remaining
.GetRequiredAltitudeWithDrift(),
156 alt_required_at_aircraft
));
157 ok1(equals(solution_remaining
.altitude_difference
,
158 aircraft
.altitude
- alt_required_at_aircraft
));
159 ok1(equals(solution_remaining
.height_climb
,
160 positive(glide_polar
.GetMC())
161 ? alt_required_at_aircraft
- aircraft
.altitude
166 CheckLegEqualsTotal(const GlideResult
&leg
, const GlideResult
&total
)
169 ok1(equals(total
.height_climb
, leg
.height_climb
));
170 ok1(equals(total
.height_glide
, leg
.height_glide
));
171 ok1(equals(total
.altitude_difference
, leg
.altitude_difference
));
172 ok1(equals(total
.GetRequiredAltitudeWithDrift(), leg
.GetRequiredAltitudeWithDrift()));
176 TestFlightToFinish(fixed aircraft_altitude
)
178 OrderedTask
task(task_behaviour
);
179 const StartPoint
tp1(new LineSectorZone(wp1
.location
),
181 ordered_task_behaviour
.start_constraints
);
183 const FinishPoint
tp2(new LineSectorZone(wp2
.location
),
185 ordered_task_behaviour
.finish_constraints
, false);
187 task
.SetActiveTaskPoint(1);
189 ok1(task
.CheckTask());
191 AircraftState aircraft
;
193 aircraft
.location
= wp1
.location
;
194 aircraft
.altitude
= aircraft_altitude
;
195 task
.Update(aircraft
, aircraft
, glide_polar
);
197 const GeoVector vector
= wp1
.location
.DistanceBearing(wp2
.location
);
199 const TaskStats
&stats
= task
.GetStats();
200 ok1(stats
.task_valid
);
201 ok1(!stats
.task_started
);
202 ok1(!stats
.task_finished
);
203 ok1(stats
.flight_mode_final_glide
== !negative(stats
.total
.solution_remaining
.altitude_difference
));
204 ok1(equals(stats
.distance_nominal
, vector
.distance
));
205 ok1(equals(stats
.distance_min
, vector
.distance
));
206 ok1(equals(stats
.distance_max
, vector
.distance
));
208 CheckLeg(tp2
, aircraft
, stats
);
210 ok1(!stats
.total
.vector_remaining
.IsValid());
211 CheckLegEqualsTotal(stats
.current_leg
.solution_remaining
,
212 stats
.total
.solution_remaining
);
218 OrderedTask
task(task_behaviour
);
219 const StartPoint
tp1(new LineSectorZone(wp1
.location
),
221 ordered_task_behaviour
.start_constraints
);
223 const FinishPoint
tp2(new LineSectorZone(wp3
.location
),
225 ordered_task_behaviour
.finish_constraints
, false);
228 ok1(task
.CheckTask());
230 AircraftState aircraft
;
232 aircraft
.location
= MakeGeoPoint(0, 44.5);
233 aircraft
.altitude
= fixed(1700);
234 task
.Update(aircraft
, aircraft
, glide_polar
);
236 const GeoVector tp1_to_tp2
= wp1
.location
.DistanceBearing(wp3
.location
);
238 const TaskStats
&stats
= task
.GetStats();
239 ok1(stats
.task_valid
);
240 ok1(!stats
.task_started
);
241 ok1(!stats
.task_finished
);
242 ok1(!stats
.flight_mode_final_glide
);
243 ok1(equals(stats
.distance_nominal
, tp1_to_tp2
.distance
));
244 ok1(equals(stats
.distance_min
, tp1_to_tp2
.distance
));
245 ok1(equals(stats
.distance_max
, tp1_to_tp2
.distance
));
247 CheckLeg(tp1
, aircraft
, stats
);
248 CheckTotal(aircraft
, stats
, tp1
, tp1
, tp2
);
254 OrderedTask
task(task_behaviour
);
255 const StartPoint
tp1(new LineSectorZone(wp1
.location
),
257 ordered_task_behaviour
.start_constraints
);
260 wp2b
.elevation
= fixed(1000);
261 const FinishPoint
tp2(new LineSectorZone(wp2b
.location
),
262 wp2b
, task_behaviour
,
263 ordered_task_behaviour
.finish_constraints
, false);
265 task
.SetActiveTaskPoint(1);
267 ok1(task
.CheckTask());
269 AircraftState aircraft
;
271 aircraft
.location
= wp1
.location
;
272 aircraft
.altitude
= fixed(1000);
273 task
.Update(aircraft
, aircraft
, glide_polar
);
275 const GeoVector vector
= wp1
.location
.DistanceBearing(wp2
.location
);
277 const TaskStats
&stats
= task
.GetStats();
278 ok1(stats
.task_valid
);
279 ok1(!stats
.task_started
);
280 ok1(!stats
.task_finished
);
281 ok1(!stats
.flight_mode_final_glide
);
282 ok1(equals(stats
.distance_nominal
, vector
.distance
));
283 ok1(equals(stats
.distance_min
, vector
.distance
));
284 ok1(equals(stats
.distance_max
, vector
.distance
));
286 CheckLeg(tp2
, aircraft
, stats
);
288 ok1(!stats
.total
.vector_remaining
.IsValid());
289 CheckLegEqualsTotal(stats
.current_leg
.solution_remaining
,
290 stats
.total
.solution_remaining
);
296 const fixed
width(1);
297 OrderedTask
task(task_behaviour
);
298 const StartPoint
tp1(new LineSectorZone(wp1
.location
, width
),
300 ordered_task_behaviour
.start_constraints
);
302 const Waypoint wp3b
= MakeWaypoint(wp3
, 1500);
303 const ASTPoint
tp2(new LineSectorZone(wp3b
.location
, width
),
304 wp3b
, task_behaviour
);
306 const Waypoint wp4b
= MakeWaypoint(wp4
, 100);
307 const FinishPoint
tp3(new LineSectorZone(wp4b
.location
, width
),
308 wp4b
, task_behaviour
,
309 ordered_task_behaviour
.finish_constraints
, false);
311 task
.SetActiveTaskPoint(1);
313 ok1(task
.CheckTask());
315 AircraftState aircraft
;
317 aircraft
.location
= wp1
.location
;
318 aircraft
.altitude
= fixed(2000);
319 task
.Update(aircraft
, aircraft
, glide_polar
);
321 const TaskStats
&stats
= task
.GetStats();
322 ok1(stats
.task_valid
);
323 ok1(!stats
.task_started
);
324 ok1(!stats
.task_finished
);
325 ok1(!stats
.flight_mode_final_glide
);
327 CheckLeg(tp2
, aircraft
, stats
);
328 CheckTotal(aircraft
, stats
, tp1
, tp2
, tp3
);
334 const fixed
width(1);
335 OrderedTask
task(task_behaviour
);
336 const StartPoint
tp1(new LineSectorZone(wp1
.location
, width
),
338 ordered_task_behaviour
.start_constraints
);
340 const Waypoint wp3b
= MakeWaypoint(wp3
, 1500);
341 const ASTPoint
tp2(new LineSectorZone(wp3b
.location
, width
),
342 wp3b
, task_behaviour
);
344 const Waypoint wp5b
= MakeWaypoint(wp5
, 200);
345 const FinishPoint
tp3(new LineSectorZone(wp5b
.location
, width
),
346 wp5b
, task_behaviour
,
347 ordered_task_behaviour
.finish_constraints
, false);
349 task
.SetActiveTaskPoint(1);
351 ok1(task
.CheckTask());
353 AircraftState aircraft
;
355 aircraft
.location
= wp1
.location
;
356 aircraft
.altitude
= fixed(1200);
357 task
.Update(aircraft
, aircraft
, glide_polar
);
359 const TaskStats
&stats
= task
.GetStats();
360 ok1(stats
.task_valid
);
361 ok1(!stats
.task_started
);
362 ok1(!stats
.task_finished
);
363 ok1(!stats
.flight_mode_final_glide
);
365 CheckLeg(tp2
, aircraft
, stats
);
366 CheckTotal(aircraft
, stats
, tp1
, tp2
, tp3
);
372 const fixed
width(1);
373 OrderedTask
task(task_behaviour
);
374 const Waypoint wp1b
= MakeWaypoint(wp1
, 1500);
375 const StartPoint
tp1(new LineSectorZone(wp1b
.location
, width
),
376 wp1b
, task_behaviour
,
377 ordered_task_behaviour
.start_constraints
);
379 const ASTPoint
tp2(new LineSectorZone(wp2
.location
, width
),
380 wp2
, task_behaviour
);
382 const FinishPoint
tp3(new LineSectorZone(wp3
.location
, width
),
384 ordered_task_behaviour
.finish_constraints
, false);
386 task
.SetActiveTaskPoint(1);
388 ok1(task
.CheckTask());
390 AircraftState aircraft
;
392 aircraft
.location
= wp1
.location
;
393 aircraft
.altitude
= fixed(2500);
394 task
.Update(aircraft
, aircraft
, glide_polar
);
396 const TaskStats
&stats
= task
.GetStats();
397 ok1(stats
.task_valid
);
398 ok1(!stats
.task_started
);
399 ok1(!stats
.task_finished
);
400 ok1(!stats
.flight_mode_final_glide
);
402 CheckLeg(tp2
, aircraft
, stats
);
403 CheckTotal(aircraft
, stats
, tp1
, tp2
, tp3
);
409 TestFlightToFinish(fixed(2000));
410 TestFlightToFinish(fixed(1000));
418 int main(int argc
, char **argv
)
422 task_behaviour
.SetDefaults();
426 glide_polar
.SetMC(fixed(1));
429 glide_polar
.SetMC(fixed(2));
432 glide_polar
.SetMC(fixed(4));
435 return exit_status();