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;
20 namespace swift::misc::simulation
26 std::array<double, N> solveTridiagonal(std::array<std::array<double, N>, N> &matrix, std::array<double, N> &d)
29 const auto a = [&matrix](
size_t i) ->
double & {
return matrix[i][i - 1]; };
30 const auto b = [&matrix](
size_t i) ->
double & {
return matrix[i][i]; };
31 const auto c = [&matrix](
size_t i) ->
double & {
return matrix[i][i + 1]; };
36 for (
size_t i = 1; i < N; ++i)
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;
44 for (
int i = N - 2; i >= 0; --i)
46 const size_t it =
static_cast<size_t>(i);
47 d[it] -= c(it) * d[it + 1];
57 std::array<double, N> getDerivatives(
const std::array<double, N> &x,
const std::array<double, N> &y)
59 std::array<std::array<double, N>, N> a { {} };
60 std::array<double, N> b { {} };
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]));
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]));
71 for (
size_t i = 1; i < N - 1; ++i)
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]));
81 solveTridiagonal(a, b);
86 double evalSplineInterval(
double x,
double x0,
double x1,
double y0,
double y1,
double k0,
double k1)
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);
102 bool CInterpolatorSpline::fillSituationsArray()
108 if (m_lastSituation.isNull())
110 if (m_currentSituations.isEmpty())
113 m_s[0] = m_s[1] = m_s[2] = CAircraftSituation::null();
121 m_s[0] = m_s[1] = m_s[2] = f;
127 m_s[0] = m_s[1] = m_s[2] = m_lastSituation;
131 const qint64 os = qMax(CFsdSetup::c_interimPositionTimeOffsetMsec, m_s[2].getTimeOffsetMs());
132 m_s[0].addMsecs(-os);
134 if (m_currentSituations.isEmpty()) {
return false; }
143 const qint64 osNotTooClose = qRound64(0.8 * os);
145 m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted - osNotTooClose);
146 if (!older.
isNull()) { m_s[0] = older; }
150 m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted);
151 if (!closeOlder.
isNull()) { m_s[0] = closeOlder; }
154 const qint64 olderAdjusted = m_s[0].getAdjustedMSecsSinceEpoch();
158 const bool hasNewer = latestAdjusted > m_currentTimeMsSinceEpoch;
162 const bool verified =
163 verifyInterpolationSituations(m_s[0], m_s[1], m_s[2]);
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);
176 void CInterpolatorSpline::anchor() {}
182 const bool recalculate = (m_currentTimeMsSinceEpoch >= m_nextSampleAdjustedTime) ||
183 (m_situationsLastModified > m_situationsLastModifiedUsed);
189 m_situationsLastModifiedUsed = m_situationsLastModified;
190 const bool fillStatus = this->fillSituationsArray();
193 m_interpolant.setValid(
false);
194 return m_interpolant;
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() } };
201 pa.
x = { { normals[0][0], normals[1][0], normals[2][0] } };
202 pa.
y = { { normals[0][1], normals[1][1], normals[2][1] } };
203 pa.
z = { { normals[0][2], normals[1][2], normals[2][2] } };
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()) } };
208 pa.
dx = getDerivatives(pa.
t, pa.
x);
209 pa.
dy = getDerivatives(pa.
t, pa.
y);
210 pa.
dz = getDerivatives(pa.
t, pa.
z);
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);
222 const double a1 = m_s[1].getCorrectedAltitude(cg).value(altUnit);
223 const double a2 = m_s[2].getCorrectedAltitude(cg).value(altUnit);
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);
230 m_prevSampleAdjustedTime = m_s[1].getAdjustedMSecsSinceEpoch();
231 m_nextSampleAdjustedTime = m_s[2].getAdjustedMSecsSinceEpoch();
232 m_prevSampleTime = m_s[1].getMSecsSinceEpoch();
233 m_nextSampleTime = m_s[2].getMSecsSinceEpoch();
235 Q_ASSERT_X(m_prevSampleAdjustedTime < m_nextSampleAdjustedTime, Q_FUNC_INFO,
"Wrong time order");
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;
261 const qint64 interpolatedTime = m_prevSampleTime + qRound64(timeFraction * dt2);
264 m_currentInterpolationStatus.setInterpolated(
true);
265 m_interpolant.setTimes(m_currentTimeMsSinceEpoch, timeFraction, interpolatedTime);
266 m_interpolant.setRecalculated(recalculate);
268 if (this->doLogging())
281 return m_interpolant;
284 bool CInterpolatorSpline::updateElevations(
bool canSkip)
286 bool updated =
false;
289 if (s.hasGroundElevation()) {
continue; }
290 if (canSkip && s.canLikelySkipNearGroundInterpolation()) {
continue; }
293 this->findClosestElevationWithinRange(s, CElevationPlane::singlePointRadius());
294 const bool u = s.setGroundElevationChecked(plane, CAircraftSituation::FromCache);
300 bool CInterpolatorSpline::areAnyElevationsMissing()
const
302 for (
unsigned int i = 0; i < m_s.size(); i++)
304 if (!m_s[i].hasGroundElevation()) {
return true; }
309 bool CInterpolatorSpline::isAnySituationNearGroundRelevant()
const
311 for (
unsigned int i = 0; i < m_s.size(); i++)
313 if (!m_s[i].canLikelySkipNearGroundInterpolation()) {
return true; }
320 : m_pa(pa), m_altitudeUnit(altitudeUnit)
325 std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
328 const double t1 = m_pa.t[1];
329 const double t2 = m_pa.t[2];
331 bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
332 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
334 Q_ASSERT_X(t1 < t2, Q_FUNC_INFO,
335 "Expect sorted times, latest first");
336 SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO,
"invalid timestamp t1");
338 "invalid timestamp t2");
340 if (!valid) {
return { {}, {} }; }
343 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
345 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
347 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
349 valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) &&
350 CAircraftSituation::isValidVector(m_pa.z);
351 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
353 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO,
"invalid X");
354 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO,
"invalid Y");
355 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO,
"invalid Z");
357 if (!valid) {
return { {}, {} }; }
359 const std::array<double, 3> normalVector = { { newX, newY, newZ } };
362 valid = CAircraftSituation::isValidVector(normalVector);
363 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
367 << normalVector[0] << normalVector[1] << normalVector[2];
369 if (!valid) {
return { {}, {} }; }
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);
375 return { currentPosition, alt };
380 const double t1 = m_pa.t[1];
381 const double t2 = m_pa.t[2];
382 bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
383 if (!valid) {
return { COnGroundInfo::OnGroundSituationUnknown, COnGroundInfo::NotSetGroundDetails }; }
385 const double gnd1 = m_pa.gnd[1];
386 const double gnd2 = m_pa.gnd[2];
388 if (CAircraftSituation::isGfEqualAirborne(gnd1, gnd2))
390 return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
392 else if (CAircraftSituation::isGfEqualOnGround(gnd1, gnd2))
394 return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
398 const double newGnd =
399 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
405 qint64 interpolatedTimeMs)
407 m_currentTimeMsSinceEpoc = currentTimeMs;
408 m_interpolatedTime = interpolatedTimeMs;
409 m_pbh.setTimeFraction(timeFraction);
414 for (uint i = 0; i < 3; i++)
440 bool CInterpolatorSpline::verifyInterpolationSituations(
const CAircraftSituation &oldest,
445 if (!CBuildConfig::isLocalDeveloperDebugBuild()) {
return true; }
456 bool details =
false;
461 SWIFT_VERIFY_X(details, Q_FUNC_INFO,
"Once gnd.from parts -> always gnd. from parts");
466 if (!s.hasGroundElevation()) {
continue; }
467 SWIFT_VERIFY_X(!s.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO,
"Suspicous 0 gnd. value");
477 return sorted && details;
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Class for emitting a log message.
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.
void clear()
Removes all elements in the sequence.
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.
List of aircraft situations.
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.
Information about the ground status.
Plane of same elevation, can be a single point or larger area (e.g. airport)
Physical unit length (length)
Specialized class for distance units (meter, foot, nautical miles).
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].
Position arrays for interpolation.
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
void initToZero()
Init all values to zero.
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.