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()