mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-05 17:35:34 +08:00
Issue #77 Break cyclic dependencies between CAircraftParts, CAircraftLights, CAircraftSituation classes
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 ¢erOfGravity) 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);
|
||||
|
||||
@@ -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 ¢erOfGravity) 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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user