Ref T261, aircraft situation

* null altitude
* ground functions
* CG ("member")
* supporting CAircraftSituationChange
This commit is contained in:
Klaus Basan
2018-04-27 02:40:05 +02:00
committed by Roland Winklmeier
parent 1f3e5c6abf
commit bed79237bf
6 changed files with 565 additions and 51 deletions

View File

@@ -7,7 +7,9 @@
* contained in the LICENSE file.
*/
#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftpartslist.h"
#include "blackmisc/geo/elevationplane.h"
#include "blackmisc/pq/length.h"
@@ -16,11 +18,15 @@
#include "blackmisc/comparefunctions.h"
#include "blackmisc/variant.h"
#include "blackmisc/verify.h"
#include "blackconfig/buildconfig.h"
#include "QStringBuilder"
#include <QtGlobal>
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Geo;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Simulation;
using namespace BlackConfig;
namespace BlackMisc
{
@@ -51,11 +57,16 @@ namespace BlackMisc
{
return QStringLiteral("ts: ") % this->getFormattedTimestampAndOffset(true) %
QStringLiteral(" | ") % m_position.toQString(i18n) %
QStringLiteral(" | bank: ") % (m_bank.toQString(i18n)) %
QStringLiteral(" | pitch: ") % (m_pitch.toQString(i18n)) %
QStringLiteral(" | heading: ") % (m_heading.toQString(i18n)) %
QStringLiteral(" | og: ") % this->getOnGroundInfo() %
QStringLiteral(" | factor: ") % QString::number(m_onGroundFactor, 'f', 2) %
QStringLiteral(" | alt: ") % this->getAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1) %
QStringLiteral(" ") % this->getCorrectedAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1) %
QStringLiteral("[cor] | og: ") % this->getOnGroundInfo() %
(m_onGroundGuessingDetails.isEmpty() ? QStringLiteral("") : QStringLiteral(" ") % m_onGroundGuessingDetails) %
QStringLiteral(" | cg: ") %
(m_cg.isNull() ? QStringLiteral("null") : m_cg.valueRoundedWithUnit(CLengthUnit::m(), 1) % QStringLiteral(" ") % m_cg.valueRoundedWithUnit(CLengthUnit::ft(), 1)) %
QStringLiteral(" | factor [0..1]: ") % QString::number(m_onGroundFactor, 'f', 2) %
QStringLiteral(" | bank: ") % m_bank.toQString(i18n) %
QStringLiteral(" | pitch: ") % m_pitch.toQString(i18n) %
QStringLiteral(" | heading: ") % m_heading.toQString(i18n) %
QStringLiteral(" | gs: ") % m_groundSpeed.valueRoundedWithUnit(CSpeedUnit::kts(), 1, true) %
QStringLiteral(" ") % m_groundSpeed.valueRoundedWithUnit(CSpeedUnit::m_s(), 1, true) %
QStringLiteral(" | elevation: ") % (m_groundElevationPlane.toQString(i18n));
@@ -244,21 +255,30 @@ namespace BlackMisc
this->getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails;
}
void CAircraftSituation::setOnGround(bool onGround)
bool CAircraftSituation::setOnGround(bool onGround)
{
this->setOnGround(onGround ? OnGround : NotOnGround);
return this->setOnGround(onGround ? OnGround : NotOnGround);
}
void CAircraftSituation::setOnGround(CAircraftSituation::IsOnGround onGround)
bool CAircraftSituation::setOnGround(CAircraftSituation::IsOnGround onGround)
{
m_onGround = static_cast<int>(onGround);
if (this->getOnGround() == onGround) { return false; }
const int og = static_cast<int>(onGround);
m_onGround = og;
m_onGroundFactor = (onGround == OnGround) ? 1.0 : 0.0;
return true;
}
void CAircraftSituation::setOnGround(CAircraftSituation::IsOnGround onGround, CAircraftSituation::OnGroundDetails details)
bool CAircraftSituation::setOnGround(CAircraftSituation::IsOnGround onGround, CAircraftSituation::OnGroundDetails details)
{
this->setOnGround(onGround);
const bool set = this->setOnGround(onGround);
this->setOnGroundDetails(details);
if (details != OnGroundByGuessing)
{
m_onGroundGuessingDetails.clear();
}
return set;
}
void CAircraftSituation::setOnGroundFactor(double groundFactor)
@@ -276,40 +296,111 @@ namespace BlackMisc
bool CAircraftSituation::shouldGuessOnGround() const
{
return (!this->isOnGroundInfoAvailable());
return !this->hasInboundGroundDetails();
}
bool CAircraftSituation::guessOnGround(bool vtol, const PhysicalQuantities::CLength &cg)
bool CAircraftSituation::guessOnGround(const CAircraftSituationChange &change, const CAircraftModel &model)
{
Q_UNUSED(change);
if (!this->shouldGuessOnGround()) { return false; }
// for debugging purposed
QString *details = CBuildConfig::isLocalDeveloperDebugBuild() ? &m_onGroundGuessingDetails : nullptr;
// Non VTOL aircraft have to move to be not on ground
const bool vtol = model.isVtol();
if (!vtol && !this->isMoving())
{
this->setOnGround(OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("No VTOL, not moving => on ground"); }
return true;
}
// not on ground is default
this->setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByGuessing);
// guess some values we use for guessing
CLength cg = m_cg.isNull() ? model.getCG() : m_cg;
CSpeed guessedLiftOffSpeed;
CSpeed sureLiftOffSpeed = CSpeed(130, CSpeedUnit::kts());
model.getAircraftIcaoCode().guessModelParameters(cg, guessedLiftOffSpeed);
if (!guessedLiftOffSpeed.isNull())
{
// does the value make any sense?
const bool validGuessedSpeed = guessedLiftOffSpeed.value(CSpeedUnit::km_h()) > 5.0;
BLACK_VERIFY_X(validGuessedSpeed, Q_FUNC_INFO, "Wrong guessed value for lift off");
if (!validGuessedSpeed) { guessedLiftOffSpeed = CSpeed(80, CSpeedUnit::kts()); } // fix
sureLiftOffSpeed = guessedLiftOffSpeed * 1.25;
}
// "extreme" values for which we are surely not on ground
if (qAbs(this->getPitch().value(CAngleUnit::deg())) > 20) { if (details) { *details = QStringLiteral("max.pitch"); }; return true; } // some tail wheel aircraft already have 11° pitch on ground
if (qAbs(this->getBank().value(CAngleUnit::deg())) > 10) { if (details) { *details = QStringLiteral("max.bank"); }; return true; }
if (this->getGroundSpeed() > sureLiftOffSpeed) { if (details) { *details = QStringLiteral("max.gs. > ") % sureLiftOffSpeed.valueRoundedWithUnit(1); }; return true; }
// use the most accurate or reliable guesses here first
// ------------------------------------------------------
// by elevation
// we can detect "on ground" but not "not on ground" because of overflow
// we can detect "on ground" (underflow, near ground), but not "not on ground" because of overflow
// we can detect on ground for underflow, but not for overflow (so we can not rely on NotOnGround)
IsOnGround og = this->isOnGroundByElevation(cg);
if (og == OnGround)
{
if (details) { *details = QStringLiteral("elevation on ground"); }
this->setOnGround(og, CAircraftSituation::OnGroundByGuessing);
return true;
}
// we guess on speed, pitch and bank by excluding situations
this->setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByGuessing);
if (qAbs(this->getPitch().value(CAngleUnit::deg())) > 10) { return true; }
if (qAbs(this->getBank().value(CAngleUnit::deg())) > 10) { return true; }
if (this->getGroundSpeed().value(CSpeedUnit::km_h()) > 50) { return true; }
if (!change.isNull())
{
if (!vtol && change.wasConstOnGround())
{
if (change.isRotatingUp())
{
// not OG
if (details) { *details = QStringLiteral("rotating up detected"); }
return true;
}
// here we stick to ground until we detect rotate up
this->setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("waiting for rotating up"); }
return true;
}
if (change.isConstAscending())
{
// not OG
if (details) { *details = QStringLiteral("const ascending"); }
return true;
}
}
// on VTOL we stop here
if (vtol) { return false; }
if (vtol)
{
// no idea
this->setOnGround(OnGroundSituationUnknown, NotSetGroundDetails);
return false;
}
// guessed speed null -> vtol
if (!guessedLiftOffSpeed.isNull())
{
// does the value make any sense?
if (this->getGroundSpeed() < guessedLiftOffSpeed)
{
this->setOnGround(OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("Guessing, max.guessed gs.") + guessedLiftOffSpeed.valueRoundedWithUnit(CSpeedUnit::kts(), 1); }; return true;
}
}
// not sure, but this is a guess
this->setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("Fall through"); }
return true;
}
@@ -370,6 +461,11 @@ namespace BlackMisc
return this->onGroundAsString() % QLatin1Char(' ') % this->getOnDetailsAsString();
}
CAircraftSituation::IsOnGround CAircraftSituation::isOnGroundByElevation() const
{
return this->isOnGroundByElevation(m_cg);
}
CAircraftSituation::IsOnGround CAircraftSituation::isOnGroundByElevation(const CLength &cg) const
{
Q_ASSERT_X(!cg.isNegativeWithEpsilonConsidered(), Q_FUNC_INFO, "CG must not be negative");
@@ -444,12 +540,17 @@ namespace BlackMisc
return this->getAltitude() - gh;
}
CAltitude CAircraftSituation::getCorrectedAltitude(const CLength &centerOfGravity, bool enableDragToGround, AltitudeCorrection *correctetion) const
CAltitude CAircraftSituation::getCorrectedAltitude(bool enableDragToGround, CAircraftSituation::AltitudeCorrection *correction) const
{
if (correctetion) { *correctetion = UnknownCorrection; }
return this->getCorrectedAltitude(m_cg, enableDragToGround, correction);
}
CAltitude CAircraftSituation::getCorrectedAltitude(const CLength &centerOfGravity, bool enableDragToGround, AltitudeCorrection *correction) const
{
if (correction) { *correction = UnknownCorrection; }
if (!this->hasGroundElevation())
{
if (correctetion) { *correctetion = NoElevation; }
if (correction) { *correction = NoElevation; }
return this->getAltitude();
}
@@ -457,7 +558,7 @@ namespace BlackMisc
if (this->getAltitude().getReferenceDatum() == CAltitude::AboveGround)
{
BLACK_VERIFY_X(false, Q_FUNC_INFO, "Unsupported");
if (correctetion) { *correctetion = AGL; }
if (correction) { *correction = AGL; }
return this->getAltitude();
}
else
@@ -465,38 +566,44 @@ namespace BlackMisc
const CAltitude groundPlusCG = this->getGroundElevation().withOffset(centerOfGravity);
if (groundPlusCG.isNull())
{
if (correctetion) { *correctetion = NoElevation; }
if (correction) { *correction = NoElevation; }
return this->getAltitude();
}
const CLength groundDistance = this->getAltitude() - groundPlusCG;
const bool underflow = groundDistance.isNegativeWithEpsilonConsidered();
if (underflow)
{
if (correctetion) { *correctetion = Underflow; }
if (correction) { *correction = Underflow; }
return groundPlusCG;
}
const bool nearGround = groundDistance.abs() < deltaNearGround();
if (nearGround)
{
if (correctetion) { *correctetion = NoCorrection; }
if (correction) { *correction = NoCorrection; }
return groundPlusCG;
}
const bool forceDragToGround = (enableDragToGround && this->getOnGround() == OnGround) && (this->hasInboundGroundDetails() || this->getOnGroundDetails() == OnGroundByGuessing);
if (forceDragToGround)
{
if (correctetion) { *correctetion = DraggedToGround; }
if (correction) { *correction = DraggedToGround; }
return groundPlusCG;
}
if (correctetion) { *correctetion = NoCorrection; }
if (correction) { *correction = NoCorrection; }
return this->getAltitude();
}
}
CAircraftSituation::AltitudeCorrection CAircraftSituation::correctAltitude(bool enableDragToGround)
{
return this->correctAltitude(m_cg, enableDragToGround);
}
CAircraftSituation::AltitudeCorrection CAircraftSituation::correctAltitude(const CLength &centerOfGravity, bool enableDragToGround)
{
CAircraftSituation::AltitudeCorrection altCor = CAircraftSituation::UnknownCorrection;
this->setAltitude(this->getCorrectedAltitude(centerOfGravity, enableDragToGround, &altCor));
this->setCG(centerOfGravity);
return altCor;
}
@@ -509,7 +616,7 @@ namespace BlackMisc
bool CAircraftSituation::isMoving() const
{
const double gsKmh = this->getGroundSpeed().value(CSpeedUnit::km_h());
return gsKmh >= 1.0;
return gsKmh >= 2.5;
}
bool CAircraftSituation::canLikelySkipNearGroundInterpolation() const

View File

@@ -38,10 +38,12 @@
namespace BlackMisc
{
namespace Geo { class CElevationPlane; }
namespace Simulation { class CAircraftModel; }
namespace Aviation
{
class CAircraftParts;
class CAircraftPartsList;
class CAircraftSituationChange;
//! Value object encapsulating information of an aircraft's situation
class BLACKMISC_EXPORT CAircraftSituation :
@@ -127,6 +129,9 @@ namespace BlackMisc
const PhysicalQuantities::CSpeed &gs = {},
const Geo::CElevationPlane &groundElevation = {});
//! \copydoc Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
//! \copydoc Mixin::Index::propertyByIndex
CVariant propertyByIndex(const CPropertyIndex &index) const;
@@ -142,6 +147,9 @@ namespace BlackMisc
//! Position null?
bool isPositionNull() const { return m_position.isNull(); }
//! Position or altitude null?
bool isPositionOrAltitudeNull() const { return this->isPositionNull() || this->getAltitude().isNull(); }
//! Null situation
virtual bool isNull() const override;
@@ -170,13 +178,13 @@ namespace BlackMisc
bool isOnGroundInfoAvailable() const;
//! Set on ground
void setOnGround(bool onGround);
bool setOnGround(bool onGround);
//! Set on ground
void setOnGround(CAircraftSituation::IsOnGround onGround);
bool setOnGround(CAircraftSituation::IsOnGround onGround);
//! Set on ground
void setOnGround(CAircraftSituation::IsOnGround onGround, CAircraftSituation::OnGroundDetails details);
bool setOnGround(CAircraftSituation::IsOnGround onGround, CAircraftSituation::OnGroundDetails details);
//! On ground factor 0..1 (on ground), -1 not set
double getOnGroundFactor() const { return m_onGroundFactor; }
@@ -188,7 +196,7 @@ namespace BlackMisc
bool shouldGuessOnGround() const;
//! Guess on ground flag
bool guessOnGround(bool vtol = false, const PhysicalQuantities::CLength &cg = PhysicalQuantities::CLength::null());
bool guessOnGround(const CAircraftSituationChange &change, const Simulation::CAircraftModel &model);
//! Distance to ground, null if impossible to calculate
PhysicalQuantities::CLength getGroundDistance(const PhysicalQuantities::CLength &centerOfGravity) const;
@@ -230,7 +238,10 @@ namespace BlackMisc
const Geo::CElevationPlane &getGroundElevationPlane() const { return m_groundElevationPlane; }
//! Is on ground by elevation data, requires elevation and CG
//! @{
IsOnGround isOnGroundByElevation() const;
IsOnGround isOnGroundByElevation(const PhysicalQuantities::CLength &cg) const;
//! @}
//! Is ground elevation value available
bool hasGroundElevation() const;
@@ -268,10 +279,16 @@ namespace BlackMisc
//! Get altitude under consideration of ground elevation and ground flag
//! \remark with dragToGround it will also compensate overflows, otherwise only underflow
CAltitude getCorrectedAltitude(const PhysicalQuantities::CLength &centerOfGravity = PhysicalQuantities::CLength::null(), bool enableDragToGround = true, AltitudeCorrection *correctetion = nullptr) const;
//! @{
CAltitude getCorrectedAltitude(bool enableDragToGround = true, AltitudeCorrection *correction = nullptr) const;
CAltitude getCorrectedAltitude(const PhysicalQuantities::CLength &centerOfGravity, bool enableDragToGround = true, AltitudeCorrection *correction = nullptr) const;
//! @}
//! Set the corrected altitude from CAircraftSituation::getCorrectedAltitude
//! @{
AltitudeCorrection correctAltitude(bool enableDragToGround = true);
AltitudeCorrection correctAltitude(const PhysicalQuantities::CLength &centerOfGravity = PhysicalQuantities::CLength::null(), bool enableDragToGround = true);
//! @}
//! Set altitude
void setAltitude(const CAltitude &altitude) { m_position.setGeodeticHeight(altitude); }
@@ -318,6 +335,15 @@ namespace BlackMisc
//! Corresponding callsign
void setCallsign(const CCallsign &callsign);
//! Get CG if any
const PhysicalQuantities::CLength &getCG() const { return m_cg; }
//! Set CG
void setCG(const PhysicalQuantities::CLength &cg) { m_cg = cg; }
//! Has CG set?
bool hasCG() const { return !m_cg.isNull(); }
//! Set flag indicating this is an interim position update
void setInterimFlag(bool flag) { m_isInterim = flag; }
@@ -338,9 +364,6 @@ namespace BlackMisc
//! Get flag indicating this is an interim position update
bool isInterim() const { return m_isInterim; }
//! \copydoc Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
//! Enum to string
static const QString &isOnGroundToString(IsOnGround onGround);
@@ -359,6 +382,32 @@ namespace BlackMisc
//! A default CG if not other value is available
static const PhysicalQuantities::CLength &defaultCG();
//! Both on ground
static bool isGfEqualOnGround(double oldGroundFactor, double newGroundFactor)
{
return isDoubleEpsilonEqual(1.0, oldGroundFactor) && isDoubleEpsilonEqual(1.0, newGroundFactor);
}
//! Ground flag comparisons @{
//! Both not on ground
static bool isGfEqualAirborne(double oldGroundFactor, double newGroundFactor)
{
return isDoubleEpsilonEqual(0.0, oldGroundFactor) && isDoubleEpsilonEqual(0.0, newGroundFactor);
}
//! Plane is starting
static bool isGfStarting(double oldGroundFactor, double newGroundFactor)
{
return isDoubleEpsilonEqual(0.0, oldGroundFactor) && isDoubleEpsilonEqual(1.0, newGroundFactor);
}
//! Plane is landing
static bool isGfLanding(double oldGroundFactor, double newGroundFactor)
{
return isDoubleEpsilonEqual(1.0, oldGroundFactor) && isDoubleEpsilonEqual(0.0, newGroundFactor);
}
//! @}
private:
CCallsign m_correspondingCallsign;
Geo::CCoordinateGeodetic m_position; //!< NULL position as default
@@ -367,11 +416,19 @@ namespace BlackMisc
PhysicalQuantities::CAngle m_pitch { 0, nullptr };
PhysicalQuantities::CAngle m_bank { 0, nullptr };
PhysicalQuantities::CSpeed m_groundSpeed { 0, nullptr };
bool m_isInterim = false;
PhysicalQuantities::CLength m_cg { 0, nullptr };
Geo::CElevationPlane m_groundElevationPlane; //!< NULL elevation as default
bool m_isInterim = false;
int m_onGround = static_cast<int>(CAircraftSituation::OnGroundSituationUnknown);
int m_onGroundDetails = static_cast<int>(CAircraftSituation::NotSetGroundDetails);
double m_onGroundFactor = -1; //!< interpolated ground flag, 1..on ground, 0..not on ground, -1 no info
QString m_onGroundGuessingDetails; //!< only for debugging, not transferred via DBus etc.
//! Equal double values?
static bool isDoubleEpsilonEqual(double d1, double d2)
{
return qAbs(d1 - d2) <= std::numeric_limits<double>::epsilon();
}
BLACK_METACLASS(
CAircraftSituation,
@@ -382,6 +439,7 @@ namespace BlackMisc
BLACK_METAMEMBER(pitch),
BLACK_METAMEMBER(bank),
BLACK_METAMEMBER(groundSpeed),
BLACK_METAMEMBER(cg),
BLACK_METAMEMBER(groundElevationPlane),
BLACK_METAMEMBER(onGround),
BLACK_METAMEMBER(onGroundDetails),
@@ -397,5 +455,6 @@ namespace BlackMisc
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftSituation)
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftSituation::IsOnGround)
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftSituation::OnGroundDetails)
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftSituation::AltitudeCorrection)
#endif // guard

View File

@@ -7,13 +7,19 @@
* contained in the LICENSE file.
*/
#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftsituationlist.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/geo/elevationplane.h"
#include "blackmisc/math/mathutils.h"
#include "blackmisc/pq/speed.h"
#include "blackmisc/verify.h"
#include <tuple>
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Geo;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Simulation;
using namespace BlackMisc::Math;
namespace BlackMisc
{
@@ -43,19 +49,30 @@ namespace BlackMisc
return c;
}
int CAircraftSituationList::setGroundElevationCheckedAndGuessGround(const CElevationPlane &elevationPlane, bool isVtol, const CLength &cg)
int CAircraftSituationList::setGroundElevationCheckedAndGuessGround(const CElevationPlane &elevationPlane, const CAircraftModel &model)
{
if (elevationPlane.isNull()) { return 0; }
if (this->isEmpty()) { return 0; }
Q_ASSERT_X(this->isSortedAdjustedLatestFirstWithoutNullPositions(), Q_FUNC_INFO, "Need sorted situations without NULL positions");
const CAircraftSituationChange change(*this, true, true);
int c = 0;
bool first = true;
if (this->front().getCallsign().equalsString("AFL2353"))
{
c = 0;
}
for (CAircraftSituation &s : *this)
{
const bool set = s.setGroundElevationChecked(elevationPlane);
if (!set) { continue; }
if (s.shouldGuessOnGround())
if (set)
{
s.guessOnGround(isVtol, cg);
// change is only valid for the latest situation
s.guessOnGround(first ? change : CAircraftSituationChange::null(), model);
c++;
}
c++;
first = false;
}
return c;
}
@@ -125,6 +142,179 @@ namespace BlackMisc
return true;
}
bool CAircraftSituationList::isConstOnGround() const
{
if (this->isEmpty()) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
for (const CAircraftSituation &situation : *this)
{
const CAircraftSituation::IsOnGround og = situation.getOnGround();
if (og != CAircraftSituation::OnGround) { return false; }
}
return true;
}
bool CAircraftSituationList::isConstNotOnGround() const
{
if (this->isEmpty()) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
for (const CAircraftSituation &situation : *this)
{
const CAircraftSituation::IsOnGround og = situation.getOnGround();
if (og != CAircraftSituation::NotOnGround) { return false; }
}
return true;
}
bool CAircraftSituationList::isConstDescending(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
CAircraftSituation newerSituation = CAircraftSituation::null();
for (const CAircraftSituation &situation : sorted)
{
if (!newerSituation.isNull())
{
Q_ASSERT_X(situation.getAltitude().getReferenceDatum() == newerSituation.getAltitude().getReferenceDatum(), Q_FUNC_INFO, "Wrong reference");
const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
if (!delta.isNegativeWithEpsilonConsidered()) { return false; }
}
newerSituation = situation;
}
return true;
}
bool CAircraftSituationList::isConstAscending(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
CAircraftSituation newerSituation = CAircraftSituation::null();
for (const CAircraftSituation &situation : sorted)
{
if (!newerSituation.isNull())
{
Q_ASSERT_X(situation.getAltitude().getReferenceDatum() == newerSituation.getAltitude().getReferenceDatum(), Q_FUNC_INFO, "Wrong reference");
const CLength delta = newerSituation.getAltitude() - situation.getAltitude();
if (!delta.isPositiveWithEpsilonConsidered()) { return false; }
}
newerSituation = situation;
}
return true;
}
bool CAircraftSituationList::isConstAccelerating(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
CSpeed newerGs = CSpeed::null();
for (const CAircraftSituation &situation : sorted)
{
if (!newerGs.isNull())
{
const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
if (!deltaSpeed.isPositiveWithEpsilonConsidered()) { return false; }
}
newerGs = situation.getGroundSpeed();
}
return true;
}
bool CAircraftSituationList::isConstDecelarating(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
if (this->containsNullPositionOrHeight()) { return false; }
const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
CSpeed newerGs = CSpeed::null();
for (const CAircraftSituation &situation : sorted)
{
if (!newerGs.isNull())
{
const CSpeed deltaSpeed = newerGs - situation.getGroundSpeed();
if (!deltaSpeed.isNegativeWithEpsilonConsidered()) { return false; }
}
newerGs = situation.getGroundSpeed();
}
return true;
}
bool CAircraftSituationList::isGndFlagChanging(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
const CAircraftSituationList sorted(this->getLatestAdjustedTwoObjects(alreadySortedLatestFirst));
const CAircraftSituation s1 = sorted.front();
const CAircraftSituation s2 = sorted.back();
return (s1.getOnGround() == CAircraftSituation::OnGround && s2.getOnGround() == CAircraftSituation::NotOnGround) ||
(s2.getOnGround() == CAircraftSituation::OnGround && s1.getOnGround() == CAircraftSituation::NotOnGround);
}
bool CAircraftSituationList::isJustTakingOff(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
const CAircraftSituationList sorted(this->getLatestAdjustedTwoObjects(alreadySortedLatestFirst));
const CAircraftSituation latest = sorted.front();
const CAircraftSituation oldest = sorted.back();
return (latest.getOnGround() == CAircraftSituation::NotOnGround && oldest.getOnGround() == CAircraftSituation::OnGround);
}
bool CAircraftSituationList::isJustTouchingDown(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
const CAircraftSituationList sorted(this->getLatestAdjustedTwoObjects(alreadySortedLatestFirst));
const CAircraftSituation latest = sorted.front();
const CAircraftSituation oldest = sorted.back();
return (latest.getOnGround() == CAircraftSituation::OnGround && oldest.getOnGround() == CAircraftSituation::NotOnGround);
}
bool CAircraftSituationList::isRotatingUp(bool alreadySortedLatestFirst) const
{
if (this->size() < 2) { return false; }
const CAircraftSituationList sorted(alreadySortedLatestFirst ? (*this) : this->getSortedAdjustedLatestFirst());
const QList<double> pitches = sorted.pitchValues(CAngleUnit::deg());
const QPair<double, double> stdDevAndMean = CMathUtils::standardDeviationAndMean(pitches);
const double minRotate = stdDevAndMean.first + stdDevAndMean.second; // outside std deviation range
const bool rotate = pitches.front() > minRotate;
return rotate;
}
bool CAircraftSituationList::containsPushBack() const
{
for (const CAircraftSituation &situation : *this)
{
if (situation.getGroundSpeed().isNegativeWithEpsilonConsidered()) { return true; }
}
return false;
}
int CAircraftSituationList::countOnGround(CAircraftSituation::IsOnGround og) const
{
int c = 0;
for (const CAircraftSituation &situation : *this)
{
if (situation.getOnGround() == og) { c++; }
}
return c;
}
int CAircraftSituationList::setOnGround(CAircraftSituation::IsOnGround og)
{
int c = 0;
for (CAircraftSituation &situation : *this)
{
if (situation.setOnGround(og)) { c++; }
}
return c;
}
int CAircraftSituationList::setOnGroundDetails(CAircraftSituation::OnGroundDetails details)
{
int c = 0;
@@ -134,5 +324,74 @@ namespace BlackMisc
}
return c;
}
bool CAircraftSituationList::isSortedAdjustedLatestFirstWithoutNullPositions() const
{
return this->isSortedAdjustedLatestFirst() && !this->containsNullPosition();
}
CAircraftSituationList CAircraftSituationList::withoutFrontSituation() const
{
if (this->empty()) { return CAircraftSituationList(); }
CAircraftSituationList copy(*this);
copy.pop_front();
return copy;
}
QList<double> CAircraftSituationList::pitchValues(const CAngleUnit &unit) const
{
QList<double> values;
for (const CAircraftSituation &s : *this)
{
values.push_back(s.getPitch().value(unit));
}
return values;
}
QList<double> CAircraftSituationList::groundSpeedValues(const CSpeedUnit &unit) const
{
QList<double> values;
for (const CAircraftSituation &s : *this)
{
if (s.getGroundSpeed().isNull()) { continue; }
values.push_back(s.getGroundSpeed().value(unit));
}
return values;
}
QList<double> CAircraftSituationList::elevationValues(const CLengthUnit &unit) const
{
QList<double> values;
for (const CAircraftSituation &s : *this)
{
if (s.getGroundElevation().isNull()) { continue; }
values.push_back(s.getGroundElevation().value(unit));
}
return values;
}
QList<double> CAircraftSituationList::altitudeValues(const CLengthUnit &unit) const
{
QList<double> values;
for (const CAircraftSituation &s : *this)
{
const CAltitude alt(s.getAltitude());
if (alt.isNull()) { continue; }
values.push_back(alt.value(unit));
}
return values;
}
QList<double> CAircraftSituationList::correctedAltitudeValues(const CLengthUnit &unit, const CLength &cg) const
{
QList<double> values;
for (const CAircraftSituation &s : *this)
{
const CAltitude alt(s.getCorrectedAltitude(cg));
if (alt.isNull()) { continue; }
values.push_back(alt.value(unit));
}
return values;
}
} // namespace
} // namespace

View File

@@ -23,10 +23,12 @@
#include "blackmisc/variant.h"
#include <QMetaType>
#include <QList>
namespace BlackMisc
{
namespace Geo { class CElevationPlane; }
namespace Simulation { class CAircraftModel; }
namespace Aviation
{
class CAircraftParts;
@@ -54,8 +56,9 @@ namespace BlackMisc
//! Set ground elevation from elevation plane
int setGroundElevationChecked(const Geo::CElevationPlane &elevationPlane, qint64 newerThanAdjustedMs = -1);
//! Set ground elevation from elevation plane
int setGroundElevationCheckedAndGuessGround(const Geo::CElevationPlane &elevationPlane, bool isVtol, const PhysicalQuantities::CLength &cg);
//! Set ground elevation from elevation plane and guess ground
//! \note requires a sorted list latest first
int setGroundElevationCheckedAndGuessGround(const Geo::CElevationPlane &elevationPlane, const Simulation::CAircraftModel &model);
//! Adjust flag from parts by using CAircraftSituation::adjustGroundFlag
int adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor = 0.1);
@@ -78,8 +81,70 @@ namespace BlackMisc
//! Are all on ground details the same
bool areAllOnGroundDetailsSame(CAircraftSituation::OnGroundDetails details) const;
//! Are all situations on ground?
bool isConstOnGround() const;
//! Are all situations not on ground?
bool isConstNotOnGround() const;
//! Constantly descending?
bool isConstDescending(bool alreadySortedLatestFirst = false) const;
//! Constantly ascending?
bool isConstAscending(bool alreadySortedLatestFirst = false) const;
//! Constantly accelerating?
bool isConstAccelerating(bool alreadySortedLatestFirst = false) const;
//! Constantly decelarating?
bool isConstDecelarating(bool alreadySortedLatestFirst = false) const;
//! Is the ground flag changing for the recent situations
bool isGndFlagChanging(bool alreadySortedLatestFirst = false) const;
//! Is just taking off?
bool isJustTakingOff(bool alreadySortedLatestFirst = false) const;
//! Is just touch down?
bool isJustTouchingDown(bool alreadySortedLatestFirst = false) const;
//! Is rotating up?
bool isRotatingUp(bool alreadySortedLatestFirst = false) const;
//! Contains any push back
//! \remark only valid for non VTOL aircraft
bool containsPushBack() const;
//! Count the number of situations with CAircraftSituation::IsOnGround
int countOnGround(CAircraftSituation::IsOnGround og) const;
//! Set on ground
int setOnGround(CAircraftSituation::IsOnGround og);
//! Set on ground details for all situations
int setOnGroundDetails(CAircraftSituation::OnGroundDetails details);
//! Latest first and no null positions?
bool isSortedAdjustedLatestFirstWithoutNullPositions() const;
//! Remove the first situation
//! \remark normally used when the first situation represents the latest situation
CAircraftSituationList withoutFrontSituation() const;
//! All pitch values
QList<double> pitchValues(const PhysicalQuantities::CAngleUnit &unit) const;
//! All ground speed values
QList<double> groundSpeedValues(const PhysicalQuantities::CSpeedUnit &unit) const;
//! All elevation values
QList<double> elevationValues(const PhysicalQuantities::CLengthUnit &unit) const;
//! All corrected altitude values
QList<double> altitudeValues(const PhysicalQuantities::CLengthUnit &unit) const;
//! All corrected altitude values
QList<double> correctedAltitudeValues(const PhysicalQuantities::CLengthUnit &unit, const PhysicalQuantities::CLength &cg) const;
};
} // namespace
} // namespace

View File

@@ -69,6 +69,24 @@ namespace BlackMisc
});
}
template<class OBJ, class CONTAINER>
bool IGeoObjectList<OBJ, CONTAINER>::containsNullPosition() const
{
return this->container().containsBy([&](const ICoordinateGeodetic & geoObj)
{
return geoObj.isNull();
});
}
template<class OBJ, class CONTAINER>
bool IGeoObjectList<OBJ, CONTAINER>::containsNullPositionOrHeight() const
{
return this->container().containsBy([&](const ICoordinateGeodetic & geoObj)
{
return geoObj.isNull() || geoObj.isGeodeticHeightNull();
});
}
template<class OBJ, class CONTAINER>
typename IGeoObjectList<OBJ, CONTAINER>::MinMaxAverageHeight IGeoObjectList<OBJ, CONTAINER>::findMinMaxAverageHeight() const
{

View File

@@ -66,9 +66,15 @@ namespace BlackMisc
//! Elements with geodetic height (only MSL)
CONTAINER findWithGeodeticMSLHeight() const;
//! Any object in range
//! Any object in range?
bool containsObjectInRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const;
//! Any NULL position?
bool containsNullPosition() const;
//! Any NULL position or NULL height
bool containsNullPositionOrHeight() const;
//! Find min/max/average height
MinMaxAverageHeight findMinMaxAverageHeight() const;