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

@@ -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