mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 23:05:36 +08:00
Ref T261, aircraft situation
* null altitude
* ground functions
* CG ("member")
* supporting CAircraftSituationChange
This commit is contained in:
committed by
Roland Winklmeier
parent
1f3e5c6abf
commit
bed79237bf
@@ -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 ¢erOfGravity, 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 ¢erOfGravity, 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 ¢erOfGravity, 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
|
||||
|
||||
@@ -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 ¢erOfGravity) 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 ¢erOfGravity = PhysicalQuantities::CLength::null(), bool enableDragToGround = true, AltitudeCorrection *correctetion = nullptr) const;
|
||||
//! @{
|
||||
CAltitude getCorrectedAltitude(bool enableDragToGround = true, AltitudeCorrection *correction = nullptr) const;
|
||||
CAltitude getCorrectedAltitude(const PhysicalQuantities::CLength ¢erOfGravity, bool enableDragToGround = true, AltitudeCorrection *correction = nullptr) const;
|
||||
//! @}
|
||||
|
||||
//! Set the corrected altitude from CAircraftSituation::getCorrectedAltitude
|
||||
//! @{
|
||||
AltitudeCorrection correctAltitude(bool enableDragToGround = true);
|
||||
AltitudeCorrection correctAltitude(const PhysicalQuantities::CLength ¢erOfGravity = 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user