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 size_t 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 double dt1 = static_cast<double>(m_currentTimeMsSinceEpoch - m_prevSampleAdjustedTime);
247  const double 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  for (unsigned int i = 0; i < m_s.size(); i++)
299  {
300  if (!m_s[i].hasGroundElevation()) { return true; }
301  }
302  return false;
303  }
304 
305  bool CInterpolatorSpline::isAnySituationNearGroundRelevant() const
306  {
307  for (unsigned int i = 0; i < m_s.size(); i++)
308  {
309  if (!m_s[i].canLikelySkipNearGroundInterpolation()) { return true; }
310  }
311  return false;
312  }
313 
314  CInterpolatorSpline::CInterpolant::CInterpolant(const CInterpolatorSpline::PosArray &pa,
315  const CLengthUnit &altitudeUnit, const CInterpolatorLinearPbh &pbh)
316  : m_pa(pa), m_altitudeUnit(altitudeUnit)
317  {
318  m_pbh = pbh;
319  }
320 
321  std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
323  {
324  const double t1 = m_pa.t[1];
325  const double t2 = m_pa.t[2]; // latest (adjusted)
326 
327  bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
328  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
329  {
330  Q_ASSERT_X(t1 < t2, Q_FUNC_INFO,
331  "Expect sorted times, latest first"); // that means a bug in our code init the values
332  SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO, "invalid timestamp t1");
333  SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc < t2, Q_FUNC_INFO,
334  "invalid timestamp t2"); // t1==t2 results in div/0
335  }
336  if (!valid) { return { {}, {} }; }
337 
338  const double newX =
339  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
340  const double newY =
341  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
342  const double newZ =
343  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
344 
345  valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) &&
346  CAircraftSituation::isValidVector(m_pa.z);
347  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
348  {
349  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO, "invalid X"); // all x values
350  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO, "invalid Y"); // all y values
351  SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO, "invalid Z"); // all z values
352  }
353  if (!valid) { return { {}, {} }; }
354 
355  const std::array<double, 3> normalVector = { { newX, newY, newZ } };
356  const CCoordinateGeodetic currentPosition(normalVector);
357 
358  valid = CAircraftSituation::isValidVector(normalVector);
359  if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
360  {
361  SWIFT_VERIFY_X(valid, Q_FUNC_INFO, "invalid vector");
362  CLogMessage(this).warning(u"Invalid vector v: %2 %3 %4")
363  << normalVector[0] << normalVector[1] << normalVector[2];
364  }
365  if (!valid) { return { {}, {} }; }
366 
367  const double newA =
368  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
369  const CAltitude alt(newA, m_altitudeUnit);
370 
371  return { currentPosition, alt };
372  }
373 
375  {
376  const double t1 = m_pa.t[1];
377  const double t2 = m_pa.t[2]; // latest (adjusted)
378  bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
379  if (!valid) { return { COnGroundInfo::OnGroundSituationUnknown, COnGroundInfo::NotSetGroundDetails }; }
380 
381  const double gnd1 = m_pa.gnd[1];
382  const double gnd2 = m_pa.gnd[2]; // latest
383 
384  if (CAircraftSituation::isGfEqualAirborne(gnd1, gnd2))
385  {
386  return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
387  }
388  else if (CAircraftSituation::isGfEqualOnGround(gnd1, gnd2))
389  {
390  return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
391  }
392  else
393  {
394  const double newGnd =
395  evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
396  return COnGroundInfo(newGnd);
397  }
398  }
399 
400  void CInterpolatorSpline::CInterpolant::setTimes(qint64 currentTimeMs, double timeFraction,
401  qint64 interpolatedTimeMs)
402  {
403  m_currentTimeMsSinceEpoc = currentTimeMs;
404  m_interpolatedTime = interpolatedTimeMs;
405  m_pbh.setTimeFraction(timeFraction);
406  }
407 
409  {
410  for (uint i = 0; i < 3; i++)
411  {
412  x[i] = 0;
413  y[i] = 0;
414  z[i] = 0;
415  a[i] = 0;
416  t[i] = 0;
417  dx[i] = 0;
418  dy[i] = 0;
419  dz[i] = 0;
420  da[i] = 0;
421  gnd[i] = 0;
422  dgnd[i] = 0;
423  }
424  }
425 
427  {
428  static const PosArray pa = [] {
429  PosArray p;
430  p.initToZero();
431  return p;
432  }();
433  return pa;
434  }
435 
436  bool CInterpolatorSpline::verifyInterpolationSituations(const CAircraftSituation &oldest,
437  const CAircraftSituation &newer,
438  const CAircraftSituation &latest,
440  {
441  if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return true; }
442  CAircraftSituationList situations;
443 
444  // oldest last, null ignored
445  if (!latest.isNull()) { situations.push_back(latest); }
446  if (!newer.isNull()) { situations.push_back(newer); }
447  if (!oldest.isNull()) { situations.push_back(oldest); }
448 
449  const bool sorted = situations.isSortedAdjustedLatestFirstWithoutNullPositions();
450  if (setup.isNull() || !setup.isAircraftPartsEnabled()) { return sorted; }
451 
452  bool details = false;
453  if (situations.containsOnGroundDetails(COnGroundInfo::InFromParts))
454  {
455  // if a client supports parts, all ground situations are supposed to be parts based
456  details = situations.areAllOnGroundDetailsSame(COnGroundInfo::InFromParts);
457  SWIFT_VERIFY_X(details, Q_FUNC_INFO, "Once gnd.from parts -> always gnd. from parts");
458  }
459 
460  for (const CAircraftSituation &s : situations)
461  {
462  if (!s.hasGroundElevation()) { continue; }
463  SWIFT_VERIFY_X(!s.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO, "Suspicous 0 gnd. value");
464  }
465 
466  // check if middle situation is missing
467  if (latest.hasGroundElevation() && oldest.hasGroundElevation())
468  {
469  SWIFT_VERIFY_X(newer.hasGroundElevation(), Q_FUNC_INFO, "Middle ground elevation is missing");
470  }
471 
472  // result
473  return sorted && details;
474  }
475 
476 } // 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.
virtual bool isNull() const
Null situation.
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