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 auto 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 auto dt1 = 
static_cast<double>(m_currentTimeMsSinceEpoch - m_prevSampleAdjustedTime);
 
  247         const auto 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         return std::any_of(m_s.begin(), m_s.end(),
 
  299                            [](
const auto &situation) { return !situation.hasGroundElevation(); });
 
  302     bool CInterpolatorSpline::isAnySituationNearGroundRelevant()
 const 
  304         return std::any_of(m_s.begin(), m_s.end(),
 
  305                            [](
const auto &situation) { return !situation.canLikelySkipNearGroundInterpolation(); });
 
  310         : m_pa(pa), m_altitudeUnit(altitudeUnit)
 
  315     std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
 
  318         const double t1 = m_pa.t[1];
 
  319         const double t2 = m_pa.t[2]; 
 
  321         bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
 
  322         if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
 
  324             Q_ASSERT_X(t1 < t2, Q_FUNC_INFO,
 
  325                        "Expect sorted times, latest first"); 
 
  326             SWIFT_VERIFY_X(m_currentTimeMsSinceEpoc >= t1, Q_FUNC_INFO, 
"invalid timestamp t1");
 
  328                            "invalid timestamp t2"); 
 
  330         if (!valid) { 
return { {}, {} }; }
 
  333             evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
 
  335             evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
 
  337             evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
 
  339         valid = CAircraftSituation::isValidVector(m_pa.x) && CAircraftSituation::isValidVector(m_pa.y) &&
 
  340                 CAircraftSituation::isValidVector(m_pa.z);
 
  341         if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
 
  343             SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO, 
"invalid X"); 
 
  344             SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO, 
"invalid Y"); 
 
  345             SWIFT_VERIFY_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO, 
"invalid Z"); 
 
  347         if (!valid) { 
return { {}, {} }; }
 
  349         const std::array<double, 3> normalVector = { { newX, newY, newZ } };
 
  352         valid = CAircraftSituation::isValidVector(normalVector);
 
  353         if (!valid && CBuildConfig::isLocalDeveloperDebugBuild())
 
  357                 << normalVector[0] << normalVector[1] << normalVector[2];
 
  359         if (!valid) { 
return { {}, {} }; }
 
  362             evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
 
  363         const CAltitude alt(newA, m_altitudeUnit);
 
  365         return { currentPosition, alt };
 
  370         const double t1 = m_pa.t[1];
 
  371         const double t2 = m_pa.t[2]; 
 
  372         bool valid = (t1 < t2) && (m_currentTimeMsSinceEpoc >= t1) && (m_currentTimeMsSinceEpoc < t2);
 
  373         if (!valid) { 
return { COnGroundInfo::OnGroundSituationUnknown, COnGroundInfo::NotSetGroundDetails }; }
 
  375         const double gnd1 = m_pa.gnd[1];
 
  376         const double gnd2 = m_pa.gnd[2]; 
 
  378         if (CAircraftSituation::isGfEqualAirborne(gnd1, gnd2))
 
  380             return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
 
  382         else if (CAircraftSituation::isGfEqualOnGround(gnd1, gnd2))
 
  384             return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
 
  388             const double newGnd =
 
  389                 evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
 
  395                                                      qint64 interpolatedTimeMs)
 
  397         m_currentTimeMsSinceEpoc = currentTimeMs;
 
  398         m_interpolatedTime = interpolatedTimeMs;
 
  399         m_pbh.setTimeFraction(timeFraction);
 
  404         for (uint i = 0; i < 3; i++)
 
  430     bool CInterpolatorSpline::verifyInterpolationSituations(
const CAircraftSituation &oldest,
 
  435         if (!CBuildConfig::isLocalDeveloperDebugBuild()) { 
return true; }
 
  446         bool details = 
false;
 
  451             SWIFT_VERIFY_X(details, Q_FUNC_INFO, 
"Once gnd.from parts -> always gnd. from parts");
 
  456             if (!s.hasGroundElevation()) { 
continue; }
 
  457             SWIFT_VERIFY_X(!s.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO, 
"Suspicous 0 gnd. value");
 
  467         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.
 
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.