// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 #include "misc/simulation/simulationenvironmentprovider.h" #include #include "config/buildconfig.h" #include "misc/aviation/aircraftsituationchange.h" #include "misc/logmessage.h" #include "misc/verify.h" using namespace swift::config; using namespace swift::misc; using namespace swift::misc::aviation; using namespace swift::misc::geo; using namespace swift::misc::physical_quantities; using namespace swift::misc::simulation::settings; namespace swift::misc::simulation { bool ISimulationEnvironmentProvider::rememberGroundElevation(const CCallsign &requestedForCallsign, bool likelyOnGroundElevation, const ICoordinateGeodetic &elevationCoordinate, const CLength &epsilon) { if (!elevationCoordinate.hasMSLGeodeticHeight()) { SWIFT_AUDIT_X(false, Q_FUNC_INFO, "Elevation needs to be MSL NON NULL"); return false; } const CLength minRange = ISimulationEnvironmentProvider::minRange(epsilon); const double elvFt = elevationCoordinate.geodeticHeight().value(CLengthUnit::ft()); CCoordinateGeodetic alreadyInRange; CCoordinateGeodetic alreadyInRangeGnd; { QReadLocker l(&m_lockElvCoordinates); if (!m_enableElevation) { return false; } // check if we have already an elevation within range alreadyInRangeGnd = m_elvCoordinatesGnd.findFirstWithinRangeOrDefault(elevationCoordinate, minRange); alreadyInRange = m_elvCoordinates.findFirstWithinRangeOrDefault(elevationCoordinate, minRange); } constexpr double maxDistFt = 30.0; // here we deal with gnd situation and do not expect a lot of variance if (!alreadyInRangeGnd.isNull()) { // found const double distFt = qAbs(alreadyInRangeGnd.geodeticHeight().value(CLengthUnit::ft()) - elvFt); if (distFt > maxDistFt) { // such a huge distance to existing value CLogMessage(this).debug(u"Suspicious GND elevation distance '%1': %2ft at %3") << requestedForCallsign.asString() << distFt << elevationCoordinate.geodeticHeight().valueRoundedAsString(CLengthUnit::ft(), 1); SWIFT_AUDIT_X(!CBuildConfig::isLocalDeveloperDebugBuild(), Q_FUNC_INFO, "Suspicious gnd. elevation distance"); } return false; } // here we deal with all kind of values, so it can be that // values vary in a much larger range if (!alreadyInRange.isNull()) { // found const double distFt = qAbs(alreadyInRange.geodeticHeight().value(CLengthUnit::ft()) - elvFt); if (distFt > maxDistFt) { // such a huge distance to existing value CLogMessage(this).debug(u"Suspicious NON GND elevation distance for '%1': %2ft at %3") << requestedForCallsign.asString() << distFt << elevationCoordinate.geodeticHeight().valueRoundedAsString(CLengthUnit::ft(), 1); // SWIFT_AUDIT_X(!CBuildConfig::isLocalDeveloperDebugBuild(), Q_FUNC_INFO, "Suspicious elevation // distance"); } return false; } const qint64 now = QDateTime::currentMSecsSinceEpoch(); { // we keep latest at front // * we assume we find them faster // * and need them more frequently (the recent ones) QWriteLocker l(&m_lockElvCoordinates); if (likelyOnGroundElevation) { if (m_elvCoordinatesGnd.size() > m_maxElevationsGnd) { m_elvCoordinatesGnd.pop_back(); } m_elvCoordinatesGnd.push_front(elevationCoordinate); } else { if (m_elvCoordinates.size() > m_maxElevations) { m_elvCoordinates.pop_back(); } m_elvCoordinates.push_front(elevationCoordinate); } // statistics if (m_pendingElevationRequests.contains(requestedForCallsign)) { const qint64 startedMs = m_pendingElevationRequests.value(requestedForCallsign); const qint64 deltaMs = now - startedMs; m_pendingElevationRequests.remove(requestedForCallsign); m_statsCurrentElevRequestTimeMs = deltaMs; if (m_statsMaxElevRequestTimeMs < deltaMs) { m_statsMaxElevRequestTimeMs = deltaMs; } } } return true; } bool ISimulationEnvironmentProvider::rememberGroundElevation(const CCallsign &requestedForCallsign, bool likelyOnGroundElevation, const CElevationPlane &elevationPlane) { if (!elevationPlane.hasMSLGeodeticHeight()) { SWIFT_AUDIT_X(false, Q_FUNC_INFO, "Elevation plane needs to be MSL NON NULL"); return false; } return this->rememberGroundElevation(requestedForCallsign, likelyOnGroundElevation, elevationPlane, elevationPlane.getRadius()); } bool ISimulationEnvironmentProvider::insertCG(const CLength &cg, const CCallsign &cs) { if (cs.isEmpty()) { return false; } const bool remove = cg.isNull(); { QWriteLocker l(&m_lockCG); if (remove) { m_cgsPerCallsign.remove(cs); } else { m_cgsPerCallsign[cs] = cg; } } return true; } bool ISimulationEnvironmentProvider::insertCG(const CLength &cg, const QString &modelString, const CCallsign &cs) { bool stored = false; QWriteLocker l(&m_lockCG); if (!m_enableCG) { return false; } if (!cs.isEmpty()) { if (m_cgsPerCallsignOverridden.contains(cs)) { // only keep as overridden value m_cgsPerCallsignOverridden[cs] = cg; } else { m_cgsPerCallsign[cs] = cg; stored = true; } } if (!modelString.isEmpty()) { const QString ms = modelString.toUpper(); if (m_cgsPerModelOverridden.contains(ms)) { // only keep as overridden value m_cgsPerModelOverridden[ms] = cg; } else { m_cgsPerModel[ms] = cg; stored = true; } } return stored; } bool ISimulationEnvironmentProvider::insertCGOverridden(const CLength &cg, const CCallsign &cs) { if (cs.isEmpty()) { return false; } QWriteLocker l(&m_lockCG); if (!m_enableCG) { return false; } if (cg.isNull()) { m_cgsPerCallsignOverridden.remove(cs); return false; } m_cgsPerCallsignOverridden[cs] = cg; return true; } bool ISimulationEnvironmentProvider::insertCGOverridden(const CLength &cg, const CCallsignSet &callsigns) { if (callsigns.isEmpty()) { return false; } QWriteLocker l(&m_lockCG); if (!m_enableCG) { return false; } for (const CCallsign &cs : callsigns) { if (cg.isNull()) { m_cgsPerCallsignOverridden.remove(cs); } else { m_cgsPerCallsignOverridden[cs] = cg; } } return true; } bool ISimulationEnvironmentProvider::insertCGForModelString(const CLength &cg, const QString &modelString) { if (modelString.isEmpty()) { return false; } QWriteLocker l(&m_lockCG); if (!m_enableCG) { return false; } if (cg.isNull()) { m_cgsPerModel.remove(modelString.toUpper()); return false; } m_cgsPerModel[modelString.toUpper()] = cg; return true; } bool ISimulationEnvironmentProvider::insertCGForModelStringOverridden(const CLength &cg, const QString &modelString) { if (modelString.isEmpty()) { return false; } QWriteLocker l(&m_lockCG); if (cg.isNull()) { m_cgsPerModelOverridden.remove(modelString.toUpper()); return false; } m_cgsPerModelOverridden[modelString.toUpper()] = cg; return true; } CLengthPerCallsign ISimulationEnvironmentProvider::clearCGOverrides() { QWriteLocker l(&m_lockCG); m_cgsPerModelOverridden.clear(); m_cgsPerCallsignOverridden.clear(); return m_cgsPerCallsign; // all remaining CGs } CLength ISimulationEnvironmentProvider::overriddenCGorDefault(const CLength &defaultCG, const QString &modelString) const { if (modelString.isEmpty()) { return defaultCG; } const QString ms = modelString.toUpper(); QReadLocker l(&m_lockCG); if (!m_cgsPerModelOverridden.contains(ms)) { return defaultCG; } return m_cgsPerModelOverridden[ms]; } int ISimulationEnvironmentProvider::removeSimulatorCG(const CCallsign &cs) { if (cs.isEmpty()) { return 0; } QWriteLocker l(&m_lockCG); m_cgsPerCallsignOverridden.remove(cs); return m_cgsPerCallsign.remove(cs); } void ISimulationEnvironmentProvider::removePendingElevationRequest(const CCallsign &cs) { QWriteLocker l(&m_lockElvCoordinates); m_pendingElevationRequests.remove(cs); } CLength ISimulationEnvironmentProvider::minRange(const CLength &range) { return (range.isNull() || range < CElevationPlane::singlePointRadius()) ? CElevationPlane::singlePointRadius() : range; } CCoordinateGeodeticList ISimulationEnvironmentProvider::getAllElevationCoordinates() const { QReadLocker l(&m_lockElvCoordinates); CCoordinateGeodeticList cl(m_elvCoordinatesGnd); cl.push_back(m_elvCoordinates); return cl; } CCoordinateGeodeticList ISimulationEnvironmentProvider::getElevationCoordinatesOnGround() const { QReadLocker l(&m_lockElvCoordinates); return m_elvCoordinatesGnd; } CElevationPlane ISimulationEnvironmentProvider::averageElevationOfOnGroundAircraft( const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const { const CCoordinateGeodeticList coordinates = this->getElevationCoordinatesOnGround(); return coordinates.averageGeodeticHeight(reference, range, CAircraftSituation::allowedAltitudeDeviation(), minValues, sufficientValues); } CAltitude ISimulationEnvironmentProvider::highestElevation() const { const CCoordinateGeodeticList coordinates = this->getElevationCoordinatesOnGround(); if (coordinates.isEmpty()) { return CAltitude::null(); } return coordinates.findMaxHeight(); } CCoordinateGeodeticList ISimulationEnvironmentProvider::getAllElevationCoordinates(int &maxRemembered) const { QReadLocker l(&m_lockElvCoordinates); maxRemembered = m_maxElevations; CCoordinateGeodeticList cl(m_elvCoordinatesGnd); cl.push_back(m_elvCoordinates); return cl; } int ISimulationEnvironmentProvider::cleanUpElevations(const ICoordinateGeodetic &referenceCoordinate, int maxNumber) { int currentMax; CCoordinateGeodeticList coordinates(this->getAllElevationCoordinates(currentMax)); if (maxNumber < 0) { maxNumber = currentMax; } const int size = coordinates.size(); if (size <= maxNumber) { return 0; } coordinates.sortByEuclideanDistanceSquared(referenceCoordinate); coordinates.truncate(maxNumber); const int delta = size - coordinates.size(); { QWriteLocker l(&m_lockElvCoordinates); m_elvCoordinates = coordinates; } return delta; } CElevationPlane ISimulationEnvironmentProvider::findClosestElevationWithinRange(const ICoordinateGeodetic &reference, const CLength &range) const { if (!this->isElevationProviderEnabled()) { return CElevationPlane::null(); } // for single point we use a slightly optimized version const bool singlePoint = (&range == &CElevationPlane::singlePointRadius() || range.isNull() || range <= CElevationPlane::singlePointRadius()); const CCoordinateGeodeticList elevations = this->getAllElevationCoordinates(); const CCoordinateGeodetic coordinate = singlePoint ? elevations.findFirstWithinRangeOrDefault(reference, CElevationPlane::singlePointRadius()) : elevations.findClosestWithinRange(reference, range); const bool found = !coordinate.isNull(); { QWriteLocker l { &m_lockElvCoordinates }; if (found) { m_elvFound++; return CElevationPlane(coordinate, reference); // plane with radius = distance to reference } else { m_elvMissed++; return CElevationPlane::null(); } } } CElevationPlane ISimulationEnvironmentProvider::findClosestElevationWithinRangeOrRequest( const ICoordinateGeodetic &reference, const CLength &range, const CCallsign &callsign) { if (!this->isElevationProviderEnabled()) { return CElevationPlane::null(); } const CElevationPlane ep = ISimulationEnvironmentProvider::findClosestElevationWithinRange(reference, range); if (ep.isNull()) { const qint64 now = QDateTime::currentMSecsSinceEpoch(); this->requestElevation(reference, callsign); QWriteLocker l(&m_lockElvCoordinates); m_pendingElevationRequests[callsign] = now; } return ep; } bool ISimulationEnvironmentProvider::requestElevationBySituation(const CAircraftSituation &situation) { if (!this->isElevationProviderEnabled()) { return false; } return this->requestElevation(situation, situation.getCallsign()); } QPair ISimulationEnvironmentProvider::getElevationsFoundMissed() const { QReadLocker l(&m_lockElvCoordinates); return QPair(m_elvFound, m_elvMissed); } QString ISimulationEnvironmentProvider::getElevationsFoundMissedInfo() const { static const QString info("%1/%2 %3% in %4 (all)/%5 (gnd)"); const QPair foundMissed = this->getElevationsFoundMissed(); const int f = foundMissed.first; const int m = foundMissed.second; const double hitRatioPercent = 100.0 * static_cast(f) / static_cast(f + m); int elvGnd; int elv; { QReadLocker l(&m_lockElvCoordinates); elvGnd = m_elvCoordinatesGnd.sizeInt(); elv = m_elvCoordinates.sizeInt(); } return info.arg(f).arg(m).arg(QString::number(hitRatioPercent, 'f', 1)).arg(elv).arg(elvGnd); } QPair ISimulationEnvironmentProvider::getElevationRequestTimes() const { QReadLocker l(&m_lockElvCoordinates); return QPair(m_statsCurrentElevRequestTimeMs, m_statsMaxElevRequestTimeMs); } QString ISimulationEnvironmentProvider::getElevationRequestTimesInfo() const { if (!this->isElevationProviderEnabled()) { return QStringLiteral("Elevation provider disabled"); } static const QString info("%1ms/%2ms"); QPair times = this->getElevationRequestTimes(); if (times.first < 0 || times.second < 0) { return QStringLiteral("no req. times"); } return info.arg(times.first).arg(times.second); } CSimulatorPluginInfo ISimulationEnvironmentProvider::getSimulatorPluginInfo() const { QReadLocker l(&m_lockSimInfo); return m_simulatorPluginInfo; } CSimulatorInfo ISimulationEnvironmentProvider::getSimulatorInfo() const { return this->getSimulatorPluginInfo().getSimulatorInfo(); } QString ISimulationEnvironmentProvider::getSimulatorNameAndVersion() const { QString n; QString v; { QReadLocker l(&m_lockSimInfo); n = m_simulatorName; v = m_simulatorVersion; } if (!n.isEmpty() && !v.isEmpty()) { return n % u' ' % v; } if (!n.isEmpty()) { return n; } const CSimulatorInfo simInfo = this->getSimulatorInfo(); if (!simInfo.isUnspecified()) { return simInfo.toQString(true); } return QStringLiteral("not available"); } CAircraftModel ISimulationEnvironmentProvider::getDefaultModel() const { QReadLocker l(&m_lockModel); return m_defaultModel; } CLengthPerCallsign ISimulationEnvironmentProvider::getSimulatorCGsPerCallsign() const { QReadLocker l(&m_lockCG); return m_cgsPerCallsign; } QHash ISimulationEnvironmentProvider::getSimulatorCGsPerModelString() const { QReadLocker l(&m_lockCG); return m_cgsPerModel; } CLength ISimulationEnvironmentProvider::getSimulatorCG(const aviation::CCallsign &callsign) const { if (callsign.isEmpty()) { return CLength::null(); } QReadLocker l(&m_lockCG); if (!m_enableCG) { return CLength::null(); } if (m_cgsPerCallsignOverridden.contains(callsign)) { return m_cgsPerCallsignOverridden[callsign]; } if (!m_cgsPerCallsign.contains(callsign)) { return CLength::null(); } return m_cgsPerCallsign.value(callsign); } CLength ISimulationEnvironmentProvider::getSimulatorOrDbCG(const CCallsign &callsign, const CLength &dbCG) const { if (callsign.isEmpty()) { return CLength::null(); } const CSimulatorSettings::CGSource source = m_settings.getCGSource(); if (source == CSimulatorSettings::CGFromDBOnly || (source == CSimulatorSettings::CGFromDBFirst && !dbCG.isNull())) { return dbCG; } const CLength simCG = this->getSimulatorCG(callsign); if (source == CSimulatorSettings::CGFromSimulatorOnly || (source == CSimulatorSettings::CGFromSimulatorFirst && !simCG.isNull())) { return simCG; } return dbCG; } CLength ISimulationEnvironmentProvider::getSimulatorCGPerModelString(const QString &modelString) const { if (modelString.isEmpty()) { return CLength::null(); } const QString ms = modelString.toUpper(); QReadLocker l(&m_lockCG); if (!m_enableCG) { return CLength::null(); } if (m_cgsPerModelOverridden.contains(ms)) { return m_cgsPerModelOverridden.value(ms); } if (!m_cgsPerModel.contains(ms)) { return CLength::null(); } return m_cgsPerModel.value(ms); } CLength ISimulationEnvironmentProvider::getSimulatorOrDbCGPerModelString(const QString &modelString, const CLength &dbCG) const { if (modelString.isEmpty()) { return CLength::null(); } const CSimulatorSettings::CGSource source = m_settings.getCGSource(); const QString ms = modelString.toUpper(); { QReadLocker l(&m_lockCG); if (m_cgsPerModelOverridden.contains(ms)) { return m_cgsPerModelOverridden.value(ms); } } if (source == CSimulatorSettings::CGFromDBOnly || (!dbCG.isNull() && source == CSimulatorSettings::CGFromDBFirst)) { return dbCG; } const CLength simCG = this->getSimulatorCGPerModelString(modelString); if (source == CSimulatorSettings::CGFromSimulatorOnly || (source == CSimulatorSettings::CGFromSimulatorFirst && simCG.isNull())) { return simCG; } return dbCG; } bool ISimulationEnvironmentProvider::hasSimulatorCG(const aviation::CCallsign &callsign) const { if (callsign.isEmpty()) { return false; } QReadLocker l(&m_lockCG); return m_enableCG && (m_cgsPerCallsign.contains(callsign) || m_cgsPerCallsignOverridden.contains(callsign)); } bool ISimulationEnvironmentProvider::hasSameSimulatorCG(const CLength &cg, const CCallsign &callsign) const { if (callsign.isEmpty()) { return false; } QReadLocker l(&m_lockCG); if (m_cgsPerCallsignOverridden.contains(callsign)) { return m_cgsPerCallsignOverridden[callsign] == cg; } // normal values if (!m_cgsPerCallsign.contains(callsign)) { return false; } return m_cgsPerCallsign[callsign] == cg; } int ISimulationEnvironmentProvider::setMaxElevationsRemembered(int max) { QWriteLocker l(&m_lockElvCoordinates); m_maxElevations = qMax(max, 50); return m_maxElevations; } int ISimulationEnvironmentProvider::getMaxElevationsRemembered() const { QReadLocker l(&m_lockElvCoordinates); return m_maxElevations; } void ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics() { QWriteLocker l(&m_lockElvCoordinates); m_statsCurrentElevRequestTimeMs = -1; m_statsMaxElevRequestTimeMs = -1; m_elvFound = m_elvMissed = 0; } int ISimulationEnvironmentProvider::removeElevationValues(const CAircraftSituation &reference, const CLength &removeRange) { QWriteLocker l(&m_lockElvCoordinates); const int r = m_elvCoordinatesGnd.removeInsideRange(reference, removeRange); return r; } bool ISimulationEnvironmentProvider::cleanElevationValues(const CAircraftSituation &reference, const CLength &keptRange, bool forced) { if (reference.isNull() || keptRange.isNull()) { return false; } const CLength r = minRange(keptRange); CCoordinateGeodeticList cleanedKeptElvs; bool maxReached = false; bool cleaned = false; { QReadLocker l(&m_lockElvCoordinates); cleanedKeptElvs = m_elvCoordinates; maxReached = m_elvCoordinates.size() >= m_maxElevations; } if (!cleanedKeptElvs.isEmpty() && (forced || maxReached)) { const int removed = cleanedKeptElvs.removeOutsideRange(reference, r); if (removed > 0) { cleaned = true; QWriteLocker l(&m_lockElvCoordinates); m_elvCoordinates = cleanedKeptElvs; } } cleanedKeptElvs.clear(); { QReadLocker l(&m_lockElvCoordinates); cleanedKeptElvs = m_elvCoordinatesGnd; maxReached = m_elvCoordinatesGnd.size() >= m_maxElevationsGnd; } if (!cleanedKeptElvs.isEmpty() && (forced || maxReached)) { const int removed = cleanedKeptElvs.removeOutsideRange(reference, r); if (removed > 0) { cleaned = true; QWriteLocker l(&m_lockElvCoordinates); m_elvCoordinatesGnd = cleanedKeptElvs; } } return cleaned; } ISimulationEnvironmentProvider::ISimulationEnvironmentProvider(const CSimulatorPluginInfo &pluginInfo) : m_simulatorPluginInfo(pluginInfo) {} ISimulationEnvironmentProvider::ISimulationEnvironmentProvider(const CSimulatorPluginInfo &pluginInfo, const CSimulatorSettings &settings, bool supportElevation, bool supportCG) : m_simulatorPluginInfo(pluginInfo), m_settings(settings), m_enableElevation(supportElevation), m_enableCG(supportCG) {} bool ISimulationEnvironmentProvider::isCgProviderEnabled() const { QReadLocker l(&m_lockCG); return m_enableCG; } bool ISimulationEnvironmentProvider::isElevationProviderEnabled() const { QReadLocker l(&m_lockElvCoordinates); return m_enableElevation; } void ISimulationEnvironmentProvider::setCgProviderEnabled(bool enabled) { QWriteLocker l(&m_lockCG); m_enableCG = enabled; } void ISimulationEnvironmentProvider::setElevationProviderEnabled(bool enabled) { QWriteLocker l(&m_lockElvCoordinates); m_enableElevation = enabled; } void ISimulationEnvironmentProvider::setSimulationProviderEnabled(bool elvEnabled, bool cgEnabled) { setElevationProviderEnabled(elvEnabled); setCgProviderEnabled(cgEnabled); } void ISimulationEnvironmentProvider::setNewPluginInfo(const CSimulatorPluginInfo &info, const CSimulatorSettings &settings, const CAircraftModel &defaultModel) { this->setNewPluginInfo(info, settings); this->setDefaultModel(defaultModel); } void ISimulationEnvironmentProvider::setNewPluginInfo(const CSimulatorPluginInfo &info, const CSimulatorSettings &settings) { QWriteLocker l(&m_lockSimInfo); m_simulatorPluginInfo = info; m_settings = settings; } void ISimulationEnvironmentProvider::setSimulatorDetails(const QString &name, const QString &details, const QString &version) { QWriteLocker l(&m_lockSimInfo); m_simulatorName = name; m_simulatorDetails = details; m_simulatorVersion = version; } QString ISimulationEnvironmentProvider::getSimulatorName() const { QReadLocker l(&m_lockSimInfo); return m_simulatorName; } QString ISimulationEnvironmentProvider::getSimulatorVersion() const { QReadLocker l(&m_lockSimInfo); return m_simulatorVersion; } QString ISimulationEnvironmentProvider::getSimulatorDetails() const { QReadLocker l(&m_lockSimInfo); return m_simulatorDetails; } void ISimulationEnvironmentProvider::setDefaultModel(const CAircraftModel &defaultModel) { QWriteLocker l(&m_lockModel); m_defaultModel = defaultModel; } void ISimulationEnvironmentProvider::clearDefaultModel() { QWriteLocker l(&m_lockModel); m_defaultModel = CAircraftModel(); } void ISimulationEnvironmentProvider::clearElevations() { QWriteLocker l(&m_lockElvCoordinates); m_elvCoordinates.clear(); m_elvCoordinatesGnd.clear(); m_pendingElevationRequests.clear(); m_statsCurrentElevRequestTimeMs = -1; m_statsMaxElevRequestTimeMs = -1; m_elvFound = m_elvMissed = 0; } void ISimulationEnvironmentProvider::clearCGs() { QWriteLocker l(&m_lockCG); m_cgsPerCallsign.clear(); m_cgsPerCallsignOverridden.clear(); // intentionally not cleaning CGs per model, as models will not change, callsign do! } void ISimulationEnvironmentProvider::clearSimulationEnvironmentData() { this->clearDefaultModel(); this->clearElevations(); this->clearCGs(); this->resetSimulationEnvironmentStatistics(); } // pin vtables to this file void CSimulationEnvironmentAware::anchor() {} CElevationPlane CSimulationEnvironmentAware::findClosestElevationWithinRange(const ICoordinateGeodetic &reference, const physical_quantities::CLength &range) const { if (!this->hasProvider()) { return CElevationPlane::null(); } return this->provider()->findClosestElevationWithinRange(reference, range); } CElevationPlane CSimulationEnvironmentAware::findClosestElevationWithinRangeOrRequest( const ICoordinateGeodetic &reference, const CLength &range, const CCallsign &callsign) { if (!this->hasProvider()) { return CElevationPlane::null(); } return this->provider()->findClosestElevationWithinRangeOrRequest(reference, range, callsign); } CElevationPlane CSimulationEnvironmentAware::averageElevationOfOnGroundAircraft(const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const { if (!this->hasProvider()) { return CElevationPlane::null(); } return this->provider()->averageElevationOfOnGroundAircraft(reference, range, minValues, sufficientValues); } CAltitude CSimulationEnvironmentAware::highestElevation() const { if (!this->hasProvider()) { return CAltitude::null(); } return this->provider()->highestElevation(); } bool CSimulationEnvironmentAware::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &callsign) { if (!this->hasProvider()) { return false; } return this->provider()->requestElevation(reference, callsign); } bool CSimulationEnvironmentAware::requestElevation(const CAircraftSituation &situation) { return this->requestElevation(situation, situation.getCallsign()); } QPair CSimulationEnvironmentAware::getElevationsFoundMissed() const { if (!this->hasProvider()) { return QPair(0, 0); } return this->provider()->getElevationsFoundMissed(); } QString CSimulationEnvironmentAware::getElevationsFoundMissedInfo() const { if (!this->hasProvider()) { return QString(); } return this->provider()->getElevationsFoundMissedInfo(); } QPair CSimulationEnvironmentAware::getElevationRequestTimes() const { if (!this->hasProvider()) { return QPair(-1, -1); } return this->provider()->getElevationRequestTimes(); } QString CSimulationEnvironmentAware::getElevationRequestTimesInfo() const { if (!this->hasProvider()) { return QString(); } return this->provider()->getElevationRequestTimesInfo(); } CSimulatorPluginInfo CSimulationEnvironmentAware::getSimulatorPluginInfo() const { if (!this->hasProvider()) { return CSimulatorPluginInfo(); } return this->provider()->getSimulatorPluginInfo(); } CSimulatorInfo CSimulationEnvironmentAware::getSimulatorInfo() const { if (!this->hasProvider()) { return CSimulatorInfo(); } return this->provider()->getSimulatorInfo(); } QString CSimulationEnvironmentAware::getSimulatorNameAndVersion() const { if (!this->hasProvider()) { return "not available"; } return this->provider()->getSimulatorNameAndVersion(); } CAircraftModel CSimulationEnvironmentAware::getDefaultModel() const { if (!this->hasProvider()) { return CAircraftModel(); } return this->provider()->getDefaultModel(); } CLength CSimulationEnvironmentAware::getSimulatorCG(const CCallsign &callsign) const { if (!this->hasProvider()) { return CLength::null(); } return this->provider()->getSimulatorCG(callsign); } CLength CSimulationEnvironmentAware::getSimulatorOrDbCG(const CCallsign &callsign, const CLength &dbCG) const { if (!this->hasProvider()) { return CLength::null(); } return this->provider()->getSimulatorOrDbCG(callsign, dbCG); } bool CSimulationEnvironmentAware::hasSimulatorCG(const CCallsign &callsign) const { if (!this->hasProvider()) { return false; } return this->provider()->hasSimulatorCG(callsign); } bool CSimulationEnvironmentAware::cleanElevationValues(const CAircraftSituation &reference, const CLength &range, bool forced) { if (!this->hasProvider()) { return false; } return this->provider()->cleanElevationValues(reference, range, forced); } } // namespace swift::misc::simulation