diff --git a/src/blackmisc/simulation/interpolant.h b/src/blackmisc/simulation/interpolant.h index 13a76cd61..1c81adfa0 100644 --- a/src/blackmisc/simulation/interpolant.h +++ b/src/blackmisc/simulation/interpolant.h @@ -43,7 +43,7 @@ namespace BlackMisc qint64 m_interpolatedTime = -1; //!< "Real time "of interpolated situation int m_situationsAvailable = 0; //!< used situations - CInterpolatorPbh m_pbh; //!< the used PBH interpolator + CInterpolatorPbh m_pbh; //!< the used PBH interpolator }; } // namespace } // namespace diff --git a/src/blackmisc/simulation/interpolator.cpp b/src/blackmisc/simulation/interpolator.cpp index 779336eaf..d64587d89 100644 --- a/src/blackmisc/simulation/interpolator.cpp +++ b/src/blackmisc/simulation/interpolator.cpp @@ -76,16 +76,16 @@ namespace BlackMisc { const bool vtol = setup.isForcingVtolInterpolation() || m_model.isVtol(); CAircraftSituationList validSituations = this->remoteAircraftSituations(m_callsign); - m_currentSituationChange = CAircraftSituationChange(validSituations, m_model.getCG(), vtol, true, true); - if (setup.isFixingSceneryOffset() && m_currentSituationChange.hasSceneryDeviation() && m_model.hasCG()) + m_situationsChange = CAircraftSituationChange(validSituations, m_model.getCG(), vtol, true, true); + if (setup.isFixingSceneryOffset() && m_situationsChange.hasSceneryDeviation() && m_model.hasCG()) { - const CLength os = m_currentSituationChange.getGuessedSceneryDeviationCG(); + const CLength os = m_situationsChange.getGuessedSceneryDeviationCG(); m_currentSceneryOffset = os; if (!os.isNull()) { const CLength addValue = os * -1.0; // positive values means too high, negative values too low int changed = validSituations.addAltitudeOffset(addValue); - m_currentSituationChange = CAircraftSituationChange(validSituations, m_model.getCG(), vtol, true, true); // recalculate + m_situationsChange = CAircraftSituationChange(validSituations, m_model.getCG(), vtol, true, true); // recalculate Q_UNUSED(changed); } } @@ -173,40 +173,16 @@ namespace BlackMisc return CAircraftSituation::null(); } - const CAircraftSituation latest = m_currentSituations.front(); - CAircraftSituation currentSituation = m_lastSituation.isNull() ? latest : m_lastSituation; - if (currentSituation.getCallsign() != m_callsign) - { - BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign"); - currentSituation.setCallsign(m_callsign); - } - - // 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 - const CLength cg(this->getModelCG()); - currentSituation.setCG(cg); - - // interpolant function from derived class + // interpolant as function of derived class // CInterpolatorLinear::Interpolant or CInterpolatorSpline::Interpolant SituationLog log; const auto interpolant = derived()->getInterpolant(log); - - // succeeded so far? - if (!m_currentInterpolationStatus.isInterpolated()) - { - m_currentInterpolationStatus.checkIfValidSituation(currentSituation); - return currentSituation; - } - - // Pitch bank heading - // first, so follow up steps could use those values const CInterpolatorPbh pbh = interpolant.pbh(); + + // init interpolated situation + CAircraftSituation currentSituation = this->initInterpolatedSituation(pbh.getOldSituation(), pbh.getNewSituation()); + + // Pitch bank heading first, so follow up steps could use those values currentSituation.setHeading(pbh.getHeading()); currentSituation.setPitch(pbh.getPitch()); currentSituation.setBank(pbh.getBank()); @@ -217,12 +193,23 @@ namespace BlackMisc currentSituation = interpolant.interpolatePositionAndAltitude(currentSituation, interpolateGndFlag); if (!interpolateGndFlag) { currentSituation.guessOnGround(CAircraftSituationChange::null(), m_model); } - // correct itself + // if we do not have a ground elevation from preset, then we try to transfer here + if (!currentSituation.hasGroundElevation()) + { + const CLength radius = currentSituation.getDistancePerTime(250); + if (!m_lastSituation.transferGroundElevation(currentSituation, radius)) + { + const CElevationPlane groundElevation = this->findClosestElevationWithinRange(currentSituation, radius); + m_lastSituation.setGroundElevationChecked(groundElevation, CAircraftSituation::FromCache); + } + } + + // correct altitude itself CAircraftSituation::AltitudeCorrection altCorrection = CAircraftSituation::NoCorrection; if (!interpolateGndFlag && currentSituation.getOnGroundDetails() != CAircraftSituation::OnGroundByGuessing) { // just in case - altCorrection = currentSituation.correctAltitude(cg, true); + altCorrection = currentSituation.correctAltitude(true); // we have CG set } // status @@ -238,10 +225,10 @@ namespace BlackMisc log.groundFactor = currentSituation.getOnGroundFactor(); log.altCorrection = CAircraftSituation::altitudeCorrectionToString(altCorrection); log.situationCurrent = currentSituation; - log.change = m_currentSituationChange; + log.change = m_situationsChange; log.usedSetup = m_currentSetup; log.elevationInfo = elv.arg(elvStats.first).arg(elvStats.second); - log.cgAboveGround = cg; + log.cgAboveGround = currentSituation.getCG(); log.sceneryOffset = m_currentSceneryOffset; m_logger->logInterpolation(log); } @@ -309,7 +296,7 @@ namespace BlackMisc if (!m_currentPartsStatus.isSupportingParts()) { // check if model has been thru model matching - parts.guessParts(m_lastSituation, m_currentSituationChange, m_model); + parts.guessParts(m_lastSituation, m_situationsChange, m_model); this->logParts(parts, 0, false); } @@ -362,7 +349,7 @@ namespace BlackMisc this->resetLastInterpolation(); m_model = CAircraftModel(); m_currentSceneryOffset = CLength::null(); - m_currentSituationChange = CAircraftSituationChange::null(); + m_situationsChange = CAircraftSituationChange::null(); m_currentSituations.clear(); m_currentTimeMsSinceEpoch = -1; m_situationsLastModified = -1; @@ -405,9 +392,9 @@ namespace BlackMisc { const bool inRange = this->isAircraftInRange(m_callsign); m_lastSituation = CAircraftSituation::null(); // no interpolation possible for that step - m_currentInterpolationStatus.setExtraInfo(inRange ? - QString("No situations, but remote aircraft '%1'").arg(m_callsign.asString()) : - QString("Unknown remote aircraft: '%1'").arg(m_callsign.asString())); + static const QString extraNoSituations("No situations, but remote aircraft '%1'"); + static const QString extraNoRemoteAircraft("Unknown remote aircraft: '%1'"); + m_currentInterpolationStatus.setExtraInfo((inRange ? extraNoSituations : extraNoRemoteAircraft).arg(m_callsign.asString())); } else { @@ -426,6 +413,68 @@ namespace BlackMisc return success; } + template + CAircraftSituation CInterpolator::initInterpolatedSituation(const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation) const + { + if (m_currentSituations.isEmpty()) { return CAircraftSituation::null(); } + CAircraftSituation currentSituation = m_lastSituation.isNull() ? m_currentSituations.front() : m_lastSituation; + if (currentSituation.getCallsign() != m_callsign) + { + BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign"); + currentSituation.setCallsign(m_callsign); + } + + // do not set elevation here, as we do not know where the situation will be + // after the interpolation step! + this->presetGroundElevation(currentSituation, oldSituation, newSituation); + + // fetch CG once + const CLength cg(this->getModelCG()); + currentSituation.setCG(cg); + return currentSituation; + } + + template + bool CInterpolator::presetGroundElevation(CAircraftSituation &situation, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation) const + { + // IMPORTANT: we do not know what the situation will be interpolated to, so we cannot transfer + situation.resetGroundElevation(); + do + { + if (oldSituation.equalNormalVectorDouble(newSituation)) + { + if (oldSituation.hasGroundElevation()) + { + // same positions, we can use existing elevation + situation.setGroundElevation(oldSituation.getGroundElevationPlane(), CAircraftSituation::TransferredElevation); + break; + } + } + + const CLength distance = newSituation.calculateGreatCircleDistance(oldSituation); + if (distance < newSituation.getDistancePerTime(250)) + { + if (oldSituation.hasGroundElevation()) + { + // almost same positions, we can use existing elevation + situation.setGroundElevation(oldSituation.getGroundElevationPlane(), CAircraftSituation::TransferredElevation); + break; + } + } + + static const CLength allowedStdDev(3, CLengthUnit::ft()); + QPair elvDevMean = m_situationsChange.getElevationStdDevAndMean(); + if (!elvDevMean.first.isNull() && elvDevMean.first < allowedStdDev) + { + // not much change in known elevations + situation.setGroundElevation(elvDevMean.second, CAircraftSituation::SituationChange); + break; + } + } + while (false); + return situation.hasGroundElevation(); + } + template void CInterpolator::initCorrespondingModel(const CAircraftModel &model) { @@ -495,6 +544,7 @@ namespace BlackMisc m_extraInfo.clear(); m_isValidSituation = false; m_isInterpolated = false; + m_isSameSituation = false; m_situations = -1; } @@ -503,20 +553,24 @@ namespace BlackMisc return QStringLiteral("Interpolated: ") % boolToYesNo(m_isInterpolated) % QStringLiteral(" | situations: ") % QString::number(m_situations) % QStringLiteral(" | situation valid: ") % boolToYesNo(m_isValidSituation) % + QStringLiteral(" | same: ") % boolToYesNo(m_isSameSituation) % ( m_extraInfo.isEmpty() ? QStringLiteral("") : QStringLiteral(" info: ") % m_extraInfo ); } - bool CPartsStatus::allTrue() const - { - return m_supportsParts; - } - void CPartsStatus::reset() { m_supportsParts = false; m_resusedParts = false; + m_isSameParts = false; + } + + QString CPartsStatus::toQString() const + { + return QStringLiteral("Supported parts: ") % boolToYesNo(m_supportsParts) % + QStringLiteral(" | reused: ") % boolToYesNo(m_resusedParts) % + QStringLiteral(" | same: ") % boolToYesNo(m_isSameParts); } // see here for the reason of thess forward instantiations diff --git a/src/blackmisc/simulation/interpolator.h b/src/blackmisc/simulation/interpolator.h index 41217bd43..6fba061a8 100644 --- a/src/blackmisc/simulation/interpolator.h +++ b/src/blackmisc/simulation/interpolator.h @@ -46,6 +46,12 @@ namespace BlackMisc //! Set succeeded void setInterpolated(bool interpolated) { m_isInterpolated = interpolated; } + //! Interpolating between 2 same situations? + bool isSameSituation() const { return m_isSameSituation; } + + //! Interpolating between 2 same situations? + void setSameSituation(bool same) { m_isSameSituation = same; } + //! Set situations count void setSituationsCount(int count) { m_situations = count; } @@ -73,8 +79,9 @@ namespace BlackMisc private: bool m_isInterpolated = false; //!< position is interpolated (means enough values, etc.) bool m_isValidSituation = false; //!< is valid situation + bool m_isSameSituation = false; //!< interpolation between 2 same situations int m_situations = -1; //!< number of situations used for interpolation - QString m_extraInfo; //!< optional details + QString m_extraInfo; //!< optional details }; //! Status regarding parts @@ -87,27 +94,35 @@ namespace BlackMisc //! Ctor CPartsStatus(bool supportsParts) : m_supportsParts(supportsParts) {} - //! all OK - bool allTrue() const; - //! Supporting parts bool isSupportingParts() const { return m_supportsParts; } //! Set support flag void setSupportsParts(bool supports) { m_supportsParts = supports; } - //! Is a reused parts, means using last value again - bool isReusedPArts() const { return m_resusedParts; } + //! Is a reused parts object? + //! \remark means using last value again + bool isReusedParts() const { return m_resusedParts; } //! Mark as reused void setReusedParts(bool reused) { m_resusedParts = reused; } + //! Same parts as last parts? + bool isSameParts() const { return m_isSameParts; } + + //! Same parts as last parts? + void setSameParts(bool same) { m_isSameParts = same; } + //! Reset to default values void reset(); + //! Info string + QString toQString() const; + private: bool m_supportsParts = false; //!< supports parts for given callsign - bool m_resusedParts = false; //!< reusing from last step + bool m_resusedParts = false; //!< reusing from last step + bool m_isSameParts = false; //!< same as last parts? }; //! Combined results @@ -226,9 +241,16 @@ namespace BlackMisc //! \param aircraftNumber passing the aircraft number allows to equally distribute among the steps and not to do it always together for all aircraft bool initIniterpolationStepData(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, int aircraftNumber); + //! Init the interpolated situation + Aviation::CAircraftSituation initInterpolatedSituation(const Aviation::CAircraftSituation &oldSituation, const Aviation::CAircraftSituation &newSituation) const; + //! Current interpolated situation Aviation::CAircraftSituation getInterpolatedSituation(); + //! Preset the ground elevation based on info we already have + //! \remark either sets a gnd.elevation or sets it to null + bool presetGroundElevation(Aviation::CAircraftSituation &situation, const Aviation::CAircraftSituation &oldSituation, const Aviation::CAircraftSituation &newSituation) const; + //! Parts before given offset time Aviation::CAircraftParts getInterpolatedParts(); @@ -251,10 +273,10 @@ namespace BlackMisc // values for current interpolation step qint64 m_currentTimeMsSinceEpoch = -1; //!< current time Aviation::CAircraftSituationList m_currentSituations; //!< current situations - Aviation::CAircraftSituationChange m_currentSituationChange; //!< situations change + Aviation::CAircraftSituationChange m_situationsChange; //!< situations change of provider (i.e. network) situations CInterpolationAndRenderingSetupPerCallsign m_currentSetup; //!< used setup - CInterpolationStatus m_currentInterpolationStatus; //!< this step's status - CPartsStatus m_currentPartsStatus; //!< this step's status + CInterpolationStatus m_currentInterpolationStatus; //!< this step's situation status + CPartsStatus m_currentPartsStatus; //!< this step's parts status CPartsStatus m_lastPartsStatus; //!< status for last parts, used when last parts are re-used because of m_partsToSituationInterpolationRatio int m_partsToSituationInterpolationRatio = 2; //!< ratio between parts and situation interpolation, 1..always, 2..every 2nd situation Aviation::CAircraftSituation m_lastSituation { Aviation::CAircraftSituation::null() }; //!< latest interpolation