Revert "Device/Driver/LX: Add small delay after baud rate change"
[xcsoar.git] / test / src / AnalyseFlight.cpp
blob8180a9858662ba80c46e3d673390adf17ce732ff
1 /* Copyright_License {
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/Trace/Trace.hpp"
24 #include "Contest/ContestManager.hpp"
25 #include "OS/Args.hpp"
26 #include "Computer/CirclingComputer.hpp"
27 #include "DebugReplay.hpp"
28 #include "Util/Macros.hpp"
29 #include "IO/TextWriter.hpp"
30 #include "Formatter/TimeFormatter.hpp"
31 #include "JSON/Writer.hpp"
32 #include "JSON/GeoWriter.hpp"
33 #include "FlightPhaseDetector.hpp"
34 #include "FlightPhaseJSON.hpp"
35 #include "Computer/Settings.hpp"
37 struct Result {
38 BrokenDateTime takeoff_time, release_time, landing_time;
39 GeoPoint takeoff_location, release_location, landing_location;
41 Result() {
42 takeoff_time.Clear();
43 landing_time.Clear();
44 release_time.Clear();
46 takeoff_location.SetInvalid();
47 landing_location.SetInvalid();
48 release_location.SetInvalid();
53 static CirclingComputer circling_computer;
54 static FlightPhaseDetector flight_phase_detector;
56 static void
57 Update(const MoreData &basic, const FlyingState &state,
58 Result &result)
60 if (!basic.time_available || !basic.date_available)
61 return;
63 if (state.flying && !result.takeoff_time.Plausible()) {
64 result.takeoff_time = basic.GetDateTimeAt(state.takeoff_time);
65 result.takeoff_location = state.takeoff_location;
68 if (!state.flying && result.takeoff_time.Plausible() &&
69 !result.landing_time.Plausible()) {
70 result.landing_time = basic.GetDateTimeAt(state.landing_time);
71 result.landing_location = state.landing_location;
74 if (!negative(state.release_time) && !result.release_time.Plausible()) {
75 result.release_time = basic.GetDateTimeAt(state.release_time);
76 result.release_location = state.release_location;
80 static void
81 Update(const MoreData &basic, const DerivedInfo &calculated,
82 Result &result)
84 Update(basic, calculated.flight, result);
87 static void
88 ComputeCircling(DebugReplay &replay, const CirclingSettings &circling_settings)
90 circling_computer.TurnRate(replay.SetCalculated(),
91 replay.Basic(),
92 replay.Calculated().flight);
93 circling_computer.Turning(replay.SetCalculated(),
94 replay.Basic(),
95 replay.Calculated().flight,
96 circling_settings);
99 static void
100 Finish(const MoreData &basic, const DerivedInfo &calculated,
101 Result &result)
103 if (!basic.time_available || !basic.date_available)
104 return;
106 if (result.takeoff_time.Plausible() && !result.landing_time.Plausible()) {
107 result.landing_time = basic.date_time_utc;
109 if (basic.location_available)
110 result.landing_location = basic.location;
114 static void
115 Run(DebugReplay &replay, Result &result,
116 Trace &full_trace, Trace &triangle_trace, Trace &sprint_trace)
118 CirclingSettings circling_settings;
119 circling_settings.SetDefaults();
121 bool released = false;
123 GeoPoint last_location = GeoPoint::Invalid();
124 constexpr Angle max_longitude_change = Angle::Degrees(30);
125 constexpr Angle max_latitude_change = Angle::Degrees(1);
127 while (replay.Next()) {
128 ComputeCircling(replay, circling_settings);
130 const MoreData &basic = replay.Basic();
132 Update(basic, replay.Calculated(), result);
133 flight_phase_detector.Update(replay.Basic(), replay.Calculated());
135 if (!basic.time_available || !basic.location_available ||
136 !basic.NavAltitudeAvailable())
137 continue;
139 if (last_location.IsValid() &&
140 ((last_location.latitude - basic.location.latitude).Absolute() > max_latitude_change ||
141 (last_location.longitude - basic.location.longitude).Absolute() > max_longitude_change))
142 /* there was an implausible warp, which is usually triggered by
143 an invalid point declared "valid" by a bugged logger; if that
144 happens, we stop the analysis, because the IGC file is
145 obviously broken */
146 break;
148 last_location = basic.location;
150 if (!released && !negative(replay.Calculated().flight.release_time)) {
151 released = true;
153 full_trace.EraseEarlierThan(replay.Calculated().flight.release_time);
154 triangle_trace.EraseEarlierThan(replay.Calculated().flight.release_time);
155 sprint_trace.EraseEarlierThan(replay.Calculated().flight.release_time);
158 if (released && !replay.Calculated().flight.flying)
159 /* the aircraft has landed, stop here */
160 /* TODO: at some point, we might want to emit the analysis of
161 all flights in this IGC file */
162 break;
164 const TracePoint point(basic);
165 full_trace.push_back(point);
166 triangle_trace.push_back(point);
167 sprint_trace.push_back(point);
170 Update(replay.Basic(), replay.Calculated(), result);
171 Finish(replay.Basic(), replay.Calculated(), result);
172 flight_phase_detector.Finish();
175 gcc_pure
176 static ContestStatistics
177 SolveContest(Contest contest,
178 Trace &full_trace, Trace &triangle_trace, Trace &sprint_trace)
180 ContestManager manager(contest, full_trace, triangle_trace, sprint_trace);
181 manager.SolveExhaustive();
182 return manager.GetStats();
185 static void
186 WriteEventAttributes(TextWriter &writer,
187 const BrokenDateTime &time, const GeoPoint &location)
189 JSON::ObjectWriter object(writer);
191 if (time.Plausible()) {
192 NarrowString<64> buffer;
193 FormatISO8601(buffer.buffer(), time);
194 object.WriteElement("time", JSON::WriteString, buffer);
197 if (location.IsValid())
198 JSON::WriteGeoPointAttributes(object, location);
201 static void
202 WriteEvent(JSON::ObjectWriter &object, const char *name,
203 const BrokenDateTime &time, const GeoPoint &location)
205 if (time.Plausible() || location.IsValid())
206 object.WriteElement(name, WriteEventAttributes, time, location);
209 static void
210 WriteEvents(TextWriter &writer, const Result &result)
212 JSON::ObjectWriter object(writer);
214 WriteEvent(object, "takeoff", result.takeoff_time, result.takeoff_location);
215 WriteEvent(object, "release", result.release_time, result.release_location);
216 WriteEvent(object, "landing", result.landing_time, result.landing_location);
219 static void
220 WriteResult(JSON::ObjectWriter &root, const Result &result)
222 root.WriteElement("events", WriteEvents, result);
225 static void
226 WritePoint(TextWriter &writer, const ContestTracePoint &point,
227 const ContestTracePoint *previous)
229 JSON::ObjectWriter object(writer);
231 object.WriteElement("time", JSON::WriteLong, (long)point.GetTime());
232 JSON::WriteGeoPointAttributes(object, point.GetLocation());
234 if (previous != NULL) {
235 fixed distance = point.DistanceTo(previous->GetLocation());
236 object.WriteElement("distance", JSON::WriteUnsigned, uround(distance));
238 unsigned duration =
239 std::max((int)point.GetTime() - (int)previous->GetTime(), 0);
240 object.WriteElement("duration", JSON::WriteUnsigned, duration);
242 if (duration > 0) {
243 fixed speed = distance / duration;
244 object.WriteElement("speed", JSON::WriteFixed, speed);
249 static void
250 WriteTrace(TextWriter &writer, const ContestTraceVector &trace)
252 JSON::ArrayWriter array(writer);
254 const ContestTracePoint *previous = NULL;
255 for (auto i = trace.begin(), end = trace.end(); i != end; ++i) {
256 array.WriteElement(WritePoint, *i, previous);
257 previous = &*i;
261 static void
262 WriteContest(TextWriter &writer,
263 const ContestResult &result, const ContestTraceVector &trace)
265 JSON::ObjectWriter object(writer);
267 object.WriteElement("score", JSON::WriteFixed, result.score);
268 object.WriteElement("distance", JSON::WriteFixed, result.distance);
269 object.WriteElement("duration", JSON::WriteUnsigned, (unsigned)result.time);
270 object.WriteElement("speed", JSON::WriteFixed, result.GetSpeed());
272 object.WriteElement("turnpoints", WriteTrace, trace);
275 static void
276 WriteOLCPlus(TextWriter &writer, const ContestStatistics &stats)
278 JSON::ObjectWriter object(writer);
280 object.WriteElement("classic", WriteContest,
281 stats.result[0], stats.solution[0]);
282 object.WriteElement("triangle", WriteContest,
283 stats.result[1], stats.solution[1]);
284 object.WriteElement("plus", WriteContest,
285 stats.result[2], stats.solution[2]);
288 static void
289 WriteDMSt(TextWriter &writer, const ContestStatistics &stats)
291 JSON::ObjectWriter object(writer);
293 object.WriteElement("quadrilateral", WriteContest,
294 stats.result[0], stats.solution[0]);
297 static void
298 WriteContests(TextWriter &writer, const ContestStatistics &olc_plus,
299 const ContestStatistics &dmst)
301 JSON::ObjectWriter object(writer);
303 object.WriteElement("olc_plus", WriteOLCPlus, olc_plus);
304 object.WriteElement("dmst", WriteDMSt, dmst);
307 int main(int argc, char **argv)
309 unsigned full_max_points = 512,
310 triangle_max_points = 1024,
311 sprint_max_points = 64;
313 Args args(argc, argv,
314 "[options] DRIVER FILE\n"
315 "Options:\n"
316 " --full-points=512 Maximum number of full trace points (default = 512)\n"
317 " --triangle-points=1024 Maximum number of triangle trace points (default = 1024)\n"
318 " --sprint-points=64 Maximum number of sprint trace points (default = 64)");
320 const char *arg;
321 while ((arg = args.PeekNext()) != nullptr && *arg == '-') {
322 args.Skip();
324 const char *value;
325 if ((value = StringAfterPrefix(arg, "--full-points=")) != nullptr) {
326 unsigned _points = strtol(value, NULL, 10);
327 if (_points > 0)
328 full_max_points = _points;
329 else {
330 fputs("The start parameter could not be parsed correctly.\n", stderr);
331 args.UsageError();
334 } else if ((value = StringAfterPrefix(arg, "--triangle-points=")) != nullptr) {
335 unsigned _points = strtol(value, NULL, 10);
336 if (_points > 0)
337 triangle_max_points = _points;
338 else {
339 fputs("The start parameter could not be parsed correctly.\n", stderr);
340 args.UsageError();
343 } else if ((value = StringAfterPrefix(arg, "--sprint-points=")) != nullptr) {
344 unsigned _points = strtol(value, NULL, 10);
345 if (_points > 0)
346 sprint_max_points = _points;
347 else {
348 fputs("The start parameter could not be parsed correctly.\n", stderr);
349 args.UsageError();
352 } else {
353 args.UsageError();
357 DebugReplay *replay = CreateDebugReplay(args);
358 if (replay == NULL)
359 return EXIT_FAILURE;
361 args.ExpectEnd();
363 static Trace full_trace(0, Trace::null_time, full_max_points);
364 static Trace triangle_trace(0, Trace::null_time, triangle_max_points);
365 static Trace sprint_trace(0, 9000, sprint_max_points);
367 Result result;
368 Run(*replay, result, full_trace, triangle_trace, sprint_trace);
369 delete replay;
371 const ContestStatistics olc_plus = SolveContest(Contest::OLC_PLUS, full_trace, triangle_trace, sprint_trace);
372 const ContestStatistics dmst = SolveContest(Contest::DMST, full_trace, triangle_trace, sprint_trace);
374 TextWriter writer("/dev/stdout", true);
377 JSON::ObjectWriter root(writer);
379 WriteResult(root, result);
380 root.WriteElement("phases", WritePhaseList,
381 flight_phase_detector.GetPhases());
382 root.WriteElement("performance", WritePerformanceStats,
383 flight_phase_detector.GetTotals());
384 root.WriteElement("contests", WriteContests, olc_plus, dmst);