swift
simulationenvironmentprovider.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QStringBuilder>
7 
8 #include "config/buildconfig.h"
10 #include "misc/logmessage.h"
11 #include "misc/verify.h"
12 
13 using namespace swift::config;
14 using namespace swift::misc;
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;
19 
20 namespace swift::misc::simulation
21 {
22  bool ISimulationEnvironmentProvider::rememberGroundElevation(const CCallsign &requestedForCallsign,
23  bool likelyOnGroundElevation,
24  const ICoordinateGeodetic &elevationCoordinate,
25  const CLength &epsilon)
26  {
27  if (!elevationCoordinate.hasMSLGeodeticHeight())
28  {
29  SWIFT_AUDIT_X(false, Q_FUNC_INFO, "Elevation needs to be MSL NON NULL");
30  return false;
31  }
32 
33  const CLength minRange = ISimulationEnvironmentProvider::minRange(epsilon);
34  const double elvFt = elevationCoordinate.geodeticHeight().value(CLengthUnit::ft());
35 
36  CCoordinateGeodetic alreadyInRange;
37  CCoordinateGeodetic alreadyInRangeGnd;
38  {
39  QReadLocker l(&m_lockElvCoordinates);
40  if (!m_enableElevation) { return false; }
41 
42  // check if we have already an elevation within range
43  alreadyInRangeGnd = m_elvCoordinatesGnd.findFirstWithinRangeOrDefault(elevationCoordinate, minRange);
44  alreadyInRange = m_elvCoordinates.findFirstWithinRangeOrDefault(elevationCoordinate, minRange);
45  }
46 
47  constexpr double maxDistFt = 30.0;
48 
49  // here we deal with gnd situation and do not expect a lot of variance
50  if (!alreadyInRangeGnd.isNull())
51  {
52  // found
53  const double distFt = qAbs(alreadyInRangeGnd.geodeticHeight().value(CLengthUnit::ft()) - elvFt);
54  if (distFt > maxDistFt)
55  {
56  // such a huge distance to existing value
57  CLogMessage(this).debug(u"Suspicious GND elevation distance '%1': %2ft at %3")
58  << requestedForCallsign.asString() << distFt
59  << elevationCoordinate.geodeticHeight().valueRoundedAsString(CLengthUnit::ft(), 1);
61  "Suspicious gnd. elevation distance");
62  }
63  return false;
64  }
65 
66  // here we deal with all kind of values, so it can be that
67  // values vary in a much larger range
68  if (!alreadyInRange.isNull())
69  {
70  // found
71  const double distFt = qAbs(alreadyInRange.geodeticHeight().value(CLengthUnit::ft()) - elvFt);
72  if (distFt > maxDistFt)
73  {
74  // such a huge distance to existing value
75  CLogMessage(this).debug(u"Suspicious NON GND elevation distance for '%1': %2ft at %3")
76  << requestedForCallsign.asString() << distFt
77  << elevationCoordinate.geodeticHeight().valueRoundedAsString(CLengthUnit::ft(), 1);
78  // SWIFT_AUDIT_X(!CBuildConfig::isLocalDeveloperDebugBuild(), Q_FUNC_INFO, "Suspicious elevation
79  // distance");
80  }
81  return false;
82  }
83 
84  const qint64 now = QDateTime::currentMSecsSinceEpoch();
85  {
86  // we keep latest at front
87  // * we assume we find them faster
88  // * and need them more frequently (the recent ones)
89  QWriteLocker l(&m_lockElvCoordinates);
90  if (likelyOnGroundElevation)
91  {
92  if (m_elvCoordinatesGnd.size() > m_maxElevationsGnd) { m_elvCoordinatesGnd.pop_back(); }
93  m_elvCoordinatesGnd.push_front(elevationCoordinate);
94  }
95  else
96  {
97  if (m_elvCoordinates.size() > m_maxElevations) { m_elvCoordinates.pop_back(); }
98  m_elvCoordinates.push_front(elevationCoordinate);
99  }
100 
101  // statistics
102  if (m_pendingElevationRequests.contains(requestedForCallsign))
103  {
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; }
109  }
110  }
111  return true;
112  }
113 
114  bool ISimulationEnvironmentProvider::rememberGroundElevation(const CCallsign &requestedForCallsign,
115  bool likelyOnGroundElevation,
116  const CElevationPlane &elevationPlane)
117  {
118  if (!elevationPlane.hasMSLGeodeticHeight())
119  {
120  SWIFT_AUDIT_X(false, Q_FUNC_INFO, "Elevation plane needs to be MSL NON NULL");
121  return false;
122  }
123  return this->rememberGroundElevation(requestedForCallsign, likelyOnGroundElevation, elevationPlane,
124  elevationPlane.getRadius());
125  }
126 
127  bool ISimulationEnvironmentProvider::insertCG(const CLength &cg, const CCallsign &cs)
128  {
129  if (cs.isEmpty()) { return false; }
130 
131  const bool remove = cg.isNull();
132  {
133  QWriteLocker l(&m_lockCG);
134  if (remove) { m_cgsPerCallsign.remove(cs); }
135  else { m_cgsPerCallsign[cs] = cg; }
136  }
137  return true;
138  }
139 
140  bool ISimulationEnvironmentProvider::insertCG(const CLength &cg, const QString &modelString, const CCallsign &cs)
141  {
142  bool stored = false;
143  QWriteLocker l(&m_lockCG);
144  if (!m_enableCG) { return false; }
145  if (!cs.isEmpty())
146  {
147  if (m_cgsPerCallsignOverridden.contains(cs))
148  {
149  // only keep as overridden value
150  m_cgsPerCallsignOverridden[cs] = cg;
151  }
152  else
153  {
154  m_cgsPerCallsign[cs] = cg;
155  stored = true;
156  }
157  }
158  if (!modelString.isEmpty())
159  {
160  const QString ms = modelString.toUpper();
161  if (m_cgsPerModelOverridden.contains(ms))
162  {
163  // only keep as overridden value
164  m_cgsPerModelOverridden[ms] = cg;
165  }
166  else
167  {
168  m_cgsPerModel[ms] = cg;
169  stored = true;
170  }
171  }
172  return stored;
173  }
174 
175  bool ISimulationEnvironmentProvider::insertCGOverridden(const CLength &cg, const CCallsign &cs)
176  {
177  if (cs.isEmpty()) { return false; }
178 
179  QWriteLocker l(&m_lockCG);
180  if (!m_enableCG) { return false; }
181  if (cg.isNull())
182  {
183  m_cgsPerCallsignOverridden.remove(cs);
184  return false;
185  }
186 
187  m_cgsPerCallsignOverridden[cs] = cg;
188  return true;
189  }
190 
191  bool ISimulationEnvironmentProvider::insertCGOverridden(const CLength &cg, const CCallsignSet &callsigns)
192  {
193  if (callsigns.isEmpty()) { return false; }
194 
195  QWriteLocker l(&m_lockCG);
196  if (!m_enableCG) { return false; }
197  for (const CCallsign &cs : callsigns)
198  {
199  if (cg.isNull()) { m_cgsPerCallsignOverridden.remove(cs); }
200  else { m_cgsPerCallsignOverridden[cs] = cg; }
201  }
202  return true;
203  }
204 
205  bool ISimulationEnvironmentProvider::insertCGForModelString(const CLength &cg, const QString &modelString)
206  {
207  if (modelString.isEmpty()) { return false; }
208 
209  QWriteLocker l(&m_lockCG);
210  if (!m_enableCG) { return false; }
211  if (cg.isNull())
212  {
213  m_cgsPerModel.remove(modelString.toUpper());
214  return false;
215  }
216 
217  m_cgsPerModel[modelString.toUpper()] = cg;
218  return true;
219  }
220 
221  bool ISimulationEnvironmentProvider::insertCGForModelStringOverridden(const CLength &cg, const QString &modelString)
222  {
223  if (modelString.isEmpty()) { return false; }
224  QWriteLocker l(&m_lockCG);
225  if (cg.isNull())
226  {
227  m_cgsPerModelOverridden.remove(modelString.toUpper());
228  return false;
229  }
230  m_cgsPerModelOverridden[modelString.toUpper()] = cg;
231  return true;
232  }
233 
234  CLengthPerCallsign ISimulationEnvironmentProvider::clearCGOverrides()
235  {
236  QWriteLocker l(&m_lockCG);
237  m_cgsPerModelOverridden.clear();
238  m_cgsPerCallsignOverridden.clear();
239  return m_cgsPerCallsign; // all remaining CGs
240  }
241 
242  CLength ISimulationEnvironmentProvider::overriddenCGorDefault(const CLength &defaultCG,
243  const QString &modelString) const
244  {
245  if (modelString.isEmpty()) { return defaultCG; }
246  const QString ms = modelString.toUpper();
247  QReadLocker l(&m_lockCG);
248  if (!m_cgsPerModelOverridden.contains(ms)) { return defaultCG; }
249  return m_cgsPerModelOverridden[ms];
250  }
251 
252  int ISimulationEnvironmentProvider::removeSimulatorCG(const CCallsign &cs)
253  {
254  if (cs.isEmpty()) { return 0; }
255 
256  QWriteLocker l(&m_lockCG);
257  m_cgsPerCallsignOverridden.remove(cs);
258  return m_cgsPerCallsign.remove(cs);
259  }
260 
261  void ISimulationEnvironmentProvider::removePendingElevationRequest(const CCallsign &cs)
262  {
263  QWriteLocker l(&m_lockElvCoordinates);
264  m_pendingElevationRequests.remove(cs);
265  }
266 
267  CLength ISimulationEnvironmentProvider::minRange(const CLength &range)
268  {
269  return (range.isNull() || range < CElevationPlane::singlePointRadius()) ? CElevationPlane::singlePointRadius() :
270  range;
271  }
272 
273  CCoordinateGeodeticList ISimulationEnvironmentProvider::getAllElevationCoordinates() const
274  {
275  QReadLocker l(&m_lockElvCoordinates);
276  CCoordinateGeodeticList cl(m_elvCoordinatesGnd);
277  cl.push_back(m_elvCoordinates);
278  return cl;
279  }
280 
281  CCoordinateGeodeticList ISimulationEnvironmentProvider::getElevationCoordinatesOnGround() const
282  {
283  QReadLocker l(&m_lockElvCoordinates);
284  return m_elvCoordinatesGnd;
285  }
286 
287  CElevationPlane ISimulationEnvironmentProvider::averageElevationOfOnGroundAircraft(
288  const CAircraftSituation &reference, const CLength &range, int minValues, int sufficientValues) const
289  {
290  const CCoordinateGeodeticList coordinates = this->getElevationCoordinatesOnGround();
291  return coordinates.averageGeodeticHeight(reference, range, CAircraftSituation::allowedAltitudeDeviation(),
292  minValues, sufficientValues);
293  }
294 
295  CAltitude ISimulationEnvironmentProvider::highestElevation() const
296  {
297  const CCoordinateGeodeticList coordinates = this->getElevationCoordinatesOnGround();
298  if (coordinates.isEmpty()) { return CAltitude::null(); }
299  return coordinates.findMaxHeight();
300  }
301 
302  CCoordinateGeodeticList ISimulationEnvironmentProvider::getAllElevationCoordinates(int &maxRemembered) const
303  {
304  QReadLocker l(&m_lockElvCoordinates);
305  maxRemembered = m_maxElevations;
306  CCoordinateGeodeticList cl(m_elvCoordinatesGnd);
307  cl.push_back(m_elvCoordinates);
308  return cl;
309  }
310 
311  int ISimulationEnvironmentProvider::cleanUpElevations(const ICoordinateGeodetic &referenceCoordinate, int maxNumber)
312  {
313  int currentMax;
314  CCoordinateGeodeticList coordinates(this->getAllElevationCoordinates(currentMax));
315  if (maxNumber < 0) { maxNumber = currentMax; }
316  const int size = coordinates.size();
317  if (size <= maxNumber) { return 0; }
318  coordinates.sortByEuclideanDistanceSquared(referenceCoordinate);
319  coordinates.truncate(maxNumber);
320  const int delta = size - coordinates.size();
321  {
322  QWriteLocker l(&m_lockElvCoordinates);
323  m_elvCoordinates = coordinates;
324  }
325  return delta;
326  }
327 
329  ISimulationEnvironmentProvider::findClosestElevationWithinRange(const ICoordinateGeodetic &reference,
330  const CLength &range) const
331  {
332  if (!this->isElevationProviderEnabled()) { return CElevationPlane::null(); }
333 
334  // for single point we use a slightly optimized version
335  const bool singlePoint = (&range == &CElevationPlane::singlePointRadius() || range.isNull() ||
336  range <= CElevationPlane::singlePointRadius());
337  const CCoordinateGeodeticList elevations = this->getAllElevationCoordinates();
338  const CCoordinateGeodetic coordinate =
339  singlePoint ? elevations.findFirstWithinRangeOrDefault(reference, CElevationPlane::singlePointRadius()) :
340  elevations.findClosestWithinRange(reference, range);
341  const bool found = !coordinate.isNull();
342 
343  {
344  QWriteLocker l { &m_lockElvCoordinates };
345  if (found)
346  {
347  m_elvFound++;
348  return CElevationPlane(coordinate, reference); // plane with radius = distance to reference
349  }
350  else
351  {
352  m_elvMissed++;
353  return CElevationPlane::null();
354  }
355  }
356  }
357 
358  CElevationPlane ISimulationEnvironmentProvider::findClosestElevationWithinRangeOrRequest(
359  const ICoordinateGeodetic &reference, const CLength &range, const CCallsign &callsign)
360  {
361  if (!this->isElevationProviderEnabled()) { return CElevationPlane::null(); }
362  const CElevationPlane ep = ISimulationEnvironmentProvider::findClosestElevationWithinRange(reference, range);
363  if (ep.isNull())
364  {
365  const qint64 now = QDateTime::currentMSecsSinceEpoch();
366  this->requestElevation(reference, callsign);
367  QWriteLocker l(&m_lockElvCoordinates);
368  m_pendingElevationRequests[callsign] = now;
369  }
370  return ep;
371  }
372 
373  bool ISimulationEnvironmentProvider::requestElevationBySituation(const CAircraftSituation &situation)
374  {
375  if (!this->isElevationProviderEnabled()) { return false; }
376  return this->requestElevation(situation, situation.getCallsign());
377  }
378 
379  QPair<int, int> ISimulationEnvironmentProvider::getElevationsFoundMissed() const
380  {
381  QReadLocker l(&m_lockElvCoordinates);
382  return QPair<int, int>(m_elvFound, m_elvMissed);
383  }
384 
385  QString ISimulationEnvironmentProvider::getElevationsFoundMissedInfo() const
386  {
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);
392 
393  int elvGnd;
394  int elv;
395  {
396  QReadLocker l(&m_lockElvCoordinates);
397  elvGnd = m_elvCoordinatesGnd.sizeInt();
398  elv = m_elvCoordinates.sizeInt();
399  }
400  return info.arg(f).arg(m).arg(QString::number(hitRatioPercent, 'f', 1)).arg(elv).arg(elvGnd);
401  }
402 
403  QPair<qint64, qint64> ISimulationEnvironmentProvider::getElevationRequestTimes() const
404  {
405  QReadLocker l(&m_lockElvCoordinates);
406  return QPair<qint64, qint64>(m_statsCurrentElevRequestTimeMs, m_statsMaxElevRequestTimeMs);
407  }
408 
409  QString ISimulationEnvironmentProvider::getElevationRequestTimesInfo() const
410  {
411  if (!this->isElevationProviderEnabled()) { return QStringLiteral("Elevation provider disabled"); }
412 
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);
417  }
418 
419  CSimulatorPluginInfo ISimulationEnvironmentProvider::getSimulatorPluginInfo() const
420  {
421  QReadLocker l(&m_lockSimInfo);
422  return m_simulatorPluginInfo;
423  }
424 
425  CSimulatorInfo ISimulationEnvironmentProvider::getSimulatorInfo() const
426  {
427  return this->getSimulatorPluginInfo().getSimulatorInfo();
428  }
429 
430  QString ISimulationEnvironmentProvider::getSimulatorNameAndVersion() const
431  {
432  QString n;
433  QString v;
434  {
435  QReadLocker l(&m_lockSimInfo);
436  n = m_simulatorName;
437  v = m_simulatorVersion;
438  }
439 
440  if (!n.isEmpty() && !v.isEmpty()) { return n % u' ' % v; }
441  if (!n.isEmpty()) { return n; }
442 
443  const CSimulatorInfo simInfo = this->getSimulatorInfo();
444  if (!simInfo.isUnspecified()) { return simInfo.toQString(true); }
445  return QStringLiteral("not available");
446  }
447 
448  CAircraftModel ISimulationEnvironmentProvider::getDefaultModel() const
449  {
450  QReadLocker l(&m_lockModel);
451  return m_defaultModel;
452  }
453 
454  CLengthPerCallsign ISimulationEnvironmentProvider::getSimulatorCGsPerCallsign() const
455  {
456  QReadLocker l(&m_lockCG);
457  return m_cgsPerCallsign;
458  }
459 
460  QHash<QString, CLength> ISimulationEnvironmentProvider::getSimulatorCGsPerModelString() const
461  {
462  QReadLocker l(&m_lockCG);
463  return m_cgsPerModel;
464  }
465 
466  CLength ISimulationEnvironmentProvider::getSimulatorCG(const aviation::CCallsign &callsign) const
467  {
468  if (callsign.isEmpty()) { return CLength::null(); }
469  QReadLocker l(&m_lockCG);
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);
474  }
475 
476  CLength ISimulationEnvironmentProvider::getSimulatorOrDbCG(const CCallsign &callsign, const CLength &dbCG) const
477  {
478  if (callsign.isEmpty()) { return CLength::null(); }
479  const CSimulatorSettings::CGSource source = m_settings.getCGSource();
480  if (source == CSimulatorSettings::CGFromDBOnly ||
481  (source == CSimulatorSettings::CGFromDBFirst && !dbCG.isNull()))
482  {
483  return dbCG;
484  }
485  const CLength simCG = this->getSimulatorCG(callsign);
486  if (source == CSimulatorSettings::CGFromSimulatorOnly ||
487  (source == CSimulatorSettings::CGFromSimulatorFirst && !simCG.isNull()))
488  {
489  return simCG;
490  }
491  return dbCG;
492  }
493 
494  CLength ISimulationEnvironmentProvider::getSimulatorCGPerModelString(const QString &modelString) const
495  {
496  if (modelString.isEmpty()) { return CLength::null(); }
497  const QString ms = modelString.toUpper();
498  QReadLocker l(&m_lockCG);
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);
503  }
504 
505  CLength ISimulationEnvironmentProvider::getSimulatorOrDbCGPerModelString(const QString &modelString,
506  const CLength &dbCG) const
507  {
508  if (modelString.isEmpty()) { return CLength::null(); }
509  const CSimulatorSettings::CGSource source = m_settings.getCGSource();
510  const QString ms = modelString.toUpper();
511  {
512  QReadLocker l(&m_lockCG);
513  if (m_cgsPerModelOverridden.contains(ms)) { return m_cgsPerModelOverridden.value(ms); }
514  }
515  if (source == CSimulatorSettings::CGFromDBOnly ||
516  (!dbCG.isNull() && source == CSimulatorSettings::CGFromDBFirst))
517  {
518  return dbCG;
519  }
520  const CLength simCG = this->getSimulatorCGPerModelString(modelString);
521  if (source == CSimulatorSettings::CGFromSimulatorOnly ||
522  (source == CSimulatorSettings::CGFromSimulatorFirst && simCG.isNull()))
523  {
524  return simCG;
525  }
526  return dbCG;
527  }
528 
529  bool ISimulationEnvironmentProvider::hasSimulatorCG(const aviation::CCallsign &callsign) const
530  {
531  if (callsign.isEmpty()) { return false; }
532  QReadLocker l(&m_lockCG);
533  return m_enableCG && (m_cgsPerCallsign.contains(callsign) || m_cgsPerCallsignOverridden.contains(callsign));
534  }
535 
536  bool ISimulationEnvironmentProvider::hasSameSimulatorCG(const CLength &cg, const CCallsign &callsign) const
537  {
538  if (callsign.isEmpty()) { return false; }
539  QReadLocker l(&m_lockCG);
540  if (m_cgsPerCallsignOverridden.contains(callsign)) { return m_cgsPerCallsignOverridden[callsign] == cg; }
541 
542  // normal values
543  if (!m_cgsPerCallsign.contains(callsign)) { return false; }
544  return m_cgsPerCallsign[callsign] == cg;
545  }
546 
547  int ISimulationEnvironmentProvider::setMaxElevationsRemembered(int max)
548  {
549  QWriteLocker l(&m_lockElvCoordinates);
550  m_maxElevations = qMax(max, 50);
551  return m_maxElevations;
552  }
553 
554  int ISimulationEnvironmentProvider::getMaxElevationsRemembered() const
555  {
556  QReadLocker l(&m_lockElvCoordinates);
557  return m_maxElevations;
558  }
559 
560  void ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics()
561  {
562  QWriteLocker l(&m_lockElvCoordinates);
563  m_statsCurrentElevRequestTimeMs = -1;
564  m_statsMaxElevRequestTimeMs = -1;
565  m_elvFound = m_elvMissed = 0;
566  }
567 
568  int ISimulationEnvironmentProvider::removeElevationValues(const CAircraftSituation &reference,
569  const CLength &removeRange)
570  {
571  QWriteLocker l(&m_lockElvCoordinates);
572  const int r = m_elvCoordinatesGnd.removeInsideRange(reference, removeRange);
573  return r;
574  }
575 
576  bool ISimulationEnvironmentProvider::cleanElevationValues(const CAircraftSituation &reference,
577  const CLength &keptRange, bool forced)
578  {
579  if (reference.isNull() || keptRange.isNull()) { return false; }
580  const CLength r = minRange(keptRange);
581 
582  CCoordinateGeodeticList cleanedKeptElvs;
583  bool maxReached = false;
584  bool cleaned = false;
585 
586  {
587  QReadLocker l(&m_lockElvCoordinates);
588  cleanedKeptElvs = m_elvCoordinates;
589  maxReached = m_elvCoordinates.size() >= m_maxElevations;
590  }
591  if (!cleanedKeptElvs.isEmpty() && (forced || maxReached))
592  {
593  const int removed = cleanedKeptElvs.removeOutsideRange(reference, r);
594  if (removed > 0)
595  {
596  cleaned = true;
597  QWriteLocker l(&m_lockElvCoordinates);
598  m_elvCoordinates = cleanedKeptElvs;
599  }
600  }
601 
602  cleanedKeptElvs.clear();
603  {
604  QReadLocker l(&m_lockElvCoordinates);
605  cleanedKeptElvs = m_elvCoordinatesGnd;
606  maxReached = m_elvCoordinatesGnd.size() >= m_maxElevationsGnd;
607  }
608  if (!cleanedKeptElvs.isEmpty() && (forced || maxReached))
609  {
610  const int removed = cleanedKeptElvs.removeOutsideRange(reference, r);
611  if (removed > 0)
612  {
613  cleaned = true;
614  QWriteLocker l(&m_lockElvCoordinates);
615  m_elvCoordinatesGnd = cleanedKeptElvs;
616  }
617  }
618 
619  return cleaned;
620  }
621 
622  ISimulationEnvironmentProvider::ISimulationEnvironmentProvider(const CSimulatorPluginInfo &pluginInfo)
623  : m_simulatorPluginInfo(pluginInfo)
624  {}
625 
627  const CSimulatorSettings &settings,
628  bool supportElevation, bool supportCG)
629  : m_simulatorPluginInfo(pluginInfo), m_settings(settings), m_enableElevation(supportElevation),
630  m_enableCG(supportCG)
631  {}
632 
634  {
635  QReadLocker l(&m_lockCG);
636  return m_enableCG;
637  }
638 
640  {
641  QReadLocker l(&m_lockElvCoordinates);
642  return m_enableElevation;
643  }
644 
646  {
647  QWriteLocker l(&m_lockCG);
648  m_enableCG = enabled;
649  }
650 
652  {
653  QWriteLocker l(&m_lockElvCoordinates);
654  m_enableElevation = enabled;
655  }
656 
658  {
659  setElevationProviderEnabled(elvEnabled);
660  setCgProviderEnabled(cgEnabled);
661  }
662 
664  const CSimulatorSettings &settings,
665  const CAircraftModel &defaultModel)
666  {
667  this->setNewPluginInfo(info, settings);
668  this->setDefaultModel(defaultModel);
669  }
670 
672  const CSimulatorSettings &settings)
673  {
674  QWriteLocker l(&m_lockSimInfo);
675  m_simulatorPluginInfo = info;
676  m_settings = settings;
677  }
678 
679  void ISimulationEnvironmentProvider::setSimulatorDetails(const QString &name, const QString &details,
680  const QString &version)
681  {
682  QWriteLocker l(&m_lockSimInfo);
683  m_simulatorName = name;
684  m_simulatorDetails = details;
685  m_simulatorVersion = version;
686  }
687 
689  {
690  QReadLocker l(&m_lockSimInfo);
691  return m_simulatorName;
692  }
693 
695  {
696  QReadLocker l(&m_lockSimInfo);
697  return m_simulatorVersion;
698  }
699 
701  {
702  QReadLocker l(&m_lockSimInfo);
703  return m_simulatorDetails;
704  }
705 
707  {
708  QWriteLocker l(&m_lockModel);
709  m_defaultModel = defaultModel;
710  }
711 
713  {
714  QWriteLocker l(&m_lockModel);
715  m_defaultModel = CAircraftModel();
716  }
717 
719  {
720  QWriteLocker l(&m_lockElvCoordinates);
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;
727  }
728 
730  {
731  QWriteLocker l(&m_lockCG);
732  m_cgsPerCallsign.clear();
733  m_cgsPerCallsignOverridden.clear();
734  // intentionally not cleaning CGs per model, as models will not change, callsign do!
735  }
736 
738  {
739  this->clearDefaultModel();
740  this->clearElevations();
741  this->clearCGs();
743  }
744 
745  // pin vtables to this file
746  void CSimulationEnvironmentAware::anchor() {}
747 
750  const physical_quantities::CLength &range) const
751  {
752  if (!this->hasProvider()) { return CElevationPlane::null(); }
753  return this->provider()->findClosestElevationWithinRange(reference, range);
754  }
755 
757  const ICoordinateGeodetic &reference, const CLength &range, const CCallsign &callsign)
758  {
759  if (!this->hasProvider()) { return CElevationPlane::null(); }
760  return this->provider()->findClosestElevationWithinRangeOrRequest(reference, range, callsign);
761  }
762 
764  const CLength &range, int minValues,
765  int sufficientValues) const
766  {
767  if (!this->hasProvider()) { return CElevationPlane::null(); }
768  return this->provider()->averageElevationOfOnGroundAircraft(reference, range, minValues, sufficientValues);
769  }
770 
772  {
773  if (!this->hasProvider()) { return CAltitude::null(); }
774  return this->provider()->highestElevation();
775  }
776 
778  {
779  if (!this->hasProvider()) { return false; }
780  return this->provider()->requestElevation(reference, callsign);
781  }
782 
784  {
785  return this->requestElevation(situation, situation.getCallsign());
786  }
787 
789  {
790  if (!this->hasProvider()) { return QPair<int, int>(0, 0); }
791  return this->provider()->getElevationsFoundMissed();
792  }
793 
795  {
796  if (!this->hasProvider()) { return QString(); }
797  return this->provider()->getElevationsFoundMissedInfo();
798  }
799 
801  {
802  if (!this->hasProvider()) { return QPair<qint64, qint64>(-1, -1); }
803  return this->provider()->getElevationRequestTimes();
804  }
805 
807  {
808  if (!this->hasProvider()) { return QString(); }
809  return this->provider()->getElevationRequestTimesInfo();
810  }
811 
813  {
814  if (!this->hasProvider()) { return CSimulatorPluginInfo(); }
815  return this->provider()->getSimulatorPluginInfo();
816  }
817 
819  {
820  if (!this->hasProvider()) { return CSimulatorInfo(); }
821  return this->provider()->getSimulatorInfo();
822  }
823 
825  {
826  if (!this->hasProvider()) { return "not available"; }
827  return this->provider()->getSimulatorNameAndVersion();
828  }
829 
831  {
832  if (!this->hasProvider()) { return CAircraftModel(); }
833  return this->provider()->getDefaultModel();
834  }
835 
837  {
838  if (!this->hasProvider()) { return CLength::null(); }
839  return this->provider()->getSimulatorCG(callsign);
840  }
841 
843  {
844  if (!this->hasProvider()) { return CLength::null(); }
845  return this->provider()->getSimulatorOrDbCG(callsign, dbCG);
846  }
847 
849  {
850  if (!this->hasProvider()) { return false; }
851  return this->provider()->hasSimulatorCG(callsign);
852  }
853 
855  bool forced)
856  {
857  if (!this->hasProvider()) { return false; }
858  return this->provider()->cleanElevationValues(reference, range, forced);
859  }
860 } // namespace swift::misc::simulation
static bool isLocalDeveloperDebugBuild()
Local build for developers.
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
Class for emitting a log message.
Definition: logmessage.h:27
Derived & debug()
Set the severity to debug.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void truncate(size_type maxSize)
Changes the size of the sequence, if it is bigger than the given size.
Definition: sequence.h:291
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
ISimulationEnvironmentProvider * provider()
Provider.
Definition: provider.h:72
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.
Definition: altitude.h:52
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Value object for a set of callsigns.
Definition: callsignset.h:26
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.
Definition: geoobjectlist.h:50
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.
Definition: mixinstring.h:76
Physical unit length (length)
Definition: length.h:18
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)
Definition: aircraftmodel.h:71
QPair< qint64, qint64 > getElevationRequestTimes() const
The elevation request times.
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.
Definition: simulatorinfo.h:41
bool isUnspecified() const
Unspecified simulator.
void setSimulationProviderEnabled(bool elvEnabled, bool cgEnabled)
Provider enabled.
void setNewPluginInfo(const CSimulatorPluginInfo &info, const settings::CSimulatorSettings &settings, const CAircraftModel &defaultModel)
New plugin info and default model.
void setSimulatorDetails(const QString &name, const QString &details, const QString &version)
Set version and simulator details from running simulator.
QString getSimulatorVersion() const
Simulator version as set from the running simulator.
ISimulationEnvironmentProvider(const CSimulatorPluginInfo &pluginInfo)
Ctor.
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.
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.
Definition: verify.h:38