swift
Loading...
Searching...
No Matches
interpolatorlinear.cpp
1// SPDX-FileCopyrightText: Copyright (C) 2014 swift Project Community / Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3
5
6#include <array>
7
12#include "misc/logmessage.h"
14#include "misc/range.h"
16#include "misc/verify.h"
17
18using namespace swift::config;
19using namespace swift::misc::aviation;
20using namespace swift::misc::geo;
21using namespace swift::misc::math;
22using namespace swift::misc::physical_quantities;
23using namespace swift::misc::simulation;
24
25namespace swift::misc::simulation
26{
28 : m_startSituation(startSituation), m_pbh(0, startSituation, startSituation)
29 {}
30
33 : m_startSituation(startSituation), m_pbh(pbh)
34 {}
35
37 const CAircraftSituation &endSituation, double timeFraction,
38 qint64 interpolatedTime)
39 : IInterpolant(interpolatedTime), m_startSituation(startSituation), m_endSituation(endSituation),
40 m_simulationTimeFraction(timeFraction)
41 {
43 {
44 SWIFT_VERIFY_X(isValidTimeFraction(m_simulationTimeFraction), Q_FUNC_INFO,
45 "Time fraction needs to be within [0;1]");
46 }
47 m_pbh = CInterpolatorLinearPbh(m_simulationTimeFraction, startSituation, endSituation);
48 }
49
50 void CInterpolatorLinear::anchor() {}
51
52 std::tuple<geo::CCoordinateGeodetic, aviation::CAltitude>
54 {
55 const std::array<double, 3> startVec(m_startSituation.getPosition().normalVectorDouble());
56 const std::array<double, 3> endVec(m_endSituation.getPosition().normalVectorDouble());
57
59 {
60 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(startVec), Q_FUNC_INFO, "Invalid start vector");
61 SWIFT_VERIFY_X(CAircraftSituation::isValidVector(endVec), Q_FUNC_INFO, "Invalid end vector");
62 SWIFT_VERIFY_X(isAcceptableTimeFraction(m_simulationTimeFraction), Q_FUNC_INFO, "Invalid fraction");
63 }
64
65 // Interpolate position: pos = (posB - posA) * t + posA
66 CCoordinateGeodetic interpolatedPosition;
67 const double tf = clampValidTimeFraction(m_simulationTimeFraction);
68 interpolatedPosition.setNormalVector((endVec[0] - startVec[0]) * tf + startVec[0],
69 (endVec[1] - startVec[1]) * tf + startVec[1],
70 (endVec[2] - startVec[2]) * tf + startVec[2]);
71
73 {
74 SWIFT_VERIFY_X(interpolatedPosition.isValidVectorRange(), Q_FUNC_INFO, "Invalid vector");
75 }
76
77 // Interpolate altitude: Alt = (AltB - AltA) * t + AltA
78 // avoid underflow below ground elevation by using getCorrectedAltitude
79 const CAltitude oldAlt(m_startSituation.getCorrectedAltitude());
80 const CAltitude newAlt(m_endSituation.getCorrectedAltitude());
81 Q_ASSERT_X(oldAlt.getReferenceDatum() == CAltitude::MeanSeaLevel &&
82 oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(),
83 Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
84 const CAltitude altitude((newAlt - oldAlt) * tf + oldAlt, oldAlt.getReferenceDatum());
85
86 return { interpolatedPosition, altitude };
87 }
88
90 {
91 const double startGroundFactor = m_startSituation.getOnGroundInfo().getGroundFactor();
92 const double endGroundFactor = m_endSituation.getOnGroundInfo().getGroundFactor();
93 if (CAircraftSituation::isGfEqualAirborne(startGroundFactor, endGroundFactor))
94 {
95 return { COnGroundInfo::NotOnGround, COnGroundInfo::OnGroundByInterpolation };
96 }
97 else if (CAircraftSituation::isGfEqualOnGround(startGroundFactor, endGroundFactor))
98 {
99 return { COnGroundInfo::OnGround, COnGroundInfo::OnGroundByInterpolation };
100 }
101 else
102 {
103 const double tf = clampValidTimeFraction(m_simulationTimeFraction);
104 const double interpolatedGroundFactor = (endGroundFactor - startGroundFactor) * tf + startGroundFactor;
105 return COnGroundInfo(interpolatedGroundFactor);
106 }
107 }
108
110 {
111 // set default situations
112 CAircraftSituation startSituation = m_interpolant.getStartSituation();
113 CAircraftSituation endSituation = m_interpolant.getEndSituation();
114
115 Q_ASSERT_X(endSituation.getAdjustedMSecsSinceEpoch() >= startSituation.getAdjustedMSecsSinceEpoch(),
116 Q_FUNC_INFO, "Wrong order");
117
119 const bool newSplit = endSituation.getAdjustedMSecsSinceEpoch() < m_currentTimeMsSinceEpoch;
120 const bool recalculate = updated || newSplit;
121
122 if (recalculate)
123 {
125
126 // find the first situation earlier than the current time
127 const auto pivot =
128 std::partition_point(m_currentSituations.begin(), m_currentSituations.end(), [=, this](auto &&s) {
129 return s.getAdjustedMSecsSinceEpoch() > m_currentTimeMsSinceEpoch;
130 });
131 const auto situationsNewer = makeRange(m_currentSituations.begin(), pivot);
132 const auto situationsOlder = makeRange(pivot, m_currentSituations.end());
133
134 // latest first, now 00:20 split time
135 // time pos
136 // 00:25 10 newer
137 // 00:20 11 newer
138 // <----- split
139 // 00:15 12 older
140 // 00:10 13 older
141 // 00:05 14 older
142
143 // The first condition covers a situation, when there are no before / after situations.
144 // We just place at the last position until we get before / after situations
145 if (situationsOlder.isEmpty() || situationsNewer.isEmpty())
146 {
147 // no before situations
148 if (situationsOlder.isEmpty())
149 {
150 const CAircraftSituation currentSituation(*(situationsNewer.end() - 1)); // oldest newest
151 m_currentInterpolationStatus.setInterpolatedAndCheckSituation(false, currentSituation);
152 m_interpolant = { currentSituation };
153 return m_interpolant;
154 }
155
156 // only one before situation
157 if (situationsOlder.size() < 2)
158 {
159 const CAircraftSituation currentSituation(situationsOlder.front()); // latest oldest
160 m_currentInterpolationStatus.setInterpolatedAndCheckSituation(false, currentSituation);
161 m_interpolant = { currentSituation };
162 return m_interpolant;
163 }
164
165 // extrapolate from two before situations
166 startSituation = *(situationsOlder.begin() + 1); // before newest
167 endSituation = situationsOlder.front(); // newest
168 }
169 else
170 {
171 startSituation = situationsOlder.front(); // first oldest (aka newest oldest)
172 endSituation = *(situationsNewer.end() - 1); // latest newest (aka oldest of newer block)
173 Q_ASSERT(startSituation.getAdjustedMSecsSinceEpoch() < endSituation.getAdjustedMSecsSinceEpoch());
174 }
175
176 // adjust ground if required
177 if (!startSituation.canLikelySkipNearGroundInterpolation() && !startSituation.hasGroundElevation())
178 {
179 const CElevationPlane planeOld =
182 }
183 if (!endSituation.canLikelySkipNearGroundInterpolation() && !endSituation.hasGroundElevation())
184 {
185 const CElevationPlane planeNew =
188 }
189 } // modified situations
190
191 CAircraftSituation currentSituation(startSituation); // also sets ground elevation if available
192
193 // Time between start and end packet
194 const qint64 sampleDeltaTimeMs =
195 endSituation.getAdjustedMSecsSinceEpoch() - startSituation.getAdjustedMSecsSinceEpoch();
196 Q_ASSERT_X(sampleDeltaTimeMs >= 0, Q_FUNC_INFO, "Negative delta time");
197 log.interpolator = 'l';
198
199 // Fraction of the deltaTime, ideally [0.0 - 1.0]
200 // < 0 should not happen due to the split, > 1 can happen if new values are delayed beyond split time
201 // 1) values > 1 mean extrapolation
202 // 2) values > 2 mean no new situations coming in
203 const double distanceToSplitTimeMs = endSituation.getAdjustedMSecsSinceEpoch() - m_currentTimeMsSinceEpoch;
204 double simulationTimeFraction = qMax(1.0 - (distanceToSplitTimeMs / sampleDeltaTimeMs), 0.0);
205 if (simulationTimeFraction >= 1.0)
206 {
207 simulationTimeFraction = 1.0;
208 if (qAbs(distanceToSplitTimeMs) > 100)
209 {
210 CLogMessage(this).debug(u"Distance to split: %1") << distanceToSplitTimeMs;
211 }
212 }
213
214 const double deltaTimeFractionMs = sampleDeltaTimeMs * simulationTimeFraction;
215 const qint64 interpolatedTime = startSituation.getMSecsSinceEpoch() + qRound(deltaTimeFractionMs);
216
217 // Ref T297 adjust offset time, but this already the interpolated situation
218 currentSituation.setTimeOffsetMs(
219 startSituation.getTimeOffsetMs() +
220 qRound((endSituation.getTimeOffsetMs() - startSituation.getTimeOffsetMs()) * simulationTimeFraction));
221 currentSituation.setMSecsSinceEpoch(interpolatedTime);
222 m_currentInterpolationStatus.setInterpolatedAndCheckSituation(true, currentSituation);
223
224 if (this->doLogging())
225 {
227 log.deltaSampleTimesMs = sampleDeltaTimeMs;
228 log.simTimeFraction = simulationTimeFraction;
229 log.deltaSampleTimesMs = sampleDeltaTimeMs;
230 log.tsInterpolated = interpolatedTime;
232 log.interpolationSituations.push_back(startSituation); // oldest at front
233 log.interpolationSituations.push_back(endSituation); // latest at back
234 log.interpolantRecalc = recalculate;
235 }
236
237 m_interpolant = { startSituation, endSituation, simulationTimeFraction, interpolatedTime };
238 m_interpolant.setRecalculated(recalculate);
239
240 return m_interpolant;
241 }
242} // namespace swift::misc::simulation
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Class for emitting a log message.
Definition logmessage.h:27
Derived & debug()
Set the severity to debug.
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
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
void setMSecsSinceEpoch(qint64 mSecsSinceEpoch)
Timestamp as ms value.
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
qint64 getTimeOffsetMs() const
Milliseconds to add to timestamp for interpolation.
void setTimeOffsetMs(qint64 offset)
Milliseconds to add to timestamp for interpolation.
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.
bool setGroundElevationChecked(const geo::CElevationPlane &elevationPlane, GndElevationInfo info, bool transferred=false)
Set elevation of the ground directly beneath, but checked.
bool canLikelySkipNearGroundInterpolation() const
Situation looks like an aircraft not near ground.
static bool isGfEqualOnGround(double oldGroundFactor, double newGroundFactor)
Both on ground.
Altitude as used in aviation, can be AGL or MSL altitude.
Definition altitude.h:52
ReferenceDatum getReferenceDatum() const
Get reference datum (MSL or AGL).
Definition altitude.h:145
Information about the ground status.
@ OnGroundByInterpolation
strongest for remote aircraft
double getGroundFactor() const
Get the ground factor Use this for interpolation only!! For just checking if the info is OnGround or ...
void setNormalVector(const QVector3D &normal)
Set normal vector.
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.
qint64 m_situationsLastModified
when situations were last modified
CInterpolationStatus m_currentInterpolationStatus
this step's situation status
qint64 m_situationsLastModifiedUsed
interpolant based on situations last updated
aviation::CAircraftSituationList m_currentSituations
current situations obtained by remoteAircraftSituationsAndChange
aviation::COnGroundInfo interpolateGroundFactor() const
Interpolate the ground information/factor.
const IInterpolatorPbh & pbh() const
Get the PBH interpolator.
std::tuple< geo::CCoordinateGeodetic, aviation::CAltitude > interpolatePositionAndAltitude() const
Perform the interpolation.
const IInterpolant & getInterpolant(SituationLog &log)
Get the interpolant for the given time point.
Simple linear interpolator for pitch, bank, heading and groundspeed from start to end situation.
geo::CElevationPlane findClosestElevationWithinRange(const geo::ICoordinateGeodetic &reference, const physical_quantities::CLength &range) const
Find closest elevation (or return NULL).
IInterpolant()=default
Default ctor.
bool isValidTimeFraction(double timeFraction)
Valid time fraction [0,1].
bool isAcceptableTimeFraction(double timeFraction)
Valid time fraction [0,1], this allows minor overshooting.
double clampValidTimeFraction(double timeFraction)
Clamp time fraction [0,1].
auto makeRange(I begin, I2 end) -> CRange< I >
Returns a CRange constructed from begin and end iterators of deduced types.
Definition range.h:328
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