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"
38 BrokenDateTime takeoff_time
, release_time
, landing_time
;
39 GeoPoint takeoff_location
, release_location
, landing_location
;
46 takeoff_location
.SetInvalid();
47 landing_location
.SetInvalid();
48 release_location
.SetInvalid();
53 static CirclingComputer circling_computer
;
54 static FlightPhaseDetector flight_phase_detector
;
57 Update(const MoreData
&basic
, const FlyingState
&state
,
60 if (!basic
.time_available
|| !basic
.date_available
)
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
;
81 Update(const MoreData
&basic
, const DerivedInfo
&calculated
,
84 Update(basic
, calculated
.flight
, result
);
88 ComputeCircling(DebugReplay
&replay
, const CirclingSettings
&circling_settings
)
90 circling_computer
.TurnRate(replay
.SetCalculated(),
92 replay
.Calculated().flight
);
93 circling_computer
.Turning(replay
.SetCalculated(),
95 replay
.Calculated().flight
,
100 Finish(const MoreData
&basic
, const DerivedInfo
&calculated
,
103 if (!basic
.time_available
|| !basic
.date_available
)
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
;
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())
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
148 last_location
= basic
.location
;
150 if (!released
&& !negative(replay
.Calculated().flight
.release_time
)) {
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 */
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();
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();
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
);
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
);
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
);
220 WriteResult(JSON::ObjectWriter
&root
, const Result
&result
)
222 root
.WriteElement("events", WriteEvents
, result
);
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
));
239 std::max((int)point
.GetTime() - (int)previous
->GetTime(), 0);
240 object
.WriteElement("duration", JSON::WriteUnsigned
, duration
);
243 fixed speed
= distance
/ duration
;
244 object
.WriteElement("speed", JSON::WriteFixed
, speed
);
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
);
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
);
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]);
289 WriteDMSt(TextWriter
&writer
, const ContestStatistics
&stats
)
291 JSON::ObjectWriter
object(writer
);
293 object
.WriteElement("quadrilateral", WriteContest
,
294 stats
.result
[0], stats
.solution
[0]);
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"
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)");
321 while ((arg
= args
.PeekNext()) != nullptr && *arg
== '-') {
325 if ((value
= StringAfterPrefix(arg
, "--full-points=")) != nullptr) {
326 unsigned _points
= strtol(value
, NULL
, 10);
328 full_max_points
= _points
;
330 fputs("The start parameter could not be parsed correctly.\n", stderr
);
334 } else if ((value
= StringAfterPrefix(arg
, "--triangle-points=")) != nullptr) {
335 unsigned _points
= strtol(value
, NULL
, 10);
337 triangle_max_points
= _points
;
339 fputs("The start parameter could not be parsed correctly.\n", stderr
);
343 } else if ((value
= StringAfterPrefix(arg
, "--sprint-points=")) != nullptr) {
344 unsigned _points
= strtol(value
, NULL
, 10);
346 sprint_max_points
= _points
;
348 fputs("The start parameter could not be parsed correctly.\n", stderr
);
357 DebugReplay
*replay
= CreateDebugReplay(args
);
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
);
368 Run(*replay
, result
, full_trace
, triangle_trace
, sprint_trace
);
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
);