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