swift
Loading...
Searching...
No Matches
aircraftsituationlist.cpp
1// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3
5
6#include <algorithm>
7
8#include <QList>
9
11#include "misc/math/mathutils.h"
12#include "misc/pq/speed.h"
13
14using namespace swift::misc::geo;
15using namespace swift::misc::physical_quantities;
16using namespace swift::misc::math;
17
18SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::aviation, CAircraftSituation, CAircraftSituationList)
19
20namespace swift::misc::aviation
21{
25
27 {
28 if (this->isEmpty())
29 {
31 }
32 return this->front();
33 }
34
36 {
37 if (this->isEmpty())
38 {
40 }
41 return this->back();
42 }
43
45 {
46 if (this->size() > index)
47 {
48 return (*this)[index];
49 }
51 }
52
55 qint64 newerThanAdjustedMs)
56 {
57 if (elevationPlane.isNull())
58 {
59 return 0;
60 }
61 int c = 0;
62 for (CAircraftSituation &s : *this)
63 {
64 if (newerThanAdjustedMs >= 0 && s.getAdjustedMSecsSinceEpoch() <= newerThanAdjustedMs)
65 {
66 continue;
67 }
68 const bool set = s.setGroundElevationChecked(elevationPlane, info);
69 if (set)
70 {
71 c++;
72 }
73 }
74 return c;
75 }
76
77 int CAircraftSituationList::adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor)
78 {
79 int c = 0;
80 for (CAircraftSituation &situation : *this)
81 {
83 if (situation.adjustGroundFlag(parts, true, timeDeviationFactor))
84 {
85 c++;
86 };
87 }
88 return c;
89 }
90
92 {
93 return std::ranges::any_of(*this, [&details](const CAircraftSituation &sit) {
94 return sit.getOnGroundInfo().getGroundDetails() == details;
95 });
96 }
97
99 {
100 return std::ranges::all_of(*this, [&](const CAircraftSituation &situation) {
101 return situation.getOnGroundInfo().getGroundDetails() == details;
102 });
103 }
104
106 {
107 if (this->isEmpty())
108 {
109 return false;
110 }
112 {
113 return false;
114 }
115 return std::ranges::all_of(*this, [](const CAircraftSituation &situation) { return situation.isOnGround(); });
116 }
117
119 {
120 if (this->isEmpty())
121 {
122 return false;
123 }
125 {
126 return false;
127 }
128 return std::ranges::all_of(*this, [](const CAircraftSituation &situation) {
129 return situation.getOnGroundInfo().getOnGround() == COnGroundInfo::NotOnGround;
130 });
131 }
132
133 bool CAircraftSituationList::isConstDescending(bool alreadySortedLatestFirst) const
134 {
135 if (this->size() < 2)
136 {
137 return false;
138 }
140 {
141 return false;
142 }
143
144 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
146 for (const CAircraftSituation &situation : sorted)
147 {
148 if (!newerSituation.isNull())
149 {
150 Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
151 newerSituation.getAltitude().getReferenceDatum(),
152 Q_FUNC_INFO, "Wrong reference");
153 const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
155 {
156 return false;
157 }
158 }
159 newerSituation = situation;
160 }
161 return true;
162 }
163
164 bool CAircraftSituationList::isConstAscending(bool alreadySortedLatestFirst) const
165 {
166 if (this->size() < 2)
167 {
168 return false;
169 }
171 {
172 return false;
173 }
174
175 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
177 for (const CAircraftSituation &situation : sorted)
178 {
179 // latest first
180 if (!newerSituation.isNull())
181 {
182 Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
183 newerSituation.getAltitude().getReferenceDatum(),
184 Q_FUNC_INFO, "Wrong reference");
185 const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
187 {
188 return false;
189 }
190 }
191 newerSituation = situation;
192 }
193 return true;
194 }
195
196 bool CAircraftSituationList::isConstAccelerating(bool alreadySortedLatestFirst) const
197 {
198 if (this->size() < 2)
199 {
200 return false;
201 }
203 {
204 return false;
205 }
206
207 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
208 CSpeed newerGs = CSpeed::null();
209 for (const CAircraftSituation &situation : sorted)
210 {
211 if (!newerGs.isNull())
212 {
213 const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
214 if (!deltaSpeed.isPositiveWithEpsilonConsidered())
215 {
216 return false;
217 }
218 }
219 newerGs = situation.getGroundSpeed();
220 }
221 return true;
222 }
223
224 bool CAircraftSituationList::isConstDecelarating(bool alreadySortedLatestFirst) const
225 {
226 if (this->size() < 2)
227 {
228 return false;
229 }
231 {
232 return false;
233 }
234
235 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
236 CSpeed newerGs = CSpeed::null();
237 for (const CAircraftSituation &situation : sorted)
238 {
239 if (!newerGs.isNull())
240 {
241 const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
242 if (!deltaSpeed.isNegativeWithEpsilonConsidered())
243 {
244 return false;
245 }
246 }
247 newerGs = situation.getGroundSpeed();
248 }
249 return true;
250 }
251
252 QPair<bool, COnGroundInfo::IsOnGround>
253 CAircraftSituationList::isGndFlagStableChanging(bool alreadySortedLatestFirst) const
254 {
255 if (this->size() < 2)
256 {
257 return { false, COnGroundInfo::OnGroundSituationUnknown };
258 }
259
260 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
261 const COnGroundInfo::IsOnGround f = sorted.front().getOnGroundInfo().getOnGround();
262 const COnGroundInfo::IsOnGround t = sorted.back().getOnGroundInfo().getOnGround();
263 QPair<bool, COnGroundInfo::IsOnGround> ret(false, f); // changing to front (latest)
264 if (f == t)
265 {
266 return ret;
267 }
268
269 bool changed = false;
270
271 for (const CAircraftSituation &s : sorted)
272 {
273 if (!changed && s.getOnGroundInfo().getOnGround() == f)
274 {
275 continue;
276 } // find 1st changing
277 if (!changed)
278 {
279 changed = true;
280 continue;
281 } // just changed
282 if (s.getOnGroundInfo().getOnGround() != t)
283 {
284 return ret;
285 } // jitter, something like gnd, no gnd, gnd
286 }
287 ret.first = changed;
288 return ret;
289 }
290
291 bool CAircraftSituationList::isJustTakingOff(bool alreadySortedLatestFirst) const
292 {
293 if (this->size() < 2)
294 {
295 return false;
296 }
297
298 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
299 const CAircraftSituation latest = sorted.front();
300 if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::NotOnGround)
301 {
302 return false;
303 }
304 const int c = this->countOnGround(COnGroundInfo::OnGround);
305 return this->size() - 1 == c; // all others on ground
306 }
307
308 bool CAircraftSituationList::isJustTouchingDown(bool alreadySortedLatestFirst) const
309 {
310 if (this->size() < 2)
311 {
312 return false;
313 }
314
315 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
316 const CAircraftSituation latest = sorted.front();
317 if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::OnGround)
318 {
319 return false;
320 }
321 const int c = this->countOnGround(COnGroundInfo::NotOnGround);
322 return this->size() - 1 == c; // all others not on ground
323 }
324
325 bool CAircraftSituationList::isRotatingUp(bool alreadySortedLatestFirst) const
326 {
327 if (this->size() < 2)
328 {
329 return false;
330 }
331 const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
332 const QList<double> pitches = sorted.pitchValues(CAngleUnit::deg());
333 const QPair<double, double> stdDevAndMean = CMathUtils::standardDeviationAndMean(pitches);
334 const double minRotate = stdDevAndMean.first + stdDevAndMean.second; // outside std deviation range
335 const bool rotate = pitches.front() > minRotate;
336 return rotate;
337 }
338
340 {
341 return std::ranges::any_of(*this, [](const CAircraftSituation &situation) {
343 });
344 }
345
347 {
348 return std::count_if(begin(), end(), [&og](const CAircraftSituation &situation) {
349 return situation.getOnGroundInfo().getOnGround() == og;
350 });
351 }
352
354 const CLength &range) const
355 {
356 const CLength r = range.isNull() || range < CElevationPlane::singlePointRadius() ?
358 range;
359 CAircraftSituation situationWithElevation = CAircraftSituation::null();
360
361 CLength bestDistance = CLength::null();
362 for (const CAircraftSituation &s : *this)
363 {
364 if (!s.hasGroundElevation())
365 {
366 continue;
367 }
368
369 // we need to calculate distance to coordinates of the plane
370 // not the situation using the coordinate
371 // const CLength distance = s.calculateGreatCircleDistance(coordinate);
372 const CLength distance = s.getGroundElevationPlane().calculateGreatCircleDistance(coordinate);
373
374 if (distance > r)
375 {
376 continue;
377 }
378 if (bestDistance.isNull() || bestDistance > distance)
379 {
380 situationWithElevation = s;
381 bestDistance = distance;
382 }
383 }
384 return situationWithElevation;
385 }
386
388 {
389 for (CAircraftSituation &situation : *this)
390 {
391 situation.setOnGroundInfo(info);
392 }
393 }
394
396 {
397 for (CAircraftSituation &situation : *this)
398 {
399 situation.setOnGroundDetails(details);
400 }
401 }
402
404 {
405 if (offset.isNull() || this->isEmpty())
406 {
407 return 0;
408 }
409 int c = 0;
410 for (CAircraftSituation &s : *this)
411 {
412 s.addAltitudeOffset(offset);
413 c++;
414 }
415 return c;
416 }
417
422
424 {
425 if (this->empty())
426 {
427 return {};
428 }
429 CAircraftSituationList copy(*this);
430 copy.pop_front();
431 return copy;
432 }
433
435 {
436 QList<double> values;
437 for (const CAircraftSituation &s : *this)
438 {
439 values.push_back(s.getPitch().value(unit));
440 }
441 return values;
442 }
443
445 {
446 QList<double> values;
447 for (const CAircraftSituation &s : *this)
448 {
449 if (s.getGroundSpeed().isNull())
450 {
451 continue;
452 }
453 values.push_back(s.getGroundSpeed().value(unit));
454 }
455 return values;
456 }
457
459 {
460 QList<double> values;
461 for (const CAircraftSituation &s : *this)
462 {
463 if (s.getGroundElevation().isNull())
464 {
465 continue;
466 }
467 values.push_back(s.getGroundElevation().value(unit));
468 }
469 return values;
470 }
471
473 {
474 QList<double> values;
475 for (const CAircraftSituation &s : *this)
476 {
477 const CAltitude alt(s.getAltitude());
478 if (alt.isNull())
479 {
480 continue;
481 }
482 values.push_back(alt.value(unit));
483 }
484 return values;
485 }
486
488 {
489 const QList<double> gsValues = this->groundSpeedValues(CSpeedUnit::kts());
490 if (gsValues.size() != this->size())
491 {
492 return { CSpeed::null(), CSpeed::null() };
493 }
494 const QPair<double, double> gsKts = CMathUtils::standardDeviationAndMean(gsValues);
495 return { CSpeed(gsKts.first, CSpeedUnit::kts()), CSpeed(gsKts.second, CSpeedUnit::kts()) };
496 }
497
499 {
501 if (pitchValues.size() != this->size())
502 {
503 return { CAngle::null(), CAngle::null() };
504 }
505 const QPair<double, double> pitchDeg = CMathUtils::standardDeviationAndMean(pitchValues);
506 return { CAngle(pitchDeg.first, CAngleUnit::deg()), CAngle(pitchDeg.second, CAngleUnit::deg()) };
507 }
508
510 {
511 if (this->size() < 2)
512 {
513 return 0;
514 }
515 Q_ASSERT_X(m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst, Q_FUNC_INFO,
516 "need latest first");
517 int c = 0;
518 for (int i = 1; i < this->size(); ++i)
519 {
520 const CAircraftSituation &oldSituation = (*this)[i];
521 CAircraftSituation &newSituation = (*this)[i - 1];
522 if (oldSituation.transferGroundElevationFromMe(newSituation, radius))
523 {
524 c++;
525 }
526 }
527 return c;
528 }
529
531 const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const
532 {
533 if (this->size() < minValues)
534 {
535 return CElevationPlane::null();
536 } // no change to succeed
537
539 .findWithinRange(reference, range)
540 .sortedByEuclideanDistanceSquared(reference);
541 if (sorted.size() < minValues)
542 {
543 return CElevationPlane::null();
544 }
545 QList<double> valuesInFt;
546 for (const CAircraftSituation &situation : *this)
547 {
549 {
550 continue;
551 }
552 const bool canUse =
553 !situation.isMoving() || (situation.isOnGroundFromNetwork() || situation.isOnGroundFromParts());
554 if (!canUse)
555 {
556 continue;
557 }
558
559 const double elvFt = situation.getGroundElevationPlane().getAltitude().value(CLengthUnit::ft());
560 valuesInFt.push_back(elvFt);
561 if (valuesInFt.size() >= sufficientValues)
562 {
563 break;
564 }
565 }
566
567 if (valuesInFt.size() < minValues)
568 {
569 return CElevationPlane::null();
570 }
571
572 static const double MaxDevFt = CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft());
573 const QPair<double, double> elvStdDevMean = CMathUtils::standardDeviationAndMean(valuesInFt);
574 if (elvStdDevMean.first > MaxDevFt)
575 {
576 return CElevationPlane::null();
577 }
578 return { reference, elvStdDevMean.second, CElevationPlane::singlePointRadius() };
579 }
580} // namespace swift::misc::aviation
QPair< CAngle, CAngle > CAnglePair
Pair of angle.
Definition angle.h:115
static CAngleUnit deg()
Degrees.
Definition units.h:276
Q_REQUIRED_RESULT CSequence sorted(Predicate p) const
Definition sequence.h:610
void pop_front()
Removes an element at the front of the sequence.
Definition sequence.h:383
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
Value object encapsulating information of aircraft's parts.
Value object encapsulating information of an aircraft's situation.
bool hasGroundElevation() const
Is ground elevation value available.
CAltitude addAltitudeOffset(const physical_quantities::CLength &offset)
Add offset to altitude.
bool transferGroundElevationFromMe(CAircraftSituation &transferToSituation, const physical_quantities::CLength &radius=geo::CElevationPlane::singlePointRadius()) const
Transfer from "this" situation to otherSituation.
aviation::COnGroundInfo getOnGroundInfo() const
On ground info.
GndElevationInfo getGroundElevationInfo() const
How did we get gnd.elevation?
GndElevationInfo
Where did we get elevation from?
@ FromProvider
from swift::misc::simulation::ISimulationEnvironmentProvider
bool setGroundElevationChecked(const geo::CElevationPlane &elevationPlane, GndElevationInfo info, bool transferred=false)
Set elevation of the ground directly beneath, but checked.
static const CAircraftSituation & null()
Null situation.
const CAltitude & getGroundElevation() const
Elevation of the ground directly beneath.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
const physical_quantities::CAngle & getPitch() const
Get pitch.
const CAltitude & getAltitude() const
Get altitude.
const physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
const geo::CElevationPlane & getGroundElevationPlane() const
Elevation of the ground directly beneath.
bool isMoving() const
Is moving? Means ground speed > epsilon.
bool isOnGroundFromNetwork() const
On ground by network flag?
void setOnGroundDetails(COnGroundInfo::OnGroundDetails details)
On ground details.
bool isOnGroundFromParts() const
On ground by parts?
static const physical_quantities::CLength & allowedAltitudeDeviation()
Within this range deviation is so small we consider values "almost constant".
bool adjustGroundFlag(const CAircraftParts &parts, bool alwaysSetDetails, double timeDeviationFactor=0.1, qint64 *differenceMs=nullptr)
Transfer ground flag from parts.
bool isJustTouchingDown(bool alreadySortedLatestFirst=false) const
Is just touching down?
CAircraftSituation findClosestElevationWithinRange(const geo::ICoordinateGeodetic &coordinate, const physical_quantities::CLength &range=geo::CElevationPlane::singlePointRadius()) const
CLosest elevation within given range.
CAircraftSituationList withoutFrontSituation() const
Remove the first situation.
bool isConstAccelerating(bool alreadySortedLatestFirst=false) const
Constantly accelerating?
CAircraftSituationList()=default
Default constructor.
bool isConstNotOnGround() const
Are all situations not on ground?
physical_quantities::CAnglePair pitchStandardDeviationAndMean() const
Pitch angles standard deviation and mean.
QPair< bool, COnGroundInfo::IsOnGround > isGndFlagStableChanging(bool alreadySortedLatestFirst=false) const
Is the ground flag changing for the situations.
int countOnGround(COnGroundInfo::IsOnGround og) const
Count the number of situations with COnGroundInfo::IsOnGround.
CSequence()=default
Default constructor.
QList< double > elevationValues(const physical_quantities::CLengthUnit &unit) const
All elevation values.
QList< double > groundSpeedValues(const physical_quantities::CSpeedUnit &unit) const
All ground speed values.
QList< double > pitchValues(const physical_quantities::CAngleUnit &unit) const
All pitch values.
int setGroundElevationChecked(const geo::CElevationPlane &elevationPlane, CAircraftSituation::GndElevationInfo info, qint64 newerThanAdjustedMs=-1)
Set ground elevation from elevation plane.
geo::CElevationPlane averageElevationOfTaxiingOnGroundAircraft(const CAircraftSituation &reference, const physical_quantities::CLength &range, int minValues=1, int sufficientValues=2) const
Average elevation for "nearby" aircraft "not/slowly moving" and having an elevation.
bool isConstDecelarating(bool alreadySortedLatestFirst=false) const
Constantly decelarating?
CAircraftSituation indexOrNull(int index) const
Index or NULL.
bool isConstDescending(bool alreadySortedLatestFirst=false) const
Constantly descending?
int adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor=0.1)
Adjust flag from parts by using CAircraftSituation::adjustGroundFlag.
bool isConstOnGround() const
Are all situations on ground?
int addAltitudeOffset(const physical_quantities::CLength &offset)
Add an offset to each altitude.
bool isRotatingUp(bool alreadySortedLatestFirst=false) const
Is rotating up?
bool isJustTakingOff(bool alreadySortedLatestFirst=false) const
Is just taking off?
int transferElevationForward(const physical_quantities::CLength &radius=geo::CElevationPlane::singlePointRadius())
Transfer elevations forward from older to newer.
void setOnGroundInfo(const COnGroundInfo &info)
Set on ground.
void setOnGroundDetails(COnGroundInfo::OnGroundDetails details)
Set on ground details for all situations.
bool areAllOnGroundDetailsSame(COnGroundInfo::OnGroundDetails details) const
Are all on ground details the same?
bool containsOnGroundDetails(COnGroundInfo::OnGroundDetails details) const
Contains on ground details?
QList< double > altitudeValues(const physical_quantities::CLengthUnit &unit) const
All altitude values.
bool isSortedAdjustedLatestFirstWithoutNullPositions() const
Latest first and no null positions?
bool isConstAscending(bool alreadySortedLatestFirst=false) const
Constantly ascending?
CAircraftSituation backOrNull() const
Back or NULL.
CAircraftSituation frontOrNull() const
Front or NULL.
bool containsPushBack() const
Contains any push back?
physical_quantities::CSpeedPair groundSpeedStandardDeviationAndMean() const
Ground speed standard deviation and mean.
Altitude as used in aviation, can be AGL or MSL altitude.
Definition altitude.h:52
ReferenceDatum getReferenceDatum() const
Get reference datum (MSL or AGL).
Definition altitude.h:145
Information about the ground status.
OnGroundDetails
Reliability of on ground information.
@ InFromParts
set from aircraft parts
IsOnGround getOnGround() const
Is on ground?
OnGroundDetails getGroundDetails() const
Get ground details.
Plane of same elevation, can be a single point or larger area (e.g. airport).
static const physical_quantities::CLength & singlePointRadius()
Radius for single point.
bool isNull() const
Existing value?
const aviation::CAltitude & getAltitude() const
Altitude (synonym for geodetic height).
static const CElevationPlane & null()
NULL plane.
Geodetic coordinate, a position in 3D space relative to the reference geoid.
physical_quantities::CLength calculateGreatCircleDistance(const ICoordinateGeodetic &otherCoordinate) const
Great circle distance.
static QPair< double, double > standardDeviationAndMean(const QList< double > &values)
Standard deviation (first) and mean (second).
Physical unit angle (radians, degrees).
Definition angle.h:23
Specialized class for angles (degrees, radian).
Definition units.h:231
Physical unit length (length).
Definition length.h:18
Specialized class for distance units (meter, foot, nautical miles).
Definition units.h:95
static CLengthUnit ft()
Foot ft.
Definition units.h:157
bool isNegativeWithEpsilonConsidered() const
Value <= 0 epsilon considered.
double value(MU unit) const
Value in given unit.
bool isPositiveWithEpsilonConsidered() const
Value >= 0 epsilon considered.
Specialized class for speed units (m/s, ft/s, NM/h).
Definition units.h:764
static CSpeedUnit kts()
Knots.
Definition units.h:817
QList< T >::reference front()
void push_back(QList< T >::parameter_type value)
qsizetype size() const const
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition sequence.h:63
QPair< CSpeed, CSpeed > CSpeedPair
Pair of speeds.
Definition speed.h:32