swift
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 <QList>
7 
9 #include "misc/math/mathutils.h"
10 #include "misc/pq/speed.h"
11 
12 using namespace swift::misc::geo;
13 using namespace swift::misc::physical_quantities;
14 using namespace swift::misc::math;
15 
16 SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::aviation, CAircraftSituation, CAircraftSituationList)
17 
18 namespace swift::misc::aviation
19 {
20  CAircraftSituationList::CAircraftSituationList(const CSequence<CAircraftSituation> &other)
22  {}
23 
25  {
26  if (this->isEmpty()) { return CAircraftSituation::null(); }
27  return this->front();
28  }
29 
31  {
32  if (this->isEmpty()) { return CAircraftSituation::null(); }
33  return this->back();
34  }
35 
37  {
38  if (this->size() > index) { return (*this)[index]; }
39  return CAircraftSituation::null();
40  }
41 
44  qint64 newerThanAdjustedMs)
45  {
46  if (elevationPlane.isNull()) { return 0; }
47  int c = 0;
48  for (CAircraftSituation &s : *this)
49  {
50  if (newerThanAdjustedMs >= 0 && s.getAdjustedMSecsSinceEpoch() <= newerThanAdjustedMs) { continue; }
51  const bool set = s.setGroundElevationChecked(elevationPlane, info);
52  if (set) { c++; }
53  }
54  return c;
55  }
56 
57  int CAircraftSituationList::adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor)
58  {
59  int c = 0;
60  for (CAircraftSituation &situation : *this)
61  {
63  if (situation.adjustGroundFlag(parts, true, timeDeviationFactor)) { c++; };
64  }
65  return c;
66  }
67 
69  {
70  return std::any_of(begin(), end(), [&details](const CAircraftSituation &sit) {
71  return sit.getOnGroundInfo().getGroundDetails() == details;
72  });
73  }
74 
76  {
77  for (const CAircraftSituation &situation : *this)
78  {
79  if (situation.getOnGroundInfo().getGroundDetails() != details) { return false; }
80  }
81  return true;
82  }
83 
85  {
86  if (this->isEmpty()) { return false; }
87  if (this->containsNullPositionOrHeight()) { return false; }
88  return std::all_of(begin(), end(), [](const CAircraftSituation &situation) { return situation.isOnGround(); });
89  }
90 
92  {
93  if (this->isEmpty()) { return false; }
94  if (this->containsNullPositionOrHeight()) { return false; }
95  return std::all_of(begin(), end(), [](const CAircraftSituation &situation) {
96  return situation.getOnGroundInfo().getOnGround() == COnGroundInfo::NotOnGround;
97  });
98  }
99 
100  bool CAircraftSituationList::isConstDescending(bool alreadySortedLatestFirst) const
101  {
102  if (this->size() < 2) { return false; }
103  if (this->containsNullPositionOrHeight()) { return false; }
104 
105  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
106  CAircraftSituation newerSituation = CAircraftSituation::null();
107  for (const CAircraftSituation &situation : sorted)
108  {
109  if (!newerSituation.isNull())
110  {
111  Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
112  newerSituation.getAltitude().getReferenceDatum(),
113  Q_FUNC_INFO, "Wrong reference");
114  const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
115  if (!delta.isNegativeWithEpsilonConsidered()) { return false; }
116  }
117  newerSituation = situation;
118  }
119  return true;
120  }
121 
122  bool CAircraftSituationList::isConstAscending(bool alreadySortedLatestFirst) const
123  {
124  if (this->size() < 2) { return false; }
125  if (this->containsNullPositionOrHeight()) { return false; }
126 
127  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
128  CAircraftSituation newerSituation = CAircraftSituation::null();
129  for (const CAircraftSituation &situation : sorted)
130  {
131  // latest first
132  if (!newerSituation.isNull())
133  {
134  Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
135  newerSituation.getAltitude().getReferenceDatum(),
136  Q_FUNC_INFO, "Wrong reference");
137  const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
138  if (!delta.isPositiveWithEpsilonConsidered()) { return false; }
139  }
140  newerSituation = situation;
141  }
142  return true;
143  }
144 
145  bool CAircraftSituationList::isConstAccelerating(bool alreadySortedLatestFirst) const
146  {
147  if (this->size() < 2) { return false; }
148  if (this->containsNullPositionOrHeight()) { return false; }
149 
150  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
151  CSpeed newerGs = CSpeed::null();
152  for (const CAircraftSituation &situation : sorted)
153  {
154  if (!newerGs.isNull())
155  {
156  const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
157  if (!deltaSpeed.isPositiveWithEpsilonConsidered()) { return false; }
158  }
159  newerGs = situation.getGroundSpeed();
160  }
161  return true;
162  }
163 
164  bool CAircraftSituationList::isConstDecelarating(bool alreadySortedLatestFirst) const
165  {
166  if (this->size() < 2) { return false; }
167  if (this->containsNullPositionOrHeight()) { return false; }
168 
169  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
170  CSpeed newerGs = CSpeed::null();
171  for (const CAircraftSituation &situation : sorted)
172  {
173  if (!newerGs.isNull())
174  {
175  const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
176  if (!deltaSpeed.isNegativeWithEpsilonConsidered()) { return false; }
177  }
178  newerGs = situation.getGroundSpeed();
179  }
180  return true;
181  }
182 
183  QPair<bool, COnGroundInfo::IsOnGround>
184  CAircraftSituationList::isGndFlagStableChanging(bool alreadySortedLatestFirst) const
185  {
186  if (this->size() < 2)
187  {
188  return QPair<bool, COnGroundInfo::IsOnGround>(false, COnGroundInfo::OnGroundSituationUnknown);
189  }
190 
191  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
194  QPair<bool, COnGroundInfo::IsOnGround> ret(false, f); // changing to front (latest)
195  if (f == t) { return ret; }
196 
197  bool changed = false;
198 
199  for (const CAircraftSituation &s : sorted)
200  {
201  if (!changed && s.getOnGroundInfo().getOnGround() == f) { continue; } // find 1st changing
202  if (!changed)
203  {
204  changed = true;
205  continue;
206  } // just changed
207  if (s.getOnGroundInfo().getOnGround() != t) { return ret; } // jitter, something like gnd, no gnd, gnd
208  }
209  ret.first = changed;
210  return ret;
211  }
212 
213  bool CAircraftSituationList::isJustTakingOff(bool alreadySortedLatestFirst) const
214  {
215  if (this->size() < 2) { return false; }
216 
217  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
218  const CAircraftSituation latest = sorted.front();
219  if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::NotOnGround) { return false; }
220  const int c = this->countOnGround(COnGroundInfo::OnGround);
221  return this->size() - 1 == c; // all others on ground
222  }
223 
224  bool CAircraftSituationList::isJustTouchingDown(bool alreadySortedLatestFirst) const
225  {
226  if (this->size() < 2) { return false; }
227 
228  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
229  const CAircraftSituation latest = sorted.front();
230  if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::OnGround) { return false; }
231  const int c = this->countOnGround(COnGroundInfo::NotOnGround);
232  return this->size() - 1 == c; // all others not on ground
233  }
234 
235  bool CAircraftSituationList::isRotatingUp(bool alreadySortedLatestFirst) const
236  {
237  if (this->size() < 2) { return false; }
238  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
239  const QList<double> pitches = sorted.pitchValues(CAngleUnit::deg());
240  const QPair<double, double> stdDevAndMean = CMathUtils::standardDeviationAndMean(pitches);
241  const double minRotate = stdDevAndMean.first + stdDevAndMean.second; // outside std deviation range
242  const bool rotate = pitches.front() > minRotate;
243  return rotate;
244  }
245 
247  {
248  for (const CAircraftSituation &situation : *this)
249  {
250  if (situation.getGroundSpeed().isNegativeWithEpsilonConsidered()) { return true; }
251  }
252  return false;
253  }
254 
256  {
257  return std::count_if(begin(), end(), [&og](const CAircraftSituation &situation) {
258  return situation.getOnGroundInfo().getOnGround() == og;
259  });
260  }
261 
263  const CLength &range) const
264  {
265  const CLength r = range.isNull() || range < CElevationPlane::singlePointRadius() ?
266  CElevationPlane::singlePointRadius() :
267  range;
268  CAircraftSituation situationWithElevation = CAircraftSituation::null();
269 
270  CLength bestDistance = CLength::null();
271  for (const CAircraftSituation &s : *this)
272  {
273  if (!s.hasGroundElevation()) { continue; }
274 
275  // we need to calculate distance to coordinates of the plane
276  // not the situation using the coordinate
277  // const CLength distance = s.calculateGreatCircleDistance(coordinate);
278  const CLength distance = s.getGroundElevationPlane().calculateGreatCircleDistance(coordinate);
279 
280  if (distance > r) { continue; }
281  if (bestDistance.isNull() || bestDistance > distance)
282  {
283  situationWithElevation = s;
284  bestDistance = distance;
285  }
286  }
287  return situationWithElevation;
288  }
289 
291  {
292  for (CAircraftSituation &situation : *this) { situation.setOnGroundInfo(info); }
293  }
294 
296  {
297  for (CAircraftSituation &situation : *this) { situation.setOnGroundDetails(details); }
298  }
299 
301  {
302  if (offset.isNull() || this->isEmpty()) { return 0; }
303  int c = 0;
304  for (CAircraftSituation &s : *this)
305  {
306  s.addAltitudeOffset(offset);
307  c++;
308  }
309  return c;
310  }
311 
313  {
314  return this->isSortedAdjustedLatestFirst() && !this->containsNullPosition();
315  }
316 
318  {
319  if (this->empty()) { return CAircraftSituationList(); }
320  CAircraftSituationList copy(*this);
321  copy.pop_front();
322  return copy;
323  }
324 
325  QList<double> CAircraftSituationList::pitchValues(const CAngleUnit &unit) const
326  {
327  QList<double> values;
328  for (const CAircraftSituation &s : *this) { values.push_back(s.getPitch().value(unit)); }
329  return values;
330  }
331 
332  QList<double> CAircraftSituationList::groundSpeedValues(const CSpeedUnit &unit) const
333  {
334  QList<double> values;
335  for (const CAircraftSituation &s : *this)
336  {
337  if (s.getGroundSpeed().isNull()) { continue; }
338  values.push_back(s.getGroundSpeed().value(unit));
339  }
340  return values;
341  }
342 
343  QList<double> CAircraftSituationList::elevationValues(const CLengthUnit &unit) const
344  {
345  QList<double> values;
346  for (const CAircraftSituation &s : *this)
347  {
348  if (s.getGroundElevation().isNull()) { continue; }
349  values.push_back(s.getGroundElevation().value(unit));
350  }
351  return values;
352  }
353 
354  QList<double> CAircraftSituationList::altitudeValues(const CLengthUnit &unit) const
355  {
356  QList<double> values;
357  for (const CAircraftSituation &s : *this)
358  {
359  const CAltitude alt(s.getAltitude());
360  if (alt.isNull()) { continue; }
361  values.push_back(alt.value(unit));
362  }
363  return values;
364  }
365 
367  {
368  const QList<double> gsValues = this->groundSpeedValues(CSpeedUnit::kts());
369  if (gsValues.size() != this->size()) { return QPair<CSpeed, CSpeed>(CSpeed::null(), CSpeed::null()); }
370  const QPair<double, double> gsKts = CMathUtils::standardDeviationAndMean(gsValues);
371  return CSpeedPair(CSpeed(gsKts.first, CSpeedUnit::kts()), CSpeed(gsKts.second, CSpeedUnit::kts()));
372  }
373 
375  {
376  const QList<double> pitchValues = this->pitchValues(CAngleUnit::deg());
377  if (pitchValues.size() != this->size()) { return QPair<CAngle, CAngle>(CAngle::null(), CAngle::null()); }
378  const QPair<double, double> pitchDeg = CMathUtils::standardDeviationAndMean(pitchValues);
379  return CAnglePair(CAngle(pitchDeg.first, CAngleUnit::deg()), CAngle(pitchDeg.second, CAngleUnit::deg()));
380  }
381 
383  {
384  if (this->size() < 2) { return 0; }
385  Q_ASSERT_X(m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst, Q_FUNC_INFO,
386  "need latest first");
387  int c = 0;
388  for (int i = 1; i < this->size(); ++i)
389  {
390  const CAircraftSituation &oldSituation = (*this)[i];
391  CAircraftSituation &newSituation = (*this)[i - 1];
392  if (oldSituation.transferGroundElevationFromMe(newSituation, radius)) { c++; }
393  }
394  return c;
395  }
396 
398  const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const
399  {
400  if (this->size() < minValues) { return CElevationPlane::null(); } // no change to succeed
401 
403  .findWithinRange(reference, range)
404  .sortedByEuclideanDistanceSquared(reference);
405  if (sorted.size() < minValues) { return CElevationPlane::null(); }
406  QList<double> valuesInFt;
407  for (const CAircraftSituation &situation : *this)
408  {
409  if (situation.getGroundElevationInfo() != CAircraftSituation::FromProvider) { continue; }
410  const bool canUse =
411  !situation.isMoving() || (situation.isOnGroundFromNetwork() || situation.isOnGroundFromParts());
412  if (!canUse) { continue; }
413 
414  const double elvFt = situation.getGroundElevationPlane().getAltitude().value(CLengthUnit::ft());
415  valuesInFt.push_back(elvFt);
416  if (valuesInFt.size() >= sufficientValues) { break; }
417  }
418 
419  if (valuesInFt.size() < minValues) { return CElevationPlane::null(); }
420 
421  static const double MaxDevFt = CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft());
422  const QPair<double, double> elvStdDevMean = CMathUtils::standardDeviationAndMean(valuesInFt);
423  if (elvStdDevMean.first > MaxDevFt) { return CElevationPlane::null(); }
424  return CElevationPlane(reference, elvStdDevMean.second, CElevationPlane::singlePointRadius());
425  }
426 } // namespace swift::misc::aviation
QPair< CAngle, CAngle > CAnglePair
Pair of angle.
Definition: angle.h:115
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
iterator begin()
Returns iterator at the beginning of the sequence.
Definition: sequence.h:163
Q_REQUIRED_RESULT CSequence sorted(Predicate p) const
Return a copy sorted by a given comparator predicate.
Definition: sequence.h:583
bool empty() const
Returns true if the sequence is empty.
Definition: sequence.h:282
reference front()
Access the first element.
Definition: sequence.h:225
reference back()
Access the last element.
Definition: sequence.h:249
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
iterator end()
Returns iterator one past the end of the sequence.
Definition: sequence.h:172
void pop_front()
Removes an element at the front of the sequence.
Definition: sequence.h:374
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
Value object encapsulating information of an aircraft's situation.
const CAltitude & getGroundElevation() const
Elevation of the ground directly beneath.
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 physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
const CAltitude & getAltitude() const
Get altitude.
virtual bool isNull() const
Null situation.
const physical_quantities::CAngle & getPitch() const
Get pitch.
bool isMoving() const
Is moving? Means ground speed > epsilon.
bool isOnGroundFromNetwork() const
On ground by network flag?
const geo::CElevationPlane & getGroundElevationPlane() const
Elevation of the ground directly beneath.
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.
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.
Definition: ongroundinfo.h:19
OnGroundDetails
Reliability of on ground information.
Definition: ongroundinfo.h:31
@ InFromParts
set from aircraft parts
Definition: ongroundinfo.h:39
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)
const aviation::CAltitude & getAltitude() const
Altitude (synonym for geodetic height)
virtual bool isNull() const
Existing value?
Geodetic coordinate, a position in 3D space relative to the reference geoid.
physical_quantities::CLength calculateGreatCircleDistance(const ICoordinateGeodetic &otherCoordinate) const
Great circle distance.
bool containsNullPositionOrHeight() const
Any NULL position or NULL height.
Definition: geoobjectlist.h:90
CAircraftSituationList findWithGeodeticMSLHeight() const
Elements with geodetic height (only MSL)
Definition: geoobjectlist.h:58
Physical unit angle (radians, degrees)
Definition: angle.h:23
Specialized class for angles (degrees, radian).
Definition: units.h:233
Physical unit length (length)
Definition: length.h:18
Specialized class for distance units (meter, foot, nautical miles).
Definition: units.h:95
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:778
#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