Issue #77 Break cyclic dependencies between CAircraftParts, CAircraftLights, CAircraftSituation classes

This commit is contained in:
Mat Sutcliffe
2020-11-10 21:26:41 +00:00
parent 761fd56cc9
commit b2ae19111f
18 changed files with 488 additions and 520 deletions

View File

@@ -1215,6 +1215,44 @@ namespace BlackCore
return aircraft;
}
bool CAirspaceMonitor::extrapolateElevation(CAircraftSituationList &situations, const CAircraftSituationChange &change)
{
if (situations.size() < 3) { return false; }
//Q_ASSERT_X(situations.m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst, Q_FUNC_INFO, "Need latest first");
const CAircraftSituation old = situations[1];
const CAircraftSituation older = situations[2];
return extrapolateElevation(situations.front(), old, older, change);
}
bool CAirspaceMonitor::extrapolateElevation(CAircraftSituation &situationToBeUpdated, const CAircraftSituation &oldSituation, const CAircraftSituation &olderSituation, const CAircraftSituationChange &oldChange)
{
if (situationToBeUpdated.hasGroundElevation()) { return false; }
// if acceptable transfer
if (oldSituation.transferGroundElevationFromMe(situationToBeUpdated))
{
// change or keep type is the question
// situationToBeUpdated.setGroundElevationInfo(Extrapolated);
return true;
}
if (oldSituation.isNull() || olderSituation.isNull()) { return false; }
if (oldChange.isNull()) { return false; }
if (oldChange.isConstOnGround() && oldChange.hasAltitudeDevWithinAllowedRange() && oldChange.hasElevationDevWithinAllowedRange())
{
// we have almost const altitudes and elevations
const double deltaAltFt = qAbs(situationToBeUpdated.getAltitude().value(CLengthUnit::ft()) - olderSituation.getAltitude().value(CLengthUnit::ft()));
if (deltaAltFt <= CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft()))
{
// the current alt is also not much different
situationToBeUpdated.setGroundElevation(oldSituation.getGroundElevation(), CAircraftSituation::Extrapolated);
return true;
}
}
return false;
}
void CAirspaceMonitor::onAircraftUpdateReceived(const CAircraftSituation &situation, const CTransponder &transponder)
{
Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Called in different thread");
@@ -1539,7 +1577,7 @@ namespace BlackCore
// values before updating (i.e. "storing") so the new situation is not yet considered
if (situationsBeforeStoring.size() > 1)
{
const bool extrapolated = correctedSituation.extrapolateElevation(situationsBeforeStoring[0], situationsBeforeStoring[1], changesBeforeStoring);
const bool extrapolated = extrapolateElevation(correctedSituation, situationsBeforeStoring[0], situationsBeforeStoring[1], changesBeforeStoring);
triedExtrapolation = true;
couldNotExtrapolate = !extrapolated;
fromWhere = 20;

View File

@@ -419,6 +419,16 @@ namespace BlackCore
//! Set matching readiness flag
Readiness &addMatchingReadinessFlag(const BlackMisc::Aviation::CCallsign &callsign, MatchingReadinessFlag mrf);
//! Extrapolates elevation into front (first) element from 2nd and 3rd element
//! \pre the list must be sorted latest first and containt at least 3 elements
static bool extrapolateElevation(BlackMisc::Aviation::CAircraftSituationList &situations, const BlackMisc::Aviation::CAircraftSituationChange &change);
//! Extrapolated between the 2 situations for situation
//! \remark normally used if situationToBeUpdated is not between oldSituation and olderSituation (that would be interpolation)
//! \return false if there are no two elevations, there is already an elevation, or no extrapolation is possible (too much deviation)
static bool extrapolateElevation(BlackMisc::Aviation::CAircraftSituation &situationToBeUpdated, const BlackMisc::Aviation::CAircraftSituation &oldSituation,
const BlackMisc::Aviation::CAircraftSituation &olderSituation, const BlackMisc::Aviation::CAircraftSituationChange &oldChange);
//! Create aircraft in range, this is the only place where a new aircraft should be added
void onAircraftUpdateReceived(const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);

View File

@@ -7,13 +7,10 @@
*/
#include "blackmisc/aviation/aircraftlights.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/stringutils.h"
#include "blackmisc/comparefunctions.h"
#include <QStringBuilder>
using namespace BlackMisc::PhysicalQuantities;
namespace BlackMisc
{
namespace Aviation
@@ -36,52 +33,6 @@ namespace BlackMisc
return CAircraftLights {false, false, false, false, false, false, false, false};
}
CAircraftLights CAircraftLights::guessedLights(const CAircraftSituation &situation)
{
const bool isOnGround = situation.getOnGround() == CAircraftSituation::OnGround;
const double gsKts = situation.getGroundSpeed().value(CSpeedUnit::kts());
CAircraftLights lights;
lights.setCabinOn(true);
lights.setRecognitionOn(true);
// when first detected moving, lights on
if (isOnGround)
{
lights.setTaxiOn(true);
lights.setBeaconOn(true);
lights.setNavOn(true);
if (gsKts > 5)
{
// mode taxi
lights.setTaxiOn(true);
lights.setLandingOn(false);
}
else if (gsKts > 30)
{
// mode accelaration for takeoff
lights.setTaxiOn(false);
lights.setLandingOn(true);
}
else
{
// slow movements or parking
lights.setTaxiOn(false);
lights.setLandingOn(false);
}
}
else
{
// not on ground
lights.setTaxiOn(false);
lights.setBeaconOn(true);
lights.setNavOn(true);
// landing lights for < 10000ft (normally MSL, here ignored)
lights.setLandingOn(situation.getAltitude().value(CLengthUnit::ft()) < 10000);
}
return lights;
}
QString CAircraftLights::convertToQString(bool i18n) const
{
Q_UNUSED(i18n);
@@ -178,10 +129,5 @@ namespace BlackMisc
m_recognition = false;
m_cabin = false;
}
void CAircraftLights::guessLights(const CAircraftSituation &situation)
{
*this = guessedLights(situation);
}
} // namespace
} // namespace

View File

@@ -23,8 +23,6 @@ namespace BlackMisc
{
namespace Aviation
{
class CAircraftSituation;
//! Value object encapsulating information about aircraft's lights
class BLACKMISC_EXPORT CAircraftLights : public CValueObject<CAircraftLights>
{
@@ -109,9 +107,6 @@ namespace BlackMisc
//! All off
void setAllOff();
//! Guess the lights
void guessLights(const CAircraftSituation &situation);
//! \copydoc BlackMisc::Mixin::Index::propertyByIndex
QVariant propertyByIndex(CPropertyIndexRef index) const;
@@ -130,9 +125,6 @@ namespace BlackMisc
//! Returns object with all lights switched off
static CAircraftLights allLightsOff();
//! Guessed lights
static CAircraftLights guessedLights(const CAircraftSituation &situation);
//! Null?
bool isNull() const { return m_isNull; }

View File

@@ -9,8 +9,6 @@
#include "blackmisc/simulation/aircraftmodel.h"
#include "aircraftparts.h"
#include "aircraftlights.h"
#include "aircraftsituation.h"
#include "aircraftsituationchange.h"
#include "blackmisc/comparefunctions.h"
#include "blackmisc/stringutils.h"
#include "blackmisc/verify.h"
@@ -88,135 +86,6 @@ namespace BlackMisc
return null;
}
CAircraftParts CAircraftParts::guessedParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const CAircraftModel &model)
{
CAircraftParts parts;
parts.setMSecsSinceEpoch(situation.getMSecsSinceEpoch());
parts.setTimeOffsetMs(situation.getTimeOffsetMs());
parts.setPartsDetails(GuessedParts);
parts.setLights(CAircraftLights::guessedLights(situation));
QString *details = CBuildConfig::isLocalDeveloperDebugBuild() ? &parts.m_guessingDetails : nullptr;
CAircraftEngineList engines;
const bool vtol = model.isVtol();
const int engineCount = model.getEngineCount();
CSpeed guessedVRotate = CSpeed::null();
CLength guessedCG = model.getCG();
model.getAircraftIcaoCode().guessModelParameters(guessedCG, guessedVRotate);
if (situation.getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails)
{
do
{
// set some reasonable values
const bool isOnGround = situation.isOnGround();
engines.initEngines(engineCount, !isOnGround || situation.isMoving());
parts.setGearDown(isOnGround);
parts.setSpoilersOut(false);
parts.setEngines(engines);
if (!change.isNull())
{
if (change.isConstDecelarating())
{
parts.setSpoilersOut(true);
parts.setFlapsPercent(10);
break;
}
}
const CSpeed slowSpeed = guessedVRotate * 0.30;
if (situation.getGroundSpeed() < slowSpeed)
{
if (details) { *details += u"slow speed <" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
parts.setFlapsPercent(0);
break;
}
else
{
if (details) { *details += u"faster speed >" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
parts.setFlapsPercent(0);
break;
}
}
while (false);
}
else
{
if (details) { *details = QStringLiteral("no ground info"); }
// no idea if on ground or not
engines.initEngines(engineCount, true);
parts.setEngines(engines);
parts.setGearDown(true);
parts.setSpoilersOut(false);
}
const double pitchDeg = situation.getPitch().value(CAngleUnit::deg());
const bool isLikelyTakeOffOrClimbing = change.isNull() ? pitchDeg > 20 : (change.isRotatingUp() || change.isConstAscending());
const bool isLikelyLanding = change.isNull() ? false : change.isConstDescending();
if (situation.hasGroundElevation())
{
const CLength aboveGnd = situation.getHeightAboveGround();
if (aboveGnd.isNull() || std::isnan(aboveGnd.value()))
{
BLACK_VERIFY_X(false, Q_FUNC_INFO, "above gnd.is null");
return parts;
}
const double nearGround1Ft = 300;
const double nearGround2Ft = isLikelyTakeOffOrClimbing ? 500 : 1000;
const double aGroundFt = aboveGnd.value(CLengthUnit::ft());
static const QString detailsInfo("above ground: %1ft near grounds: %2ft %3ft likely takeoff: %4 likely landing: %5");
if (details) { *details = detailsInfo.arg(aGroundFt).arg(nearGround1Ft).arg(nearGround2Ft).arg(boolToYesNo(isLikelyTakeOffOrClimbing), boolToYesNo(isLikelyLanding)); }
if (aGroundFt < nearGround1Ft)
{
if (details) { details->prepend(QStringLiteral("near ground: ")); }
parts.setGearDown(true);
parts.setFlapsPercent(25);
}
else if (aGroundFt < nearGround2Ft)
{
if (details) { details->prepend(QStringLiteral("2nd layer: ")); }
const bool gearDown = !isLikelyTakeOffOrClimbing && (situation.getGroundSpeed() < guessedVRotate || isLikelyLanding);
parts.setGearDown(gearDown);
parts.setFlapsPercent(10);
}
else
{
if (details) { details->prepend(QStringLiteral("airborne: ")); }
parts.setGearDown(false);
parts.setFlapsPercent(0);
}
}
else
{
if (situation.getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails)
{
// we have no ground elevation but a ground info
if (situation.getOnGroundDetails() == CAircraftSituation::OnGroundByGuessing)
{
// should be OK
if (details) { *details = QStringLiteral("on ground, no elv."); }
}
else
{
if (!vtol)
{
const bool gearDown = situation.getGroundSpeed() < guessedVRotate;
parts.setGearDown(gearDown);
if (details) { *details = QStringLiteral("not on ground elv., gs < ") + guessedVRotate.valueRoundedWithUnit(1); }
}
}
}
}
return parts;
}
const QString &CAircraftParts::partsDetailsToString(CAircraftParts::PartsDetails details)
{
static const QString guessed("guessed");
@@ -341,10 +210,5 @@ namespace BlackMisc
engines.setEngines(engine, engineNumber);
m_engines = engines;
}
void CAircraftParts::guessParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const CAircraftModel &model)
{
*this = CAircraftParts::guessedParts(situation, change, model);
}
} // namespace
} // namespace

View File

@@ -27,9 +27,6 @@ namespace BlackMisc
namespace Simulation { class CAircraftModel; }
namespace Aviation
{
class CAircraftSituation;
class CAircraftSituationChange;
//! Value object encapsulating information of aircraft's parts
class BLACKMISC_EXPORT CAircraftParts :
public CValueObject<CAircraftParts>,
@@ -158,9 +155,6 @@ namespace BlackMisc
//! Set parts details
void setPartsDetails(PartsDetails details) { m_partsDetails = static_cast<int>(details); }
//! Guess the parts
void guessParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const Simulation::CAircraftModel &model);
//! \copydoc BlackMisc::Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
@@ -181,9 +175,6 @@ namespace BlackMisc
//! NULL parts object
static const CAircraftParts &null();
//! Guessed parts
static CAircraftParts guessedParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const Simulation::CAircraftModel &model);
//! Convert to QString
static const QString &partsDetailsToString(PartsDetails details);

View File

@@ -8,8 +8,8 @@
#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftpartslist.h"
#include "blackmisc/aviation/aircraftlights.h"
#include "blackmisc/geo/elevationplane.h"
#include "blackmisc/pq/length.h"
#include "blackmisc/pq/units.h"
@@ -31,6 +31,59 @@ namespace BlackMisc
{
namespace Aviation
{
const CLength &CAircraftSituation::allowedAltitudeDeviation()
{
// approx. 1 meter
static const CLength allowedStdDev(3, CLengthUnit::ft());
return allowedStdDev;
}
CAircraftLights CAircraftSituation::guessLights() const
{
const bool isOnGround = getOnGround() == CAircraftSituation::OnGround;
const double gsKts = getGroundSpeed().value(CSpeedUnit::kts());
CAircraftLights lights;
lights.setCabinOn(true);
lights.setRecognitionOn(true);
// when first detected moving, lights on
if (isOnGround)
{
lights.setTaxiOn(true);
lights.setBeaconOn(true);
lights.setNavOn(true);
if (gsKts > 5)
{
// mode taxi
lights.setTaxiOn(true);
lights.setLandingOn(false);
}
else if (gsKts > 30)
{
// mode accelaration for takeoff
lights.setTaxiOn(false);
lights.setLandingOn(true);
}
else
{
// slow movements or parking
lights.setTaxiOn(false);
lights.setLandingOn(false);
}
}
else
{
// not on ground
lights.setTaxiOn(false);
lights.setBeaconOn(true);
lights.setNavOn(true);
// landing lights for < 10000ft (normally MSL, here ignored)
lights.setLandingOn(getAltitude().value(CLengthUnit::ft()) < 10000);
}
return lights;
}
void CAircraftSituation::registerMetadata()
{
CValueObject<CAircraftSituation>::registerMetadata();
@@ -207,53 +260,6 @@ namespace BlackMisc
return cg;
}
bool CAircraftSituation::presetGroundElevation(CAircraftSituation &situationToPreset, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CAircraftSituationChange &change)
{
// IMPORTANT: we do not know what the situation will be (interpolated to), so we cannot transfer
situationToPreset.resetGroundElevation();
do
{
if (oldSituation.equalNormalVectorDouble(newSituation))
{
if (oldSituation.hasGroundElevation())
{
// same positions, we can use existing elevation
// means we were not moving between old an new
situationToPreset.transferGroundElevationToMe(oldSituation, true);
break;
}
}
const CLength distance = newSituation.calculateGreatCircleDistance(oldSituation);
if (distance < newSituation.getDistancePerTime250ms(CElevationPlane::singlePointRadius()))
{
if (oldSituation.hasGroundElevation())
{
// almost same positions, we can use existing elevation
situationToPreset.transferGroundElevationToMe(oldSituation, true);
break;
}
}
if (change.hasElevationDevWithinAllowedRange())
{
// not much change in known elevations
const CAltitudePair elvDevMean = change.getElevationStdDevAndMean();
situationToPreset.setGroundElevation(elvDevMean.second, CAircraftSituation::SituationChange);
break;
}
const CElevationPlane epInterpolated = CAircraftSituation::interpolatedElevation(CAircraftSituation::null(), oldSituation, newSituation, distance);
if (!epInterpolated.isNull())
{
situationToPreset.setGroundElevation(epInterpolated, CAircraftSituation::Interpolated);
break;
}
}
while (false);
return situationToPreset.hasGroundElevation();
}
CElevationPlane CAircraftSituation::interpolatedElevation(const CAircraftSituation &situation, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CLength &distance)
{
if (oldSituation.isNull() || newSituation.isNull()) { return CElevationPlane::null(); }
@@ -297,35 +303,6 @@ namespace BlackMisc
}
}
bool CAircraftSituation::extrapolateElevation(CAircraftSituation &situationToBeUpdated, const CAircraftSituation &oldSituation, const CAircraftSituation &olderSituation, const CAircraftSituationChange &oldChange)
{
if (situationToBeUpdated.hasGroundElevation()) { return false; }
// if acceptable transfer
if (oldSituation.transferGroundElevationFromMe(situationToBeUpdated))
{
// change or keep type is the question
// situationToBeUpdated.setGroundElevationInfo(Extrapolated);
return true;
}
if (oldSituation.isNull() || olderSituation.isNull()) { return false; }
if (oldChange.isNull()) { return false; }
if (oldChange.isConstOnGround() && oldChange.hasAltitudeDevWithinAllowedRange() && oldChange.hasElevationDevWithinAllowedRange())
{
// we have almost const altitudes and elevations
const double deltaAltFt = qAbs(situationToBeUpdated.getAltitude().value(CLengthUnit::ft()) - olderSituation.getAltitude().value(CLengthUnit::ft()));
if (deltaAltFt <= CAircraftSituationChange::allowedAltitudeDeviation().value(CLengthUnit::ft()))
{
// the current alt is also not much different
situationToBeUpdated.setGroundElevation(oldSituation.getGroundElevation(), Extrapolated);
return true;
}
}
return false;
}
QVariant CAircraftSituation::propertyByIndex(BlackMisc::CPropertyIndexRef index) const
{
if (index.isMyself()) { return QVariant::fromValue(*this); }
@@ -556,117 +533,6 @@ namespace BlackMisc
return !this->hasInboundGroundDetails();
}
bool CAircraftSituation::guessOnGround(const CAircraftSituationChange &change, const CAircraftModel &model)
{
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)
{
if (this->getGroundSpeed().isNegativeWithEpsilonConsidered())
{
this->setOnGround(OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("No VTOL, push back"); }
return true;
}
if (!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);
CLength cg = m_cg.isNull() ? model.getCG() : m_cg;
CSpeed guessedRotateSpeed = CSpeed::null();
CSpeed sureRotateSpeed = CSpeed(130, CSpeedUnit::kts());
model.getAircraftIcaoCode().guessModelParameters(cg, guessedRotateSpeed);
if (!guessedRotateSpeed.isNull())
{
// does the value make any sense?
const bool validGuessedSpeed = (guessedRotateSpeed.value(CSpeedUnit::km_h()) > 5.0);
BLACK_VERIFY_X(validGuessedSpeed, Q_FUNC_INFO, "Wrong guessed value for lift off");
if (!validGuessedSpeed) { guessedRotateSpeed = CSpeed(80, CSpeedUnit::kts()); } // fix
sureRotateSpeed = guessedRotateSpeed * 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() > sureRotateSpeed) { if (details) { *details = u"gs. > vr " % sureRotateSpeed.valueRoundedWithUnit(1); } return true; }
// use the most accurate or reliable guesses here first
// ------------------------------------------------------
// by elevation
// 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;
}
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)
{
// no idea
this->setOnGround(OnGroundSituationUnknown, NotSetGroundDetails);
return false;
}
// guessed speed null -> vtol
if (!guessedRotateSpeed.isNull())
{
// does the value make any sense?
if (this->getGroundSpeed() < guessedRotateSpeed)
{
this->setOnGround(OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("Guessing, max.guessed gs.") + guessedRotateSpeed.valueRoundedWithUnit(CSpeedUnit::kts(), 1); }
return true;
}
}
// not sure, but this is a guess
if (details) { *details = QStringLiteral("Fall through"); }
return true;
}
CLength CAircraftSituation::getGroundDistance(const CLength &centerOfGravity) const
{
if (centerOfGravity.isNull() || !this->hasGroundElevation()) { return CLength::null(); }
@@ -783,16 +649,6 @@ namespace BlackMisc
return this->setGroundElevation(fromSituation.getGroundElevationPlane(), fromSituation.getGroundElevationInfo(), transferred);
}
bool CAircraftSituation::presetGroundElevation(const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CAircraftSituationChange &change)
{
return CAircraftSituation::presetGroundElevation(*this, oldSituation, newSituation, change);
}
bool CAircraftSituation::extrapolateElevation(const CAircraftSituation &oldSituation, const CAircraftSituation &olderSituation, const CAircraftSituationChange &change)
{
return CAircraftSituation::extrapolateElevation(*this, oldSituation, olderSituation, change);
}
bool CAircraftSituation::interpolateElevation(const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation)
{
const CElevationPlane ep = CAircraftSituation::interpolatedElevation(*this, oldSituation, newSituation);

View File

@@ -41,7 +41,7 @@ namespace BlackMisc
{
class CAircraftParts;
class CAircraftPartsList;
class CAircraftSituationChange;
class CAircraftLights;
//! Value object encapsulating information of an aircraft's situation
class BLACKMISC_EXPORT CAircraftSituation :
@@ -241,9 +241,6 @@ namespace BlackMisc
//! Should we guess on ground?
bool shouldGuessOnGround() const;
//! Guess on ground flag
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;
@@ -316,19 +313,6 @@ namespace BlackMisc
//! Transfer ground elevation from given situation (to me)
bool transferGroundElevationToMe(const CAircraftSituation &fromSituation, bool transferred);
//! Preset "this" elevation from the two adjacent positions
//! \remark it is not required that the position of "this" is already known
//! \remark "transfer" can be used, if the positions are known, "preset" if they are still unknown
//! \sa CAircraftSituation::transferGroundElevation
//! \sa CAircraftSituation::interpolateElevation
bool presetGroundElevation(const Aviation::CAircraftSituation &oldSituation, const Aviation::CAircraftSituation &newSituation, const CAircraftSituationChange &change);
//! Set "my" elevation from older situations.
//! \remark this object normally is a future value
//! \sa CAircraftSituation::transferGroundElevation
//! \sa CAircraftSituation::interpolateElevation
bool extrapolateElevation(const Aviation::CAircraftSituation &oldSituation, const Aviation::CAircraftSituation &olderSituation, const CAircraftSituationChange &change);
//! Interpolate "this" elevation from the two adjacent positions
//! \remark "transfer" can be used, if the positions are known, "preset" if they are still unknown
//! \sa CAircraftSituation::transferGroundElevation
@@ -553,18 +537,6 @@ namespace BlackMisc
}
//! @}
//! Preset the ground elevation based on info we already have, either by transfer or elevation
//! \remark either sets a gnd. elevation or sets it to null
//! \remark situationToPreset position is unknown
//! \remark situationToPreset needs to be between oldSituation and newSituation
//! \sa CAircraftSituation::transferGroundElevation
static bool presetGroundElevation(CAircraftSituation &situationToPreset, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CAircraftSituationChange &change);
//! Extrapolated between the 2 situations for situation
//! \remark normally used if situationToBeUpdated is not between oldSituation and olderSituation (that would be interpolation)
//! \return false if there are no two elevations, there is already an elevation, or no extrapolation is possible (too much deviation)
static bool extrapolateElevation(CAircraftSituation &situationToBeUpdated, const CAircraftSituation &oldSituation, const CAircraftSituation &olderSituation, const CAircraftSituationChange &oldChange);
//! Interpolate between the 2 situations for situation
//! \remark NULL if there are no two elevations or threshold MaxDeltaElevationFt is exceeded
static Geo::CElevationPlane interpolatedElevation(const CAircraftSituation &situation, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const PhysicalQuantities::CLength &distance = PhysicalQuantities::CLength::null());
@@ -572,6 +544,12 @@ namespace BlackMisc
//! Threshold until we interpolate elevations
static constexpr double MaxDeltaElevationFt = 25.0;
//! Within this range deviation is so small we consider values "almost constant"
static const PhysicalQuantities::CLength &allowedAltitudeDeviation();
//! Guessed lights
CAircraftLights guessLights() const;
//! Register metadata
static void registerMetadata();

View File

@@ -9,6 +9,7 @@
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftsituationlist.h"
#include "blackmisc/aviation/callsign.h"
#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/pq/length.h"
#include "blackmisc/pq/angle.h"
#include "blackmisc/pq/units.h"
@@ -87,6 +88,117 @@ namespace BlackMisc
}
}
bool CAircraftSituationChange::guessOnGround(CAircraftSituation &situation, const Simulation::CAircraftModel &model) const
{
if (!situation.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)
{
if (situation.getGroundSpeed().isNegativeWithEpsilonConsidered())
{
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("No VTOL, push back"); }
return true;
}
if (!situation.isMoving())
{
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("No VTOL, not moving => on ground"); }
return true;
}
}
// not on ground is default
situation.setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByGuessing);
CLength cg = situation.hasCG() ? situation.getCG() : model.getCG();
CSpeed guessedRotateSpeed = CSpeed::null();
CSpeed sureRotateSpeed = CSpeed(130, CSpeedUnit::kts());
model.getAircraftIcaoCode().guessModelParameters(cg, guessedRotateSpeed);
if (!guessedRotateSpeed.isNull())
{
// does the value make any sense?
const bool validGuessedSpeed = (guessedRotateSpeed.value(CSpeedUnit::km_h()) > 5.0);
BLACK_VERIFY_X(validGuessedSpeed, Q_FUNC_INFO, "Wrong guessed value for lift off");
if (!validGuessedSpeed) { guessedRotateSpeed = CSpeed(80, CSpeedUnit::kts()); } // fix
sureRotateSpeed = guessedRotateSpeed * 1.25;
}
// "extreme" values for which we are surely not on ground
if (qAbs(situation.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(situation.getBank().value(CAngleUnit::deg())) > 10) { if (details) { *details = QStringLiteral("max.bank"); } return true; }
if (situation.getGroundSpeed() > sureRotateSpeed) { if (details) { *details = u"gs. > vr " % sureRotateSpeed.valueRoundedWithUnit(1); } return true; }
// use the most accurate or reliable guesses here first
// ------------------------------------------------------
// by elevation
// 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)
CAircraftSituation::IsOnGround og = situation.isOnGroundByElevation(cg);
if (og == CAircraftSituation::OnGround)
{
if (details) { *details = QStringLiteral("elevation on ground"); }
situation.setOnGround(og, CAircraftSituation::OnGroundByGuessing);
return true;
}
if (!isNull())
{
if (!vtol && wasConstOnGround())
{
if (isRotatingUp())
{
// not OG
if (details) { *details = QStringLiteral("rotating up detected"); }
return true;
}
// here we stick to ground until we detect rotate up
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("waiting for rotating up"); }
return true;
}
if (isConstAscending())
{
// not OG
if (details) { *details = QStringLiteral("const ascending"); }
return true;
}
}
// on VTOL we stop here
if (vtol)
{
// no idea
situation.setOnGround(CAircraftSituation::OnGroundSituationUnknown, CAircraftSituation::NotSetGroundDetails);
return false;
}
// guessed speed null -> vtol
if (!guessedRotateSpeed.isNull())
{
// does the value make any sense?
if (situation.getGroundSpeed() < guessedRotateSpeed)
{
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
if (details) { *details = QStringLiteral("Guessing, max.guessed gs.") + guessedRotateSpeed.valueRoundedWithUnit(CSpeedUnit::kts(), 1); }
return true;
}
}
// not sure, but this is a guess
if (details) { *details = QStringLiteral("Fall through"); }
return true;
}
bool CAircraftSituationChange::hasSceneryDeviation() const
{
return !m_guessedSceneryDeviation.isNull();
@@ -95,13 +207,13 @@ namespace BlackMisc
bool CAircraftSituationChange::hasElevationDevWithinAllowedRange() const
{
if (m_elvStdDev.isNull()) { return false; }
return m_elvStdDev < allowedAltitudeDeviation();
return m_elvStdDev < CAircraftSituation::allowedAltitudeDeviation();
}
bool CAircraftSituationChange::hasAltitudeDevWithinAllowedRange() const
{
if (m_altStdDev.isNull()) { return false; }
return m_altStdDev < allowedAltitudeDeviation();
return m_altStdDev < CAircraftSituation::allowedAltitudeDeviation();
}
QString CAircraftSituationChange::convertToQString(bool i18n) const
@@ -286,13 +398,6 @@ namespace BlackMisc
return noInfo;
}
const CLength &CAircraftSituationChange::allowedAltitudeDeviation()
{
// approx. 1 meter
static const CLength allowedStdDev(3, CLengthUnit::ft());
return allowedStdDev;
}
void CAircraftSituationChange::setSceneryDeviation(const CLength &deviation, const CLength &cg, CAircraftSituationChange::GuessedSceneryDeviation hint)
{
m_guessedSceneryDeviation = deviation;

View File

@@ -23,6 +23,10 @@
namespace BlackMisc
{
namespace Simulation
{
class CAircraftModel;
}
namespace Aviation
{
class CAircraftSituation;
@@ -134,6 +138,9 @@ namespace BlackMisc
//! \copydoc BlackMisc::Aviation::CAircraftSituationList::minMaxGroundDistance
PhysicalQuantities::CLengthPair getMinMaxGroundDistance() const { return PhysicalQuantities::CLengthPair(m_minGroundDistance, m_maxGroundDistance); }
//! Guess on ground flag
bool guessOnGround(CAircraftSituation &situation, const Simulation::CAircraftModel &model) const;
//! Scnenery deviation (if it can be calculated, otherwise PhysicalQuantities::CLength::null)
//! \remark This is without CG, so substract CG to get deviation
const PhysicalQuantities::CLength &getGuessedSceneryDeviation() const { return m_guessedSceneryDeviation; }
@@ -177,9 +184,6 @@ namespace BlackMisc
//! The enum as string
static const QString &guessedSceneryDeviationToString(GuessedSceneryDeviation hint);
//! Within this range deviation is so small we consider values "almost constant"
static const PhysicalQuantities::CLength &allowedAltitudeDeviation();
//! Register metadata
static void registerMetadata();

View File

@@ -7,7 +7,6 @@
*/
#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftsituationlist.h"
#include "blackmisc/geo/elevationplane.h"
#include "blackmisc/math/mathutils.h"
@@ -64,52 +63,6 @@ namespace BlackMisc
return c;
}
int CAircraftSituationList::setGroundElevationCheckedAndGuessGround(
const CElevationPlane &elevationPlane, CAircraftSituation::GndElevationInfo info, const CAircraftModel &model,
CAircraftSituationChange *changeOut, bool *setForOnGroundPosition)
{
if (setForOnGroundPosition) { *setForOnGroundPosition = false; } // set a default
if (elevationPlane.isNull()) { return 0; }
if (this->isEmpty()) { return 0; }
// the change has the timestamps of the latest situation
Q_ASSERT_X(m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst || this->isSortedAdjustedLatestFirstWithoutNullPositions(), Q_FUNC_INFO, "Need sorted situations without NULL positions");
const CAircraftSituationChange simpleChange(*this, model.getCG(), model.isVtol(), true, false);
int c = 0; // changed elevations
bool latest = true;
bool setForOnGndPosition = false;
for (CAircraftSituation &s : *this)
{
const bool set = s.setGroundElevationChecked(elevationPlane, info);
if (set)
{
// simpleChange is only valid for the latest situation
// this will do nothing if not appropriate!
const bool guessed = s.guessOnGround(latest ? simpleChange : CAircraftSituationChange::null(), model);
Q_UNUSED(guessed)
c++;
// if not guessed and "on ground" we mark the "elevation"
// as an elevation for a ground position
if (!setForOnGndPosition && s.hasInboundGroundDetails() && s.isOnGround())
{
setForOnGndPosition = true;
}
}
latest = false; // only first pos. is "the latest" one
}
if (setForOnGroundPosition) { *setForOnGroundPosition = setForOnGndPosition; }
if (changeOut)
{
const CAircraftSituationChange change(*this, model.getCG(), model.isVtol(), true, true);
*changeOut = change;
}
return c;
}
int CAircraftSituationList::adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor)
{
int c = 0;
@@ -141,15 +94,6 @@ namespace BlackMisc
return c;
}
bool CAircraftSituationList::extrapolateElevation(const CAircraftSituationChange &change)
{
if (this->size() < 3) { return false; }
Q_ASSERT_X(m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst, Q_FUNC_INFO, "Need latest first");
const CAircraftSituation old = (*this)[1];
const CAircraftSituation older = (*this)[2];
return this->front().extrapolateElevation(old, older, change);
}
CAircraftSituationList CAircraftSituationList::findByInboundGroundInformation(bool hasGroundInfo) const
{
return this->findBy(&CAircraftSituation::hasInboundGroundDetails, hasGroundInfo);
@@ -632,7 +576,7 @@ namespace BlackMisc
if (valuesInFt.size() < minValues) { return CElevationPlane::null(); }
static const double MaxDevFt = CAircraftSituationChange::allowedAltitudeDeviation().value(CLengthUnit::ft());
static const double MaxDevFt = CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft());
const QPair<double, double> elvStdDevMean = CMathUtils::standardDeviationAndMean(valuesInFt);
if (elvStdDevMean.first > MaxDevFt) { return CElevationPlane::null(); }
return CElevationPlane(reference, elvStdDevMean.second, CElevationPlane::singlePointRadius());

View File

@@ -63,21 +63,12 @@ namespace BlackMisc
//! Set ground elevation from elevation plane
int setGroundElevationChecked(const Geo::CElevationPlane &elevationPlane, CAircraftSituation::GndElevationInfo info, qint64 newerThanAdjustedMs = -1);
//! Set ground elevation from elevation plane and guess ground
//! \note requires a sorted list latest first
int setGroundElevationCheckedAndGuessGround(const Geo::CElevationPlane &elevationPlane, CAircraftSituation::GndElevationInfo info, const Simulation::CAircraftModel &model, CAircraftSituationChange *changeOut, bool *setForOnGroundPosition);
//! Adjust flag from parts by using CAircraftSituation::adjustGroundFlag
int adjustGroundFlag(const CAircraftParts &parts, double timeDeviationFactor = 0.1);
//! Extrapolate ground flag into the future
int extrapolateGroundFlag();
//! Extrapolates elevation into front (first) element from 2nd and 3rd element
//! \sa CAircraftSituation::extrapolateElevation
//! \pre the list must be sorted latest first and containt at least 3 elements
bool extrapolateElevation(const CAircraftSituationChange &change);
//! Find if having inbound information
CAircraftSituationList findByInboundGroundInformation(bool hasGroundInfo) const;

View File

@@ -11,6 +11,8 @@
#include "blackmisc/simulation/interpolationlogger.h"
#include "blackmisc/simulation/interpolatorlinear.h"
#include "blackmisc/simulation/interpolatorspline.h"
#include "blackmisc/aviation/aircraftsituationchange.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/network/fsdsetup.h"
#include "blackmisc/aviation/callsign.h"
#include "blackmisc/aviation/heading.h"
@@ -117,6 +119,54 @@ namespace BlackMisc
return validSituations;
}
template<typename Derived>
bool CInterpolator<Derived>::presetGroundElevation(CAircraftSituation &situationToPreset, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CAircraftSituationChange &change)
{
// IMPORTANT: we do not know what the situation will be (interpolated to), so we cannot transfer
situationToPreset.resetGroundElevation();
do
{
if (oldSituation.equalNormalVectorDouble(newSituation))
{
if (oldSituation.hasGroundElevation())
{
// same positions, we can use existing elevation
// means we were not moving between old an new
situationToPreset.transferGroundElevationToMe(oldSituation, true);
break;
}
}
const CLength distance = newSituation.calculateGreatCircleDistance(oldSituation);
if (distance < newSituation.getDistancePerTime250ms(CElevationPlane::singlePointRadius()))
{
if (oldSituation.hasGroundElevation())
{
// almost same positions, we can use existing elevation
situationToPreset.transferGroundElevationToMe(oldSituation, true);
break;
}
}
if (change.hasElevationDevWithinAllowedRange())
{
// not much change in known elevations
const CAltitudePair elvDevMean = change.getElevationStdDevAndMean();
situationToPreset.setGroundElevation(elvDevMean.second, CAircraftSituation::SituationChange);
break;
}
const CElevationPlane epInterpolated = CAircraftSituation::interpolatedElevation(CAircraftSituation::null(), oldSituation, newSituation, distance);
if (!epInterpolated.isNull())
{
situationToPreset.setGroundElevation(epInterpolated, CAircraftSituation::Interpolated);
break;
}
}
while (false);
return situationToPreset.hasGroundElevation();
}
template<typename Derived>
void CInterpolator<Derived>::deferredInit()
{
@@ -238,7 +288,7 @@ namespace BlackMisc
}
// GND flag.
if (!interpolateGndFlag) { currentSituation.guessOnGround(CAircraftSituationChange::null(), m_model); }
if (!interpolateGndFlag) { CAircraftSituationChange::null().guessOnGround(currentSituation, m_model); }
// as we now have the position and can interpolate elevation
currentSituation.interpolateElevation(pbh.getOldSituation(), pbh.getNewSituation());
@@ -428,7 +478,7 @@ namespace BlackMisc
// check if model has been thru model matching
if (!m_lastSituation.isNull())
{
parts.guessParts(m_lastSituation, m_pastSituationsChange, m_model);
parts = guessParts(m_lastSituation, m_pastSituationsChange, m_model);
this->logParts(parts, 0, false);
}
else
@@ -470,6 +520,136 @@ namespace BlackMisc
return this->hasAttachedLogger() && m_currentSetup.logInterpolation();
}
template<typename Derived>
CAircraftParts CInterpolator<Derived>::guessParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const CAircraftModel &model)
{
CAircraftParts parts;
parts.setMSecsSinceEpoch(situation.getMSecsSinceEpoch());
parts.setTimeOffsetMs(situation.getTimeOffsetMs());
parts.setPartsDetails(CAircraftParts::GuessedParts);
parts.setLights(situation.guessLights());
QString *details = /*CBuildConfig::isLocalDeveloperDebugBuild() ? &parts.m_guessingDetails :*/ nullptr;
CAircraftEngineList engines;
const bool vtol = model.isVtol();
const int engineCount = model.getEngineCount();
CSpeed guessedVRotate = CSpeed::null();
CLength guessedCG = model.getCG();
model.getAircraftIcaoCode().guessModelParameters(guessedCG, guessedVRotate);
if (situation.getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails)
{
do
{
// set some reasonable values
const bool isOnGround = situation.isOnGround();
engines.initEngines(engineCount, !isOnGround || situation.isMoving());
parts.setGearDown(isOnGround);
parts.setSpoilersOut(false);
parts.setEngines(engines);
if (!change.isNull())
{
if (change.isConstDecelarating())
{
parts.setSpoilersOut(true);
parts.setFlapsPercent(10);
break;
}
}
const CSpeed slowSpeed = guessedVRotate * 0.30;
if (situation.getGroundSpeed() < slowSpeed)
{
if (details) { *details += u"slow speed <" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
parts.setFlapsPercent(0);
break;
}
else
{
if (details) { *details += u"faster speed >" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
parts.setFlapsPercent(0);
break;
}
}
while (false);
}
else
{
if (details) { *details = QStringLiteral("no ground info"); }
// no idea if on ground or not
engines.initEngines(engineCount, true);
parts.setEngines(engines);
parts.setGearDown(true);
parts.setSpoilersOut(false);
}
const double pitchDeg = situation.getPitch().value(CAngleUnit::deg());
const bool isLikelyTakeOffOrClimbing = change.isNull() ? pitchDeg > 20 : (change.isRotatingUp() || change.isConstAscending());
const bool isLikelyLanding = change.isNull() ? false : change.isConstDescending();
if (situation.hasGroundElevation())
{
const CLength aboveGnd = situation.getHeightAboveGround();
if (aboveGnd.isNull() || std::isnan(aboveGnd.value()))
{
BLACK_VERIFY_X(false, Q_FUNC_INFO, "above gnd.is null");
return parts;
}
const double nearGround1Ft = 300;
const double nearGround2Ft = isLikelyTakeOffOrClimbing ? 500 : 1000;
const double aGroundFt = aboveGnd.value(CLengthUnit::ft());
static const QString detailsInfo("above ground: %1ft near grounds: %2ft %3ft likely takeoff: %4 likely landing: %5");
if (details) { *details = detailsInfo.arg(aGroundFt).arg(nearGround1Ft).arg(nearGround2Ft).arg(boolToYesNo(isLikelyTakeOffOrClimbing), boolToYesNo(isLikelyLanding)); }
if (aGroundFt < nearGround1Ft)
{
if (details) { details->prepend(QStringLiteral("near ground: ")); }
parts.setGearDown(true);
parts.setFlapsPercent(25);
}
else if (aGroundFt < nearGround2Ft)
{
if (details) { details->prepend(QStringLiteral("2nd layer: ")); }
const bool gearDown = !isLikelyTakeOffOrClimbing && (situation.getGroundSpeed() < guessedVRotate || isLikelyLanding);
parts.setGearDown(gearDown);
parts.setFlapsPercent(10);
}
else
{
if (details) { details->prepend(QStringLiteral("airborne: ")); }
parts.setGearDown(false);
parts.setFlapsPercent(0);
}
}
else
{
if (situation.getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails)
{
// we have no ground elevation but a ground info
if (situation.getOnGroundDetails() == CAircraftSituation::OnGroundByGuessing)
{
// should be OK
if (details) { *details = QStringLiteral("on ground, no elv."); }
}
else
{
if (!vtol)
{
const bool gearDown = situation.getGroundSpeed() < guessedVRotate;
parts.setGearDown(gearDown);
if (details) { *details = QStringLiteral("not on ground elv., gs < ") + guessedVRotate.valueRoundedWithUnit(1); }
}
}
}
}
return parts;
}
template<typename Derived>
void CInterpolator<Derived>::logParts(const CAircraftParts &parts, int partsNo, bool empty) const
{
@@ -592,7 +772,7 @@ namespace BlackMisc
}
// preset elevation here, as we do not know where the situation will be after the interpolation step!
const bool preset = currentSituation.presetGroundElevation(oldSituation, newSituation, m_pastSituationsChange);
const bool preset = presetGroundElevation(currentSituation, oldSituation, newSituation, m_pastSituationsChange);
Q_UNUSED(preset)
// fetch CG once

View File

@@ -30,6 +30,11 @@
namespace BlackMisc
{
namespace Aviation
{
class CAircraftSituation;
class CAircraftSituationChange;
}
namespace Simulation
{
class CInterpolationLogger;
@@ -309,6 +314,9 @@ namespace BlackMisc
CInterpolationLogger *m_logger = nullptr; //!< optional interpolation logger
QTimer m_initTimer; //!< timer to init model, will be deleted when interpolator is deleted and cancel the call
//! Guessed parts
static Aviation::CAircraftParts guessParts(const Aviation::CAircraftSituation &situation, const Aviation::CAircraftSituationChange &change, const Simulation::CAircraftModel &model);
//! Log parts
void logParts(const Aviation::CAircraftParts &parts, int partsNo, bool empty) const;
@@ -319,6 +327,13 @@ namespace BlackMisc
//! Center of gravity, fetched from provider in case needed
PhysicalQuantities::CLength getAndFetchModelCG(const PhysicalQuantities::CLength &dbCG);
//! Preset the ground elevation based on info we already have, either by transfer or elevation
//! \remark either sets a gnd. elevation or sets it to null
//! \remark situationToPreset position is unknown
//! \remark situationToPreset needs to be between oldSituation and newSituation
//! \sa CAircraftSituation::transferGroundElevation
static bool presetGroundElevation(Aviation::CAircraftSituation &situationToPreset, const Aviation::CAircraftSituation &oldSituation, const Aviation::CAircraftSituation &newSituation, const Aviation::CAircraftSituationChange &change);
//! Deferred init
void deferredInit();

View File

@@ -321,7 +321,7 @@ namespace BlackMisc
const CAircraftSituationChange simpleChange(updatedSituations, situationCorrected.getCG(), aircraftModel.isVtol(), true, false);
// guess GND
newSituationsList.front().guessOnGround(simpleChange, aircraftModel);
simpleChange.guessOnGround(newSituationsList.front(), aircraftModel);
}
updatedSituations = m_situationsByCallsign[cs];
@@ -504,7 +504,7 @@ namespace BlackMisc
{
if (aircraftModel.hasCG() && !situation.hasCG()) { situation.setCG(aircraftModel.getCG()); }
if (!situation.shouldGuessOnGround()) { return false; }
return situation.guessOnGround(change, aircraftModel);
return change.guessOnGround(situation, aircraftModel);
}
bool CRemoteAircraftProvider::updateAircraftEnabled(const CCallsign &callsign, bool enabledForRendering)
@@ -590,7 +590,7 @@ namespace BlackMisc
QWriteLocker l(&m_lockSituations);
CAircraftSituationList &situations = m_situationsByCallsign[callsign];
if (situations.isEmpty()) { return 0; }
updated = situations.setGroundElevationCheckedAndGuessGround(elevation, info, model, &change, &setForOnGndPosition);
updated = setGroundElevationCheckedAndGuessGround(situations, elevation, info, model, &change, &setForOnGndPosition);
if (updated < 1) { return 0; }
m_situationsLastModified[callsign] = now;
const CAircraftSituation latestSituation = situations.front();
@@ -772,6 +772,52 @@ namespace BlackMisc
return m_enableReverseLookupMsgs;
}
int CRemoteAircraftProvider::setGroundElevationCheckedAndGuessGround(
CAircraftSituationList &situations, const CElevationPlane &elevationPlane, CAircraftSituation::GndElevationInfo info, const CAircraftModel &model,
CAircraftSituationChange *changeOut, bool *setForOnGroundPosition)
{
if (setForOnGroundPosition) { *setForOnGroundPosition = false; } // set a default
if (elevationPlane.isNull()) { return 0; }
if (situations.isEmpty()) { return 0; }
// the change has the timestamps of the latest situation
//Q_ASSERT_X(situations.m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst || situations.isSortedAdjustedLatestFirstWithoutNullPositions(), Q_FUNC_INFO, "Need sorted situations without NULL positions");
const CAircraftSituationChange simpleChange(situations, model.getCG(), model.isVtol(), true, false);
int c = 0; // changed elevations
bool latest = true;
bool setForOnGndPosition = false;
for (CAircraftSituation &s : situations)
{
const bool set = s.setGroundElevationChecked(elevationPlane, info);
if (set)
{
// simpleChange is only valid for the latest situation
// this will do nothing if not appropriate!
const bool guessed = (latest ? simpleChange : CAircraftSituationChange::null()).guessOnGround(s, model);
Q_UNUSED(guessed)
c++;
// if not guessed and "on ground" we mark the "elevation"
// as an elevation for a ground position
if (!setForOnGndPosition && s.hasInboundGroundDetails() && s.isOnGround())
{
setForOnGndPosition = true;
}
}
latest = false; // only first pos. is "the latest" one
}
if (setForOnGroundPosition) { *setForOnGroundPosition = setForOnGndPosition; }
if (changeOut)
{
const CAircraftSituationChange change(situations, model.getCG(), model.isVtol(), true, true);
*changeOut = change;
}
return c;
}
CStatusMessageList CRemoteAircraftProvider::getAircraftPartsHistory(const CCallsign &callsign) const
{
QReadLocker l(&m_lockPartsHistory);

View File

@@ -473,6 +473,10 @@ namespace BlackMisc
//! \threadsafe
ReverseLookupLogging whatToReverseLog() const;
//! Set ground elevation from elevation plane and guess ground
//! \note requires a sorted list latest first
static int setGroundElevationCheckedAndGuessGround(Aviation::CAircraftSituationList &situations, const Geo::CElevationPlane &elevationPlane, Aviation::CAircraftSituation::GndElevationInfo info, const Simulation::CAircraftModel &model, Aviation::CAircraftSituationChange *changeOut, bool *setForOnGroundPosition);
private:
//! Store the latest changes
//! \remark latest first

View File

@@ -287,7 +287,7 @@ namespace BlackMisc
CElevationPlane ISimulationEnvironmentProvider::averageElevationOfOnGroundAircraft(const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const
{
const CCoordinateGeodeticList coordinates = this->getElevationCoordinatesOnGround();
return coordinates.averageGeodeticHeight(reference, range, CAircraftSituationChange::allowedAltitudeDeviation(), minValues, sufficientValues);
return coordinates.averageGeodeticHeight(reference, range, CAircraftSituation::allowedAltitudeDeviation(), minValues, sufficientValues);
}
CAltitude ISimulationEnvironmentProvider::highestElevation() const