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)
28 const auto a = [&matrix](
size_t i) ->
double & {
return matrix[i][i - 1]; };
29 const auto b = [&matrix](
size_t i) ->
double & {
return matrix[i][i]; };
30 const auto c = [&matrix](
size_t i) ->
double & {
return matrix[i][i + 1]; };
35 for (
size_t i = 1; i < N; ++i)
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;
43 for (
int i = N - 2; i >= 0; --i)
45 const size_t it =
static_cast<size_t>(i);
46 d[it] -= c(it) * d[it + 1];
55 std::array<double, N> getDerivatives(
const std::array<double, N> &x,
const std::array<double, N> &y)
57 std::array<std::array<double, N>, N> a { {} };
58 std::array<double, N> b { {} };
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]));
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]));
68 for (
size_t i = 1; i < N - 1; ++i)
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]));
77 solveTridiagonal(a, b);
82 double evalSplineInterval(
double x,
double x0,
double x1,
double y0,
double y1,
double k0,
double k1)
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);
98 bool CInterpolatorSpline::fillSituationsArray()
104 if (m_lastSituation.isNull())
106 if (m_currentSituations.isEmpty())
109 m_s[0] = m_s[1] = m_s[2] = CAircraftSituation::null();
117 m_s[0] = m_s[1] = m_s[2] = f;
123 m_s[0] = m_s[1] = m_s[2] = m_lastSituation;
127 const qint64 os = qMax(CFsdSetup::c_interimPositionTimeOffsetMsec, m_s[2].getTimeOffsetMs());
128 m_s[0].addMsecs(-os);
130 if (m_currentSituations.isEmpty()) {
return false; }
139 const qint64 osNotTooClose = qRound64(0.8 * os);
141 m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted - osNotTooClose);
142 if (!older.
isNull()) { m_s[0] = older; }
146 m_currentSituations.findObjectBeforeAdjustedOrDefault(currentAdjusted);
147 if (!closeOlder.
isNull()) { m_s[0] = closeOlder; }
150 const qint64 olderAdjusted = m_s[0].getAdjustedMSecsSinceEpoch();
154 const bool hasNewer = latestAdjusted > m_currentTimeMsSinceEpoch;
158 const bool verified =
159 verifyInterpolationSituations(m_s[0], m_s[1], m_s[2]);
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);
172 void CInterpolatorSpline::anchor() {}
178 const bool recalculate = (m_currentTimeMsSinceEpoch >= m_nextSampleAdjustedTime) ||
179 (m_situationsLastModified > m_situationsLastModifiedUsed);
185 m_situationsLastModifiedUsed = m_situationsLastModified;
186 const bool fillStatus = this->fillSituationsArray();
189 m_interpolant.setValid(
false);
190 return m_interpolant;
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() } };
197 pa.
x = { { normals[0][0], normals[1][0], normals[2][0] } };
198 pa.
y = { { normals[0][1], normals[1][1], normals[2][1] } };
199 pa.
z = { { normals[0][2], normals[1][2], normals[2][2] } };
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()) } };
204 pa.
dx = getDerivatives(pa.
t, pa.
x);
205 pa.
dy = getDerivatives(pa.
t, pa.
y);
206 pa.
dz = getDerivatives(pa.
t, pa.
z);
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);
218 const double a1 = m_s[1].getCorrectedAltitude(cg).value(altUnit);
219 const double a2 = m_s[2].getCorrectedAltitude(cg).value(altUnit);
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);
226 m_prevSampleAdjustedTime = m_s[1].getAdjustedMSecsSinceEpoch();
227 m_nextSampleAdjustedTime = m_s[2].getAdjustedMSecsSinceEpoch();
228 m_prevSampleTime = m_s[1].getMSecsSinceEpoch();
229 m_nextSampleTime = m_s[2].getMSecsSinceEpoch();
231 Q_ASSERT_X(m_prevSampleAdjustedTime < m_nextSampleAdjustedTime, Q_FUNC_INFO,
"Wrong time order");
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;
257 const qint64 interpolatedTime = m_prevSampleTime + qRound64(timeFraction * dt2);
260 m_currentInterpolationStatus.setInterpolated(
true);
261 m_interpolant.setTimes(m_currentTimeMsSinceEpoch, timeFraction, interpolatedTime);
262 m_interpolant.setRecalculated(recalculate);
264 if (this->doLogging())
277 return m_interpolant;
280 bool CInterpolatorSpline::updateElevations(
bool canSkip)
282 bool updated =
false;
285 if (s.hasGroundElevation()) {
continue; }
286 if (canSkip && s.canLikelySkipNearGroundInterpolation()) {
continue; }
289 this->findClosestElevationWithinRange(s, CElevationPlane::singlePointRadius());
290 const bool u = s.setGroundElevationChecked(plane, CAircraftSituation::FromCache);
296 bool CInterpolatorSpline::areAnyElevationsMissing()
const
298 for (
unsigned int i = 0; i < m_s.size(); i++)
300 if (!m_s[i].hasGroundElevation()) {
return true; }
305 bool CInterpolatorSpline::isAnySituationNearGroundRelevant()
const
307 for (
unsigned int i = 0; i < m_s.size(); i++)
309 if (!m_s[i].canLikelySkipNearGroundInterpolation()) {
return true; }
316 : m_pa(pa), m_altitudeUnit(altitudeUnit)
321 std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
324 const double t1 = m_pa.t[1];
325 const double t2 = m_pa.t[2];
327 bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
328 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
330 Q_ASSERT_X(t1 < t2, Q_FUNC_INFO,
331 "Expect sorted times, latest first");
332 SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO,
"invalid timestamp t1");
334 "invalid timestamp t2");
336 if (!valid) {
return { {}, {} }; }
339 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
341 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
343 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
345 valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) &&
346 CAircraftSituation::isValidVector(m_pa.z);
347 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
349 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO,
"invalid X");
350 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO,
"invalid Y");
351 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO,
"invalid Z");
353 if (!valid) {
return { {}, {} }; }
355 const std::array<double, 3> normalVector = { { newX, newY, newZ } };
358 valid = CAircraftSituation::isValidVector(normalVector);
359 if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
363 << normalVector[0] << normalVector[1] << normalVector[2];
365 if (!valid) {
return { {}, {} }; }
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);
371 return { currentPosition, alt };
376 const double t1 = m_pa.t[1];
377 const double t2 = m_pa.t[2];
378 bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
379 if (!valid) {
return { COnGroundInfo::OnGroundSituationUnknown, COnGroundInfo::NotSetGroundDetails }; }
381 const double gnd1 = m_pa.gnd[1];
382 const double gnd2 = m_pa.gnd[2];
384 if (CAircraftSituation::isGfEqualAirborne(gnd1, gnd2))
386 return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
388 else if (CAircraftSituation::isGfEqualOnGround(gnd1, gnd2))
390 return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
394 const double newGnd =
395 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
401 qint64 interpolatedTimeMs)
403 m_currentTimeMsSinceEpoc = currentTimeMs;
404 m_interpolatedTime = interpolatedTimeMs;
405 m_pbh.setTimeFraction(timeFraction);
410 for (uint i = 0; i < 3; i++)
436 bool CInterpolatorSpline::verifyInterpolationSituations(
const CAircraftSituation &oldest,
441 if (!CBuildConfig::isLocalDeveloperDebugBuild()) {
return true; }
452 bool details =
false;
457 SWIFT_VERIFY_X(details, Q_FUNC_INFO,
"Once gnd.from parts -> always gnd. from parts");
462 if (!s.hasGroundElevation()) {
continue; }
463 SWIFT_VERIFY_X(!s.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO,
"Suspicous 0 gnd. value");
473 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].
QString arg(Args &&... args) const const
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.