6 #include <QStringBuilder>
13 using namespace swift::config;
15 using namespace swift::misc::aviation;
16 using namespace swift::misc::geo;
17 using namespace swift::misc::physical_quantities;
18 using namespace swift::misc::simulation::settings;
20 namespace swift::misc::simulation
22 bool ISimulationEnvironmentProvider::rememberGroundElevation(
const CCallsign &requestedForCallsign,
23 bool likelyOnGroundElevation,
29 SWIFT_AUDIT_X(
false, Q_FUNC_INFO,
"Elevation needs to be MSL NON NULL");
33 const CLength minRange = ISimulationEnvironmentProvider::minRange(epsilon);
40 if (!m_enableElevation) {
return false; }
43 alreadyInRangeGnd = m_elvCoordinatesGnd.findFirstWithinRangeOrDefault(elevationCoordinate, minRange);
44 alreadyInRange = m_elvCoordinates.findFirstWithinRangeOrDefault(elevationCoordinate, minRange);
47 constexpr
double maxDistFt = 30.0;
50 if (!alreadyInRangeGnd.
isNull())
53 const double distFt = qAbs(alreadyInRangeGnd.
geodeticHeight().
value(CLengthUnit::ft()) - elvFt);
54 if (distFt > maxDistFt)
57 CLogMessage(
this).
debug(u
"Suspicious GND elevation distance '%1': %2ft at %3")
58 << requestedForCallsign.
asString() << distFt
61 "Suspicious gnd. elevation distance");
68 if (!alreadyInRange.
isNull())
71 const double distFt = qAbs(alreadyInRange.
geodeticHeight().
value(CLengthUnit::ft()) - elvFt);
72 if (distFt > maxDistFt)
75 CLogMessage(
this).
debug(u
"Suspicious NON GND elevation distance for '%1': %2ft at %3")
76 << requestedForCallsign.
asString() << distFt
90 if (likelyOnGroundElevation)
92 if (m_elvCoordinatesGnd.size() > m_maxElevationsGnd) { m_elvCoordinatesGnd.pop_back(); }
93 m_elvCoordinatesGnd.push_front(elevationCoordinate);
97 if (m_elvCoordinates.size() > m_maxElevations) { m_elvCoordinates.pop_back(); }
98 m_elvCoordinates.push_front(elevationCoordinate);
102 if (m_pendingElevationRequests.contains(requestedForCallsign))
104 const qint64 startedMs = m_pendingElevationRequests.value(requestedForCallsign);
105 const qint64 deltaMs = now - startedMs;
106 m_pendingElevationRequests.remove(requestedForCallsign);
107 m_statsCurrentElevRequestTimeMs = deltaMs;
108 if (m_statsMaxElevRequestTimeMs < deltaMs) { m_statsMaxElevRequestTimeMs = deltaMs; }
114 bool ISimulationEnvironmentProvider::rememberGroundElevation(
const CCallsign &requestedForCallsign,
115 bool likelyOnGroundElevation,
120 SWIFT_AUDIT_X(
false, Q_FUNC_INFO,
"Elevation plane needs to be MSL NON NULL");
123 return this->rememberGroundElevation(requestedForCallsign, likelyOnGroundElevation, elevationPlane,
129 if (cs.
isEmpty()) {
return false; }
131 const bool remove = cg.
isNull();
134 if (remove) { m_cgsPerCallsign.remove(cs); }
135 else { m_cgsPerCallsign[cs] = cg; }
144 if (!m_enableCG) {
return false; }
147 if (m_cgsPerCallsignOverridden.contains(cs))
150 m_cgsPerCallsignOverridden[cs] = cg;
154 m_cgsPerCallsign[cs] = cg;
161 if (m_cgsPerModelOverridden.contains(ms))
164 m_cgsPerModelOverridden[ms] = cg;
168 m_cgsPerModel[ms] = cg;
175 bool ISimulationEnvironmentProvider::insertCGOverridden(
const CLength &cg,
const CCallsign &cs)
177 if (cs.
isEmpty()) {
return false; }
180 if (!m_enableCG) {
return false; }
183 m_cgsPerCallsignOverridden.remove(cs);
187 m_cgsPerCallsignOverridden[cs] = cg;
193 if (callsigns.
isEmpty()) {
return false; }
196 if (!m_enableCG) {
return false; }
199 if (cg.
isNull()) { m_cgsPerCallsignOverridden.remove(cs); }
200 else { m_cgsPerCallsignOverridden[cs] = cg; }
205 bool ISimulationEnvironmentProvider::insertCGForModelString(
const CLength &cg,
const QString &modelString)
207 if (modelString.
isEmpty()) {
return false; }
210 if (!m_enableCG) {
return false; }
213 m_cgsPerModel.remove(modelString.
toUpper());
217 m_cgsPerModel[modelString.
toUpper()] = cg;
221 bool ISimulationEnvironmentProvider::insertCGForModelStringOverridden(
const CLength &cg,
const QString &modelString)
223 if (modelString.
isEmpty()) {
return false; }
227 m_cgsPerModelOverridden.remove(modelString.
toUpper());
230 m_cgsPerModelOverridden[modelString.
toUpper()] = cg;
237 m_cgsPerModelOverridden.clear();
238 m_cgsPerCallsignOverridden.clear();
239 return m_cgsPerCallsign;
242 CLength ISimulationEnvironmentProvider::overriddenCGorDefault(
const CLength &defaultCG,
243 const QString &modelString)
const
245 if (modelString.
isEmpty()) {
return defaultCG; }
248 if (!m_cgsPerModelOverridden.contains(ms)) {
return defaultCG; }
249 return m_cgsPerModelOverridden[ms];
252 int ISimulationEnvironmentProvider::removeSimulatorCG(
const CCallsign &cs)
254 if (cs.
isEmpty()) {
return 0; }
257 m_cgsPerCallsignOverridden.remove(cs);
258 return m_cgsPerCallsign.remove(cs);
261 void ISimulationEnvironmentProvider::removePendingElevationRequest(
const CCallsign &cs)
264 m_pendingElevationRequests.remove(cs);
269 return (range.
isNull() || range < CElevationPlane::singlePointRadius()) ? CElevationPlane::singlePointRadius() :
284 return m_elvCoordinatesGnd;
291 return coordinates.
averageGeodeticHeight(reference, range, CAircraftSituation::allowedAltitudeDeviation(),
292 minValues, sufficientValues);
295 CAltitude ISimulationEnvironmentProvider::highestElevation()
const
298 if (coordinates.
isEmpty()) {
return CAltitude::null(); }
305 maxRemembered = m_maxElevations;
311 int ISimulationEnvironmentProvider::cleanUpElevations(
const ICoordinateGeodetic &referenceCoordinate,
int maxNumber)
315 if (maxNumber < 0) { maxNumber = currentMax; }
316 const int size = coordinates.
size();
317 if (size <= maxNumber) {
return 0; }
320 const int delta = size - coordinates.
size();
323 m_elvCoordinates = coordinates;
332 if (!this->isElevationProviderEnabled()) {
return CElevationPlane::null(); }
335 const bool singlePoint = (&range == &CElevationPlane::singlePointRadius() || range.
isNull() ||
336 range <= CElevationPlane::singlePointRadius());
341 const bool found = !coordinate.
isNull();
353 return CElevationPlane::null();
358 CElevationPlane ISimulationEnvironmentProvider::findClosestElevationWithinRangeOrRequest(
361 if (!this->isElevationProviderEnabled()) {
return CElevationPlane::null(); }
362 const CElevationPlane ep = ISimulationEnvironmentProvider::findClosestElevationWithinRange(reference, range);
366 this->requestElevation(reference, callsign);
368 m_pendingElevationRequests[callsign] = now;
373 bool ISimulationEnvironmentProvider::requestElevationBySituation(
const CAircraftSituation &situation)
375 if (!this->isElevationProviderEnabled()) {
return false; }
376 return this->requestElevation(situation, situation.
getCallsign());
379 QPair<int, int> ISimulationEnvironmentProvider::getElevationsFoundMissed()
const
382 return QPair<int, int>(m_elvFound, m_elvMissed);
385 QString ISimulationEnvironmentProvider::getElevationsFoundMissedInfo()
const
387 static const QString info(
"%1/%2 %3% in %4 (all)/%5 (gnd)");
388 const QPair<int, int> foundMissed = this->getElevationsFoundMissed();
389 const int f = foundMissed.first;
390 const int m = foundMissed.second;
391 const double hitRatioPercent = 100.0 *
static_cast<double>(f) /
static_cast<double>(f + m);
397 elvGnd = m_elvCoordinatesGnd.sizeInt();
398 elv = m_elvCoordinates.sizeInt();
403 QPair<qint64, qint64> ISimulationEnvironmentProvider::getElevationRequestTimes()
const
406 return QPair<qint64, qint64>(m_statsCurrentElevRequestTimeMs, m_statsMaxElevRequestTimeMs);
409 QString ISimulationEnvironmentProvider::getElevationRequestTimesInfo()
const
411 if (!this->isElevationProviderEnabled()) {
return QStringLiteral(
"Elevation provider disabled"); }
413 static const QString info(
"%1ms/%2ms");
414 QPair<qint64, qint64> times = this->getElevationRequestTimes();
415 if (times.first < 0 || times.second < 0) {
return QStringLiteral(
"no req. times"); }
416 return info.
arg(times.first).
arg(times.second);
422 return m_simulatorPluginInfo;
427 return this->getSimulatorPluginInfo().getSimulatorInfo();
430 QString ISimulationEnvironmentProvider::getSimulatorNameAndVersion()
const
437 v = m_simulatorVersion;
441 if (!n.
isEmpty()) {
return n; }
445 return QStringLiteral(
"not available");
451 return m_defaultModel;
457 return m_cgsPerCallsign;
463 return m_cgsPerModel;
468 if (callsign.
isEmpty()) {
return CLength::null(); }
470 if (!m_enableCG) {
return CLength::null(); }
471 if (m_cgsPerCallsignOverridden.contains(callsign)) {
return m_cgsPerCallsignOverridden[callsign]; }
472 if (!m_cgsPerCallsign.contains(callsign)) {
return CLength::null(); }
473 return m_cgsPerCallsign.value(callsign);
478 if (callsign.
isEmpty()) {
return CLength::null(); }
480 if (source == CSimulatorSettings::CGFromDBOnly ||
481 (source == CSimulatorSettings::CGFromDBFirst && !dbCG.
isNull()))
485 const CLength simCG = this->getSimulatorCG(callsign);
486 if (source == CSimulatorSettings::CGFromSimulatorOnly ||
487 (source == CSimulatorSettings::CGFromSimulatorFirst && !simCG.
isNull()))
494 CLength ISimulationEnvironmentProvider::getSimulatorCGPerModelString(
const QString &modelString)
const
496 if (modelString.
isEmpty()) {
return CLength::null(); }
499 if (!m_enableCG) {
return CLength::null(); }
500 if (m_cgsPerModelOverridden.contains(ms)) {
return m_cgsPerModelOverridden.value(ms); }
501 if (!m_cgsPerModel.contains(ms)) {
return CLength::null(); }
502 return m_cgsPerModel.value(ms);
505 CLength ISimulationEnvironmentProvider::getSimulatorOrDbCGPerModelString(
const QString &modelString,
508 if (modelString.
isEmpty()) {
return CLength::null(); }
513 if (m_cgsPerModelOverridden.contains(ms)) {
return m_cgsPerModelOverridden.value(ms); }
515 if (source == CSimulatorSettings::CGFromDBOnly ||
516 (!dbCG.
isNull() && source == CSimulatorSettings::CGFromDBFirst))
520 const CLength simCG = this->getSimulatorCGPerModelString(modelString);
521 if (source == CSimulatorSettings::CGFromSimulatorOnly ||
522 (source == CSimulatorSettings::CGFromSimulatorFirst && simCG.
isNull()))
531 if (callsign.
isEmpty()) {
return false; }
533 return m_enableCG && (m_cgsPerCallsign.contains(callsign) || m_cgsPerCallsignOverridden.contains(callsign));
536 bool ISimulationEnvironmentProvider::hasSameSimulatorCG(
const CLength &cg,
const CCallsign &callsign)
const
538 if (callsign.
isEmpty()) {
return false; }
540 if (m_cgsPerCallsignOverridden.contains(callsign)) {
return m_cgsPerCallsignOverridden[callsign] == cg; }
543 if (!m_cgsPerCallsign.contains(callsign)) {
return false; }
544 return m_cgsPerCallsign[callsign] == cg;
547 int ISimulationEnvironmentProvider::setMaxElevationsRemembered(
int max)
550 m_maxElevations = qMax(max, 50);
551 return m_maxElevations;
554 int ISimulationEnvironmentProvider::getMaxElevationsRemembered()
const
557 return m_maxElevations;
560 void ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics()
563 m_statsCurrentElevRequestTimeMs = -1;
564 m_statsMaxElevRequestTimeMs = -1;
565 m_elvFound = m_elvMissed = 0;
572 const int r = m_elvCoordinatesGnd.removeInsideRange(reference, removeRange);
577 const CLength &keptRange,
bool forced)
579 if (reference.
isNull() || keptRange.
isNull()) {
return false; }
580 const CLength r = minRange(keptRange);
583 bool maxReached =
false;
584 bool cleaned =
false;
588 cleanedKeptElvs = m_elvCoordinates;
589 maxReached = m_elvCoordinates.
size() >= m_maxElevations;
591 if (!cleanedKeptElvs.
isEmpty() && (forced || maxReached))
598 m_elvCoordinates = cleanedKeptElvs;
602 cleanedKeptElvs.
clear();
605 cleanedKeptElvs = m_elvCoordinatesGnd;
606 maxReached = m_elvCoordinatesGnd.
size() >= m_maxElevationsGnd;
608 if (!cleanedKeptElvs.
isEmpty() && (forced || maxReached))
615 m_elvCoordinatesGnd = cleanedKeptElvs;
623 : m_simulatorPluginInfo(pluginInfo)
628 bool supportElevation,
bool supportCG)
629 : m_simulatorPluginInfo(pluginInfo), m_settings(settings), m_enableElevation(supportElevation),
630 m_enableCG(supportCG)
642 return m_enableElevation;
648 m_enableCG = enabled;
654 m_enableElevation = enabled;
675 m_simulatorPluginInfo = info;
676 m_settings = settings;
683 m_simulatorName = name;
684 m_simulatorDetails = details;
685 m_simulatorVersion = version;
691 return m_simulatorName;
697 return m_simulatorVersion;
703 return m_simulatorDetails;
709 m_defaultModel = defaultModel;
721 m_elvCoordinates.
clear();
722 m_elvCoordinatesGnd.
clear();
723 m_pendingElevationRequests.
clear();
724 m_statsCurrentElevRequestTimeMs = -1;
725 m_statsMaxElevRequestTimeMs = -1;
726 m_elvFound = m_elvMissed = 0;
732 m_cgsPerCallsign.
clear();
733 m_cgsPerCallsignOverridden.
clear();
746 void CSimulationEnvironmentAware::anchor() {}
752 if (!this->
hasProvider()) {
return CElevationPlane::null(); }
753 return this->
provider()->findClosestElevationWithinRange(reference, range);
759 if (!this->
hasProvider()) {
return CElevationPlane::null(); }
760 return this->
provider()->findClosestElevationWithinRangeOrRequest(reference, range, callsign);
764 const CLength &range,
int minValues,
765 int sufficientValues)
const
767 if (!this->
hasProvider()) {
return CElevationPlane::null(); }
768 return this->
provider()->averageElevationOfOnGroundAircraft(reference, range, minValues, sufficientValues);
773 if (!this->
hasProvider()) {
return CAltitude::null(); }
774 return this->
provider()->highestElevation();
780 return this->
provider()->requestElevation(reference, callsign);
790 if (!this->
hasProvider()) {
return QPair<int, int>(0, 0); }
791 return this->
provider()->getElevationsFoundMissed();
797 return this->
provider()->getElevationsFoundMissedInfo();
802 if (!this->
hasProvider()) {
return QPair<qint64, qint64>(-1, -1); }
803 return this->
provider()->getElevationRequestTimes();
809 return this->
provider()->getElevationRequestTimesInfo();
815 return this->
provider()->getSimulatorPluginInfo();
821 return this->
provider()->getSimulatorInfo();
826 if (!this->
hasProvider()) {
return "not available"; }
827 return this->
provider()->getSimulatorNameAndVersion();
833 return this->
provider()->getDefaultModel();
838 if (!this->
hasProvider()) {
return CLength::null(); }
839 return this->
provider()->getSimulatorCG(callsign);
844 if (!this->
hasProvider()) {
return CLength::null(); }
845 return this->
provider()->getSimulatorOrDbCG(callsign, dbCG);
851 return this->
provider()->hasSimulatorCG(callsign);
858 return this->
provider()->cleanElevationValues(reference, range, forced);
static bool isLocalDeveloperDebugBuild()
Local build for developers.
bool isEmpty() const
Synonym for empty.
Class for emitting a log message.
Derived & debug()
Set the severity to debug.
size_type size() const
Returns number of elements in the sequence.
void truncate(size_type maxSize)
Changes the size of the sequence, if it is bigger than the given size.
void push_back(const T &value)
Appends an element at the end of the sequence.
void clear()
Removes all elements in the sequence.
bool isEmpty() const
Synonym for empty.
bool hasProvider() const
Has provider?
ISimulationEnvironmentProvider * provider()
Provider.
Value object encapsulating information of an aircraft's situation.
const CCallsign & getCallsign() const
Corresponding callsign.
virtual bool isNull() const
Null situation.
Altitude as used in aviation, can be AGL or MSL altitude.
Value object encapsulating information of a callsign.
const QString & asString() const
Get callsign (normalized)
bool isEmpty() const
Is empty?
Value object for a set of callsigns.
virtual const aviation::CAltitude & geodeticHeight() const
Height, ellipsoidal or geodetic height (used in GPS)
virtual bool isNull() const
Is null?
Value object encapsulating a list of coordinates.
CElevationPlane averageGeodeticHeight(const CCoordinateGeodetic &reference, const physical_quantities::CLength &range, const physical_quantities::CLength &maxDeviation=physical_quantities::CLength(1.0, physical_quantities::CLengthUnit::m()), int minValues=3, int sufficentValues=5) const
Average height within range and having an height.
Plane of same elevation, can be a single point or larger area (e.g. airport)
virtual bool isNull() const
Existing value?
const physical_quantities::CLength & getRadius() const
Radius.
Geodetic coordinate, a position in 3D space relative to the reference geoid.
bool hasMSLGeodeticHeight() const
Geodetic height not null and aviation::CAltitude::MeanSeaLevel.
virtual const aviation::CAltitude & geodeticHeight() const =0
Height, ellipsoidal or geodetic height (used in GPS)
void sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate)
Sort by distance.
OBJ findFirstWithinRangeOrDefault(const ICoordinateGeodetic &coordinate, const physical_quantities::CLength &range) const
Find first in range.
OBJ findClosestWithinRange(const ICoordinateGeodetic &coordinate, const physical_quantities::CLength &range) const
Find closest within range to the given coordinate.
int removeOutsideRange(const ICoordinateGeodetic &coordinate, const physical_quantities::CLength &range)
Remove outside range.
aviation::CAltitude findMaxHeight() const
Find min/max/average height.
QString toQString(bool i18n=false) const
Cast as QString.
Physical unit length (length)
bool isNull() const
Is quantity null?
double value(MU unit) const
Value in given unit.
QString valueRoundedAsString(MU unit, int digits=-1) const
Rounded value in given unit.
Aircraft model (used by another pilot, my models on disk)
QPair< qint64, qint64 > getElevationRequestTimes() const
The elevation request times.
CAircraftModel getDefaultModel() const
Default model.
bool requestElevation(const geo::ICoordinateGeodetic &reference, const aviation::CCallsign &callsign)
Request elevation, there is no guarantee the requested elevation will be available in the provider.
physical_quantities::CLength getSimulatorOrDbCG(const aviation::CCallsign &callsign, const physical_quantities::CLength &dbCG) const
Get CG per callsign, NULL if not found.
QString getElevationRequestTimesInfo() const
Elevation request times.
aviation::CAltitude highestElevation() const
Highest elevation.
bool cleanElevationValues(const aviation::CAircraftSituation &reference, const physical_quantities::CLength &range, bool forced=false)
Remove cached elevations outside range, "forced" cleans always, otherwise only if max....
QString getSimulatorNameAndVersion() const
Version and simulator details info.
geo::CElevationPlane averageElevationOfOnGroundAircraft(const aviation::CAircraftSituation &reference, const physical_quantities::CLength &range, int minValues, int sufficientValues) const
Average elevation of "on ground" cached values.
physical_quantities::CLength getSimulatorCG(const aviation::CCallsign &callsign) const
Get CG per callsign, NULL if not found.
geo::CElevationPlane findClosestElevationWithinRangeOrRequest(const geo::ICoordinateGeodetic &reference, const physical_quantities::CLength &range, const aviation::CCallsign &callsign)
Find closest elevation or request elevation.
geo::CElevationPlane findClosestElevationWithinRange(const geo::ICoordinateGeodetic &reference, const physical_quantities::CLength &range) const
Find closest elevation (or return NULL)
QString getElevationsFoundMissedInfo() const
Elevations found/missed statistics info as string.
QPair< int, int > getElevationsFoundMissed() const
Elevations found/missed statistics.
CSimulatorInfo getSimulatorInfo() const
Get the represented plugin.
bool hasSimulatorCG(const aviation::CCallsign &callsign) const
Has a CG?
CSimulatorPluginInfo getSimulatorPluginInfo() const
Get the represented plugin.
Simple hardcoded info about the corresponding simulator.
bool isUnspecified() const
Unspecified simulator.
Describing a simulator plugin.
void setSimulationProviderEnabled(bool elvEnabled, bool cgEnabled)
Provider enabled.
void resetSimulationEnvironmentStatistics()
Reset statistics.
void clearElevations()
Clear elevations.
bool isElevationProviderEnabled() const
Provider enabled.
void setNewPluginInfo(const CSimulatorPluginInfo &info, const settings::CSimulatorSettings &settings, const CAircraftModel &defaultModel)
New plugin info and default model.
void setCgProviderEnabled(bool enabled)
Provider enabled.
void setSimulatorDetails(const QString &name, const QString &details, const QString &version)
Set version and simulator details from running simulator.
bool isCgProviderEnabled() const
Provider enabled.
void clearCGs()
Clear CGs.
QString getSimulatorVersion() const
Simulator version as set from the running simulator.
void setElevationProviderEnabled(bool enabled)
Provider enabled.
void clearDefaultModel()
Clear default model.
ISimulationEnvironmentProvider(const CSimulatorPluginInfo &pluginInfo)
Ctor.
void clearSimulationEnvironmentData()
Clear data.
QString getSimulatorName() const
Simulator name as set from the running simulator.
void setDefaultModel(const CAircraftModel &defaultModel)
Default model.
QString getSimulatorDetails() const
Simulator details as set from the running simulator.
Settings for simulator Driver independent parts (such as directories), also used in model loaders.
CGSource
Where we get the CG (aka vertical offset) from.
Free functions in swift::misc.
qint64 currentMSecsSinceEpoch()
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString toUpper() const const
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.