swift
interpolatorspline.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2017 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include "config/buildconfig.h"
7 #include "misc/logmessage.h"
10 #include "misc/verify.h"
11 
12 using namespace swift::config;
13 using namespace swift::misc::aviation;
14 using namespace swift::misc::geo;
15 using namespace swift::misc::math;
16 using namespace swift::misc::network;
17 using namespace swift::misc::physical_quantities;
18 using namespace swift::misc::simulation;
19 
20 namespace swift::misc::simulation
21 {
22  namespace
23  {
25  template <size_t N>
26  std::array<double, N> solveTridiagonal(std::array<std::array<double, N>, N> &matrix, std::array<double, N> &d)
27  {
28  const auto a = [&matrix](size_t i) -> double & { return matrix[i][i - 1]; }; // subdiagonal
29  const auto b = [&matrix](size_t i) -> double & { return matrix[i][i]; }; // main diagonal
30  const auto c = [&matrix](size_t i) -> double & { return matrix[i][i + 1]; }; // superdiagonal
31 
32  // forward sweep
33  c(0) /= b(0);
34  d[0] /= b(0);
35  for (size_t i = 1; i < N; ++i)
36  {
37  const double denom = b(i) - a(i) * c(i - 1);
38  if (i < N - 1) { c(i) /= denom; }
39  d[i] = (d[i] - a(i) * d[i - 1]) / denom;
40  }
41 
42  // back substitution
43  for (int i = N - 2; i >= 0; --i)
44  {
45  const auto it = static_cast<size_t>(i);
46  d[it] -= c(it) * d[it + 1];
47  }
48  return d;
49  }
50 
54  template <size_t N>
55  std::array<double, N> getDerivatives(const std::array<double, N> &x, const std::array<double, N> &y)
56  {
57  std::array<std::array<double, N>, N> a { {} };
58  std::array<double, N> b { {} };
59 
60  a[0][0] = 2.0 / (x[1] - x[0]);
61  a[0][1] = 1.0 / (x[1] - x[0]);
62  b[0] = 3.0 * (y[1] - y[0]) / ((x[1] - x[0]) * (x[1] - x[0]));
63 
64  a[N - 1][N - 2] = 1.0 / (x[N - 1] - x[N - 2]);
65  a[N - 1][N - 1] = 2.0 / (x[N - 1] - x[N - 2]);
66  b[N - 1] = 3.0 * (y[N - 1] - y[N - 2]) / ((x[N - 1] - x[N - 2]) * (x[N - 1] - x[N - 2]));
67 
68  for (size_t i = 1; i < N - 1; ++i)
69  {
70  a[i][i - 1] = 1.0 / (x[i] - x[i - 1]);
71  a[i][i] = 2.0 / (x[i] - x[i - 1]) + 2.0 / (x[i + 1] - x[i]);
72  a[i][i + 1] = 1.0 / (x[i + 1] - x[i]);
73  b[i] = 3.0 * (y[i] - y[i - 1]) / ((x[i] - x[i - 1]) * (x[i] - x[i - 1])) +
74  3.0 * (y[i + 1] - y[i]) / ((x[i + 1] - x[i]) * (x[i + 1] - x[i]));
75  }
76 
77  solveTridiagonal(a, b);
78  return b;
79  }
80 
82  double evalSplineInterval(double x, double x0, double x1, double y0, double y1, double k0, double k1)
83  {
84  const double t = (x - x0) / (x1 - x0);
85  const double a = k0 * (x1 - x0) - (y1 - y0);
86  const double b = -k1 * (x1 - x0) + (y1 - y0);
87  const double y = (1 - t) * y0 + t * y1 + t * (1 - t) * (a * (1 - t) + b * t);
88 
90  {
91  SWIFT_VERIFY_X(t >= 0, Q_FUNC_INFO, "Expect t >= 0");
92  SWIFT_VERIFY_X(t <= 1.0, Q_FUNC_INFO, "Expect t <= 1");
93  }
94  return y;
95  }
96  } // namespace
97 
98  bool CInterpolatorSpline::fillSituationsArray()
99  {
100  // m_s[0] .. oldest -> m_[2] .. latest
101  // general idea, we interpolate from current situation -> latest situation
102 
103  // do we have the last interpolated situation?
104  if (m_lastSituation.isNull())
105  {
106  if (m_currentSituations.isEmpty())
107  {
108  // nothing we can do
109  m_s[0] = m_s[1] = m_s[2] = CAircraftSituation::null();
110  return false;
111  }
112  else
113  {
114  // we start with the latest situation just to init the values
115  CAircraftSituation f = m_currentSituations.front();
116  f.setAdjustedMSecsSinceEpoch(m_currentTimeMsSinceEpoch); // adjusted time exactly "now"
117  m_s[0] = m_s[1] = m_s[2] = f;
118  }
119  }
120  else
121  {
122  // in normal cases init some default values
123  m_s[0] = m_s[1] = m_s[2] = m_lastSituation; // current position
124  }
125 
126  // set some default values
127  const qint64 os = qMax(CFsdSetup::c_interimPositionTimeOffsetMsec, m_s[2].getTimeOffsetMs());
128  m_s[0].addMsecs(-os); // oldest, Ref T297 default offset time to fill data
129  m_s[2].addMsecs(os); // latest, Ref T297 default offset time to fill data
130  if (m_currentSituations.isEmpty()) { return false; }
131 
132  // and use the real values if available
133  // m_s[0] .. oldest -> m_[2] .. latest
134  const CAircraftSituation latest = m_currentSituations.front();
135  if (latest.isNewerThanAdjusted(m_s[1])) { m_s[2] = latest; }
136  const qint64 currentAdjusted = m_s[1].getAdjustedMSecsSinceEpoch();
137 
138  // avoid 2 very close positions currently done by time, maybe we can also choose distance
139  const qint64 osNotTooClose = qRound64(0.8 * os);
140  const CAircraftSituation older =
141  m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted - osNotTooClose);
142  if (!older.isNull()) { m_s[0] = older; }
143  else
144  {
145  const CAircraftSituation closeOlder =
146  m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted);
147  if (!closeOlder.isNull()) { m_s[0] = closeOlder; }
148  }
149  const qint64 latestAdjusted = m_s[2].getAdjustedMSecsSinceEpoch();
150  const qint64 olderAdjusted = m_s[0].getAdjustedMSecsSinceEpoch();
151 
152  // not having a new situation itself is quite normal,
153  // only if it persits it is critical.
154  const bool hasNewer = latestAdjusted > m_currentTimeMsSinceEpoch;
155 
157  {
158  const bool verified =
159  verifyInterpolationSituations(m_s[0], m_s[1], m_s[2]); // oldest -> latest, only verify order
160  if (!verified)
161  {
162  static const QString vm("Unverified situations, m0-2 (oldest latest) %1 %2 %3");
163  const QString vmValues = vm.arg(olderAdjusted).arg(currentAdjusted).arg(latestAdjusted);
164  CLogMessage(this).warning(vmValues);
165  Q_UNUSED(vmValues)
166  }
167  }
168  return hasNewer;
169  }
170 
171  // pin vtables to this file
172  void CInterpolatorSpline::anchor() {}
173 
174  const IInterpolant &CInterpolatorSpline::getInterpolant(SituationLog &log)
175  {
176  // recalculate derivatives only if they changed
177  // m_situationsLastModified updated in initIniterpolationStepData
178  const bool recalculate = (m_currentTimeMsSinceEpoch >= m_nextSampleAdjustedTime) || // new step
179  (m_situationsLastModified > m_situationsLastModifiedUsed); // modified
180 
181  if (recalculate)
182  {
183  // with the latest updates of T243 the order and the offsets are supposed to be correct
184  // so even mixing fast/slow updates shall work
185  m_situationsLastModifiedUsed = m_situationsLastModified;
186  const bool fillStatus = this->fillSituationsArray();
187  if (!fillStatus)
188  {
189  m_interpolant.setValid(false);
190  return m_interpolant;
191  }
192 
193  const std::array<std::array<double, 3>, 3> normals { { m_s[0].getPosition().normalVectorDouble(),
194  m_s[1].getPosition().normalVectorDouble(),
195  m_s[2].getPosition().normalVectorDouble() } };
196  PosArray pa;
197  pa.x = { { normals[0][0], normals[1][0], normals[2][0] } }; // oldest -> latest
198  pa.y = { { normals[0][1], normals[1][1], normals[2][1] } };
199  pa.z = { { normals[0][2], normals[1][2], normals[2][2] } }; // latest
200  pa.t = { { static_cast<double>(m_s[0].getAdjustedMSecsSinceEpoch()),
201  static_cast<double>(m_s[1].getAdjustedMSecsSinceEpoch()),
202  static_cast<double>(m_s[2].getAdjustedMSecsSinceEpoch()) } };
203 
204  pa.dx = getDerivatives(pa.t, pa.x);
205  pa.dy = getDerivatives(pa.t, pa.y);
206  pa.dz = getDerivatives(pa.t, pa.z);
207 
208  // - altitude unit must be the same for all three, but the unit itself does not matter
209  // - ground elevantion here normally is not available
210  // - some info how fast a plane moves: 100km/h => 1sec 27,7m => 5 secs 136m
211  // - on an airport the plane does not move very fast, or not at all
212  // - and the elevation remains (almost) constant for a wider area
213  // - during flying the ground elevation not really matters
214  this->updateElevations(true);
215  static const CLengthUnit altUnit = CAltitude::defaultUnit();
216  const CLength cg(this->getModelCG().switchedUnit(altUnit));
217  const double a0 = m_s[0].getCorrectedAltitude(cg).value(altUnit); // oldest
218  const double a1 = m_s[1].getCorrectedAltitude(cg).value(altUnit);
219  const double a2 = m_s[2].getCorrectedAltitude(cg).value(altUnit); // latest
220  pa.a = { { a0, a1, a2 } };
221  pa.gnd = { { m_s[0].getOnGroundInfo().getGroundFactor(), m_s[1].getOnGroundInfo().getGroundFactor(),
222  m_s[2].getOnGroundInfo().getGroundFactor() } };
223  pa.da = getDerivatives(pa.t, pa.a);
224  pa.dgnd = getDerivatives(pa.t, pa.gnd);
225 
226  m_prevSampleAdjustedTime = m_s[1].getAdjustedMSecsSinceEpoch();
227  m_nextSampleAdjustedTime = m_s[2].getAdjustedMSecsSinceEpoch(); // latest
228  m_prevSampleTime = m_s[1].getMSecsSinceEpoch(); // last interpolated situation normally
229  m_nextSampleTime = m_s[2].getMSecsSinceEpoch(); // latest
230  m_interpolant = CInterpolant(pa, altUnit, CInterpolatorLinearPbh(m_s[1], m_s[2])); // older, newer
231  Q_ASSERT_X(m_prevSampleAdjustedTime < m_nextSampleAdjustedTime, Q_FUNC_INFO, "Wrong time order");
232  }
233 
234  // Example:
235  // prev.sample time 5 (received at 0) , next sample time 10 (received at 5)
236  // cur.time 6: dt1=6-5=1, dt2=5 => fraction 1/5
237  // cur.time 9: dt1=9-5=4, dt2=5 => fraction 4/5
238  //
239  // we use different offset times for interim pos. updates
240  // prev.sample time 5 (received at 0) , 7/r:5, 10 (rec. at 5)
241  // cur.time 6: dt1=6-5=1, dt2=7-5 => fraction 1/2
242  // cur.time 9: dt1=9-7=2, dt2=10-7=3 => fraction 2/3
243  // we use different offset times for fast pos. updates
244  // KB: is that correct with dt2, or would it be m_nextSampleTime - m_prevSampleTime
245  // as long as the offset time is constant, it does not matter
246  const auto dt1 = static_cast<double>(m_currentTimeMsSinceEpoch - m_prevSampleAdjustedTime);
247  const auto dt2 = static_cast<double>(m_nextSampleAdjustedTime - m_prevSampleAdjustedTime);
248  double timeFraction = dt1 / dt2;
249 
251  {
252  SWIFT_VERIFY_X(dt1 >= 0, Q_FUNC_INFO, "Expect postive dt1");
253  SWIFT_VERIFY_X(dt2 > 0, Q_FUNC_INFO, "Expect postive dt2");
254  SWIFT_VERIFY_X(isAcceptableTimeFraction(timeFraction), Q_FUNC_INFO, "Expect fraction 0-1");
255  }
256  timeFraction = clampValidTimeFraction(timeFraction);
257  const qint64 interpolatedTime = m_prevSampleTime + qRound64(timeFraction * dt2);
258 
259  // time fraction is expected between 0-1
260  m_currentInterpolationStatus.setInterpolated(true);
261  m_interpolant.setTimes(m_currentTimeMsSinceEpoch, timeFraction, interpolatedTime);
262  m_interpolant.setRecalculated(recalculate);
263 
264  if (this->doLogging())
265  {
269  log.interpolationSituations.push_back(m_s[2]); // latest at end
270  log.interpolator = 's';
271  log.deltaSampleTimesMs = dt2;
272  log.simTimeFraction = timeFraction;
273  log.tsInterpolated = interpolatedTime; // without offsets
274  log.interpolantRecalc = m_interpolant.isRecalculated();
275  }
276 
277  return m_interpolant;
278  }
279 
280  bool CInterpolatorSpline::updateElevations(bool canSkip)
281  {
282  bool updated = false;
283  for (CAircraftSituation &s : m_s)
284  {
285  if (s.hasGroundElevation()) { continue; } // do not override existing values
286  if (canSkip && s.canLikelySkipNearGroundInterpolation()) { continue; }
287 
288  const CElevationPlane plane =
289  this->findClosestElevationWithinRange(s, CElevationPlane::singlePointRadius());
290  const bool u = s.setGroundElevationChecked(plane, CAircraftSituation::FromCache);
291  updated |= u;
292  }
293  return updated;
294  }
295 
296  bool CInterpolatorSpline::areAnyElevationsMissing() const
297  {
298  return std::any_of(m_s.begin(), m_s.end(),
299  [](const auto &situation) { return !situation.hasGroundElevation(); });
300  }
301 
302  bool CInterpolatorSpline::isAnySituationNearGroundRelevant() const
303  {
304  return std::any_of(m_s.begin(), m_s.end(),
305  [](const auto &situation) { return !situation.canLikelySkipNearGroundInterpolation(); });
306  }
307 
308  CInterpolatorSpline::CInterpolant::CInterpolant(const CInterpolatorSpline::PosArray &pa,
309  const CLengthUnit &altitudeUnit, const CInterpolatorLinearPbh &pbh)
310  : m_pa(pa), m_altitudeUnit(altitudeUnit)
311  {
312  m_pbh = pbh;
313  }
314 
315  std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
317  {
318  const double t1 = m_pa.t[1];
319  const double t2 = m_pa.t[2]; // latest (adjusted)
320 
321  bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
322  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
323  {
324  Q_ASSERT_X(t1 < t2, Q_FUNC_INFO,
325  "Expect sorted times, latest first"); // that means a bug in our code init the values
326  SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO, "invalid timestamp t1");
327  SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc < t2, Q_FUNC_INFO,
328  "invalid timestamp t2"); // t1==t2 results in div/0
329  }
330  if (!valid) { return { {}, {} }; }
331 
332  const double newX =
333  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
334  const double newY =
335  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
336  const double newZ =
337  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
338 
339  valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) &&
340  CAircraftSituation::isValidVector(m_pa.z);
341  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
342  {
343  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO, "invalid X"); // all x values
344  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO, "invalid Y"); // all y values
345  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO, "invalid Z"); // all z values
346  }
347  if (!valid) { return { {}, {} }; }
348 
349  const std::array<double, 3> normalVector = { { newX, newY, newZ } };
350  const CCoordinateGeodetic currentPosition(normalVector);
351 
352  valid = CAircraftSituation::isValidVector(normalVector);
353  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
354  {
355  SWIFT_VERIFY_X(valid, Q_FUNC_INFO, "invalid vector");
356  CLogMessage(this).warning(u"Invalid vector v: %2 %3 %4")
357  << normalVector[0] << normalVector[1] << normalVector[2];
358  }
359  if (!valid) { return { {}, {} }; }
360 
361  const double newA =
362  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
363  const CAltitude alt(newA, m_altitudeUnit);
364 
365  return { currentPosition, alt };
366  }
367 
369  {
370  const double t1 = m_pa.t[1];
371  const double t2 = m_pa.t[2]; // latest (adjusted)
372  bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
373  if (!valid) { return { COnGroundInfo::OnGroundSituationUnknown, COnGroundInfo::NotSetGroundDetails }; }
374 
375  const double gnd1 = m_pa.gnd[1];
376  const double gnd2 = m_pa.gnd[2]; // latest
377 
378  if (CAircraftSituation::isGfEqualAirborne(gnd1, gnd2))
379  {
380  return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
381  }
382  else if (CAircraftSituation::isGfEqualOnGround(gnd1, gnd2))
383  {
384  return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
385  }
386  else
387  {
388  const double newGnd =
389  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
390  return COnGroundInfo(newGnd);
391  }
392  }
393 
394  void CInterpolatorSpline::CInterpolant::setTimes(qint64 currentTimeMs, double timeFraction,
395  qint64 interpolatedTimeMs)
396  {
397  m_currentTimeMsSinceEpoc = currentTimeMs;
398  m_interpolatedTime = interpolatedTimeMs;
399  m_pbh.setTimeFraction(timeFraction);
400  }
401 
403  {
404  for (uint i = 0; i < 3; i++)
405  {
406  x[i] = 0;
407  y[i] = 0;
408  z[i] = 0;
409  a[i] = 0;
410  t[i] = 0;
411  dx[i] = 0;
412  dy[i] = 0;
413  dz[i] = 0;
414  da[i] = 0;
415  gnd[i] = 0;
416  dgnd[i] = 0;
417  }
418  }
419 
421  {
422  static const PosArray pa = [] {
423  PosArray p;
424  p.initToZero();
425  return p;
426  }();
427  return pa;
428  }
429 
430  bool CInterpolatorSpline::verifyInterpolationSituations(const CAircraftSituation &oldest,
431  const CAircraftSituation &newer,
432  const CAircraftSituation &latest,
434  {
435  if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return true; }
436  CAircraftSituationList situations;
437 
438  // oldest last, null ignored
439  if (!latest.isNull()) { situations.push_back(latest); }
440  if (!newer.isNull()) { situations.push_back(newer); }
441  if (!oldest.isNull()) { situations.push_back(oldest); }
442 
443  const bool sorted = situations.isSortedAdjustedLatestFirstWithoutNullPositions();
444  if (setup.isNull() || !setup.isAircraftPartsEnabled()) { return sorted; }
445 
446  bool details = false;
447  if (situations.containsOnGroundDetails(COnGroundInfo::InFromParts))
448  {
449  // if a client supports parts, all ground situations are supposed to be parts based
450  details = situations.areAllOnGroundDetailsSame(COnGroundInfo::InFromParts);
451  SWIFT_VERIFY_X(details, Q_FUNC_INFO, "Once gnd.from parts -> always gnd. from parts");
452  }
453 
454  for (const CAircraftSituation &s : situations)
455  {
456  if (!s.hasGroundElevation()) { continue; }
457  SWIFT_VERIFY_X(!s.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO, "Suspicous 0 gnd. value");
458  }
459 
460  // check if middle situation is missing
461  if (latest.hasGroundElevation() && oldest.hasGroundElevation())
462  {
463  SWIFT_VERIFY_X(newer.hasGroundElevation(), Q_FUNC_INFO, "Middle ground elevation is missing");
464  }
465 
466  // result
467  return sorted && details;
468  }
469 
470 } // namespace swift::misc::simulation
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
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
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
void setAdjustedMSecsSinceEpoch(qint64 adjustedTimeMs)
Set timestamp with offset added for interpolation.
bool isNewerThanAdjusted(const ITimestampWithOffsetBased &otherTimestampObj) const
Is this newer than other?
Value object encapsulating information of an aircraft's situation.
bool hasGroundElevation() const
Is ground elevation value available.
bool areAllOnGroundDetailsSame(COnGroundInfo::OnGroundDetails details) const
Are all on ground details the same?
bool containsOnGroundDetails(COnGroundInfo::OnGroundDetails details) const
Contains on ground details?
bool isSortedAdjustedLatestFirstWithoutNullPositions() const
Latest first and no null positions?
Altitude as used in aviation, can be AGL or MSL altitude.
Definition: altitude.h:52
Information about the ground status.
Definition: ongroundinfo.h:19
Plane of same elevation, can be a single point or larger area (e.g. airport)
Physical unit length (length)
Definition: length.h:18
Specialized class for distance units (meter, foot, nautical miles).
Definition: units.h:95
bool isAircraftPartsEnabled() const
Aircraft parts enabled (still requires the other aircraft to send parts)
Value object for interpolator and rendering per callsign.
Simple linear interpolator for pitch, bank, heading and groundspeed from start to end situation.
Cubic function that performs the actual interpolation.
aviation::COnGroundInfo interpolateGroundFactor() const
Interpolate the ground information/factor.
std::tuple< geo::CCoordinateGeodetic, aviation::CAltitude > interpolatePositionAndAltitude() const
Perform the interpolation.
const IInterpolatorPbh & pbh() const
Get the PBH interpolator.
void setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs)
Set the time values.
bool isAcceptableTimeFraction(double timeFraction)
Valid time fraction [0,1], this allows minor overshooting.
double clampValidTimeFraction(double timeFraction)
Clamp time fraction [0,1].
QString arg(Args &&... args) const const
std::array< double, 3 > dz
3 coordinates for spline interpolation
std::array< double, 3 > dx
3 coordinates for spline interpolation
static const PosArray & zeroPosArray()
Zero initialized position array.
std::array< double, 3 > a
3 coordinates for spline interpolation
std::array< double, 3 > y
3 coordinates for spline interpolation
std::array< double, 3 > t
3 coordinates for spline interpolation
std::array< double, 3 > x
3 coordinates for spline interpolation
std::array< double, 3 > gnd
3 coordinates for spline interpolation
std::array< double, 3 > dgnd
3 coordinates for spline interpolation
std::array< double, 3 > z
3 coordinates for spline interpolation
std::array< double, 3 > dy
3 coordinates for spline interpolation
std::array< double, 3 > da
3 coordinates for spline interpolation
Log entry for situation interpolation.
QChar interpolator
what interpolator is used
aviation::CAircraftSituationList interpolationSituations
the interpolator uses 2, 3 situations (latest at end)
qint64 tsInterpolated
timestamp interpolated
double deltaSampleTimesMs
delta time between samples (i.e.
double simTimeFraction
time fraction, expected 0..1
bool interpolantRecalc
interpolant recalculated
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26