swift
interpolator.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDateTime>
7 #include <QStringBuilder>
8 #include <QTimer>
9 
10 #include "config/buildconfig.h"
13 #include "misc/aviation/callsign.h"
14 #include "misc/aviation/heading.h"
15 #include "misc/logmessage.h"
16 #include "misc/network/fsdsetup.h"
17 #include "misc/pq/angle.h"
18 #include "misc/pq/length.h"
19 #include "misc/pq/speed.h"
20 #include "misc/pq/units.h"
24 #include "misc/stringutils.h"
25 #include "misc/verify.h"
26 
27 using namespace swift::config;
28 using namespace swift::misc::aviation;
29 using namespace swift::misc::geo;
30 using namespace swift::misc::math;
31 using namespace swift::misc::network;
32 using namespace swift::misc::physical_quantities;
33 
34 namespace swift::misc::simulation
35 {
36  CInterpolator::CInterpolator(const CCallsign &callsign, ISimulationEnvironmentProvider *simEnvProvider,
37  IInterpolationSetupProvider *setupProvider, IRemoteAircraftProvider *remoteProvider,
38  CInterpolationLogger *logger)
39  : m_callsign(callsign)
40  {
41  // normally when created m_cg is still null since there is no CG in the provider yet
42 
43  if (simEnvProvider) { this->setSimulationEnvironmentProvider(simEnvProvider); }
44  if (setupProvider) { this->setInterpolationSetupProvider(setupProvider); }
45  if (remoteProvider)
46  {
47  this->setRemoteAircraftProvider(remoteProvider);
48  QObject::connect(&m_initTimer, &QTimer::timeout, [=] { this->deferredInit(); });
49  m_initTimer.setSingleShot(true);
50  m_initTimer.start(2500);
51  }
52  this->attachLogger(logger);
53  }
54 
55  CLength CInterpolator::getAndFetchModelCG(const CLength &dbCG)
56  {
57  CLength cgDb = dbCG;
58  if (cgDb.isNull())
59  {
60  // no input DB value, try to find one
62  CLength::null();
63  }
64  else if (this->getRemoteAircraftProvider())
65  {
66  // if a value has been passed, remember it
67  if (m_model.hasModelString())
68  {
70  }
71  if (!m_model.getCallsign().isEmpty())
72  {
74  }
75  }
76 
77  const CLength cg = this->getSimulatorOrDbCG(m_callsign, cgDb); // simulation environment
78  m_model.setCG(cg);
80  return cg;
81  }
82 
84  CInterpolator::remoteAircraftSituationsAndChange(const CInterpolationAndRenderingSetupPerCallsign &setup)
85  {
87 
88  // get the changes, we need the second value as we want to look in the past
89  // the first value is already based on the latest situation
92 
93  // fixing offset
94  if (setup.isFixingSceneryOffset() && m_pastSituationsChange.hasSceneryDeviation() && m_model.hasCG())
95  {
98  if (!os.isNull())
99  {
100  const CLength addValue = os * -1.0; // positive values means too high, negative values too low
101  int changed = validSituations.addAltitudeOffset(addValue);
102  Q_UNUSED(changed)
103  }
104  }
105  else { m_currentSceneryOffset = CLength::null(); }
106  return validSituations;
107  }
108 
109  bool CInterpolator::presetGroundElevation(CAircraftSituation &situationToPreset,
110  const CAircraftSituation &oldSituation,
111  const CAircraftSituation &newSituation,
112  const CAircraftSituationChange &change)
113  {
114  // IMPORTANT: we do not know what the situation will be (interpolated to), so we cannot transfer
115  situationToPreset.resetGroundElevation();
116  do {
117  if (oldSituation.equalNormalVectorDouble(newSituation))
118  {
119  if (oldSituation.hasGroundElevation())
120  {
121  // same positions, we can use existing elevation
122  // means we were not moving between old an new
123  situationToPreset.transferGroundElevationToMe(oldSituation, true);
124  break;
125  }
126  }
127 
128  const CLength distance = newSituation.calculateGreatCircleDistance(oldSituation);
129  if (distance < newSituation.getDistancePerTime250ms(CElevationPlane::singlePointRadius()))
130  {
131  if (oldSituation.hasGroundElevation())
132  {
133  // almost same positions, we can use existing elevation
134  situationToPreset.transferGroundElevationToMe(oldSituation, true);
135  break;
136  }
137  }
138 
140  {
141  // not much change in known elevations
142  const CAltitudePair elvDevMean = change.getElevationStdDevAndMean();
143  situationToPreset.setGroundElevation(elvDevMean.second, CAircraftSituation::SituationChange);
144  break;
145  }
146 
147  const CElevationPlane epInterpolated = CAircraftSituation::interpolatedElevation(
148  CAircraftSituation::null(), oldSituation, newSituation, distance);
149  if (!epInterpolated.isNull())
150  {
151  situationToPreset.setGroundElevation(epInterpolated, CAircraftSituation::Interpolated);
152  break;
153  }
154  }
155  while (false);
156  return situationToPreset.hasGroundElevation();
157  }
158 
159  void CInterpolator::deferredInit()
160  {
161  if (m_model.hasModelString()) { return; } // set in-between
162  this->initCorrespondingModel();
163  }
164 
165  const QStringList &CInterpolator::getLogCategories()
166  {
167  static const QStringList cats { CLogCategories::interpolator() };
168  return cats;
169  }
170 
173  uint32_t aircraftNumber)
174  {
175  CInterpolationResult result;
176 
177  const bool init = this->initIniterpolationStepData(currentTimeSinceEpoch, setup, aircraftNumber);
178  Q_ASSERT_X(!m_currentInterpolationStatus.isInterpolated(), Q_FUNC_INFO, "Expect reset status");
179  if (init || m_unitTest) // ignore failure in unittest
180  {
181  Q_ASSERT_X(m_currentTimeMsSinceEpoch > 0, Q_FUNC_INFO, "No valid timestamp, interpolator initialized?");
182  const CAircraftSituation interpolatedSituation = this->getInterpolatedSituation();
183  const CAircraftParts interpolatedParts = this->getInterpolatedOrGuessedParts(aircraftNumber);
184  result.setValues(interpolatedSituation, interpolatedParts);
185  }
186 
188  return result;
189  }
190 
191  CAircraftSituation CInterpolator::getInterpolatedSituation()
192  {
193  Q_ASSERT_X(!m_currentInterpolationStatus.isInterpolated(), Q_FUNC_INFO, "Expect reset status");
195  {
196  m_lastSituation = CAircraftSituation::null();
197  return CAircraftSituation::null();
198  }
199 
200  // interpolant as function of derived class
201  // CInterpolatorLinear::Interpolant or CInterpolatorSpline::Interpolant
202  SituationLog log;
203  const IInterpolant &interpolant = getInterpolant(log);
204  const bool isValidInterpolant = interpolant.isValid();
205 
206  CAircraftSituation currentSituation = m_lastSituation;
207  CAircraftSituation::AltitudeCorrection altCorrection = CAircraftSituation::NoCorrection;
208 
209  bool isValidInterpolation = false;
210  do {
211  if (!isValidInterpolant) { break; }
212 
213  const IInterpolatorPbh &pbh = interpolant.pbh();
214 
215  // init interpolated situation
216  currentSituation = this->initInterpolatedSituation(pbh.getStartSituation(), pbh.getEndSituation());
217 
218  // Pitch bank heading first, so follow up steps could use those values
219  currentSituation.setHeading(pbh.getHeading());
220  currentSituation.setPitch(pbh.getPitch());
221  currentSituation.setBank(pbh.getBank());
222  currentSituation.setGroundSpeed(pbh.getGroundSpeed());
223 
224  // use derived interpolant function
225  const auto [interpolatedPosition, interpolatedAltitude] = interpolant.interpolatePositionAndAltitude();
226  currentSituation.setPosition(interpolatedPosition);
227  currentSituation.setAltitude(interpolatedAltitude);
228  currentSituation.setMSecsSinceEpoch(interpolant.getInterpolatedTime());
229 
230  const bool interpolateGndFlag = pbh.getEndSituation().hasGroundDetailsForGndInterpolation() &&
231  pbh.getStartSituation().hasGroundDetailsForGndInterpolation();
232  if (interpolateGndFlag) { currentSituation.setOnGroundInfo(interpolant.interpolateGroundFactor()); }
233 
234  if (currentSituation.isNull()) { break; }
235 
236  // if we get here and the vector is invalid it means we haven't handled it correctly in one of the
237  // interpolators
238  if (!currentSituation.isValidVectorRange())
239  {
240  if (CBuildConfig::isLocalDeveloperDebugBuild())
241  {
242  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Invalid interpolation situation");
243  }
244  return CAircraftSituation::null();
245  }
246 
247  // GND flag.
248  if (!interpolateGndFlag) { CAircraftSituationChange::null().guessOnGround(currentSituation, m_model); }
249 
250  // as we now have the position and can interpolate elevation
251  currentSituation.interpolateElevation(pbh.getStartSituation(), pbh.getEndSituation());
252  if (!currentSituation.hasGroundElevation())
253  {
254  // we still have no elevation
255  const CLength radius = currentSituation.getDistancePerTime250ms(CElevationPlane::singlePointRadius());
256  if (!m_lastSituation.transferGroundElevationFromMe(currentSituation, radius))
257  {
258  if (currentSituation.canLikelySkipNearGroundInterpolation())
259  {
260  // skipped
261  }
262  else
263  {
264  const CElevationPlane groundElevation =
265  this->findClosestElevationWithinRange(currentSituation, radius);
266  m_lastSituation.setGroundElevationChecked(groundElevation, CAircraftSituation::FromCache);
267  }
268  }
269  }
270 
271  // correct altitude itself
272  if (!interpolateGndFlag &&
273  currentSituation.getOnGroundInfo().getGroundDetails() != COnGroundInfo::OnGroundByGuessing)
274  {
275  // just in case
276  altCorrection = currentSituation.correctAltitude(true); // we have CG set
277  }
278 
279  // correct pitch on ground
280  if (currentSituation.isOnGround())
281  {
282  const CAngle correctedPitchOnGround = m_currentSetup.getPitchOnGround();
283  if (!correctedPitchOnGround.isNull()) { currentSituation.setPitch(correctedPitchOnGround); }
284  }
285 
286  isValidInterpolation = true;
287  }
288  while (false);
289 
290  const bool valid = isValidInterpolant && isValidInterpolation;
291  if (!valid)
292  {
293  // further handling could go here, mainly we continue with last situation
295 
296  // avoid flooding of log.
298  {
300  const bool noSituation = m_lastSituation.isNull();
301 
302  // Problem 1, we have no "last situation"
303  // Problem 2, "it takes too long to recover"
304  CStatusMessage m;
305  if (noSituation)
306  {
307  m = CStatusMessage(this).warning(
308  u"No situation #%1 for interpolation reported for '%2' (Interpolant: %3 interpolation: %4)")
309  << m_invalidSituations << m_callsign.asString() << boolToTrueFalse(isValidInterpolant)
310  << boolToTrueFalse(isValidInterpolation);
311  }
312  else
313  {
314  const qint64 diff = m_currentTimeMsSinceEpoch - currentSituation.getAdjustedMSecsSinceEpoch();
315  m = CStatusMessage(this).warning(u"Invalid situation, diff. %1ms #%2 for interpolation reported "
316  u"for '%3' (Interpolant: %4 interpolation: %5)")
317  << diff << m_invalidSituations << m_callsign.asString() << boolToTrueFalse(isValidInterpolant)
318  << boolToTrueFalse(isValidInterpolation);
319  }
320  if (!m.isEmpty())
321  {
322  if (m_interpolationMessages.sizeInt() == 2)
323  {
324  // display second message as a hint in the general log
325  // we DO NOT display the first message, as this can happen due to pilot logging off
326  // if it happens twice we consider it worth displaying
328  }
330  }
331  }
332  } // valid?
333 
334  // situation and status
335  if (valid)
336  {
337  Q_ASSERT_X(currentSituation.hasMSLGeodeticHeight(), Q_FUNC_INFO, "No MSL altitude");
338  m_lastSituation = currentSituation;
340  }
341  else
342  {
343  currentSituation = m_lastSituation;
346  }
347 
348  // logging
349  if (this->doLogging())
350  {
351  log.tsCurrent = m_currentTimeMsSinceEpoch;
352  log.callsign = m_callsign;
353  log.groundFactor = currentSituation.getOnGroundInfo().getGroundFactor();
354  log.altCorrection = CAircraftSituation::altitudeCorrectionToString(altCorrection);
355  log.situationCurrent = currentSituation;
356  log.interpolantRecalc = interpolant.isRecalculated();
357  log.change = m_pastSituationsChange;
358  log.usedSetup = m_currentSetup;
359  log.elevationInfo = this->getElevationsFoundMissedInfo();
360  log.cgAboveGround = currentSituation.getCG();
361  log.sceneryOffset = m_currentSceneryOffset;
362  log.noInvalidSituations = m_invalidSituations;
363  log.noNetworkSituations = m_currentSituations.sizeInt();
364  log.useParts = this->isRemoteAircraftSupportingParts(m_callsign);
365  m_logger->logInterpolation(log);
366  }
367 
368  // bye
369  return currentSituation;
370  }
371 
372  CAircraftParts CInterpolator::getInterpolatedParts()
373  {
374  // Parts are supposed to be in correct order, latest first
375  const CAircraftPartsList validParts = this->remoteAircraftParts(m_callsign);
376 
377  // log for empty parts aircraft parts
378  if (validParts.isEmpty())
379  {
380  static const CAircraftParts emptyParts;
381  this->logParts(emptyParts, validParts.size(), true);
382  return emptyParts;
383  }
384 
386  CAircraftParts currentParts;
387 
388  // find the first parts earlier than the current time
389  const auto pivot = std::partition_point(validParts.begin(), validParts.end(), [=](auto &&p) {
390  return p.getAdjustedMSecsSinceEpoch() > m_currentTimeMsSinceEpoch;
391  });
392  const auto partsNewer = makeRange(validParts.begin(), pivot).reverse();
393  const auto partsOlder = makeRange(pivot, validParts.end());
394 
395  if (partsOlder.isEmpty()) { currentParts = *(partsNewer.begin()); }
396  else
397  {
398  currentParts = partsOlder.front(); // latest older parts
399  }
400 
401  this->logParts(currentParts, validParts.size(), false);
402  return currentParts;
403  }
404 
405  CAircraftParts CInterpolator::getInterpolatedOrGuessedParts(int aircraftNumber)
406  {
408  "Wrong ratio");
409  const bool needParts = m_unitTest || m_lastParts.isNull();
410  const bool doInterpolation =
411  needParts ||
413  const bool doGuess =
414  needParts || ((m_interpolatedSituationsCounter + aircraftNumber) % m_partsToSituationGuessingRatio == 0);
415 
416  if (!doGuess && !doInterpolation)
417  {
418  // reuse
419  return this->logAndReturnNullParts("neither guess nor interpolation", true);
420  }
421 
422  CAircraftParts parts = CAircraftParts::null();
424  {
425  // this already logs and sets status
426  parts = this->getInterpolatedParts();
427  }
428 
429  // if we have supported parts, we skip this step, but it can happen the parts are still empty
431  {
432  if (!doGuess) { return this->logAndReturnNullParts("not supporting parts, and marked for guessing", true); }
433 
434  // check if model has been thru model matching
435  if (!m_lastSituation.isNull())
436  {
437  parts = guessParts(m_lastSituation, m_pastSituationsChange, m_model);
438  this->logParts(parts, 0, false);
439  }
440  else
441  {
442  // quite normal initial situation, just return NULL
443  return this->logAndReturnNullParts("guessing, but no situation yet", false);
444  }
445  }
446 
447  m_lastParts = parts;
449  return parts;
450  }
451 
452  const CAircraftParts &CInterpolator::logAndReturnNullParts(const QString &info, bool log)
453  {
454  if (!m_lastParts.isNull())
455  {
458  return m_lastParts;
459  }
460 
461  if (log)
462  {
463  const CStatusMessage m = CStatusMessage(this).warning(u"NULL parts reported for '%1', '%2')")
464  << m_callsign.asString() << info;
467  }
469  return CAircraftParts::null();
470  }
471 
473 
474  CAircraftParts CInterpolator::guessParts(const CAircraftSituation &situation,
475  const CAircraftSituationChange &change, const CAircraftModel &model)
476  {
477  CAircraftParts parts;
478  parts.setMSecsSinceEpoch(situation.getMSecsSinceEpoch());
479  parts.setTimeOffsetMs(situation.getTimeOffsetMs());
480  parts.setPartsDetails(CAircraftParts::GuessedParts);
481  parts.setLights(situation.guessLights());
482 
483  QString *details = /*CBuildConfig::isLocalDeveloperDebugBuild() ? &parts.m_guessingDetails :*/ nullptr;
484 
485  CAircraftEngineList engines;
486  const bool vtol = model.isVtol();
487  const int engineCount = model.getEngineCount();
488  CSpeed guessedVRotate = CSpeed::null();
489  CLength guessedCG = model.getCG();
490  model.getAircraftIcaoCode().guessModelParameters(guessedCG, guessedVRotate);
491 
492  if (situation.getOnGroundInfo().getGroundDetails() != COnGroundInfo::NotSetGroundDetails)
493  {
494  do {
495  // set some reasonable values
496  const bool isOnGround = situation.isOnGround();
497  engines.initEngines(engineCount, !isOnGround || situation.isMoving());
498  parts.setGearDown(isOnGround);
499  parts.setSpoilersOut(false);
500  parts.setEngines(engines);
501 
502  if (!change.isNull())
503  {
504  if (change.isConstDecelarating())
505  {
506  parts.setSpoilersOut(true);
507  parts.setFlapsPercent(10);
508  break;
509  }
510  }
511 
512  const CSpeed slowSpeed = guessedVRotate * 0.30;
513  if (situation.getGroundSpeed() < slowSpeed)
514  {
515  if (details) { *details += u"slow speed <" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
516  parts.setFlapsPercent(0);
517  break;
518  }
519  else
520  {
521  if (details) { *details += u"faster speed >" % slowSpeed.valueRoundedWithUnit(1) % u" on ground"; }
522  parts.setFlapsPercent(0);
523  break;
524  }
525  }
526  while (false);
527  }
528  else
529  {
530  if (details) { *details = QStringLiteral("no ground info"); }
531 
532  // no idea if on ground or not
533  engines.initEngines(engineCount, true);
534  parts.setEngines(engines);
535  parts.setGearDown(true);
536  parts.setSpoilersOut(false);
537  }
538 
539  const double pitchDeg = situation.getPitch().value(CAngleUnit::deg());
540  const bool isLikelyTakeOffOrClimbing =
541  change.isNull() ? pitchDeg > 20 : (change.isRotatingUp() || change.isConstAscending());
542  const bool isLikelyLanding = change.isNull() ? false : change.isConstDescending();
543 
544  if (situation.hasGroundElevation())
545  {
546  const CLength aboveGnd = situation.getHeightAboveGround();
547  if (aboveGnd.isNull() || std::isnan(aboveGnd.value()))
548  {
549  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "above gnd.is null");
550  return parts;
551  }
552 
553  const double nearGround1Ft = 300;
554  const double nearGround2Ft = isLikelyTakeOffOrClimbing ? 500 : 1000;
555  const double aGroundFt = aboveGnd.value(CLengthUnit::ft());
556  static const QString detailsInfo(
557  "above ground: %1ft near grounds: %2ft %3ft likely takeoff: %4 likely landing: %5");
558 
559  if (details)
560  {
561  *details = detailsInfo.arg(aGroundFt)
562  .arg(nearGround1Ft)
563  .arg(nearGround2Ft)
564  .arg(boolToYesNo(isLikelyTakeOffOrClimbing), boolToYesNo(isLikelyLanding));
565  }
566  if (aGroundFt < nearGround1Ft)
567  {
568  if (details) { details->prepend(QStringLiteral("near ground: ")); }
569  parts.setGearDown(true);
570  parts.setFlapsPercent(25);
571  }
572  else if (aGroundFt < nearGround2Ft)
573  {
574  if (details) { details->prepend(QStringLiteral("2nd layer: ")); }
575  const bool gearDown =
576  !isLikelyTakeOffOrClimbing && (situation.getGroundSpeed() < guessedVRotate || isLikelyLanding);
577  parts.setGearDown(gearDown);
578  parts.setFlapsPercent(10);
579  }
580  else
581  {
582  if (details) { details->prepend(QStringLiteral("airborne: ")); }
583  parts.setGearDown(false);
584  parts.setFlapsPercent(0);
585  }
586  }
587  else
588  {
589  if (situation.getOnGroundInfo().getGroundDetails() != COnGroundInfo::NotSetGroundDetails)
590  {
591  // we have no ground elevation but a ground info
592  if (situation.getOnGroundInfo().getGroundDetails() == COnGroundInfo::OnGroundByGuessing)
593  {
594  // should be OK
595  if (details) { *details = QStringLiteral("on ground, no elv."); }
596  }
597  else
598  {
599  if (!vtol)
600  {
601  const bool gearDown = situation.getGroundSpeed() < guessedVRotate;
602  parts.setGearDown(gearDown);
603  if (details)
604  {
605  *details =
606  QStringLiteral("not on ground elv., gs < ") + guessedVRotate.valueRoundedWithUnit(1);
607  }
608  }
609  }
610  }
611  }
612 
613  return parts;
614  }
615 
616  void CInterpolator::logParts(const CAircraftParts &parts, int partsNo, bool empty) const
617  {
618  if (!this->doLogging()) { return; }
619  PartsLog logInfo;
620  logInfo.callsign = m_callsign;
621  logInfo.noNetworkParts = partsNo;
622  logInfo.tsCurrent = m_currentTimeMsSinceEpoch;
623  logInfo.parts = parts;
624  logInfo.empty = empty;
625  m_logger->logParts(logInfo);
626  }
627 
629  {
630  return QStringLiteral("Callsign: ") % m_callsign.asString() % QStringLiteral(" situations: ") %
631  QString::number(this->remoteAircraftSituationsCount(m_callsign)) % QStringLiteral(" parts: ") %
632  QString::number(this->remoteAircraftPartsCount(m_callsign)) % QStringLiteral(" 1st interpolation: ") %
634  }
635 
637 
638  bool CInterpolator::initIniterpolationStepData(qint64 currentTimeSinceEpoch,
640  int aircraftNumber)
641  {
642  Q_ASSERT_X(!m_callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign");
643 
644  const qint64 lastModifed = this->situationsLastModified(m_callsign);
645  const bool slowUpdateStep = (((m_interpolatedSituationsCounter + aircraftNumber) % 25) ==
646  0); // flag when parts are updated, which need not to be updated every time
647  const bool changedSituations = lastModifed > m_situationsLastModified;
648 
649  m_currentTimeMsSinceEpoch = currentTimeSinceEpoch;
652  m_currentSetup = setup;
653 
654  if (changedSituations)
655  {
656  m_situationsLastModified = lastModifed;
657  m_currentSituations = this->remoteAircraftSituationsAndChange(setup); // only update when needed
658  }
659 
660  if (!m_model.hasCG() || slowUpdateStep)
661  {
662  this->getAndFetchModelCG(CLength::null()); // update CG
663  }
664 
665  bool success = false;
666  const int situationsSize = m_currentSituations.sizeInt();
669  {
670  const bool inRange = this->isAircraftInRange(m_callsign);
671  m_lastSituation = CAircraftSituation::null(); // no interpolation possible for that step
672  static const QString extraNoSituations("No situations, but remote aircraft '%1'");
673  static const QString extraNoRemoteAircraft("Unknown remote aircraft: '%1'");
675  (inRange ? extraNoSituations : extraNoRemoteAircraft).arg(m_callsign.asString()));
676  }
677  else
678  {
679  success = true;
680  m_interpolatedSituationsCounter++; // counter updated in initIniterpolationStepData
681 
682  // with the latest updates of T243 the order and the offsets are supposed to be correct
683  // so even mixing fast/slow updates shall work
684  if (!CBuildConfig::isReleaseBuild())
685  {
687  "Wrong sort order");
689  "Wrong size");
690  }
691  }
692 
693  return success;
694  }
695 
696  CAircraftSituation CInterpolator::initInterpolatedSituation(const CAircraftSituation &oldSituation,
697  const CAircraftSituation &newSituation) const
698  {
699  if (m_currentSituations.isEmpty()) { return CAircraftSituation::null(); }
700 
702  if (currentSituation.getCallsign() != m_callsign)
703  {
704  SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign");
705  currentSituation.setCallsign(m_callsign);
706  }
707 
708  if (CBuildConfig::isLocalDeveloperDebugBuild())
709  {
710  Q_ASSERT_X(currentSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid range");
711  }
712 
713  // preset elevation here, as we do not know where the situation will be after the interpolation step!
714  const bool preset = presetGroundElevation(currentSituation, oldSituation, newSituation, m_pastSituationsChange);
715  Q_UNUSED(preset)
716 
717  // fetch CG once
718  const CLength cg(this->getModelCG());
719  currentSituation.setCG(cg);
720  return currentSituation;
721  }
722 
724  {
725  if (model.hasModelString()) { m_model = model; }
726  else
727  {
729  if (foundModel.hasModelString()) { m_model = foundModel; }
730  }
731  this->getAndFetchModelCG(model.getCG());
732  }
733 
734  void CInterpolator::markAsUnitTest() { m_unitTest = true; }
735 } // namespace swift::misc::simulation
QPair< CAltitude, CAltitude > CAltitudePair
Pair of altitude.
Definition: altitude.h:251
static const QString & interpolator()
Interpolator.
Definition: logcategories.h:73
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
iterator begin()
Returns iterator at the beginning of the sequence.
Definition: sequence.h:163
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
iterator end()
Returns iterator one past the end of the sequence.
Definition: sequence.h:172
int sizeInt() const
Avoid compiler warnings when using with int.
Definition: sequence.h:276
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
void setMSecsSinceEpoch(qint64 mSecsSinceEpoch)
Timestamp as ms value.
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
qint64 getTimeOffsetMs() const
Milliseconds to add to timestamp for interpolation.
void setTimeOffsetMs(qint64 offset)
Milliseconds to add to timestamp for interpolation.
Value object encapsulating a list of aircraft engines.
void initEngines(int engineNumber, bool on)
Init some engines.
void guessModelParameters(physical_quantities::CLength &guessedCGOut, physical_quantities::CSpeed &guessedVRotateOut) const
Guess aircraft model parameters.
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
bool isNull() const
NULL parts object?
void setEngines(const CAircraftEngineList &engines)
Set engines.
void setGearDown(bool down)
Set gear down.
Definition: aircraftparts.h:97
void setPartsDetails(PartsDetails details)
Set parts details.
void setLights(const CAircraftLights &lights)
Set aircraft lights.
Definition: aircraftparts.h:81
void setFlapsPercent(int flapsPercent)
Set flaps position in percent.
void setSpoilersOut(bool out)
Set spoilers out.
Value object encapsulating a list of aircraft parts.
Value object about changes in situations.
bool hasSceneryDeviation() const
Scenery deviation available?
bool isConstDescending() const
Constantly descending?
bool isConstAscending() const
Constantly ascending?
CAltitudePair getElevationStdDevAndMean() const
Elevation standard deviation and mean.
bool hasElevationDevWithinAllowedRange() const
Elevation within CAircraftSituation::allowedAltitudeDeviation range.
bool isConstDecelarating() const
Constantly decelarating?
physical_quantities::CLength getGuessedSceneryDeviationCG() const
Get scenery deviation under consideration of CG.
Value object encapsulating a list of aircraft parts.
CAircraftSituationChange indexOrNull(int index) const
Index or NULL.
Value object encapsulating information of an aircraft's situation.
AltitudeCorrection correctAltitude(bool enableDragToGround=true)
Set the corrected altitude from CAircraftSituation::getCorrectedAltitude.
void setCallsign(const CCallsign &callsign)
Corresponding callsign.
void resetGroundElevation()
Reset ground elevation.
bool hasGroundElevation() const
Is ground elevation value available.
void setCG(const physical_quantities::CLength &cg)
Set CG.
void setGroundSpeed(const physical_quantities::CSpeed &groundspeed)
Set ground speed.
physical_quantities::CLength getDistancePerTime250ms(const physical_quantities::CLength &min=physical_quantities::CLength::null()) const
Distance per milliseconds (250ms)
bool setGroundElevation(const aviation::CAltitude &altitude, GndElevationInfo info, bool transferred=false)
Elevation of the ground directly beneath at the given situation.
bool transferGroundElevationFromMe(CAircraftSituation &transferToSituation, const physical_quantities::CLength &radius=geo::CElevationPlane::singlePointRadius()) const
Transfer from "this" situation to otherSituation.
void setBank(const physical_quantities::CAngle &bank)
Set bank (angle)
void setHeading(const CHeading &heading)
Set heading.
aviation::COnGroundInfo getOnGroundInfo() const
On ground info.
AltitudeCorrection
How was altitude corrected?
bool transferGroundElevationToMe(const CAircraftSituation &fromSituation, const physical_quantities::CLength &radius, bool transferred)
Transfer ground elevation from given situation (to me)
bool setGroundElevationChecked(const geo::CElevationPlane &elevationPlane, GndElevationInfo info, bool transferred=false)
Set elevation of the ground directly beneath, but checked.
void setAltitude(const CAltitude &altitude)
Set altitude.
bool canLikelySkipNearGroundInterpolation() const
Situation looks like an aircraft not near ground.
const CCallsign & getCallsign() const
Corresponding callsign.
const physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
CAircraftLights guessLights() const
Guessed lights.
void setPitch(const physical_quantities::CAngle &pitch)
Set pitch.
virtual bool isNull() const
Null situation.
const physical_quantities::CAngle & getPitch() const
Get pitch.
bool interpolateElevation(const aviation::CAircraftSituation &oldSituation, const aviation::CAircraftSituation &newSituation)
Interpolate "this" elevation from the two adjacent positions.
bool isMoving() const
Is moving? Means ground speed > epsilon.
const physical_quantities::CLength & getCG() const
Get CG if any.
physical_quantities::CLength getHeightAboveGround() const
Height above ground.
void setPosition(const geo::CCoordinateGeodetic &position)
Set position.
int addAltitudeOffset(const physical_quantities::CLength &offset)
Add an offset to each altitude.
bool isSortedAdjustedLatestFirstWithoutNullPositions() const
Latest first and no null positions?
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
double getGroundFactor() const
Get the ground factor Use this for interpolation only!! For just checking if the info is OnGround or ...
Definition: ongroundinfo.h:66
OnGroundDetails getGroundDetails() const
Get ground details.
Plane of same elevation, can be a single point or larger area (e.g. airport)
virtual bool isNull() const
Existing value?
bool hasMSLGeodeticHeight() const
Geodetic height not null and aviation::CAltitude::MeanSeaLevel.
physical_quantities::CLength calculateGreatCircleDistance(const ICoordinateGeodetic &otherCoordinate) const
Great circle distance.
bool isValidVectorRange() const
Check values.
bool equalNormalVectorDouble(const std::array< double, 3 > &otherVector) const
Is equal? Epsilon considered.
Physical unit angle (radians, degrees)
Definition: angle.h:23
Physical unit length (length)
Definition: length.h:18
double value(MU unit) const
Value in given unit.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
void setCallsign(const aviation::CCallsign &callsign)
Corresponding callsign if applicable.
const physical_quantities::CLength & getCG() const
Get center of gravity.
void setCG(const physical_quantities::CLength &cg)
Get center of gravity.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Aircraft ICAO code.
int getEngineCount() const
Engine count if any, -1 if no value is set.
bool hasCG() const
CG value available?
bool hasModelString() const
Non empty model string?
bool isAircraftPartsEnabled() const
Aircraft parts enabled (still requires the other aircraft to send parts)
const physical_quantities::CAngle & getPitchOnGround() const
Force a given pitch on ground.
Value object for interpolator and rendering per callsign.
Record internal state of interpolator for debugging.
void logInterpolation(const SituationLog &log)
Log current interpolation cycle, only stores in memory, for performance reasons.
void logParts(const PartsLog &log)
Log current parts cycle, only stores in memory, for performance reasons.
void setValues(const aviation::CAircraftSituation &situation, const aviation::CAircraftParts &parts)
Set values.
void setStatus(const CInterpolationStatus &interpolation, const CPartsStatus &parts)
Set status values.
void setInterpolationSetupProvider(IInterpolationSetupProvider *provider)
Provider.
void setSameSituation(bool same)
Interpolating between 2 same situations?
void setInterpolatedAndCheckSituation(bool succeeded, const aviation::CAircraftSituation &situation)
Set succeeded.
bool isInterpolated() const
Did interpolation succeed?
void setSituationsCount(int count)
Set situations count.
void setExtraInfo(const QString &info)
Extra info.
int m_partsToSituationGuessingRatio
ratio between parts guessing and situation interpolation
Definition: interpolator.h:122
int m_partsToSituationInterpolationRatio
ratio between parts and situation interpolation, 1..always, 2..every 2nd situation
Definition: interpolator.h:120
qint64 m_situationsLastModified
when situations were last modified
Definition: interpolator.h:132
aviation::CAircraftParts m_lastParts
latest parts
Definition: interpolator.h:127
static const QStringList & getLogCategories()
Log categories.
const physical_quantities::CLength & getModelCG() const
Center of gravity.
Definition: interpolator.h:100
CInterpolationStatus m_currentInterpolationStatus
this step's situation status
Definition: interpolator.h:116
physical_quantities::CLength m_currentSceneryOffset
calculated scenery offset if any
Definition: interpolator.h:128
CInterpolationResult getInterpolation(qint64 currentTimeSinceEpoch, const CInterpolationAndRenderingSetupPerCallsign &setup, uint32_t aircraftNumber)
Get interpolated situation.
CInterpolationAndRenderingSetupPerCallsign m_currentSetup
used setup
Definition: interpolator.h:115
CPartsStatus m_currentPartsStatus
this step's parts status
Definition: interpolator.h:117
aviation::CAircraftSituation m_lastSituation
latest interpolation
Definition: interpolator.h:126
void attachLogger(CInterpolationLogger *logger)
Attach an observer to read the interpolator's state for debugging.
Definition: interpolator.h:65
void resetLastInterpolation()
Reset last interpolation to null.
qint64 m_currentTimeMsSinceEpoch
current time
Definition: interpolator.h:109
int m_interpolatedSituationsCounter
counter for each interpolated situations: used for statistics, every n-th interpolation ....
Definition: interpolator.h:134
aviation::CAircraftSituationChange m_pastSituationsChange
situations change of provider (i.e.
Definition: interpolator.h:114
CStatusMessageList m_interpolationMessages
interpolation messages
Definition: interpolator.h:124
QString getInterpolatorInfo() const
Get an interpolator info string (for debug info)
CPartsStatus m_lastPartsStatus
status for last parts, used when last parts are re-used because of m_partsToSituationInterpolationRat...
Definition: interpolator.h:118
aviation::CAircraftSituationList m_currentSituations
current situations obtained by remoteAircraftSituationsAndChange
Definition: interpolator.h:112
const aviation::CCallsign m_callsign
corresponding callsign
Definition: interpolator.h:105
virtual const IInterpolant & getInterpolant(SituationLog &log)=0
Get the interpolant for the given time point.
bool hasAttachedLogger() const
Is logger attached?
Definition: interpolator.h:68
void markAsUnitTest()
Mark as unit test.
qint64 m_lastInvalidLogTs
last invalid situation timestamp
Definition: interpolator.h:110
int m_invalidSituations
mainly when there are no new situations
Definition: interpolator.h:123
CAircraftModel m_model
corresponding model (required for CG)
Definition: interpolator.h:106
void initCorrespondingModel(const CAircraftModel &model={})
Init, or re-init the corressponding model.
void setReusedParts(bool reused)
Mark as reused.
Definition: partsstatus.h:36
void setSupportsParts(bool supports)
Set support flag.
Definition: partsstatus.h:29
bool isSupportingParts() const
Supporting parts.
Definition: partsstatus.h:26
bool isAircraftInRange(const aviation::CCallsign &callsign) const
Is aircraft in range?
aviation::CAircraftSituationList remoteAircraftSituations(const aviation::CCallsign &callsign) const
Rendered aircraft situations (per callsign, time history)
qint64 situationsLastModified(const aviation::CCallsign &callsign) const
When last modified.
IRemoteAircraftProvider * getRemoteAircraftProvider() const
Get the aircraft provider.
CSimulatedAircraft getAircraftInRangeForCallsign(const aviation::CCallsign &callsign) const
Aircraft for callsign.
void setRemoteAircraftProvider(IRemoteAircraftProvider *remoteAircraftProvider)
Set remote aircraft provider.
aviation::CAircraftPartsList remoteAircraftParts(const aviation::CCallsign &callsign) const
All parts (per callsign, time history)
aviation::CAircraftSituationChangeList remoteAircraftSituationChanges(const aviation::CCallsign &callsign) const
Aircraft changes.
bool isRemoteAircraftSupportingParts(const aviation::CCallsign &callsign) const
Is remote aircraft supporting parts?
int remoteAircraftSituationsCount(const aviation::CCallsign &callsign) const
Number of remote aircraft situations for callsign.
int remoteAircraftPartsCount(const aviation::CCallsign &callsign) const
All parts (per callsign, time history)
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
physical_quantities::CLength getSimulatorOrDbCG(const aviation::CCallsign &callsign, const physical_quantities::CLength &dbCG) const
Get CG per callsign, NULL if not found.
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.
void setSimulationEnvironmentProvider(ISimulationEnvironmentProvider *provider)
Set the provider.
Direct in memory access to interpolation setup, normally implemented by simulator.
Direct thread safe in memory access to remote aircraft.
virtual physical_quantities::CLength getCGFromDB(const aviation::CCallsign &callsign) const =0
CG values from DB.
static constexpr int MaxSituationsPerCallsign
How many situations we keep per callsign.
virtual void rememberCGFromDB(const physical_quantities::CLength &cgFromDB, const aviation::CCallsign &callsign)=0
CG values from DB.
auto makeRange(I begin, I2 end) -> CRange< I >
Returns a CRange constructed from begin and end iterators of deduced types.
Definition: range.h:316
SWIFT_MISC_EXPORT const QString & boolToTrueFalse(bool v)
Bool to true/false.
SWIFT_MISC_EXPORT const QString & boolToYesNo(bool v)
Bool to yes/no.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26