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