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  return std::all_of(cbegin(), cend(), [&](const CAircraftSituation &situation) {
78  return situation.getOnGroundInfo().getGroundDetails() == details;
79  });
80  }
81 
83  {
84  if (this->isEmpty()) { return false; }
85  if (this->containsNullPositionOrHeight()) { return false; }
86  return std::all_of(begin(), end(), [](const CAircraftSituation &situation) { return situation.isOnGround(); });
87  }
88 
90  {
91  if (this->isEmpty()) { return false; }
92  if (this->containsNullPositionOrHeight()) { return false; }
93  return std::all_of(begin(), end(), [](const CAircraftSituation &situation) {
94  return situation.getOnGroundInfo().getOnGround() == COnGroundInfo::NotOnGround;
95  });
96  }
97 
98  bool CAircraftSituationList::isConstDescending(bool alreadySortedLatestFirst) const
99  {
100  if (this->size() < 2) { return false; }
101  if (this->containsNullPositionOrHeight()) { return false; }
102 
103  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
104  CAircraftSituation newerSituation = CAircraftSituation::null();
105  for (const CAircraftSituation &situation : sorted)
106  {
107  if (!newerSituation.isNull())
108  {
109  Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
110  newerSituation.getAltitude().getReferenceDatum(),
111  Q_FUNC_INFO, "Wrong reference");
112  const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
113  if (!delta.isNegativeWithEpsilonConsidered()) { return false; }
114  }
115  newerSituation = situation;
116  }
117  return true;
118  }
119 
120  bool CAircraftSituationList::isConstAscending(bool alreadySortedLatestFirst) const
121  {
122  if (this->size() < 2) { return false; }
123  if (this->containsNullPositionOrHeight()) { return false; }
124 
125  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
126  CAircraftSituation newerSituation = CAircraftSituation::null();
127  for (const CAircraftSituation &situation : sorted)
128  {
129  // latest first
130  if (!newerSituation.isNull())
131  {
132  Q_ASSERT_X(situation.getAltitude().getReferenceDatum() ==
133  newerSituation.getAltitude().getReferenceDatum(),
134  Q_FUNC_INFO, "Wrong reference");
135  const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
136  if (!delta.isPositiveWithEpsilonConsidered()) { return false; }
137  }
138  newerSituation = situation;
139  }
140  return true;
141  }
142 
143  bool CAircraftSituationList::isConstAccelerating(bool alreadySortedLatestFirst) const
144  {
145  if (this->size() < 2) { return false; }
146  if (this->containsNullPositionOrHeight()) { return false; }
147 
148  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
149  CSpeed newerGs = CSpeed::null();
150  for (const CAircraftSituation &situation : sorted)
151  {
152  if (!newerGs.isNull())
153  {
154  const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
155  if (!deltaSpeed.isPositiveWithEpsilonConsidered()) { return false; }
156  }
157  newerGs = situation.getGroundSpeed();
158  }
159  return true;
160  }
161 
162  bool CAircraftSituationList::isConstDecelarating(bool alreadySortedLatestFirst) const
163  {
164  if (this->size() < 2) { return false; }
165  if (this->containsNullPositionOrHeight()) { return false; }
166 
167  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
168  CSpeed newerGs = CSpeed::null();
169  for (const CAircraftSituation &situation : sorted)
170  {
171  if (!newerGs.isNull())
172  {
173  const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
174  if (!deltaSpeed.isNegativeWithEpsilonConsidered()) { return false; }
175  }
176  newerGs = situation.getGroundSpeed();
177  }
178  return true;
179  }
180 
181  QPair<bool, COnGroundInfo::IsOnGround>
182  CAircraftSituationList::isGndFlagStableChanging(bool alreadySortedLatestFirst) const
183  {
184  if (this->size() < 2) { return { false, COnGroundInfo::OnGroundSituationUnknown }; }
185 
186  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
189  QPair<bool, COnGroundInfo::IsOnGround> ret(false, f); // changing to front (latest)
190  if (f == t) { return ret; }
191 
192  bool changed = false;
193 
194  for (const CAircraftSituation &s : sorted)
195  {
196  if (!changed && s.getOnGroundInfo().getOnGround() == f) { continue; } // find 1st changing
197  if (!changed)
198  {
199  changed = true;
200  continue;
201  } // just changed
202  if (s.getOnGroundInfo().getOnGround() != t) { return ret; } // jitter, something like gnd, no gnd, gnd
203  }
204  ret.first = changed;
205  return ret;
206  }
207 
208  bool CAircraftSituationList::isJustTakingOff(bool alreadySortedLatestFirst) const
209  {
210  if (this->size() < 2) { return false; }
211 
212  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
213  const CAircraftSituation latest = sorted.front();
214  if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::NotOnGround) { return false; }
215  const int c = this->countOnGround(COnGroundInfo::OnGround);
216  return this->size() - 1 == c; // all others on ground
217  }
218 
219  bool CAircraftSituationList::isJustTouchingDown(bool alreadySortedLatestFirst) const
220  {
221  if (this->size() < 2) { return false; }
222 
223  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
224  const CAircraftSituation latest = sorted.front();
225  if (latest.getOnGroundInfo().getOnGround() != COnGroundInfo::OnGround) { return false; }
226  const int c = this->countOnGround(COnGroundInfo::NotOnGround);
227  return this->size() - 1 == c; // all others not on ground
228  }
229 
230  bool CAircraftSituationList::isRotatingUp(bool alreadySortedLatestFirst) const
231  {
232  if (this->size() < 2) { return false; }
233  const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
234  const QList<double> pitches = sorted.pitchValues(CAngleUnit::deg());
235  const QPair<double, double> stdDevAndMean = CMathUtils::standardDeviationAndMean(pitches);
236  const double minRotate = stdDevAndMean.first + stdDevAndMean.second; // outside std deviation range
237  const bool rotate = pitches.front() > minRotate;
238  return rotate;
239  }
240 
242  {
243  return std::any_of(cbegin(), cend(), [](const CAircraftSituation &situation) {
244  return situation.getGroundSpeed().isNegativeWithEpsilonConsidered();
245  });
246  }
247 
249  {
250  return std::count_if(begin(), end(), [&og](const CAircraftSituation &situation) {
251  return situation.getOnGroundInfo().getOnGround() == og;
252  });
253  }
254 
256  const CLength &range) const
257  {
258  const CLength r = range.isNull() || range < CElevationPlane::singlePointRadius() ?
259  CElevationPlane::singlePointRadius() :
260  range;
261  CAircraftSituation situationWithElevation = CAircraftSituation::null();
262 
263  CLength bestDistance = CLength::null();
264  for (const CAircraftSituation &s : *this)
265  {
266  if (!s.hasGroundElevation()) { continue; }
267 
268  // we need to calculate distance to coordinates of the plane
269  // not the situation using the coordinate
270  // const CLength distance = s.calculateGreatCircleDistance(coordinate);
271  const CLength distance = s.getGroundElevationPlane().calculateGreatCircleDistance(coordinate);
272 
273  if (distance > r) { continue; }
274  if (bestDistance.isNull() || bestDistance > distance)
275  {
276  situationWithElevation = s;
277  bestDistance = distance;
278  }
279  }
280  return situationWithElevation;
281  }
282 
284  {
285  for (CAircraftSituation &situation : *this) { situation.setOnGroundInfo(info); }
286  }
287 
289  {
290  for (CAircraftSituation &situation : *this) { situation.setOnGroundDetails(details); }
291  }
292 
294  {
295  if (offset.isNull() || this->isEmpty()) { return 0; }
296  int c = 0;
297  for (CAircraftSituation &s : *this)
298  {
299  s.addAltitudeOffset(offset);
300  c++;
301  }
302  return c;
303  }
304 
306  {
307  return this->isSortedAdjustedLatestFirst() && !this->containsNullPosition();
308  }
309 
311  {
312  if (this->empty()) { return {}; }
313  CAircraftSituationList copy(*this);
314  copy.pop_front();
315  return copy;
316  }
317 
319  {
320  QList<double> values;
321  for (const CAircraftSituation &s : *this) { values.push_back(s.getPitch().value(unit)); }
322  return values;
323  }
324 
326  {
327  QList<double> values;
328  for (const CAircraftSituation &s : *this)
329  {
330  if (s.getGroundSpeed().isNull()) { continue; }
331  values.push_back(s.getGroundSpeed().value(unit));
332  }
333  return values;
334  }
335 
337  {
338  QList<double> values;
339  for (const CAircraftSituation &s : *this)
340  {
341  if (s.getGroundElevation().isNull()) { continue; }
342  values.push_back(s.getGroundElevation().value(unit));
343  }
344  return values;
345  }
346 
348  {
349  QList<double> values;
350  for (const CAircraftSituation &s : *this)
351  {
352  const CAltitude alt(s.getAltitude());
353  if (alt.isNull()) { continue; }
354  values.push_back(alt.value(unit));
355  }
356  return values;
357  }
358 
360  {
361  const QList<double> gsValues = this->groundSpeedValues(CSpeedUnit::kts());
362  if (gsValues.size() != this->size()) { return { CSpeed::null(), CSpeed::null() }; }
363  const QPair<double, double> gsKts = CMathUtils::standardDeviationAndMean(gsValues);
364  return { CSpeed(gsKts.first, CSpeedUnit::kts()), CSpeed(gsKts.second, CSpeedUnit::kts()) };
365  }
366 
368  {
369  const QList<double> pitchValues = this->pitchValues(CAngleUnit::deg());
370  if (pitchValues.size() != this->size()) { return { CAngle::null(), CAngle::null() }; }
371  const QPair<double, double> pitchDeg = CMathUtils::standardDeviationAndMean(pitchValues);
372  return { CAngle(pitchDeg.first, CAngleUnit::deg()), CAngle(pitchDeg.second, CAngleUnit::deg()) };
373  }
374 
376  {
377  if (this->size() < 2) { return 0; }
378  Q_ASSERT_X(m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst, Q_FUNC_INFO,
379  "need latest first");
380  int c = 0;
381  for (int i = 1; i < this->size(); ++i)
382  {
383  const CAircraftSituation &oldSituation = (*this)[i];
384  CAircraftSituation &newSituation = (*this)[i - 1];
385  if (oldSituation.transferGroundElevationFromMe(newSituation, radius)) { c++; }
386  }
387  return c;
388  }
389 
391  const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const
392  {
393  if (this->size() < minValues) { return CElevationPlane::null(); } // no change to succeed
394 
396  .findWithinRange(reference, range)
397  .sortedByEuclideanDistanceSquared(reference);
398  if (sorted.size() < minValues) { return CElevationPlane::null(); }
399  QList<double> valuesInFt;
400  for (const CAircraftSituation &situation : *this)
401  {
402  if (situation.getGroundElevationInfo() != CAircraftSituation::FromProvider) { continue; }
403  const bool canUse =
404  !situation.isMoving() || (situation.isOnGroundFromNetwork() || situation.isOnGroundFromParts());
405  if (!canUse) { continue; }
406 
407  const double elvFt = situation.getGroundElevationPlane().getAltitude().value(CLengthUnit::ft());
408  valuesInFt.push_back(elvFt);
409  if (valuesInFt.size() >= sufficientValues) { break; }
410  }
411 
412  if (valuesInFt.size() < minValues) { return CElevationPlane::null(); }
413 
414  static const double MaxDevFt = CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft());
415  const QPair<double, double> elvStdDevMean = CMathUtils::standardDeviationAndMean(valuesInFt);
416  if (elvStdDevMean.first > MaxDevFt) { return CElevationPlane::null(); }
417  return { reference, elvStdDevMean.second, CElevationPlane::singlePointRadius() };
418  }
419 } // 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
CAircraftSituation & reference
STL compatibility.
Definition: sequence.h:95
bool empty() const
Returns true if the sequence is empty.
Definition: sequence.h:282
const_iterator cbegin() const
Returns const iterator at the beginning of the sequence.
Definition: sequence.h:169
reference front()
Access the first element.
Definition: sequence.h:225
reference back()
Access the last element.
Definition: sequence.h:249
const_iterator cend() const
Returns const iterator one past the end of the sequence.
Definition: sequence.h:178
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.
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?
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?
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)
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
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