diff --git a/src/blackmisc/simulation/interpolationlogger.cpp b/src/blackmisc/simulation/interpolationlogger.cpp
index 69b1589b1..2391780da 100644
--- a/src/blackmisc/simulation/interpolationlogger.cpp
+++ b/src/blackmisc/simulation/interpolationlogger.cpp
@@ -310,7 +310,7 @@ namespace BlackMisc
QStringLiteral("
") % msSinceEpochToTime(log.tsInterpolated) % QStringLiteral(" | ") %
QStringLiteral("") % QString::number(log.deltaSampleTimesMs) % QStringLiteral("ms | ") %
- QStringLiteral("") % QString::number(log.simulationTimeFraction) % QStringLiteral(" | ");
+ QStringLiteral("") % QString::number(log.simTimeFraction) % QStringLiteral(" | ");
tableRows +=
QStringLiteral("") % situationOld.latitudeAsString() % QStringLiteral(" | ") %
@@ -376,14 +376,8 @@ namespace BlackMisc
void CInterpolationLogger::clearLog()
{
- {
- QWriteLocker l(&m_lockSituations);
- m_situationLogs.clear();
- }
- {
- QWriteLocker l(&m_lockParts);
- m_partsLogs.clear();
- }
+ { QWriteLocker l(&m_lockSituations); m_situationLogs.clear(); }
+ { QWriteLocker l(&m_lockParts); m_partsLogs.clear(); }
}
QString CInterpolationLogger::msSinceEpochToTime(qint64 ms)
@@ -418,6 +412,8 @@ namespace BlackMisc
QStringLiteral("ts: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) %
QStringLiteral(" | type: ") % this->interpolationType() %
QStringLiteral(" | gnd.fa.: ") % QString::number(groundFactor) %
+ QStringLiteral(" | CG: ") % cgAboveGround.valueRoundedWithUnit(CLengthUnit::m(), 1) %
+ QStringLiteral(" | alt.cor.: ") % altCorrection %
QStringLiteral(" | #nw.sit.: ") % QString::number(noNetworkSituations) %
(
withSetup ?
@@ -427,9 +423,7 @@ namespace BlackMisc
(
withElevation ?
separator %
- QStringLiteral("Elev.: ") %
- QStringLiteral("transf.elv.: ") % QString::number(noTransferredElevations) %
- QStringLiteral(" | elv.info: ") % elevationInfo :
+ QStringLiteral("Elev info.: ") % elevationInfo :
QStringLiteral("")
) %
(
@@ -439,7 +433,7 @@ namespace BlackMisc
QStringLiteral(" | int.time: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsInterpolated) %
QStringLiteral(" | dt.cur.int.: ") % QString::number(deltaCurrentToInterpolatedTime()) % QStringLiteral("ms") %
QStringLiteral(" | sample dt: ") % QString::number(deltaSampleTimesMs) % QStringLiteral("ms") %
- QStringLiteral(" | fr.[0-1]: ") % QString::number(simulationTimeFraction) %
+ QStringLiteral(" | fr.[0-1]: ") % QString::number(simTimeFraction) %
QStringLiteral(" | old int.pos.: ") % situationOldInterpolation.getTimestampAndOffset(true) %
QStringLiteral(" | new int.pos.: ") % situationNewInterpolation.getTimestampAndOffset(true) %
QStringLiteral(" | #int.pos.: ") % QString::number(interpolationSituations.size()) :
diff --git a/src/blackmisc/simulation/interpolationlogger.h b/src/blackmisc/simulation/interpolationlogger.h
index 1f00da713..96e75c73e 100644
--- a/src/blackmisc/simulation/interpolationlogger.h
+++ b/src/blackmisc/simulation/interpolationlogger.h
@@ -35,17 +35,17 @@ namespace BlackMisc
qint64 tsInterpolated = -1; //!< timestamp interpolated
double groundFactor = -1; //!< current ground factor
double vtolAircraft = false; //!< VTOL aircraft
- double simulationTimeFraction = -1; //!< time fraction, expected 0..1
- double deltaSampleTimesMs = -1; //!< delta time between samples (i.e. 2 situations)
- bool useParts = false; //!< supporting aircraft parts
- int noNetworkSituations = 0; //!< available network situations
- int noTransferredElevations = 0; //!< transferred elevation to n situations
- QString elevationInfo; //!< info about elevation retrieval
- Aviation::CCallsign callsign; //!< current callsign
- Aviation::CAircraftParts parts; //!< corresponding parts used in interpolator
+ double simTimeFraction = -1; //!< time fraction, expected 0..1
+ double deltaSampleTimesMs = -1; //!< delta time between samples (i.e. 2 situations)
+ bool useParts = false; //!< supporting aircraft parts
+ int noNetworkSituations = 0; //!< available network situations
+ QString elevationInfo; //!< info about elevation retrieval
+ QString altCorrection; //!< info about altitude correction as CAircraftSituation::AltitudeCorrection
+ Aviation::CCallsign callsign; //!< current callsign
+ Aviation::CAircraftParts parts; //!< corresponding parts used in interpolator
Aviation::CAircraftSituationList interpolationSituations; //!< the interpolator uses 2, 3 situations (oldest at end)
Aviation::CAircraftSituation situationCurrent; //!< interpolated situation
- PhysicalQuantities::CLength cgAboveGround; //!< center of gravity
+ PhysicalQuantities::CLength cgAboveGround; //!< center of gravity
CInterpolationAndRenderingSetupPerCallsign usedSetup; //!< used setup
//! Delta time between interpolation and current time
diff --git a/src/blackmisc/simulation/interpolator.cpp b/src/blackmisc/simulation/interpolator.cpp
index fa2663d2d..dbff95f8c 100644
--- a/src/blackmisc/simulation/interpolator.cpp
+++ b/src/blackmisc/simulation/interpolator.cpp
@@ -20,27 +20,71 @@
#include "blackmisc/pq/length.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/verify.h"
+#include
#include
#include
using namespace BlackConfig;
-using namespace BlackMisc;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Geo;
using namespace BlackMisc::Math;
using namespace BlackMisc::PhysicalQuantities;
-using namespace BlackMisc::Simulation;
namespace BlackMisc
{
namespace Simulation
{
template
- CInterpolator::CInterpolator(const QString &objectName, const CCallsign &callsign, QObject *parent) :
- QObject(parent),
- m_callsign(callsign)
+ CInterpolator::CInterpolator(const CCallsign &callsign,
+ ISimulationEnvironmentProvider *simEnvProvider, IInterpolationSetupProvider *setupProvider,
+ IRemoteAircraftProvider *remoteAircraftProvider,
+ CInterpolationLogger *logger) : m_callsign(callsign)
{
- this->setObjectName(objectName + " for " + callsign.asString());
+ // normally when created m_cg is still null since there is no CG in the provider yet
+
+ if (simEnvProvider) { this->setSimulationEnvironmentProvider(simEnvProvider); }
+ if (setupProvider) { this->setInterpolationSetupProvider(setupProvider); }
+ if (remoteAircraftProvider)
+ {
+ this->setRemoteAircraftProvider(remoteAircraftProvider);
+ QTimer::singleShot(2500, [ = ]
+ {
+ if (m_model.hasModelString()) { return; } // set in-between
+ this->initCorrespondingModel();
+ });
+ }
+ this->attachLogger(logger);
+ }
+
+ template
+ bool CInterpolator::verifyInterpolationSituations(const CAircraftSituation &oldest, const CAircraftSituation &newer, const CAircraftSituation &latest, const CInterpolationAndRenderingSetupPerCallsign &setup)
+ {
+ if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return true; }
+ CAircraftSituationList situations;
+
+ // oldest last, null ignored
+ if (!latest.isNull()) { situations.push_back(latest); }
+ if (!newer.isNull()) { situations.push_back(newer); }
+ if (!oldest.isNull()) { situations.push_back(oldest); }
+
+ const bool sort1 = situations.isSortedLatestFirst();
+ BLACK_VERIFY_X(sort1, Q_FUNC_INFO, "Wrong timestamp order");
+ const bool sort2 = situations.isSortedAdjustedLatestFirst();
+ BLACK_VERIFY_X(sort2, Q_FUNC_INFO, "Wrong adjusted timestamp order");
+
+ bool details = true;
+ if (setup.isAircraftPartsEnabled())
+ {
+ if (situations.containsOnGroundDetails(CAircraftSituation::InFromParts))
+ {
+ // if a client supports parts, all situations are supposed to be parts based
+ details = situations.areAllOnGroundDetailsSame(CAircraftSituation::InFromParts);
+ BLACK_VERIFY_X(details, Q_FUNC_INFO, "Once gnd.from parts -> always gnd. from parts");
+ }
+ }
+
+ // result
+ return sort1 && sort2 && details;
}
template
@@ -60,28 +104,28 @@ namespace BlackMisc
// this code is used by linear and spline interpolator
status.reset();
- const bool doLogging = this->hasAttachedLogger() && setup.logInterpolation();
SituationLog log;
- SituationLog *logP = doLogging ? &log : nullptr;
+ const bool doLogging = this->hasAttachedLogger() && setup.logInterpolation();
// any data at all?
- if (m_aircraftSituations.isEmpty()) { return CAircraftSituation(m_callsign); }
- CAircraftSituation currentSituation = m_lastInterpolation.isNull() ? m_aircraftSituations.front() : m_lastInterpolation;
+ const CAircraftSituationList situations = this->remoteAircraftSituations(m_callsign);
+ if (situations.isEmpty()) { return CAircraftSituation(m_callsign); }
+ CAircraftSituation currentSituation = m_lastInterpolation.isNull() ? situations.front() : m_lastInterpolation;
if (currentSituation.getCallsign() != m_callsign)
{
BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign");
currentSituation.setCallsign(m_callsign);
}
- //! \todo KB 2018-03 ground flag refactoring
- // Update current position by hints' elevation
- // * for XP provided by hints.getElevationProvider at current position
- // * for FSX/P3D provided as hints.getElevation which is set to current position of remote aircraft in simulator
- // * As XP uses lazy init we will call getGroundElevation only when needed
- // * default here via getElevationPlane
- // CAltitude currentGroundElevation(hints.getGroundElevation(currentSituation, currentSituation.getDistancePerTime(1000), true, false, logP));
- const CElevationPlane currentGroundElevation = this->findClosestElevationWithinRange(currentSituation, currentSituation.getDistancePerTime(1000));
- currentSituation.setGroundElevationChecked(currentGroundElevation); // set as default
+ // set elevation if available
+ if (!currentSituation.hasGroundElevation())
+ {
+ const CElevationPlane currentGroundElevation = this->findClosestElevationWithinRange(currentSituation, currentSituation.getDistancePerTime(1000));
+ currentSituation.setGroundElevationChecked(currentGroundElevation); // set as default
+ }
+
+ // fetch CG once
+ if (m_cg.isNull()) { m_cg = this->getCG(m_callsign); }
// data, split situations by time
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
@@ -97,75 +141,34 @@ namespace BlackMisc
return currentSituation;
}
+ // Pitch bank heading
+ // first, so follow up steps could use those values
+ const auto pbh = interpolant.pbh();
+ currentSituation.setHeading(pbh.getHeading());
+ currentSituation.setPitch(pbh.getPitch());
+ currentSituation.setBank(pbh.getBank());
+ currentSituation.setGroundSpeed(pbh.getGroundSpeed());
+
// use derived interpolant function
- currentSituation.setPosition(interpolant.interpolatePosition(setup));
- currentSituation.setAltitude(interpolant.interpolateAltitude(setup));
- currentSituation.setMSecsSinceEpoch(interpolant.getInterpolatedTime());
+ currentSituation = interpolant.interpolatePositionAndAltitude(currentSituation);
- // PBH before ground so we can use PBH in guessing ground
- if (setup.isForcingFullInterpolation() || status.isInterpolated())
- {
- const auto pbh = interpolant.pbh();
- currentSituation.setHeading(pbh.getHeading());
- currentSituation.setPitch(pbh.getPitch());
- currentSituation.setBank(pbh.getBank());
- currentSituation.setGroundSpeed(pbh.getGroundSpeed());
- status.setInterpolatedAndCheckSituation(true, currentSituation);
- }
-
- // Interpolate between altitude and ground elevation, with proportions weighted according to interpolated onGround flag
- constexpr double NoGroundFactor = -1;
- double groundFactor = NoGroundFactor;
-
- if (setup.isAircraftPartsEnabled())
- {
- // groundFactor = hints.getAircraftParts().isOnGroundInterpolated();
- if (groundFactor > 0.0)
- {
- // if not having an ground elevation yet, we fetch from provider (if there is a provider)
- if (!currentGroundElevation.isNull())
- {
- currentGroundElevation = hints.getGroundElevation(currentSituation, true, false, logP); // "expensive on XPlane" if provider is called
- }
-
- if (!currentGroundElevation.isNull())
- {
- Q_ASSERT_X(currentGroundElevation.getAltitude().getReferenceDatum() == CAltitude::MeanSeaLevel, Q_FUNC_INFO, "Need MSL value");
- const CLength cg = this->getCG(m_callsign);
- const CAltitude groundElevationCG = currentGroundElevation.getAltitude().withOffset(cg);
- currentSituation.setGroundElevationChecked(currentGroundElevation);
- // alt = ground + aboveGround * groundFactor
- // = ground + (altitude - ground) * groundFactor
- // = ground (1 - groundFactor) + altitude * groundFactor
- currentSituation.setAltitude(CAltitude(currentSituation.getAltitude() * (1.0 - groundFactor) +
- groundElevationCG * groundFactor,
- CAltitude::MeanSeaLevel));
- }
- }
- }
-
- // depending on ground factor set ground flag and reliability
- // it will use the hints ground flag or elevation/CG or guessing
- // CInterpolator::setGroundFlagFromInterpolator(hints, groundFactor, currentSituation);
-
- // we transfer ground elevation for future usage
- if (currentSituation.hasGroundElevation())
- {
- CElevationPlane ep(currentSituation);
- ep.setSinglePointRadius();
-
- // transfer to newer situations
- log.noTransferredElevations = m_aircraftSituations.setGroundElevationChecked(ep, currentTimeMsSinceEpoc);
- }
+ // correct itself
+ const CAircraftSituation::AltitudeCorrection altCorrection = currentSituation.correctAltitude(m_cg, true);
+ status.setInterpolatedAndCheckSituation(true, currentSituation);
// logging
if (doLogging)
{
+ static const QString elv("found %1 missed %2");
+ const QPair elvStats = this->getElevationsFoundMissed();
log.tsCurrent = currentTimeMsSinceEpoc;
log.callsign = m_callsign;
- log.groundFactor = groundFactor;
+ log.groundFactor = currentSituation.getOnGroundFactor();
+ log.altCorrection = CAircraftSituation::altitudeCorrectionToString(altCorrection);
log.situationCurrent = currentSituation;
log.usedSetup = setup;
+ log.elevationInfo = elv.arg(elvStats.first).arg(elvStats.second);
+ log.cgAboveGround = m_cg;
m_logger->logInterpolation(log);
}
@@ -223,163 +226,94 @@ namespace BlackMisc
}
template
- CAircraftParts CInterpolator::getInterpolatedParts(qint64 currentTimeMsSinceEpoch,
- const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
+ CAircraftParts CInterpolator::getInterpolatedParts(
+ qint64 currentTimeMsSinceEpoch,
+ const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
{
// (!) this code is used by linear and spline interpolator
Q_UNUSED(setup);
partsStatus.reset();
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
+ // Parts are supposed to be in correct order, latest first
+ const CAircraftPartsList validParts = this->remoteAircraftParts(m_callsign);
+
// log for empty parts aircraft parts
- if (m_aircraftParts.isEmpty())
+ if (validParts.isEmpty())
{
static const CAircraftParts emptyParts;
- this->logParts(currentTimeMsSinceEpoch, emptyParts, true, log);
+ this->logParts(currentTimeMsSinceEpoch, emptyParts, validParts.size(), true, log);
return emptyParts;
}
- // Parts are supposed to be in correct order, latest first
- const CAircraftPartsList &validParts = m_aircraftParts;
-
- // stop if we don't have any parts
- if (validParts.isEmpty()) { return {}; }
-
partsStatus.setSupportsParts(true);
CAircraftParts currentParts;
do
{
// find the first parts earlier than the current time
- const auto pivot = std::partition_point(validParts.begin(), validParts.end(), [ = ](auto && p) { return p.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoch; });
+ const auto pivot = std::partition_point(validParts.begin(), validParts.end(), [ = ](auto &&p) { return p.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoch; });
const auto partsNewer = makeRange(validParts.begin(), pivot).reverse();
const auto partsOlder = makeRange(pivot, validParts.end());
// if (partsOlder.isEmpty()) { currentParts = *(partsNewer.end() - 1); break; }
if (partsOlder.isEmpty()) { currentParts = *(partsNewer.begin()); break; }
currentParts = partsOlder.front(); // latest older parts
- if (currentParts.isOnGround()) { break; }
-
- // here we know aircraft is not on ground, and we check if it was recently on ground or if it will be on ground soon
- const auto latestTakeoff = std::adjacent_find(partsOlder.begin(), partsOlder.end(), [](auto &&, auto && p) { return p.isOnGround(); });
- const auto soonestLanding = std::find_if(partsNewer.begin(), partsNewer.end(), [](auto && p) { return p.isOnGround(); });
-
- // maxSecs is the maximum effective value of `secondsSinceTakeoff` and `secondsUntilLanding`. If `secondsSinceTakeoff > significantPast` then `takeoffFactor > 1`
- // and if `secondsUntilLanding > predictableFuture` then `landingFactor > 1`, and `std::min(std::min(takeoffFactor, landingFactor), 1.0)` ensures `>1` is ignored.
- // but if the offset < 5s then we must use a smaller value for the landing, hence `std::min(max, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0)`.
- const double maxSecs = 5.0; // preferred length of time over which to blend the onground flag, when possible
-
- // our clairvoyance is limited by the time offset (all times here in seconds)
- const double significantPastSecs = maxSecs;
- const double predictableFutureSecs = soonestLanding == partsNewer.end() ? maxSecs : std::min(maxSecs, static_cast(soonestLanding->getTimeOffsetMs()) / 1000.0);
- const double secondsSinceTakeoff = latestTakeoff == partsOlder.end() ? maxSecs : (currentTimeMsSinceEpoch - latestTakeoff->getAdjustedMSecsSinceEpoch()) / 1000.0;
- const double secondsUntilLanding = soonestLanding == partsNewer.end() ? maxSecs : (soonestLanding->getAdjustedMSecsSinceEpoch() - currentTimeMsSinceEpoch) / 1000.0;
- Q_ASSERT(secondsSinceTakeoff >= 0.0);
- Q_ASSERT(secondsUntilLanding >= 0.0);
-
- //! \fixme In future, will we need to be able to support time offsets of zero?
- BLACK_VERIFY(predictableFutureSecs != 0);
- if (predictableFutureSecs == 0) { break; } // avoid divide by zero
-
- const double takeoffFactor = secondsSinceTakeoff / significantPastSecs;
- const double landingFactor = secondsUntilLanding / predictableFutureSecs;
- const double airborneFactor = std::min(std::min(takeoffFactor, landingFactor), 1.0);
- currentParts.setOnGroundInterpolated(1.0 - smootherStep(airborneFactor));
}
while (false);
- this->logParts(currentTimeMsSinceEpoch, currentParts, false, log);
+ this->logParts(currentTimeMsSinceEpoch, currentParts, validParts.size(), false, log);
return currentParts;
}
template
- void CInterpolator::logParts(qint64 timestamp, const CAircraftParts &parts, bool empty, bool log) const
+ CAircraftParts CInterpolator::getInterpolatedOrGuessedParts(qint64 currentTimeMsSinceEpoch, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
+ {
+ CAircraftParts parts = this->getInterpolatedParts(currentTimeMsSinceEpoch, setup, partsStatus, log);
+ if (!partsStatus.isSupportingParts())
+ {
+ if (!m_model.hasModelString())
+ {
+ const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(m_callsign);
+ // m_model = aircraft.getModel();
+ }
+
+ // check if model has been thru model matching
+ if (m_model.hasModelString())
+ {
+ parts.guessParts(this->getLastInterpolatedSituation(), m_model.isVtol(), m_model.getEngineCount());
+ }
+ else
+ {
+ // default guess
+ parts.guessParts(this->getLastInterpolatedSituation());
+ }
+ }
+ this->logParts(currentTimeMsSinceEpoch, parts, 0, false, log);
+ return parts;
+ }
+
+ template
+ void CInterpolator::logParts(qint64 timestamp, const CAircraftParts &parts, int partsNo, bool empty, bool log) const
{
if (!log || !m_logger) { return; }
PartsLog logInfo;
logInfo.callsign = m_callsign;
- logInfo.noNetworkParts = m_aircraftParts.size();
+ logInfo.noNetworkParts = partsNo;
logInfo.tsCurrent = timestamp;
logInfo.parts = parts;
logInfo.empty = empty;
m_logger->logParts(logInfo);
}
- template
- void CInterpolator::addAircraftSituation(const CAircraftSituation &situation)
- {
- Q_ASSERT_X(!m_callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
- Q_ASSERT_X(situation.getCallsign() == m_callsign, Q_FUNC_INFO, "Wrong callsign");
- if (m_aircraftSituations.isEmpty())
- {
- this->resetLastInterpolation(); // delete any leftover
-
- // make sure we have enough situations to do start interpolating immediately without waiting for more updates
- // the offsets here (addMSecs) do not really matter
- CAircraftSituation copy(situation);
- copy.addMsecs(-2 * IRemoteAircraftProvider::DefaultOffsetTimeMs);
- m_aircraftSituations.push_frontKeepLatestFirst(copy);
- copy.addMsecs(IRemoteAircraftProvider::DefaultOffsetTimeMs);
- m_aircraftSituations.push_frontKeepLatestFirst(copy);
- }
-
- // we add new situations at front and keep the latest values (real time) first
- m_aircraftSituations.push_frontKeepLatestFirstAdjustOffset(situation, IRemoteAircraftProvider::MaxSituationsPerCallsign);
-
-
- // with the latest updates of T243 the order and the offsets are supposed to be correct
- // so even mixing fast/slow updates shall work
- Q_ASSERT_X(!m_aircraftSituations.containsZeroOrNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
- Q_ASSERT_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
- }
-
- template
- void CInterpolator::addAircraftParts(const CAircraftParts &parts, bool adjustZeroOffset)
- {
- const bool adjustOffset = adjustZeroOffset && !parts.hasNonZeroOffsetTime();
- if (adjustOffset)
- {
- const qint64 offset = m_aircraftSituations.isEmpty() ? IRemoteAircraftProvider::DefaultOffsetTimeMs : m_aircraftSituations.front().getTimeOffsetMs();
- CAircraftParts partsCopy(parts);
- partsCopy.setTimeOffsetMs(offset); // we set the offset of the situation
- CInterpolator::addAircraftParts(partsCopy);
- return;
- }
-
- // here we have an offset
- // unlike situations we do not add parts for spline interpolation
- // this is not needed, as parts do not need 3 values
- Q_ASSERT_X(!adjustZeroOffset || parts.hasNonZeroOffsetTime(), Q_FUNC_INFO, "Missing parts offset");
-
- // we add new situations at front and keep the latest values (real time) first
- m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(parts, IRemoteAircraftProvider::MaxSituationsPerCallsign);
-
- // force remote provider to cleanup
- IRemoteAircraftProvider::removeOutdatedParts(m_aircraftParts);
-
- // with the latest updates of T243 the order and the offsets are supposed to be correct
- // so even mixing fast/slow updates shall work
- Q_ASSERT_X(adjustZeroOffset ? !m_aircraftParts.containsZeroOrNegativeOffsetTime() : !m_aircraftParts.containsNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
- Q_ASSERT_X(m_aircraftParts.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
- }
-
- template
- void CInterpolator::addAircraftParts(const CAircraftPartsList &parts, bool adjustZeroOffset)
- {
- for (const CAircraftParts &p : parts)
- {
- this->addAircraftParts(p, adjustZeroOffset);
- }
- }
-
template
QString CInterpolator::getInterpolatorInfo() const
{
return QStringLiteral("Callsign: ") %
m_callsign.asString() %
QStringLiteral(" situations: ") %
- QString::number(m_aircraftSituations.size()) %
+ QString::number(this->remoteAircraftSituationsCount(m_callsign)) %
QStringLiteral(" parts: ") %
- QString::number(m_aircraftParts.size()) %
+ QString::number(this->remoteAircraftPartsCount(m_callsign)) %
QStringLiteral(" 1st interpolation: ") %
boolToYesNo(m_lastInterpolation.isNull());
}
@@ -394,92 +328,24 @@ namespace BlackMisc
void CInterpolator::clear()
{
this->resetLastInterpolation();
- m_aircraftParts.clear();
- m_aircraftSituations.clear();
+ m_model = CAircraftModel();
}
template
- int CInterpolator::maxSituations() const
+ void CInterpolator::initCorrespondingModel(const CAircraftModel &model)
{
- return IRemoteAircraftProvider::MaxSituationsPerCallsign;
- }
-
- template
- int CInterpolator::maxParts() const
- {
- return IRemoteAircraftProvider::MaxSituationsPerCallsign;
- }
-
- template
- void CInterpolator::setGroundFlagFromInterpolator(double groundFactor, CAircraftSituation &situation) const
- {
- // by interpolation
- if (groundFactor >= 1.0)
+ if (model.hasModelString())
{
- situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByInterpolation);
- return;
+ m_model = model;
}
- if (groundFactor < 1.0 && groundFactor >= 0.0)
+ else
{
- situation.setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByInterpolation);
- return;
+ CAircraftModel model = this->getAircraftInRangeForCallsign(m_callsign).getModel();
+ if (model.hasModelString())
+ {
+ m_model = model;
+ }
}
-
- // on elevation and CG
- // remark: to some extend redundant as situation.getCorrectedAltitude() already corrects altitude
- Q_ASSERT_X(!m_callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
- if (situation.hasGroundElevation())
- {
- static const CLength onGroundThresholdLimit(1.0, CLengthUnit::m());
- static const CLength notOnGroundThresholdLimit(10.0, CLengthUnit::m()); // upper boundary
- CLength offset = onGroundThresholdLimit; // very small offset from allowed
- CAircraftSituation::OnGroundDetails reliability = CAircraftSituation::OnGroundByElevation;
- CLength cg = this->getCG(m_callsign);
- if (!cg.isNull())
- {
- offset += cg;
- reliability = CAircraftSituation::OnGroundByElevationAndCG;
- }
- else
- {
- // increase offset a bit
- offset += CLength(1.0, CLengthUnit::m());
- }
-
- Q_ASSERT_X(situation.getGroundElevation().getReferenceDatum() == CAltitude::MeanSeaLevel, Q_FUNC_INFO, "Need MSL elevation");
- if (situation.getHeightAboveGround() <= offset)
- {
- // lower boundary underflow, we can tell we are on ground
- const CAircraftSituation::IsOnGround og = CAircraftSituation::OnGround;
- situation.setOnGround(og, reliability);
- return; // for underflow we can stop here
- }
- else if (situation.getHeightAboveGround() >= notOnGroundThresholdLimit)
- {
- // upper boundary
- const CAircraftSituation::IsOnGround og = CAircraftSituation::NotOnGround;
- situation.setOnGround(og, reliability);
- return;
- }
-
- // within an interval were we cannot really tell and continue
- }
-
- // for VTOL aircraft we give up
- if (this->isVtolAircraft(m_callsign))
- {
- situation.setOnGround(CAircraftSituation::OnGroundSituationUnknown, CAircraftSituation::OnGroundReliabilityNoSet);
- return;
- }
-
- // we guess on speed, pitch and bank by excluding situations
- situation.setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByGuessing);
- if (qAbs(situation.getPitch().value(CAngleUnit::deg())) > 10) { return; }
- if (qAbs(situation.getBank().value(CAngleUnit::deg())) > 10) { return; }
- if (situation.getGroundSpeed().value(CSpeedUnit::km_h()) > 50) { return; }
-
- // not sure, but this is a guess
- situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
}
void CInterpolationStatus::setInterpolatedAndCheckSituation(bool succeeded, const CAircraftSituation &situation)
@@ -528,45 +394,3 @@ namespace BlackMisc
//! \endcond
} // namespace
} // namespace
-
-
-/**
- template
- void CInterpolator::addAircraftParts(const CAircraftParts &parts, bool adjustZeroOffset)
- {
- const bool adjustOffset = adjustZeroOffset && !parts.hasNonZeroOffsetTime();
- if (adjustOffset)
- {
- const qint64 offset = m_aircraftSituations.isEmpty() ? IRemoteAircraftProvider::DefaultOffsetTimeMs : m_aircraftSituations.front().getTimeOffsetMs();
- CAircraftParts partsCopy(parts);
- partsCopy.setTimeOffsetMs(offset); // we set the offset of the situation
- CInterpolator::addAircraftParts(partsCopy);
- return;
- }
-
- // here we have an offset
- Q_ASSERT_X(!adjustZeroOffset || parts.hasNonZeroOffsetTime(), Q_FUNC_INFO, "Missing parts offset");
- if (m_aircraftParts.isEmpty())
- {
- // make sure we have enough parts to do start interpolating immediately without waiting for more updates
- // the offsets here (addMSecs) do not really matter
- const qint64 minOffset = 100;
- CAircraftParts copy(parts);
- copy.addMsecs(-2 * std::max(parts.getTimeOffsetMs(), minOffset));
- m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(copy);
- copy.addMsecs(parts.getTimeOffsetMs());
- m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(copy);
- }
-
- // we add new situations at front and keep the latest values (real time) first
- m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(parts, IRemoteAircraftProvider::MaxSituationsPerCallsign);
-
- // force remote provider to cleanup
- IRemoteAircraftProvider::removeOutdatedParts(m_aircraftParts);
-
- // with the latest updates of T243 the order and the offsets are supposed to be correct
- // so even mixing fast/slow updates shall work
- Q_ASSERT_X(adjustZeroOffset ? !m_aircraftParts.containsZeroOrNegativeOffsetTime() : !m_aircraftParts.containsNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
- Q_ASSERT_X(m_aircraftParts.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
- }
-**/
diff --git a/src/blackmisc/simulation/interpolator.h b/src/blackmisc/simulation/interpolator.h
index 55a81111f..cf819f2c9 100644
--- a/src/blackmisc/simulation/interpolator.h
+++ b/src/blackmisc/simulation/interpolator.h
@@ -16,9 +16,11 @@
#include "blackmisc/simulation/remoteaircraftprovider.h"
#include "blackmisc/simulation/interpolationsetupprovider.h"
#include "blackmisc/simulation/simulationenvironmentprovider.h"
+#include "blackmisc/simulation/aircraftmodel.h"
#include "blackmisc/aviation/aircraftpartslist.h"
#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/aviation/aircraftpartslist.h"
+#include "blackmisc/aviation/callsign.h"
#include "blackmisc/logcategorylist.h"
#include
@@ -27,8 +29,6 @@
namespace BlackMisc
{
- class CWorker;
- namespace Aviation { class CCallsign; }
namespace Simulation
{
class CInterpolationLogger;
@@ -42,8 +42,7 @@ namespace BlackMisc
class CInterpolator :
public CSimulationEnvironmentAware,
public CInterpolationSetupAware,
- public CRemoteAircraftAware,
- public QObject
+ public CRemoteAircraftAware
{
public:
//! Log categories
@@ -52,25 +51,14 @@ namespace BlackMisc
//! Current interpolated situation
Aviation::CAircraftSituation getInterpolatedSituation(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status);
- //! Parts before given offset time (aka pending parts)
- Aviation::CAircraftParts getInterpolatedParts(
- qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
+ //! Parts before given offset time
+ Aviation::CAircraftParts getInterpolatedParts(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
- //! Add a new aircraft situation
- void addAircraftSituation(const Aviation::CAircraftSituation &situation);
+ //! Interpolated parts, if not available guessed parts
+ Aviation::CAircraftParts getInterpolatedOrGuessedParts(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
- //! Any aircraft situations?
- bool hasAircraftSituations() const { return !m_aircraftSituations.isEmpty(); }
-
- //! Add a new aircraft parts
- void addAircraftParts(const Aviation::CAircraftParts &parts, bool adjustZeroOffset = true);
-
- //! Add a new aircraft parts
- //! \remark mainly needed in unit tests
- void addAircraftParts(const Aviation::CAircraftPartsList &parts, bool adjustZeroOffset = true);
-
- //! Any aircraft parts?
- bool hasAircraftParts() const { return !m_aircraftParts.isEmpty(); }
+ //! Latest interpolation result
+ const Aviation::CAircraftSituation &getLastInterpolatedSituation() const { return m_lastInterpolation; }
//! Takes input between 0 and 1 and returns output between 0 and 1 smoothed with an S-shaped curve.
//!
@@ -97,32 +85,62 @@ namespace BlackMisc
void resetLastInterpolation();
//! Clear all data
- //! \remark mainly needed in interpolation
+ //! \remark mainly needed in UNIT tests
void clear();
- //! Max situations kept
- int maxSituations() const;
-
- //! Max parts kept
- int maxParts() const;
+ //! Init, or re-init the corressponding model
+ //! \remark either by passing a model or using the provider
+ void initCorrespondingModel(const CAircraftModel &model = {});
protected:
- Aviation::CAircraftSituationList m_aircraftSituations; //!< recent situations for one aircraft
- Aviation::CAircraftPartsList m_aircraftParts; //!< recent parts for one aircraft
- Aviation::CCallsign m_callsign; //!< callsign
- Aviation::CAircraftSituation m_lastInterpolation; //!< last interpolation
-
//! Constructor
- CInterpolator(const QString &objectName, const Aviation::CCallsign &callsign, QObject *parent);
+ CInterpolator(const Aviation::CCallsign &callsign,
+ ISimulationEnvironmentProvider *simEnvProvider, IInterpolationSetupProvider *setupProvider, IRemoteAircraftProvider *p3,
+ CInterpolationLogger *logger);
- //! Set on ground flag
- void setGroundFlagFromInterpolator(double groundFactor, Aviation::CAircraftSituation &situation) const;
+ const Aviation::CCallsign m_callsign; //!< corresponding callsign
+ PhysicalQuantities::CLength m_cg { 0, nullptr } ; //!< fetched once, stays constant
+ Aviation::CAircraftSituation m_lastInterpolation { Aviation::CAircraftSituation::null() }; //!< latest interpolation
+ CAircraftModel m_model; //!< corresponding model
+
+ //! Equal double values?
+ static bool doubleEpsilonEqual(double d1, double d2)
+ {
+ return qAbs(d1 - d2) < std::numeric_limits::epsilon();
+ }
+
+ //! Both on ground
+ static bool gfEqualOnGround(double oldGroundFactor, double newGroundFactor)
+ {
+ return doubleEpsilonEqual(1.0, oldGroundFactor) && doubleEpsilonEqual(1.0, newGroundFactor);
+ }
+
+ //! Both not on ground
+ static bool gfEqualAirborne(double oldGroundFactor, double newGroundFactor)
+ {
+ return doubleEpsilonEqual(0.0, oldGroundFactor) && doubleEpsilonEqual(0.0, newGroundFactor);
+ }
+
+ //! Plane is starting
+ static bool gfStarting(double oldGroundFactor, double newGroundFactor)
+ {
+ return doubleEpsilonEqual(0.0, oldGroundFactor) && doubleEpsilonEqual(1.0, newGroundFactor);
+ }
+
+ //! Plane is landing
+ static bool gfLanding(double oldGroundFactor, double newGroundFactor)
+ {
+ return doubleEpsilonEqual(1.0, oldGroundFactor) && doubleEpsilonEqual(0.0, newGroundFactor);
+ }
+
+ //! Verify gnd flag, times, ... true means "OK"
+ bool verifyInterpolationSituations(const Aviation::CAircraftSituation &oldest, const Aviation::CAircraftSituation &newer, const Aviation::CAircraftSituation &latest, const CInterpolationAndRenderingSetupPerCallsign &setup);
private:
CInterpolationLogger *m_logger = nullptr;
//! Log parts
- void logParts(qint64 timestamp, const Aviation::CAircraftParts &parts, bool empty, bool log) const;
+ void logParts(qint64 timestamp, const Aviation::CAircraftParts &parts, int partsNo, bool empty, bool log) const;
Derived *derived() { return static_cast(this); }
const Derived *derived() const { return static_cast(this); }
@@ -134,14 +152,9 @@ namespace BlackMisc
public:
//! Constructor
//! @{
- CInterpolatorPbh()
- {}
- CInterpolatorPbh(const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) :
- m_oldSituation(older), m_newSituation(newer)
- {}
- CInterpolatorPbh(double time, const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) :
- m_simulationTimeFraction(time), m_oldSituation(older), m_newSituation(newer)
- {}
+ CInterpolatorPbh() {}
+ CInterpolatorPbh(const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) : m_oldSituation(older), m_newSituation(newer) {}
+ CInterpolatorPbh(double time, const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) : m_simulationTimeFraction(time), m_oldSituation(older), m_newSituation(newer) {}
//! @}
//! Getter
diff --git a/src/blackmisc/simulation/interpolatorlinear.cpp b/src/blackmisc/simulation/interpolatorlinear.cpp
index 43285613b..3cd959306 100644
--- a/src/blackmisc/simulation/interpolatorlinear.cpp
+++ b/src/blackmisc/simulation/interpolatorlinear.cpp
@@ -8,7 +8,6 @@
*/
#include "interpolatorlinear.h"
-#include "blackmisc/aviation/aircraftsituation.h"
#include "blackmisc/aviation/aircraftsituationlist.h"
#include "blackmisc/aviation/altitude.h"
#include "blackmisc/geo/coordinategeodetic.h"
@@ -48,6 +47,46 @@ namespace BlackMisc
m_pbh(m_simulationTimeFraction, situation1, situation2)
{}
+ CAircraftSituation CInterpolatorLinear::Interpolant::interpolatePositionAndAltitude(const CAircraftSituation &situation) const
+ {
+ const std::array oldVec(m_oldSituation.getPosition().normalVectorDouble());
+ const std::array newVec(m_newSituation.getPosition().normalVectorDouble());
+
+ // Interpolate position: pos = (posB - posA) * t + posA
+ CCoordinateGeodetic newPosition;
+ newPosition.setNormalVector((newVec[0] - oldVec[0]) * m_simulationTimeFraction + oldVec[0],
+ (newVec[1] - oldVec[1]) * m_simulationTimeFraction + oldVec[1],
+ (newVec[2] - oldVec[2]) * m_simulationTimeFraction + oldVec[2]);
+
+ // Interpolate altitude: Alt = (AltB - AltA) * t + AltA
+ // avoid underflow below ground elevation by using getCorrectedAltitude
+ const CAltitude oldAlt(m_oldSituation.getCorrectedAltitude());
+ const CAltitude newAlt(m_newSituation.getCorrectedAltitude());
+ Q_ASSERT_X(oldAlt.getReferenceDatum() == CAltitude::MeanSeaLevel && oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(), Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
+ const CAltitude altitude((newAlt - oldAlt)
+ * m_simulationTimeFraction
+ + oldAlt,
+ oldAlt.getReferenceDatum());
+
+ CAircraftSituation newSituation(situation);
+ newSituation.setPosition(newPosition);
+ newSituation.setAltitude(altitude);
+ newSituation.setMSecsSinceEpoch(this->getInterpolatedTime());
+
+ const double oldGroundFactor = m_oldSituation.getOnGroundFactor();
+ const double newGroundFactor = m_newSituation.getOnGroundFactor();
+ do
+ {
+ if (gfEqualAirborne(oldGroundFactor, newGroundFactor)) { newSituation.setOnGround(false); break; }
+ if (gfEqualOnGround(oldGroundFactor, newGroundFactor)) { newSituation.setOnGround(true); break; }
+ const double groundFactor = (newGroundFactor - oldGroundFactor) * m_simulationTimeFraction + oldGroundFactor;
+ newSituation.setOnGroundFactor(groundFactor);
+ newSituation.setOnGroundFromGroundFactorFromInterpolation();
+ }
+ while (false);
+ return newSituation;
+ }
+
CInterpolatorLinear::Interpolant CInterpolatorLinear::getInterpolant(
qint64 currentTimeMsSinceEpoc,
const CInterpolationAndRenderingSetupPerCallsign &setup,
@@ -58,16 +97,11 @@ namespace BlackMisc
// with the latest updates of T243 the order and the offsets are supposed to be correct
// so even mixing fast/slow updates shall work
- BLACK_VERIFY_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
- Q_ASSERT_X(m_aircraftSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
-
- // Ref T243, KB 2018-02, can be removed in future, we verify situations above
- // Situations are supposed to be in correct order
- // const auto end = std::is_sorted_until(m_aircraftSituations.begin(), m_aircraftSituations.end(), [](auto && a, auto && b) { return b.getAdjustedMSecsSinceEpoch() < a.getAdjustedMSecsSinceEpoch(); });
- // const auto validSituations = makeRange(m_aircraftSituations.begin(), end);
+ const CAircraftSituationList validSituations = this->remoteAircraftSituations(m_callsign); // if needed, we could also copy here
+ BLACK_VERIFY_X(validSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
+ Q_ASSERT_X(validSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
// find the first situation earlier than the current time
- const CAircraftSituationList &validSituations = m_aircraftSituations; // if needed, we could also copy here
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
const auto situationsOlder = makeRange(pivot, validSituations.end());
@@ -147,7 +181,7 @@ namespace BlackMisc
{
log.tsCurrent = currentTimeMsSinceEpoc;
log.deltaSampleTimesMs = sampleDeltaTimeMs;
- log.simulationTimeFraction = simulationTimeFraction;
+ log.simTimeFraction = simulationTimeFraction;
log.deltaSampleTimesMs = sampleDeltaTimeMs;
log.tsInterpolated = interpolatedTime;
log.interpolationSituations.clear();
@@ -157,35 +191,5 @@ namespace BlackMisc
return { oldSituation, newSituation, simulationTimeFraction, interpolatedTime };
}
-
- CCoordinateGeodetic CInterpolatorLinear::Interpolant::interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const
- {
- Q_UNUSED(setup);
-
- const std::array oldVec(m_oldSituation.getPosition().normalVectorDouble());
- const std::array newVec(m_newSituation.getPosition().normalVectorDouble());
-
- // Interpolate position: pos = (posB - posA) * t + posA
- CCoordinateGeodetic currentPosition;
- currentPosition.setNormalVector((newVec[0] - oldVec[0]) * m_simulationTimeFraction + oldVec[0],
- (newVec[1] - oldVec[1]) * m_simulationTimeFraction + oldVec[1],
- (newVec[2] - oldVec[2]) * m_simulationTimeFraction + oldVec[2]);
- return currentPosition;
- }
-
- CAltitude CInterpolatorLinear::Interpolant::interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const
- {
- Q_UNUSED(setup);
-
- // Interpolate altitude: Alt = (AltB - AltA) * t + AltA
- // avoid underflow below ground elevation by using getCorrectedAltitude
- const CAltitude oldAlt(m_oldSituation.getCorrectedAltitude());
- const CAltitude newAlt(m_newSituation.getCorrectedAltitude());
- Q_ASSERT_X(oldAlt.getReferenceDatum() == CAltitude::MeanSeaLevel && oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(), Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
- return CAltitude((newAlt - oldAlt)
- * m_simulationTimeFraction
- + oldAlt,
- oldAlt.getReferenceDatum());
- }
} // namespace
} // namespace
diff --git a/src/blackmisc/simulation/interpolatorlinear.h b/src/blackmisc/simulation/interpolatorlinear.h
index efd9efed1..c251d876a 100644
--- a/src/blackmisc/simulation/interpolatorlinear.h
+++ b/src/blackmisc/simulation/interpolatorlinear.h
@@ -29,13 +29,12 @@ namespace BlackMisc
//! Linear interpolator, calculation inbetween positions
class BLACKMISC_EXPORT CInterpolatorLinear : public CInterpolator
{
- Q_OBJECT
-
public:
//! Constructor
- CInterpolatorLinear(const BlackMisc::Aviation::CCallsign &callsign, QObject *parent = nullptr) :
- CInterpolator("CInterpolatorLinear", callsign, parent)
- {}
+ CInterpolatorLinear(const Aviation::CCallsign &callsign,
+ ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
+ CInterpolationLogger *logger = nullptr) :
+ CInterpolator(callsign, p1, p2, p3, logger) {}
//! Linear function that performs the actual interpolation
class Interpolant
@@ -48,10 +47,7 @@ namespace BlackMisc
//! @}
//! Perform the interpolation
- //! @{
- Geo::CCoordinateGeodetic interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
- Aviation::CAltitude interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
- //! @}
+ Aviation::CAircraftSituation interpolatePositionAndAltitude(const Aviation::CAircraftSituation &situation) const;
//! Interpolator for pitch, bank, heading, groundspeed
const CInterpolatorPbh &pbh() const { return m_pbh; }
@@ -71,7 +67,7 @@ namespace BlackMisc
Aviation::CAircraftSituation m_newSituation;
double m_simulationTimeFraction = 0.0; //!< 0..1
qint64 m_interpolatedTime = 0; //!< "Real time "of interpolated situation
- const CInterpolatorPbh m_pbh;
+ const CInterpolatorPbh m_pbh; //!< pitch, bank, ground speed and heading
};
//! Get the interpolant for the given time point
diff --git a/src/blackmisc/simulation/interpolatormulti.cpp b/src/blackmisc/simulation/interpolatormulti.cpp
index d2d84f442..d00480d74 100644
--- a/src/blackmisc/simulation/interpolatormulti.cpp
+++ b/src/blackmisc/simulation/interpolatormulti.cpp
@@ -17,10 +17,9 @@ namespace BlackMisc
{
namespace Simulation
{
- CInterpolatorMulti::CInterpolatorMulti(const CCallsign &callsign, QObject *parent) :
- QObject(parent),
- m_spline(callsign, this),
- m_linear(callsign, this)
+ CInterpolatorMulti::CInterpolatorMulti(const CCallsign &callsign, ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3, CInterpolationLogger *logger) :
+ m_spline(callsign, p1, p2, p3, logger),
+ m_linear(callsign, p1, p2, p3, logger)
{}
CAircraftSituation CInterpolatorMulti::getInterpolatedSituation(qint64 currentTimeSinceEpoc,
@@ -42,6 +41,7 @@ namespace BlackMisc
{
switch (m_mode)
{
+ // currently calls the same interpolation for parts
case ModeLinear: return m_linear.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
case ModeSpline: return m_spline.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
default: break;
@@ -49,38 +49,29 @@ namespace BlackMisc
return {};
}
- void CInterpolatorMulti::addAircraftSituation(const CAircraftSituation &situation)
- {
- m_linear.addAircraftSituation(situation);
- m_spline.addAircraftSituation(situation);
- }
-
- bool CInterpolatorMulti::hasAircraftSituations() const
+ CAircraftParts CInterpolatorMulti::getInterpolatedOrGuessedParts(
+ qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
+ CPartsStatus &partsStatus, bool log) const
{
switch (m_mode)
{
- case ModeLinear: return m_linear.hasAircraftSituations();
- case ModeSpline: return m_spline.hasAircraftSituations();
+ // currently calls the same interpolation for parts
+ case ModeLinear: return m_linear.getInterpolatedOrGuessedParts(currentTimeSinceEpoc, setup, partsStatus, log);
+ case ModeSpline: return m_spline.getInterpolatedOrGuessedParts(currentTimeSinceEpoc, setup, partsStatus, log);
default: break;
}
- return false;
+ return {};
}
- void CInterpolatorMulti::addAircraftParts(const CAircraftParts &parts)
- {
- m_linear.addAircraftParts(parts);
- m_spline.addAircraftParts(parts);
- }
-
- bool CInterpolatorMulti::hasAircraftParts() const
+ const CAircraftSituation &CInterpolatorMulti::getLastInterpolatedSituation() const
{
switch (m_mode)
{
- case ModeLinear: return m_linear.hasAircraftParts();
- case ModeSpline: return m_spline.hasAircraftParts();
+ case ModeLinear: return m_linear.getLastInterpolatedSituation();
+ case ModeSpline: return m_spline.getLastInterpolatedSituation();
default: break;
}
- return false;
+ return CAircraftSituation::null();
}
void CInterpolatorMulti::attachLogger(CInterpolationLogger *logger)
@@ -89,18 +80,17 @@ namespace BlackMisc
m_spline.attachLogger(logger);
}
+ void CInterpolatorMulti::initCorrespondingModel(const CAircraftModel &model)
+ {
+ m_linear.initCorrespondingModel(model);
+ m_spline.initCorrespondingModel(model);
+ }
+
bool CInterpolatorMulti::setMode(Mode mode)
{
-#ifdef QT_DEBUG
- if (m_mode != mode)
- {
- m_mode = mode;
- return true;
- }
-#else
- Q_UNUSED(mode);
-#endif
- return false;
+ if (m_mode == mode) { return false; }
+ m_mode = mode;
+ return true;
}
bool CInterpolatorMulti::setMode(const QString &mode)
@@ -156,14 +146,9 @@ namespace BlackMisc
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper()
{ }
- CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const CCallsign &callsign, QObject *parent)
+ CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3, CInterpolationLogger *logger)
{
- m_interpolator.reset(new CInterpolatorMulti(callsign, parent));
- }
-
- CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const CCallsign &callsign, CInterpolationLogger *logger, QObject *parent)
- {
- m_interpolator.reset(new CInterpolatorMulti(callsign, parent));
+ m_interpolator.reset(new CInterpolatorMulti(callsign, p1, p2, p3));
m_interpolator->attachLogger(logger);
}
} // ns
diff --git a/src/blackmisc/simulation/interpolatormulti.h b/src/blackmisc/simulation/interpolatormulti.h
index 2adc0d6be..da8a45c8c 100644
--- a/src/blackmisc/simulation/interpolatormulti.h
+++ b/src/blackmisc/simulation/interpolatormulti.h
@@ -19,43 +19,39 @@ namespace BlackMisc
{
namespace Simulation
{
- /*!
- * Multiplexed interpolator which allows switching between modes at runtime.
- * \remark currently switching mode is only a developer feature, see https://swift-project.slack.com/archives/C04J6J76N/p1504536854000049
- */
- class BLACKMISC_EXPORT CInterpolatorMulti : public QObject
+ //! Multiplexed interpolator which allows switching between modes at runtime.
+ class BLACKMISC_EXPORT CInterpolatorMulti
{
public:
//! Constructor
- CInterpolatorMulti(const Aviation::CCallsign &callsign, QObject *parent = nullptr);
+ CInterpolatorMulti(const Aviation::CCallsign &callsign,
+ ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
+ CInterpolationLogger *logger = nullptr);
//! \copydoc CInterpolator::getInterpolatedSituation
Aviation::CAircraftSituation getInterpolatedSituation(
- qint64 currentTimeSinceEpoc,
- const CInterpolationAndRenderingSetupPerCallsign &setup,
+ qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
CInterpolationStatus &status);
//! \copydoc CInterpolator::getInterpolatedParts
Aviation::CAircraftParts getInterpolatedParts(
- qint64 currentTimeSinceEpoc,
- const CInterpolationAndRenderingSetupPerCallsign &setup,
+ qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
CPartsStatus &partsStatus, bool log = false) const;
- //! \copydoc CInterpolator::addAircraftSituation
- void addAircraftSituation(const Aviation::CAircraftSituation &situation);
+ //! \copydoc CInterpolator::getInterpolatedOrGuessedParts
+ Aviation::CAircraftParts getInterpolatedOrGuessedParts(
+ qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
+ CPartsStatus &partsStatus, bool log) const;
- //! \copydoc CInterpolator::hasAircraftSituations
- bool hasAircraftSituations() const;
-
- //! \copydoc CInterpolator::addAircraftParts
- void addAircraftParts(const Aviation::CAircraftParts &parts);
-
- //! \copydoc CInterpolator::hasAircraftParts
- bool hasAircraftParts() const;
+ //! \copydoc CInterpolator::getLastInterpolatedSituation
+ const Aviation::CAircraftSituation &getLastInterpolatedSituation() const;
//! \copydoc CInterpolator::attachLogger
void attachLogger(CInterpolationLogger *logger);
+ //! \copydoc CInterpolator::initCorrespondingModel
+ void initCorrespondingModel(const CAircraftModel &model);
+
//! Supported interpolation modes.
enum Mode
{
@@ -102,10 +98,10 @@ namespace BlackMisc
CInterpolatorMultiWrapper();
//! Constructor
- CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, QObject *parent = nullptr);
-
- //! Constructor
- CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, CInterpolationLogger *logger, QObject *parent = nullptr);
+ CInterpolatorMultiWrapper(
+ const Aviation::CCallsign &callsign,
+ ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
+ CInterpolationLogger *logger = nullptr);
//! Has interpolator initialized?
bool hasInterpolator() const { return m_interpolator; }
diff --git a/src/blackmisc/simulation/interpolatorspline.cpp b/src/blackmisc/simulation/interpolatorspline.cpp
index 40098e21f..e1795c867 100644
--- a/src/blackmisc/simulation/interpolatorspline.cpp
+++ b/src/blackmisc/simulation/interpolatorspline.cpp
@@ -10,7 +10,9 @@
#include "blackmisc/simulation/interpolatorspline.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/verify.h"
+#include "blackconfig/buildconfig.h"
+using namespace BlackConfig;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Geo;
using namespace BlackMisc::Math;
@@ -103,70 +105,67 @@ namespace BlackMisc
Q_UNUSED(setup);
// recalculate derivatives only if they changed
+ int situationsSize = -1;
if (currentTimeMsSinceEpoc > m_nextSampleAdjustedTime)
{
// with the latest updates of T243 the order and the offsets are supposed to be correct
// so even mixing fast/slow updates shall work
- Q_ASSERT_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
- Q_ASSERT_X(m_aircraftSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
-
- // Ref T243, KB 2018-02, can be removed in future, we verify situations above
- // Situations are supposed to be in correct order
- // const auto end = std::is_sorted_until(m_aircraftSituations.begin(), m_aircraftSituations.end(), [](auto && a, auto && b) { return b.getAdjustedMSecsSinceEpoch() < a.getAdjustedMSecsSinceEpoch(); });
- // const auto validSituations = makeRange(m_aircraftSituations.begin(), end);
+ const CAircraftSituationList validSituations = this->remoteAircraftSituations(m_callsign);
+ situationsSize = validSituations.size();
+ Q_ASSERT_X(validSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
+ Q_ASSERT_X(validSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
// find the first situation earlier than the current time
- const CAircraftSituationList &validSituations = m_aircraftSituations; // if needed, we could also copy here
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
const auto situationsOlder = makeRange(pivot, validSituations.end());
- if (situationsNewer.isEmpty() || situationsOlder.size() < 2)
- {
- return m_interpolant;
- }
-
+ // m_s[0] .. oldest -> m_[2] .. latest
+ if (situationsNewer.isEmpty() || situationsOlder.size() < 2) { return m_interpolant; }
m_s = std::array {{ *(situationsOlder.begin() + 1), *situationsOlder.begin(), *(situationsNewer.end() - 1) }};
- // - altitude unit must be the same for all three, but the unit itself does not matter
- // - ground elevantion here normally is not available
- // - only use elevation plane here, do not call provider
- // - some info how has a plane moves: 100km/h => 1sec 27,7m => 5 secs 136m
- // - on an airport the plane does not move very fast, or not at all
- // - and the elevation remains (almost) constant for a wider area
- // - flying the ground elevation not really matters
- const CElevationPlane plane0 = this->findClosestElevationWithinRange(m_s[0], CElevationPlane::singlePointRadius());
- const CElevationPlane plane1 = this->findClosestElevationWithinRange(m_s[1], CElevationPlane::singlePointRadius());
- const CElevationPlane plane2 = this->findClosestElevationWithinRange(m_s[2], CElevationPlane::singlePointRadius());
-
- // do not override existing values
- m_s[0].setGroundElevationChecked(plane0);
- m_s[1].setGroundElevationChecked(plane1);
- m_s[2].setGroundElevationChecked(plane2);
-
- const CLength cg = this->getCG(m_callsign);
- const double a0 = m_s[0].getCorrectedAltitude(cg).value();
- const double a1 = m_s[1].getCorrectedAltitude(cg).value();
- const double a2 = m_s[2].getCorrectedAltitude(cg).value();
-
const std::array, 3> normals {{ m_s[0].getPosition().normalVectorDouble(), m_s[1].getPosition().normalVectorDouble(), m_s[2].getPosition().normalVectorDouble() }};
PosArray pa;
- pa.x = {{ normals[0][0], normals[1][0], normals[2][0] }};
+ pa.x = {{ normals[0][0], normals[1][0], normals[2][0] }}; // oldest
pa.y = {{ normals[0][1], normals[1][1], normals[2][1] }};
- pa.z = {{ normals[0][2], normals[1][2], normals[2][2] }};
- pa.a = {{ a0, a1, a2 }};
+ pa.z = {{ normals[0][2], normals[1][2], normals[2][2] }}; // latest
pa.t = {{ static_cast(m_s[0].getAdjustedMSecsSinceEpoch()), static_cast(m_s[1].getAdjustedMSecsSinceEpoch()), static_cast(m_s[2].getAdjustedMSecsSinceEpoch()) }};
pa.dx = getDerivatives(pa.t, pa.x);
pa.dy = getDerivatives(pa.t, pa.y);
pa.dz = getDerivatives(pa.t, pa.z);
- pa.da = getDerivatives(pa.t, pa.a);
- m_prevSampleAdjustedTime = situationsOlder.begin()->getAdjustedMSecsSinceEpoch();
- m_nextSampleAdjustedTime = (situationsNewer.end() - 1)->getAdjustedMSecsSinceEpoch();
- m_prevSampleTime = situationsOlder.begin()->getMSecsSinceEpoch();
- m_nextSampleTime = (situationsNewer.end() - 1)->getMSecsSinceEpoch();
- m_interpolant = Interpolant(pa, situationsOlder.begin()->getAltitude().getUnit(), { *situationsOlder.begin(), *(situationsNewer.end() - 1) });
+ // - altitude unit must be the same for all three, but the unit itself does not matter
+ // - ground elevantion here normally is not available
+ // - some info how fast a plane moves: 100km/h => 1sec 27,7m => 5 secs 136m
+ // - on an airport the plane does not move very fast, or not at all
+ // - and the elevation remains (almost) constant for a wider area
+ // - during flying the ground elevation not really matters
+ this->updateElevations();
+ const double a0 = m_s[0].getCorrectedAltitude(m_cg).value(); // oldest
+ const double a1 = m_s[1].getCorrectedAltitude(m_cg).value();
+ const double a2 = m_s[2].getCorrectedAltitude(m_cg).value(); // latest
+ pa.a = {{ a0, a1, a2 }};
+ pa.gnd = {{ m_s[0].getOnGroundFactor(), m_s[1].getOnGroundFactor(), m_s[2].getOnGroundFactor() }};
+ pa.da = getDerivatives(pa.t, pa.a);
+ pa.dgnd = getDerivatives(pa.t, pa.gnd);
+ Q_ASSERT_X(this->areAltitudeUnitsSame(), Q_FUNC_INFO, "Altitude unit mismatch");
+
+ // m_prevSampleAdjustedTime = situationsOlder.begin()->getAdjustedMSecsSinceEpoch(); // m_s[1]
+ // m_nextSampleAdjustedTime = (situationsNewer.end() - 1)->getAdjustedMSecsSinceEpoch(); // m_s[2]
+ // m_prevSampleTime = situationsOlder.begin()->getMSecsSinceEpoch(); // m_s[1]
+ // m_nextSampleTime = (situationsNewer.end() - 1)->getMSecsSinceEpoch(); // m_s[2]
+ // m_interpolant = Interpolant(pa, situationsOlder.begin()->getAltitude().getUnit(), { *situationsOlder.begin(), *(situationsNewer.end() - 1) });
+
+ m_prevSampleAdjustedTime = m_s[1].getAdjustedMSecsSinceEpoch();
+ m_nextSampleAdjustedTime = m_s[2].getAdjustedMSecsSinceEpoch(); // latest
+ m_prevSampleTime = m_s[1].getMSecsSinceEpoch();
+ m_nextSampleTime = m_s[2].getMSecsSinceEpoch(); // latest
+ m_interpolant = Interpolant(pa, m_s[2].getAltitudeUnit(), CInterpolatorPbh(m_s[1], m_s[2]));
+ Q_ASSERT_X(m_prevSampleAdjustedTime < m_nextSampleAdjustedTime, Q_FUNC_INFO, "Wrong time order");
+
+ // VERIFY
+ this->verifyInterpolationSituations(m_s[0], m_s[1], m_s[2], setup); // oldest -> latest
}
// Example:
@@ -194,38 +193,93 @@ namespace BlackMisc
if (this->hasAttachedLogger() && setup.logInterpolation())
{
+ if (situationsSize < 0) { situationsSize = this->remoteAircraftSituationsCount(m_callsign); }
log.interpolationSituations.push_back(m_s[0]);
log.interpolationSituations.push_back(m_s[1]);
log.interpolationSituations.push_back(m_s[2]); // latest at end
log.interpolator = 's';
log.deltaSampleTimesMs = dt2;
- log.simulationTimeFraction = timeFraction;
- log.noNetworkSituations = m_aircraftSituations.size();
+ log.simTimeFraction = timeFraction;
+ log.noNetworkSituations = situationsSize;
log.tsInterpolated = interpolatedTime; // without offsets
}
return m_interpolant;
}
- CCoordinateGeodetic CInterpolatorSpline::Interpolant::interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const
+ bool CInterpolatorSpline::updateElevations()
{
- Q_UNUSED(setup);
-
- const double newX = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
- const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
- const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
-
- CCoordinateGeodetic currentPosition;
- currentPosition.setNormalVector(newX, newY, newZ);
- return currentPosition;
+ bool updated = false;
+ for (unsigned int i = 0; i < m_s.size(); i++)
+ {
+ if (m_s[i].hasGroundElevation()) { continue; } // do not override existing values
+ const CElevationPlane plane = this->findClosestElevationWithinRange(m_s[i], CElevationPlane::singlePointRadius());
+ const bool u = m_s[i].setGroundElevationChecked(plane);
+ updated |= u;
+ }
+ return updated;
}
- CAltitude CInterpolatorSpline::Interpolant::interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const
+ bool CInterpolatorSpline::areAnyElevationsMissing() const
{
- Q_UNUSED(setup);
+ for (unsigned int i = 0; i < m_s.size(); i++)
+ {
+ if (!m_s[i].hasGroundElevation()) { return true; }
+ }
+ return false;
+ }
- const double newA = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
- return CAltitude(newA, m_altitudeUnit);
+ bool CInterpolatorSpline::isAnySituationNearGroundRelevant() const
+ {
+ for (unsigned int i = 0; i < m_s.size(); i++)
+ {
+ if (!m_s[i].canLikelySkipNearGroundInterpolation()) { return true; }
+ }
+ return false;
+ }
+
+ bool CInterpolatorSpline::areAltitudeUnitsSame(const CLengthUnit &compare) const
+ {
+ if (m_s.size() < 1) { return true; }
+ const CLengthUnit c = compare.isNull() ? m_s[0].getAltitudeUnit() : compare;
+ for (unsigned int i = 0; i < m_s.size(); i++)
+ {
+ if (m_s[i].getAltitudeUnit() != c) { return false; }
+ }
+ return true;
+ }
+
+ CAircraftSituation CInterpolatorSpline::Interpolant::interpolatePositionAndAltitude(const CAircraftSituation &situation) const
+ {
+ const double t1 = m_pa.t[1];
+ const double t2 = m_pa.t[2];
+ const double newX = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
+ const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
+ const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
+
+ CAircraftSituation newSituation(situation);
+ const std::array normalVector = {{ newX, newY, newZ }};
+ const CCoordinateGeodetic currentPosition(normalVector);
+
+ const double newA = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
+ const CAltitude alt(newA, m_altitudeUnit);
+
+ newSituation.setPosition(currentPosition);
+ newSituation.setAltitude(alt);
+ newSituation.setMSecsSinceEpoch(this->getInterpolatedTime());
+
+ const double gnd1 = m_pa.gnd[1];
+ const double gnd2 = m_pa.gnd[2];
+ do
+ {
+ if (gfEqualAirborne(gnd1, gnd2)) { newSituation.setOnGround(false); break; }
+ if (gfEqualOnGround(gnd1, gnd2)) { newSituation.setOnGround(true); break; }
+ const double newGnd = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
+ newSituation.setOnGroundFactor(newGnd);
+ newSituation.setOnGroundFromGroundFactorFromInterpolation();
+ }
+ while (false);
+ return newSituation;
}
void CInterpolatorSpline::Interpolant::setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs)
@@ -243,6 +297,7 @@ namespace BlackMisc
a[i] = 0; t[i] = 0;
dx[i] = 0; dy[i] = 0; dz[i] = 0;
da[i] = 0;
+ gnd[i] = 0; dgnd[i] = 0;
}
}
diff --git a/src/blackmisc/simulation/interpolatorspline.h b/src/blackmisc/simulation/interpolatorspline.h
index 91181d80d..c71766889 100644
--- a/src/blackmisc/simulation/interpolatorspline.h
+++ b/src/blackmisc/simulation/interpolatorspline.h
@@ -26,13 +26,12 @@ namespace BlackMisc
//! Cubic spline interpolator
class BLACKMISC_EXPORT CInterpolatorSpline : public CInterpolator
{
- Q_OBJECT
-
public:
//! Constructor
- CInterpolatorSpline(const Aviation::CCallsign &callsign, QObject *parent = nullptr) :
- CInterpolator("CInterpolatorSpline", callsign, parent)
- {}
+ CInterpolatorSpline(const Aviation::CCallsign &callsign,
+ ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
+ CInterpolationLogger *logger = nullptr) :
+ CInterpolator(callsign, p1, p2, p3, logger) {}
//! Position arrays for interpolation
struct BLACKMISC_EXPORT PosArray
@@ -43,9 +42,8 @@ namespace BlackMisc
//! Zero initialized position array
static const PosArray &zeroPosArray();
- //! 3 coordinates for spline interpolation
- //! @{
- std::array x, y, z, a, t, dx, dy, dz, da;
+ //! 3 coordinates for spline interpolation @{
+ std::array x, y, z, a, gnd, t, dx, dy, dz, da, dgnd;
//! @}
};
@@ -62,10 +60,7 @@ namespace BlackMisc
m_pa(pa), m_altitudeUnit(altitudeUnit), m_pbh(pbh) {}
//! Perform the interpolation
- //! @{
- Geo::CCoordinateGeodetic interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
- Aviation::CAltitude interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
- //! @}
+ Aviation::CAircraftSituation interpolatePositionAndAltitude(const Aviation::CAircraftSituation &situation) const;
//! Interpolator for pitch, bank, heading, groundspeed
const CInterpolatorPbh &pbh() const { return m_pbh; }
@@ -83,11 +78,11 @@ namespace BlackMisc
void setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs);
private:
- PosArray m_pa;
+ PosArray m_pa; //! current positions array, latest values last
PhysicalQuantities::CLengthUnit m_altitudeUnit;
CInterpolatorPbh m_pbh;
- qint64 m_currentTimeMsSinceEpoc { 0 };
- qint64 m_interpolatedTime { 0 }; //!< represented "real time" at interpolated situation
+ qint64 m_currentTimeMsSinceEpoc { -1 };
+ qint64 m_interpolatedTime { -1 }; //!< represented "real time" at interpolated situation
};
//! Strategy used by CInterpolator::getInterpolatedSituation
@@ -95,11 +90,23 @@ namespace BlackMisc
const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status, SituationLog &log);
private:
+ //! Update the elevations used in CInterpolatorSpline::m_s
+ bool updateElevations();
+
+ //! Are any elevations missing in CInterpolatorSpline::m_s
+ bool areAnyElevationsMissing() const;
+
+ //! Ground relevant
+ bool isAnySituationNearGroundRelevant() const;
+
+ //! Are the altitude units all the same
+ bool areAltitudeUnitsSame(const PhysicalQuantities::CLengthUnit &compare = PhysicalQuantities::CLengthUnit::nullUnit()) const;
+
qint64 m_prevSampleAdjustedTime = 0; //!< previous sample time + offset
qint64 m_nextSampleAdjustedTime = 0; //!< previous sample time + offset
qint64 m_prevSampleTime = 0; //!< previous sample "real time"
qint64 m_nextSampleTime = 0; //!< next sample "real time"
- std::array m_s;
+ std::array m_s; //!< used situations
Interpolant m_interpolant;
};
} // ns
diff --git a/tests/blackmisc/testinterpolatorlinear.cpp b/tests/blackmisc/testinterpolatorlinear.cpp
index c762bc5ca..870502f0d 100644
--- a/tests/blackmisc/testinterpolatorlinear.cpp
+++ b/tests/blackmisc/testinterpolatorlinear.cpp
@@ -56,8 +56,7 @@ namespace BlackMiscTest
{
CCallsign cs("SWIFT");
CRemoteAircraftProviderDummy provider;
- CInterpolatorLinear interpolator(cs);
- interpolator.setRemoteAircraftProvider(&provider);
+ CInterpolatorLinear interpolator(cs, nullptr, nullptr, &provider);
// fixed time so everything can be debugged
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch();
diff --git a/tests/blackmisc/testinterpolatorparts.cpp b/tests/blackmisc/testinterpolatorparts.cpp
index 83b5b505c..625862694 100644
--- a/tests/blackmisc/testinterpolatorparts.cpp
+++ b/tests/blackmisc/testinterpolatorparts.cpp
@@ -32,8 +32,7 @@ namespace BlackMiscTest
{
CCallsign cs("SWIFT");
CRemoteAircraftProviderDummy provider;
- CInterpolatorSpline interpolator(cs);
- interpolator.setRemoteAircraftProvider(&provider);
+ CInterpolatorSpline interpolator(cs, nullptr, nullptr, &provider);
// fixed time so everything can be debugged
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch()
@@ -64,12 +63,10 @@ namespace BlackMiscTest
qint64 pTs = p.getAdjustedMSecsSinceEpoch();
QVERIFY2(status.isSupportingParts(), "Should support parts");
QVERIFY2(pTs == ts, "Expect latest ts");
- QCOMPARE(p.isOnGroundInterpolated(), 1.0);
p = interpolator.getInterpolatedParts(farPast, setup, status);
pTs = p.getAdjustedMSecsSinceEpoch();
QVERIFY2(status.isSupportingParts(), "Should support parts");
QVERIFY2(pTs == oldestTs, "Expect oldest ts");
- QCOMPARE(p.isOnGroundInterpolated(), 1.0);
// Testing for a time >> last time
// all on ground flags true
@@ -82,7 +79,6 @@ namespace BlackMiscTest
pTs = p.getAdjustedMSecsSinceEpoch();
QVERIFY2(status.isSupportingParts(), "Should support parts");
QVERIFY2(p.getAdjustedMSecsSinceEpoch() == pTs, "Expect latest ts");
- QCOMPARE(p.isOnGroundInterpolated(), 0.0);
}
void CTestInterpolatorParts::partsToSituationGndFlag()