mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-19 20:25:29 +08:00
Ref T259, Ref T243 interpolator changes
* interpolators use providers for client/situations/parts/ground elevation * interpolators do no longer use own situations/parts, but those from provider * interpolators are no longer QObjects (as it is not needed) * use gnd flag from situation for gnd interpolation, there is no longer a parts gnd flag interpolation * guess parts during interpolation * changed iterators to m_s[i] as it makes clearer which values are used ** the flag is transferred from parts -> situation in airspace monitor ** if the other client already provides and gnd.flag in situation this also works * adjusted logging * use providers in unit tests / adjusted tests * improved situation verification/assert
This commit is contained in:
committed by
Roland Winklmeier
parent
5c6a37c669
commit
cd1ce37ec3
@@ -310,7 +310,7 @@ namespace BlackMisc
|
|||||||
|
|
||||||
QStringLiteral("<td>") % msSinceEpochToTime(log.tsInterpolated) % QStringLiteral("</td>") %
|
QStringLiteral("<td>") % msSinceEpochToTime(log.tsInterpolated) % QStringLiteral("</td>") %
|
||||||
QStringLiteral("<td>") % QString::number(log.deltaSampleTimesMs) % QStringLiteral("ms</td>") %
|
QStringLiteral("<td>") % QString::number(log.deltaSampleTimesMs) % QStringLiteral("ms</td>") %
|
||||||
QStringLiteral("<td>") % QString::number(log.simulationTimeFraction) % QStringLiteral("</td>");
|
QStringLiteral("<td>") % QString::number(log.simTimeFraction) % QStringLiteral("</td>");
|
||||||
|
|
||||||
tableRows +=
|
tableRows +=
|
||||||
QStringLiteral("<td class=\"old\">") % situationOld.latitudeAsString() % QStringLiteral("</td>") %
|
QStringLiteral("<td class=\"old\">") % situationOld.latitudeAsString() % QStringLiteral("</td>") %
|
||||||
@@ -376,14 +376,8 @@ namespace BlackMisc
|
|||||||
|
|
||||||
void CInterpolationLogger::clearLog()
|
void CInterpolationLogger::clearLog()
|
||||||
{
|
{
|
||||||
{
|
{ QWriteLocker l(&m_lockSituations); m_situationLogs.clear(); }
|
||||||
QWriteLocker l(&m_lockSituations);
|
{ QWriteLocker l(&m_lockParts); m_partsLogs.clear(); }
|
||||||
m_situationLogs.clear();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
QWriteLocker l(&m_lockParts);
|
|
||||||
m_partsLogs.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CInterpolationLogger::msSinceEpochToTime(qint64 ms)
|
QString CInterpolationLogger::msSinceEpochToTime(qint64 ms)
|
||||||
@@ -418,6 +412,8 @@ namespace BlackMisc
|
|||||||
QStringLiteral("ts: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) %
|
QStringLiteral("ts: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsCurrent) %
|
||||||
QStringLiteral(" | type: ") % this->interpolationType() %
|
QStringLiteral(" | type: ") % this->interpolationType() %
|
||||||
QStringLiteral(" | gnd.fa.: ") % QString::number(groundFactor) %
|
QStringLiteral(" | gnd.fa.: ") % QString::number(groundFactor) %
|
||||||
|
QStringLiteral(" | CG: ") % cgAboveGround.valueRoundedWithUnit(CLengthUnit::m(), 1) %
|
||||||
|
QStringLiteral(" | alt.cor.: ") % altCorrection %
|
||||||
QStringLiteral(" | #nw.sit.: ") % QString::number(noNetworkSituations) %
|
QStringLiteral(" | #nw.sit.: ") % QString::number(noNetworkSituations) %
|
||||||
(
|
(
|
||||||
withSetup ?
|
withSetup ?
|
||||||
@@ -427,9 +423,7 @@ namespace BlackMisc
|
|||||||
(
|
(
|
||||||
withElevation ?
|
withElevation ?
|
||||||
separator %
|
separator %
|
||||||
QStringLiteral("Elev.: ") %
|
QStringLiteral("Elev info.: ") % elevationInfo :
|
||||||
QStringLiteral("transf.elv.: ") % QString::number(noTransferredElevations) %
|
|
||||||
QStringLiteral(" | elv.info: ") % elevationInfo :
|
|
||||||
QStringLiteral("")
|
QStringLiteral("")
|
||||||
) %
|
) %
|
||||||
(
|
(
|
||||||
@@ -439,7 +433,7 @@ namespace BlackMisc
|
|||||||
QStringLiteral(" | int.time: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsInterpolated) %
|
QStringLiteral(" | int.time: ") % CInterpolationLogger::msSinceEpochToTimeAndTimestamp(tsInterpolated) %
|
||||||
QStringLiteral(" | dt.cur.int.: ") % QString::number(deltaCurrentToInterpolatedTime()) % QStringLiteral("ms") %
|
QStringLiteral(" | dt.cur.int.: ") % QString::number(deltaCurrentToInterpolatedTime()) % QStringLiteral("ms") %
|
||||||
QStringLiteral(" | sample dt: ") % QString::number(deltaSampleTimesMs) % QStringLiteral("ms") %
|
QStringLiteral(" | sample dt: ") % QString::number(deltaSampleTimesMs) % QStringLiteral("ms") %
|
||||||
QStringLiteral(" | fr.[0-1]: ") % QString::number(simulationTimeFraction) %
|
QStringLiteral(" | fr.[0-1]: ") % QString::number(simTimeFraction) %
|
||||||
QStringLiteral(" | old int.pos.: ") % situationOldInterpolation.getTimestampAndOffset(true) %
|
QStringLiteral(" | old int.pos.: ") % situationOldInterpolation.getTimestampAndOffset(true) %
|
||||||
QStringLiteral(" | new int.pos.: ") % situationNewInterpolation.getTimestampAndOffset(true) %
|
QStringLiteral(" | new int.pos.: ") % situationNewInterpolation.getTimestampAndOffset(true) %
|
||||||
QStringLiteral(" | #int.pos.: ") % QString::number(interpolationSituations.size()) :
|
QStringLiteral(" | #int.pos.: ") % QString::number(interpolationSituations.size()) :
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ namespace BlackMisc
|
|||||||
qint64 tsInterpolated = -1; //!< timestamp interpolated
|
qint64 tsInterpolated = -1; //!< timestamp interpolated
|
||||||
double groundFactor = -1; //!< current ground factor
|
double groundFactor = -1; //!< current ground factor
|
||||||
double vtolAircraft = false; //!< VTOL aircraft
|
double vtolAircraft = false; //!< VTOL aircraft
|
||||||
double simulationTimeFraction = -1; //!< time fraction, expected 0..1
|
double simTimeFraction = -1; //!< time fraction, expected 0..1
|
||||||
double deltaSampleTimesMs = -1; //!< delta time between samples (i.e. 2 situations)
|
double deltaSampleTimesMs = -1; //!< delta time between samples (i.e. 2 situations)
|
||||||
bool useParts = false; //!< supporting aircraft parts
|
bool useParts = false; //!< supporting aircraft parts
|
||||||
int noNetworkSituations = 0; //!< available network situations
|
int noNetworkSituations = 0; //!< available network situations
|
||||||
int noTransferredElevations = 0; //!< transferred elevation to n situations
|
|
||||||
QString elevationInfo; //!< info about elevation retrieval
|
QString elevationInfo; //!< info about elevation retrieval
|
||||||
|
QString altCorrection; //!< info about altitude correction as CAircraftSituation::AltitudeCorrection
|
||||||
Aviation::CCallsign callsign; //!< current callsign
|
Aviation::CCallsign callsign; //!< current callsign
|
||||||
Aviation::CAircraftParts parts; //!< corresponding parts used in interpolator
|
Aviation::CAircraftParts parts; //!< corresponding parts used in interpolator
|
||||||
Aviation::CAircraftSituationList interpolationSituations; //!< the interpolator uses 2, 3 situations (oldest at end)
|
Aviation::CAircraftSituationList interpolationSituations; //!< the interpolator uses 2, 3 situations (oldest at end)
|
||||||
|
|||||||
@@ -20,27 +20,71 @@
|
|||||||
#include "blackmisc/pq/length.h"
|
#include "blackmisc/pq/length.h"
|
||||||
#include "blackmisc/logmessage.h"
|
#include "blackmisc/logmessage.h"
|
||||||
#include "blackmisc/verify.h"
|
#include "blackmisc/verify.h"
|
||||||
|
#include <QTimer>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
|
|
||||||
using namespace BlackConfig;
|
using namespace BlackConfig;
|
||||||
using namespace BlackMisc;
|
|
||||||
using namespace BlackMisc::Aviation;
|
using namespace BlackMisc::Aviation;
|
||||||
using namespace BlackMisc::Geo;
|
using namespace BlackMisc::Geo;
|
||||||
using namespace BlackMisc::Math;
|
using namespace BlackMisc::Math;
|
||||||
using namespace BlackMisc::PhysicalQuantities;
|
using namespace BlackMisc::PhysicalQuantities;
|
||||||
using namespace BlackMisc::Simulation;
|
|
||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
{
|
{
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
CInterpolator<Derived>::CInterpolator(const QString &objectName, const CCallsign &callsign, QObject *parent) :
|
CInterpolator<Derived>::CInterpolator(const CCallsign &callsign,
|
||||||
QObject(parent),
|
ISimulationEnvironmentProvider *simEnvProvider, IInterpolationSetupProvider *setupProvider,
|
||||||
m_callsign(callsign)
|
IRemoteAircraftProvider *remoteAircraftProvider,
|
||||||
|
CInterpolationLogger *logger) : m_callsign(callsign)
|
||||||
{
|
{
|
||||||
this->setObjectName(objectName + " for " + callsign.asString());
|
// normally when created m_cg is still null since there is no CG in the provider yet
|
||||||
|
|
||||||
|
if (simEnvProvider) { this->setSimulationEnvironmentProvider(simEnvProvider); }
|
||||||
|
if (setupProvider) { this->setInterpolationSetupProvider(setupProvider); }
|
||||||
|
if (remoteAircraftProvider)
|
||||||
|
{
|
||||||
|
this->setRemoteAircraftProvider(remoteAircraftProvider);
|
||||||
|
QTimer::singleShot(2500, [ = ]
|
||||||
|
{
|
||||||
|
if (m_model.hasModelString()) { return; } // set in-between
|
||||||
|
this->initCorrespondingModel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this->attachLogger(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
bool CInterpolator<Derived>::verifyInterpolationSituations(const CAircraftSituation &oldest, const CAircraftSituation &newer, const CAircraftSituation &latest, const CInterpolationAndRenderingSetupPerCallsign &setup)
|
||||||
|
{
|
||||||
|
if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return true; }
|
||||||
|
CAircraftSituationList situations;
|
||||||
|
|
||||||
|
// oldest last, null ignored
|
||||||
|
if (!latest.isNull()) { situations.push_back(latest); }
|
||||||
|
if (!newer.isNull()) { situations.push_back(newer); }
|
||||||
|
if (!oldest.isNull()) { situations.push_back(oldest); }
|
||||||
|
|
||||||
|
const bool sort1 = situations.isSortedLatestFirst();
|
||||||
|
BLACK_VERIFY_X(sort1, Q_FUNC_INFO, "Wrong timestamp order");
|
||||||
|
const bool sort2 = situations.isSortedAdjustedLatestFirst();
|
||||||
|
BLACK_VERIFY_X(sort2, Q_FUNC_INFO, "Wrong adjusted timestamp order");
|
||||||
|
|
||||||
|
bool details = true;
|
||||||
|
if (setup.isAircraftPartsEnabled())
|
||||||
|
{
|
||||||
|
if (situations.containsOnGroundDetails(CAircraftSituation::InFromParts))
|
||||||
|
{
|
||||||
|
// if a client supports parts, all situations are supposed to be parts based
|
||||||
|
details = situations.areAllOnGroundDetailsSame(CAircraftSituation::InFromParts);
|
||||||
|
BLACK_VERIFY_X(details, Q_FUNC_INFO, "Once gnd.from parts -> always gnd. from parts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// result
|
||||||
|
return sort1 && sort2 && details;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
@@ -60,28 +104,28 @@ namespace BlackMisc
|
|||||||
|
|
||||||
// this code is used by linear and spline interpolator
|
// this code is used by linear and spline interpolator
|
||||||
status.reset();
|
status.reset();
|
||||||
const bool doLogging = this->hasAttachedLogger() && setup.logInterpolation();
|
|
||||||
SituationLog log;
|
SituationLog log;
|
||||||
SituationLog *logP = doLogging ? &log : nullptr;
|
const bool doLogging = this->hasAttachedLogger() && setup.logInterpolation();
|
||||||
|
|
||||||
// any data at all?
|
// any data at all?
|
||||||
if (m_aircraftSituations.isEmpty()) { return CAircraftSituation(m_callsign); }
|
const CAircraftSituationList situations = this->remoteAircraftSituations(m_callsign);
|
||||||
CAircraftSituation currentSituation = m_lastInterpolation.isNull() ? m_aircraftSituations.front() : m_lastInterpolation;
|
if (situations.isEmpty()) { return CAircraftSituation(m_callsign); }
|
||||||
|
CAircraftSituation currentSituation = m_lastInterpolation.isNull() ? situations.front() : m_lastInterpolation;
|
||||||
if (currentSituation.getCallsign() != m_callsign)
|
if (currentSituation.getCallsign() != m_callsign)
|
||||||
{
|
{
|
||||||
BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign");
|
BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong callsign");
|
||||||
currentSituation.setCallsign(m_callsign);
|
currentSituation.setCallsign(m_callsign);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \todo KB 2018-03 ground flag refactoring
|
// set elevation if available
|
||||||
// Update current position by hints' elevation
|
if (!currentSituation.hasGroundElevation())
|
||||||
// * for XP provided by hints.getElevationProvider at current position
|
{
|
||||||
// * for FSX/P3D provided as hints.getElevation which is set to current position of remote aircraft in simulator
|
|
||||||
// * As XP uses lazy init we will call getGroundElevation only when needed
|
|
||||||
// * default here via getElevationPlane
|
|
||||||
// CAltitude currentGroundElevation(hints.getGroundElevation(currentSituation, currentSituation.getDistancePerTime(1000), true, false, logP));
|
|
||||||
const CElevationPlane currentGroundElevation = this->findClosestElevationWithinRange(currentSituation, currentSituation.getDistancePerTime(1000));
|
const CElevationPlane currentGroundElevation = this->findClosestElevationWithinRange(currentSituation, currentSituation.getDistancePerTime(1000));
|
||||||
currentSituation.setGroundElevationChecked(currentGroundElevation); // set as default
|
currentSituation.setGroundElevationChecked(currentGroundElevation); // set as default
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch CG once
|
||||||
|
if (m_cg.isNull()) { m_cg = this->getCG(m_callsign); }
|
||||||
|
|
||||||
// data, split situations by time
|
// data, split situations by time
|
||||||
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
|
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
|
||||||
@@ -97,75 +141,34 @@ namespace BlackMisc
|
|||||||
return currentSituation;
|
return currentSituation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use derived interpolant function
|
// Pitch bank heading
|
||||||
currentSituation.setPosition(interpolant.interpolatePosition(setup));
|
// first, so follow up steps could use those values
|
||||||
currentSituation.setAltitude(interpolant.interpolateAltitude(setup));
|
|
||||||
currentSituation.setMSecsSinceEpoch(interpolant.getInterpolatedTime());
|
|
||||||
|
|
||||||
// PBH before ground so we can use PBH in guessing ground
|
|
||||||
if (setup.isForcingFullInterpolation() || status.isInterpolated())
|
|
||||||
{
|
|
||||||
const auto pbh = interpolant.pbh();
|
const auto pbh = interpolant.pbh();
|
||||||
currentSituation.setHeading(pbh.getHeading());
|
currentSituation.setHeading(pbh.getHeading());
|
||||||
currentSituation.setPitch(pbh.getPitch());
|
currentSituation.setPitch(pbh.getPitch());
|
||||||
currentSituation.setBank(pbh.getBank());
|
currentSituation.setBank(pbh.getBank());
|
||||||
currentSituation.setGroundSpeed(pbh.getGroundSpeed());
|
currentSituation.setGroundSpeed(pbh.getGroundSpeed());
|
||||||
|
|
||||||
|
// use derived interpolant function
|
||||||
|
currentSituation = interpolant.interpolatePositionAndAltitude(currentSituation);
|
||||||
|
|
||||||
|
// correct itself
|
||||||
|
const CAircraftSituation::AltitudeCorrection altCorrection = currentSituation.correctAltitude(m_cg, true);
|
||||||
status.setInterpolatedAndCheckSituation(true, currentSituation);
|
status.setInterpolatedAndCheckSituation(true, currentSituation);
|
||||||
}
|
|
||||||
|
|
||||||
// Interpolate between altitude and ground elevation, with proportions weighted according to interpolated onGround flag
|
|
||||||
constexpr double NoGroundFactor = -1;
|
|
||||||
double groundFactor = NoGroundFactor;
|
|
||||||
|
|
||||||
if (setup.isAircraftPartsEnabled())
|
|
||||||
{
|
|
||||||
// groundFactor = hints.getAircraftParts().isOnGroundInterpolated();
|
|
||||||
if (groundFactor > 0.0)
|
|
||||||
{
|
|
||||||
// if not having an ground elevation yet, we fetch from provider (if there is a provider)
|
|
||||||
if (!currentGroundElevation.isNull())
|
|
||||||
{
|
|
||||||
currentGroundElevation = hints.getGroundElevation(currentSituation, true, false, logP); // "expensive on XPlane" if provider is called
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentGroundElevation.isNull())
|
|
||||||
{
|
|
||||||
Q_ASSERT_X(currentGroundElevation.getAltitude().getReferenceDatum() == CAltitude::MeanSeaLevel, Q_FUNC_INFO, "Need MSL value");
|
|
||||||
const CLength cg = this->getCG(m_callsign);
|
|
||||||
const CAltitude groundElevationCG = currentGroundElevation.getAltitude().withOffset(cg);
|
|
||||||
currentSituation.setGroundElevationChecked(currentGroundElevation);
|
|
||||||
// alt = ground + aboveGround * groundFactor
|
|
||||||
// = ground + (altitude - ground) * groundFactor
|
|
||||||
// = ground (1 - groundFactor) + altitude * groundFactor
|
|
||||||
currentSituation.setAltitude(CAltitude(currentSituation.getAltitude() * (1.0 - groundFactor) +
|
|
||||||
groundElevationCG * groundFactor,
|
|
||||||
CAltitude::MeanSeaLevel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// depending on ground factor set ground flag and reliability
|
|
||||||
// it will use the hints ground flag or elevation/CG or guessing
|
|
||||||
// CInterpolator::setGroundFlagFromInterpolator(hints, groundFactor, currentSituation);
|
|
||||||
|
|
||||||
// we transfer ground elevation for future usage
|
|
||||||
if (currentSituation.hasGroundElevation())
|
|
||||||
{
|
|
||||||
CElevationPlane ep(currentSituation);
|
|
||||||
ep.setSinglePointRadius();
|
|
||||||
|
|
||||||
// transfer to newer situations
|
|
||||||
log.noTransferredElevations = m_aircraftSituations.setGroundElevationChecked(ep, currentTimeMsSinceEpoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
if (doLogging)
|
if (doLogging)
|
||||||
{
|
{
|
||||||
|
static const QString elv("found %1 missed %2");
|
||||||
|
const QPair<int, int> elvStats = this->getElevationsFoundMissed();
|
||||||
log.tsCurrent = currentTimeMsSinceEpoc;
|
log.tsCurrent = currentTimeMsSinceEpoc;
|
||||||
log.callsign = m_callsign;
|
log.callsign = m_callsign;
|
||||||
log.groundFactor = groundFactor;
|
log.groundFactor = currentSituation.getOnGroundFactor();
|
||||||
|
log.altCorrection = CAircraftSituation::altitudeCorrectionToString(altCorrection);
|
||||||
log.situationCurrent = currentSituation;
|
log.situationCurrent = currentSituation;
|
||||||
log.usedSetup = setup;
|
log.usedSetup = setup;
|
||||||
|
log.elevationInfo = elv.arg(elvStats.first).arg(elvStats.second);
|
||||||
|
log.cgAboveGround = m_cg;
|
||||||
m_logger->logInterpolation(log);
|
m_logger->logInterpolation(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +226,8 @@ namespace BlackMisc
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
CAircraftParts CInterpolator<Derived>::getInterpolatedParts(qint64 currentTimeMsSinceEpoch,
|
CAircraftParts CInterpolator<Derived>::getInterpolatedParts(
|
||||||
|
qint64 currentTimeMsSinceEpoch,
|
||||||
const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
|
const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
|
||||||
{
|
{
|
||||||
// (!) this code is used by linear and spline interpolator
|
// (!) this code is used by linear and spline interpolator
|
||||||
@@ -231,20 +235,17 @@ namespace BlackMisc
|
|||||||
partsStatus.reset();
|
partsStatus.reset();
|
||||||
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
|
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
|
||||||
|
|
||||||
|
// Parts are supposed to be in correct order, latest first
|
||||||
|
const CAircraftPartsList validParts = this->remoteAircraftParts(m_callsign);
|
||||||
|
|
||||||
// log for empty parts aircraft parts
|
// log for empty parts aircraft parts
|
||||||
if (m_aircraftParts.isEmpty())
|
if (validParts.isEmpty())
|
||||||
{
|
{
|
||||||
static const CAircraftParts emptyParts;
|
static const CAircraftParts emptyParts;
|
||||||
this->logParts(currentTimeMsSinceEpoch, emptyParts, true, log);
|
this->logParts(currentTimeMsSinceEpoch, emptyParts, validParts.size(), true, log);
|
||||||
return emptyParts;
|
return emptyParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parts are supposed to be in correct order, latest first
|
|
||||||
const CAircraftPartsList &validParts = m_aircraftParts;
|
|
||||||
|
|
||||||
// stop if we don't have any parts
|
|
||||||
if (validParts.isEmpty()) { return {}; }
|
|
||||||
|
|
||||||
partsStatus.setSupportsParts(true);
|
partsStatus.setSupportsParts(true);
|
||||||
CAircraftParts currentParts;
|
CAircraftParts currentParts;
|
||||||
do
|
do
|
||||||
@@ -257,129 +258,62 @@ namespace BlackMisc
|
|||||||
// if (partsOlder.isEmpty()) { currentParts = *(partsNewer.end() - 1); break; }
|
// if (partsOlder.isEmpty()) { currentParts = *(partsNewer.end() - 1); break; }
|
||||||
if (partsOlder.isEmpty()) { currentParts = *(partsNewer.begin()); break; }
|
if (partsOlder.isEmpty()) { currentParts = *(partsNewer.begin()); break; }
|
||||||
currentParts = partsOlder.front(); // latest older parts
|
currentParts = partsOlder.front(); // latest older parts
|
||||||
if (currentParts.isOnGround()) { break; }
|
|
||||||
|
|
||||||
// here we know aircraft is not on ground, and we check if it was recently on ground or if it will be on ground soon
|
|
||||||
const auto latestTakeoff = std::adjacent_find(partsOlder.begin(), partsOlder.end(), [](auto &&, auto && p) { return p.isOnGround(); });
|
|
||||||
const auto soonestLanding = std::find_if(partsNewer.begin(), partsNewer.end(), [](auto && p) { return p.isOnGround(); });
|
|
||||||
|
|
||||||
// maxSecs is the maximum effective value of `secondsSinceTakeoff` and `secondsUntilLanding`. If `secondsSinceTakeoff > significantPast` then `takeoffFactor > 1`
|
|
||||||
// and if `secondsUntilLanding > predictableFuture` then `landingFactor > 1`, and `std::min(std::min(takeoffFactor, landingFactor), 1.0)` ensures `>1` is ignored.
|
|
||||||
// but if the offset < 5s then we must use a smaller value for the landing, hence `std::min(max, static_cast<double>(soonestLanding->getTimeOffsetMs()) / 1000.0)`.
|
|
||||||
const double maxSecs = 5.0; // preferred length of time over which to blend the onground flag, when possible
|
|
||||||
|
|
||||||
// our clairvoyance is limited by the time offset (all times here in seconds)
|
|
||||||
const double significantPastSecs = maxSecs;
|
|
||||||
const double predictableFutureSecs = soonestLanding == partsNewer.end() ? maxSecs : std::min(maxSecs, static_cast<double>(soonestLanding->getTimeOffsetMs()) / 1000.0);
|
|
||||||
const double secondsSinceTakeoff = latestTakeoff == partsOlder.end() ? maxSecs : (currentTimeMsSinceEpoch - latestTakeoff->getAdjustedMSecsSinceEpoch()) / 1000.0;
|
|
||||||
const double secondsUntilLanding = soonestLanding == partsNewer.end() ? maxSecs : (soonestLanding->getAdjustedMSecsSinceEpoch() - currentTimeMsSinceEpoch) / 1000.0;
|
|
||||||
Q_ASSERT(secondsSinceTakeoff >= 0.0);
|
|
||||||
Q_ASSERT(secondsUntilLanding >= 0.0);
|
|
||||||
|
|
||||||
//! \fixme In future, will we need to be able to support time offsets of zero?
|
|
||||||
BLACK_VERIFY(predictableFutureSecs != 0);
|
|
||||||
if (predictableFutureSecs == 0) { break; } // avoid divide by zero
|
|
||||||
|
|
||||||
const double takeoffFactor = secondsSinceTakeoff / significantPastSecs;
|
|
||||||
const double landingFactor = secondsUntilLanding / predictableFutureSecs;
|
|
||||||
const double airborneFactor = std::min(std::min(takeoffFactor, landingFactor), 1.0);
|
|
||||||
currentParts.setOnGroundInterpolated(1.0 - smootherStep(airborneFactor));
|
|
||||||
}
|
}
|
||||||
while (false);
|
while (false);
|
||||||
|
|
||||||
this->logParts(currentTimeMsSinceEpoch, currentParts, false, log);
|
this->logParts(currentTimeMsSinceEpoch, currentParts, validParts.size(), false, log);
|
||||||
return currentParts;
|
return currentParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Derived>
|
template<typename Derived>
|
||||||
void CInterpolator<Derived>::logParts(qint64 timestamp, const CAircraftParts &parts, bool empty, bool log) const
|
CAircraftParts CInterpolator<Derived>::getInterpolatedOrGuessedParts(qint64 currentTimeMsSinceEpoch, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log) const
|
||||||
|
{
|
||||||
|
CAircraftParts parts = this->getInterpolatedParts(currentTimeMsSinceEpoch, setup, partsStatus, log);
|
||||||
|
if (!partsStatus.isSupportingParts())
|
||||||
|
{
|
||||||
|
if (!m_model.hasModelString())
|
||||||
|
{
|
||||||
|
const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(m_callsign);
|
||||||
|
// m_model = aircraft.getModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if model has been thru model matching
|
||||||
|
if (m_model.hasModelString())
|
||||||
|
{
|
||||||
|
parts.guessParts(this->getLastInterpolatedSituation(), m_model.isVtol(), m_model.getEngineCount());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// default guess
|
||||||
|
parts.guessParts(this->getLastInterpolatedSituation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->logParts(currentTimeMsSinceEpoch, parts, 0, false, log);
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
void CInterpolator<Derived>::logParts(qint64 timestamp, const CAircraftParts &parts, int partsNo, bool empty, bool log) const
|
||||||
{
|
{
|
||||||
if (!log || !m_logger) { return; }
|
if (!log || !m_logger) { return; }
|
||||||
PartsLog logInfo;
|
PartsLog logInfo;
|
||||||
logInfo.callsign = m_callsign;
|
logInfo.callsign = m_callsign;
|
||||||
logInfo.noNetworkParts = m_aircraftParts.size();
|
logInfo.noNetworkParts = partsNo;
|
||||||
logInfo.tsCurrent = timestamp;
|
logInfo.tsCurrent = timestamp;
|
||||||
logInfo.parts = parts;
|
logInfo.parts = parts;
|
||||||
logInfo.empty = empty;
|
logInfo.empty = empty;
|
||||||
m_logger->logParts(logInfo);
|
m_logger->logParts(logInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Derived>
|
|
||||||
void CInterpolator<Derived>::addAircraftSituation(const CAircraftSituation &situation)
|
|
||||||
{
|
|
||||||
Q_ASSERT_X(!m_callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
|
|
||||||
Q_ASSERT_X(situation.getCallsign() == m_callsign, Q_FUNC_INFO, "Wrong callsign");
|
|
||||||
if (m_aircraftSituations.isEmpty())
|
|
||||||
{
|
|
||||||
this->resetLastInterpolation(); // delete any leftover
|
|
||||||
|
|
||||||
// make sure we have enough situations to do start interpolating immediately without waiting for more updates
|
|
||||||
// the offsets here (addMSecs) do not really matter
|
|
||||||
CAircraftSituation copy(situation);
|
|
||||||
copy.addMsecs(-2 * IRemoteAircraftProvider::DefaultOffsetTimeMs);
|
|
||||||
m_aircraftSituations.push_frontKeepLatestFirst(copy);
|
|
||||||
copy.addMsecs(IRemoteAircraftProvider::DefaultOffsetTimeMs);
|
|
||||||
m_aircraftSituations.push_frontKeepLatestFirst(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we add new situations at front and keep the latest values (real time) first
|
|
||||||
m_aircraftSituations.push_frontKeepLatestFirstAdjustOffset(situation, IRemoteAircraftProvider::MaxSituationsPerCallsign);
|
|
||||||
|
|
||||||
|
|
||||||
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
|
||||||
// so even mixing fast/slow updates shall work
|
|
||||||
Q_ASSERT_X(!m_aircraftSituations.containsZeroOrNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
|
|
||||||
Q_ASSERT_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Derived>
|
|
||||||
void CInterpolator<Derived>::addAircraftParts(const CAircraftParts &parts, bool adjustZeroOffset)
|
|
||||||
{
|
|
||||||
const bool adjustOffset = adjustZeroOffset && !parts.hasNonZeroOffsetTime();
|
|
||||||
if (adjustOffset)
|
|
||||||
{
|
|
||||||
const qint64 offset = m_aircraftSituations.isEmpty() ? IRemoteAircraftProvider::DefaultOffsetTimeMs : m_aircraftSituations.front().getTimeOffsetMs();
|
|
||||||
CAircraftParts partsCopy(parts);
|
|
||||||
partsCopy.setTimeOffsetMs(offset); // we set the offset of the situation
|
|
||||||
CInterpolator<Derived>::addAircraftParts(partsCopy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// here we have an offset
|
|
||||||
// unlike situations we do not add parts for spline interpolation
|
|
||||||
// this is not needed, as parts do not need 3 values
|
|
||||||
Q_ASSERT_X(!adjustZeroOffset || parts.hasNonZeroOffsetTime(), Q_FUNC_INFO, "Missing parts offset");
|
|
||||||
|
|
||||||
// we add new situations at front and keep the latest values (real time) first
|
|
||||||
m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(parts, IRemoteAircraftProvider::MaxSituationsPerCallsign);
|
|
||||||
|
|
||||||
// force remote provider to cleanup
|
|
||||||
IRemoteAircraftProvider::removeOutdatedParts(m_aircraftParts);
|
|
||||||
|
|
||||||
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
|
||||||
// so even mixing fast/slow updates shall work
|
|
||||||
Q_ASSERT_X(adjustZeroOffset ? !m_aircraftParts.containsZeroOrNegativeOffsetTime() : !m_aircraftParts.containsNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
|
|
||||||
Q_ASSERT_X(m_aircraftParts.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Derived>
|
|
||||||
void CInterpolator<Derived>::addAircraftParts(const CAircraftPartsList &parts, bool adjustZeroOffset)
|
|
||||||
{
|
|
||||||
for (const CAircraftParts &p : parts)
|
|
||||||
{
|
|
||||||
this->addAircraftParts(p, adjustZeroOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Derived>
|
template<typename Derived>
|
||||||
QString CInterpolator<Derived>::getInterpolatorInfo() const
|
QString CInterpolator<Derived>::getInterpolatorInfo() const
|
||||||
{
|
{
|
||||||
return QStringLiteral("Callsign: ") %
|
return QStringLiteral("Callsign: ") %
|
||||||
m_callsign.asString() %
|
m_callsign.asString() %
|
||||||
QStringLiteral(" situations: ") %
|
QStringLiteral(" situations: ") %
|
||||||
QString::number(m_aircraftSituations.size()) %
|
QString::number(this->remoteAircraftSituationsCount(m_callsign)) %
|
||||||
QStringLiteral(" parts: ") %
|
QStringLiteral(" parts: ") %
|
||||||
QString::number(m_aircraftParts.size()) %
|
QString::number(this->remoteAircraftPartsCount(m_callsign)) %
|
||||||
QStringLiteral(" 1st interpolation: ") %
|
QStringLiteral(" 1st interpolation: ") %
|
||||||
boolToYesNo(m_lastInterpolation.isNull());
|
boolToYesNo(m_lastInterpolation.isNull());
|
||||||
}
|
}
|
||||||
@@ -394,92 +328,24 @@ namespace BlackMisc
|
|||||||
void CInterpolator<Derived>::clear()
|
void CInterpolator<Derived>::clear()
|
||||||
{
|
{
|
||||||
this->resetLastInterpolation();
|
this->resetLastInterpolation();
|
||||||
m_aircraftParts.clear();
|
m_model = CAircraftModel();
|
||||||
m_aircraftSituations.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Derived>
|
template<typename Derived>
|
||||||
int CInterpolator<Derived>::maxSituations() const
|
void CInterpolator<Derived>::initCorrespondingModel(const CAircraftModel &model)
|
||||||
{
|
{
|
||||||
return IRemoteAircraftProvider::MaxSituationsPerCallsign;
|
if (model.hasModelString())
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Derived>
|
|
||||||
int CInterpolator<Derived>::maxParts() const
|
|
||||||
{
|
{
|
||||||
return IRemoteAircraftProvider::MaxSituationsPerCallsign;
|
m_model = model;
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Derived>
|
|
||||||
void CInterpolator<Derived>::setGroundFlagFromInterpolator(double groundFactor, CAircraftSituation &situation) const
|
|
||||||
{
|
|
||||||
// by interpolation
|
|
||||||
if (groundFactor >= 1.0)
|
|
||||||
{
|
|
||||||
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByInterpolation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (groundFactor < 1.0 && groundFactor >= 0.0)
|
|
||||||
{
|
|
||||||
situation.setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByInterpolation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on elevation and CG
|
|
||||||
// remark: to some extend redundant as situation.getCorrectedAltitude() already corrects altitude
|
|
||||||
Q_ASSERT_X(!m_callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
|
|
||||||
if (situation.hasGroundElevation())
|
|
||||||
{
|
|
||||||
static const CLength onGroundThresholdLimit(1.0, CLengthUnit::m());
|
|
||||||
static const CLength notOnGroundThresholdLimit(10.0, CLengthUnit::m()); // upper boundary
|
|
||||||
CLength offset = onGroundThresholdLimit; // very small offset from allowed
|
|
||||||
CAircraftSituation::OnGroundDetails reliability = CAircraftSituation::OnGroundByElevation;
|
|
||||||
CLength cg = this->getCG(m_callsign);
|
|
||||||
if (!cg.isNull())
|
|
||||||
{
|
|
||||||
offset += cg;
|
|
||||||
reliability = CAircraftSituation::OnGroundByElevationAndCG;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// increase offset a bit
|
CAircraftModel model = this->getAircraftInRangeForCallsign(m_callsign).getModel();
|
||||||
offset += CLength(1.0, CLengthUnit::m());
|
if (model.hasModelString())
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT_X(situation.getGroundElevation().getReferenceDatum() == CAltitude::MeanSeaLevel, Q_FUNC_INFO, "Need MSL elevation");
|
|
||||||
if (situation.getHeightAboveGround() <= offset)
|
|
||||||
{
|
{
|
||||||
// lower boundary underflow, we can tell we are on ground
|
m_model = model;
|
||||||
const CAircraftSituation::IsOnGround og = CAircraftSituation::OnGround;
|
|
||||||
situation.setOnGround(og, reliability);
|
|
||||||
return; // for underflow we can stop here
|
|
||||||
}
|
}
|
||||||
else if (situation.getHeightAboveGround() >= notOnGroundThresholdLimit)
|
|
||||||
{
|
|
||||||
// upper boundary
|
|
||||||
const CAircraftSituation::IsOnGround og = CAircraftSituation::NotOnGround;
|
|
||||||
situation.setOnGround(og, reliability);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// within an interval were we cannot really tell and continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// for VTOL aircraft we give up
|
|
||||||
if (this->isVtolAircraft(m_callsign))
|
|
||||||
{
|
|
||||||
situation.setOnGround(CAircraftSituation::OnGroundSituationUnknown, CAircraftSituation::OnGroundReliabilityNoSet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we guess on speed, pitch and bank by excluding situations
|
|
||||||
situation.setOnGround(CAircraftSituation::NotOnGround, CAircraftSituation::OnGroundByGuessing);
|
|
||||||
if (qAbs(situation.getPitch().value(CAngleUnit::deg())) > 10) { return; }
|
|
||||||
if (qAbs(situation.getBank().value(CAngleUnit::deg())) > 10) { return; }
|
|
||||||
if (situation.getGroundSpeed().value(CSpeedUnit::km_h()) > 50) { return; }
|
|
||||||
|
|
||||||
// not sure, but this is a guess
|
|
||||||
situation.setOnGround(CAircraftSituation::OnGround, CAircraftSituation::OnGroundByGuessing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInterpolationStatus::setInterpolatedAndCheckSituation(bool succeeded, const CAircraftSituation &situation)
|
void CInterpolationStatus::setInterpolatedAndCheckSituation(bool succeeded, const CAircraftSituation &situation)
|
||||||
@@ -528,45 +394,3 @@ namespace BlackMisc
|
|||||||
//! \endcond
|
//! \endcond
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
template <typename Derived>
|
|
||||||
void CInterpolator<Derived>::addAircraftParts(const CAircraftParts &parts, bool adjustZeroOffset)
|
|
||||||
{
|
|
||||||
const bool adjustOffset = adjustZeroOffset && !parts.hasNonZeroOffsetTime();
|
|
||||||
if (adjustOffset)
|
|
||||||
{
|
|
||||||
const qint64 offset = m_aircraftSituations.isEmpty() ? IRemoteAircraftProvider::DefaultOffsetTimeMs : m_aircraftSituations.front().getTimeOffsetMs();
|
|
||||||
CAircraftParts partsCopy(parts);
|
|
||||||
partsCopy.setTimeOffsetMs(offset); // we set the offset of the situation
|
|
||||||
CInterpolator<Derived>::addAircraftParts(partsCopy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// here we have an offset
|
|
||||||
Q_ASSERT_X(!adjustZeroOffset || parts.hasNonZeroOffsetTime(), Q_FUNC_INFO, "Missing parts offset");
|
|
||||||
if (m_aircraftParts.isEmpty())
|
|
||||||
{
|
|
||||||
// make sure we have enough parts to do start interpolating immediately without waiting for more updates
|
|
||||||
// the offsets here (addMSecs) do not really matter
|
|
||||||
const qint64 minOffset = 100;
|
|
||||||
CAircraftParts copy(parts);
|
|
||||||
copy.addMsecs(-2 * std::max(parts.getTimeOffsetMs(), minOffset));
|
|
||||||
m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(copy);
|
|
||||||
copy.addMsecs(parts.getTimeOffsetMs());
|
|
||||||
m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we add new situations at front and keep the latest values (real time) first
|
|
||||||
m_aircraftParts.push_frontKeepLatestFirstAdjustOffset(parts, IRemoteAircraftProvider::MaxSituationsPerCallsign);
|
|
||||||
|
|
||||||
// force remote provider to cleanup
|
|
||||||
IRemoteAircraftProvider::removeOutdatedParts(m_aircraftParts);
|
|
||||||
|
|
||||||
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
|
||||||
// so even mixing fast/slow updates shall work
|
|
||||||
Q_ASSERT_X(adjustZeroOffset ? !m_aircraftParts.containsZeroOrNegativeOffsetTime() : !m_aircraftParts.containsNegativeOffsetTime(), Q_FUNC_INFO, "Missing offset time");
|
|
||||||
Q_ASSERT_X(m_aircraftParts.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
|
||||||
}
|
|
||||||
**/
|
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
#include "blackmisc/simulation/remoteaircraftprovider.h"
|
#include "blackmisc/simulation/remoteaircraftprovider.h"
|
||||||
#include "blackmisc/simulation/interpolationsetupprovider.h"
|
#include "blackmisc/simulation/interpolationsetupprovider.h"
|
||||||
#include "blackmisc/simulation/simulationenvironmentprovider.h"
|
#include "blackmisc/simulation/simulationenvironmentprovider.h"
|
||||||
|
#include "blackmisc/simulation/aircraftmodel.h"
|
||||||
#include "blackmisc/aviation/aircraftpartslist.h"
|
#include "blackmisc/aviation/aircraftpartslist.h"
|
||||||
#include "blackmisc/aviation/aircraftsituation.h"
|
#include "blackmisc/aviation/aircraftsituation.h"
|
||||||
#include "blackmisc/aviation/aircraftpartslist.h"
|
#include "blackmisc/aviation/aircraftpartslist.h"
|
||||||
|
#include "blackmisc/aviation/callsign.h"
|
||||||
#include "blackmisc/logcategorylist.h"
|
#include "blackmisc/logcategorylist.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@@ -27,8 +29,6 @@
|
|||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
{
|
{
|
||||||
class CWorker;
|
|
||||||
namespace Aviation { class CCallsign; }
|
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
class CInterpolationLogger;
|
class CInterpolationLogger;
|
||||||
@@ -42,8 +42,7 @@ namespace BlackMisc
|
|||||||
class CInterpolator :
|
class CInterpolator :
|
||||||
public CSimulationEnvironmentAware,
|
public CSimulationEnvironmentAware,
|
||||||
public CInterpolationSetupAware,
|
public CInterpolationSetupAware,
|
||||||
public CRemoteAircraftAware,
|
public CRemoteAircraftAware
|
||||||
public QObject
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Log categories
|
//! Log categories
|
||||||
@@ -52,25 +51,14 @@ namespace BlackMisc
|
|||||||
//! Current interpolated situation
|
//! Current interpolated situation
|
||||||
Aviation::CAircraftSituation getInterpolatedSituation(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status);
|
Aviation::CAircraftSituation getInterpolatedSituation(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status);
|
||||||
|
|
||||||
//! Parts before given offset time (aka pending parts)
|
//! Parts before given offset time
|
||||||
Aviation::CAircraftParts getInterpolatedParts(
|
Aviation::CAircraftParts getInterpolatedParts(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
|
||||||
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
|
|
||||||
|
|
||||||
//! Add a new aircraft situation
|
//! Interpolated parts, if not available guessed parts
|
||||||
void addAircraftSituation(const Aviation::CAircraftSituation &situation);
|
Aviation::CAircraftParts getInterpolatedOrGuessedParts(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, CPartsStatus &partsStatus, bool log = false) const;
|
||||||
|
|
||||||
//! Any aircraft situations?
|
//! Latest interpolation result
|
||||||
bool hasAircraftSituations() const { return !m_aircraftSituations.isEmpty(); }
|
const Aviation::CAircraftSituation &getLastInterpolatedSituation() const { return m_lastInterpolation; }
|
||||||
|
|
||||||
//! Add a new aircraft parts
|
|
||||||
void addAircraftParts(const Aviation::CAircraftParts &parts, bool adjustZeroOffset = true);
|
|
||||||
|
|
||||||
//! Add a new aircraft parts
|
|
||||||
//! \remark mainly needed in unit tests
|
|
||||||
void addAircraftParts(const Aviation::CAircraftPartsList &parts, bool adjustZeroOffset = true);
|
|
||||||
|
|
||||||
//! Any aircraft parts?
|
|
||||||
bool hasAircraftParts() const { return !m_aircraftParts.isEmpty(); }
|
|
||||||
|
|
||||||
//! Takes input between 0 and 1 and returns output between 0 and 1 smoothed with an S-shaped curve.
|
//! Takes input between 0 and 1 and returns output between 0 and 1 smoothed with an S-shaped curve.
|
||||||
//!
|
//!
|
||||||
@@ -97,32 +85,62 @@ namespace BlackMisc
|
|||||||
void resetLastInterpolation();
|
void resetLastInterpolation();
|
||||||
|
|
||||||
//! Clear all data
|
//! Clear all data
|
||||||
//! \remark mainly needed in interpolation
|
//! \remark mainly needed in UNIT tests
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
//! Max situations kept
|
//! Init, or re-init the corressponding model
|
||||||
int maxSituations() const;
|
//! \remark either by passing a model or using the provider
|
||||||
|
void initCorrespondingModel(const CAircraftModel &model = {});
|
||||||
//! Max parts kept
|
|
||||||
int maxParts() const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Aviation::CAircraftSituationList m_aircraftSituations; //!< recent situations for one aircraft
|
|
||||||
Aviation::CAircraftPartsList m_aircraftParts; //!< recent parts for one aircraft
|
|
||||||
Aviation::CCallsign m_callsign; //!< callsign
|
|
||||||
Aviation::CAircraftSituation m_lastInterpolation; //!< last interpolation
|
|
||||||
|
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CInterpolator(const QString &objectName, const Aviation::CCallsign &callsign, QObject *parent);
|
CInterpolator(const Aviation::CCallsign &callsign,
|
||||||
|
ISimulationEnvironmentProvider *simEnvProvider, IInterpolationSetupProvider *setupProvider, IRemoteAircraftProvider *p3,
|
||||||
|
CInterpolationLogger *logger);
|
||||||
|
|
||||||
//! Set on ground flag
|
const Aviation::CCallsign m_callsign; //!< corresponding callsign
|
||||||
void setGroundFlagFromInterpolator(double groundFactor, Aviation::CAircraftSituation &situation) const;
|
PhysicalQuantities::CLength m_cg { 0, nullptr } ; //!< fetched once, stays constant
|
||||||
|
Aviation::CAircraftSituation m_lastInterpolation { Aviation::CAircraftSituation::null() }; //!< latest interpolation
|
||||||
|
CAircraftModel m_model; //!< corresponding model
|
||||||
|
|
||||||
|
//! Equal double values?
|
||||||
|
static bool doubleEpsilonEqual(double d1, double d2)
|
||||||
|
{
|
||||||
|
return qAbs(d1 - d2) < std::numeric_limits<double>::epsilon();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Both on ground
|
||||||
|
static bool gfEqualOnGround(double oldGroundFactor, double newGroundFactor)
|
||||||
|
{
|
||||||
|
return doubleEpsilonEqual(1.0, oldGroundFactor) && doubleEpsilonEqual(1.0, newGroundFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Both not on ground
|
||||||
|
static bool gfEqualAirborne(double oldGroundFactor, double newGroundFactor)
|
||||||
|
{
|
||||||
|
return doubleEpsilonEqual(0.0, oldGroundFactor) && doubleEpsilonEqual(0.0, newGroundFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Plane is starting
|
||||||
|
static bool gfStarting(double oldGroundFactor, double newGroundFactor)
|
||||||
|
{
|
||||||
|
return doubleEpsilonEqual(0.0, oldGroundFactor) && doubleEpsilonEqual(1.0, newGroundFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Plane is landing
|
||||||
|
static bool gfLanding(double oldGroundFactor, double newGroundFactor)
|
||||||
|
{
|
||||||
|
return doubleEpsilonEqual(1.0, oldGroundFactor) && doubleEpsilonEqual(0.0, newGroundFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Verify gnd flag, times, ... true means "OK"
|
||||||
|
bool verifyInterpolationSituations(const Aviation::CAircraftSituation &oldest, const Aviation::CAircraftSituation &newer, const Aviation::CAircraftSituation &latest, const CInterpolationAndRenderingSetupPerCallsign &setup);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CInterpolationLogger *m_logger = nullptr;
|
CInterpolationLogger *m_logger = nullptr;
|
||||||
|
|
||||||
//! Log parts
|
//! Log parts
|
||||||
void logParts(qint64 timestamp, const Aviation::CAircraftParts &parts, bool empty, bool log) const;
|
void logParts(qint64 timestamp, const Aviation::CAircraftParts &parts, int partsNo, bool empty, bool log) const;
|
||||||
|
|
||||||
Derived *derived() { return static_cast<Derived *>(this); }
|
Derived *derived() { return static_cast<Derived *>(this); }
|
||||||
const Derived *derived() const { return static_cast<const Derived *>(this); }
|
const Derived *derived() const { return static_cast<const Derived *>(this); }
|
||||||
@@ -134,14 +152,9 @@ namespace BlackMisc
|
|||||||
public:
|
public:
|
||||||
//! Constructor
|
//! Constructor
|
||||||
//! @{
|
//! @{
|
||||||
CInterpolatorPbh()
|
CInterpolatorPbh() {}
|
||||||
{}
|
CInterpolatorPbh(const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) : m_oldSituation(older), m_newSituation(newer) {}
|
||||||
CInterpolatorPbh(const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) :
|
CInterpolatorPbh(double time, const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) : m_simulationTimeFraction(time), m_oldSituation(older), m_newSituation(newer) {}
|
||||||
m_oldSituation(older), m_newSituation(newer)
|
|
||||||
{}
|
|
||||||
CInterpolatorPbh(double time, const Aviation::CAircraftSituation &older, const Aviation::CAircraftSituation &newer) :
|
|
||||||
m_simulationTimeFraction(time), m_oldSituation(older), m_newSituation(newer)
|
|
||||||
{}
|
|
||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
//! Getter
|
//! Getter
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "interpolatorlinear.h"
|
#include "interpolatorlinear.h"
|
||||||
#include "blackmisc/aviation/aircraftsituation.h"
|
|
||||||
#include "blackmisc/aviation/aircraftsituationlist.h"
|
#include "blackmisc/aviation/aircraftsituationlist.h"
|
||||||
#include "blackmisc/aviation/altitude.h"
|
#include "blackmisc/aviation/altitude.h"
|
||||||
#include "blackmisc/geo/coordinategeodetic.h"
|
#include "blackmisc/geo/coordinategeodetic.h"
|
||||||
@@ -48,6 +47,46 @@ namespace BlackMisc
|
|||||||
m_pbh(m_simulationTimeFraction, situation1, situation2)
|
m_pbh(m_simulationTimeFraction, situation1, situation2)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
CAircraftSituation CInterpolatorLinear::Interpolant::interpolatePositionAndAltitude(const CAircraftSituation &situation) const
|
||||||
|
{
|
||||||
|
const std::array<double, 3> oldVec(m_oldSituation.getPosition().normalVectorDouble());
|
||||||
|
const std::array<double, 3> newVec(m_newSituation.getPosition().normalVectorDouble());
|
||||||
|
|
||||||
|
// Interpolate position: pos = (posB - posA) * t + posA
|
||||||
|
CCoordinateGeodetic newPosition;
|
||||||
|
newPosition.setNormalVector((newVec[0] - oldVec[0]) * m_simulationTimeFraction + oldVec[0],
|
||||||
|
(newVec[1] - oldVec[1]) * m_simulationTimeFraction + oldVec[1],
|
||||||
|
(newVec[2] - oldVec[2]) * m_simulationTimeFraction + oldVec[2]);
|
||||||
|
|
||||||
|
// Interpolate altitude: Alt = (AltB - AltA) * t + AltA
|
||||||
|
// avoid underflow below ground elevation by using getCorrectedAltitude
|
||||||
|
const CAltitude oldAlt(m_oldSituation.getCorrectedAltitude());
|
||||||
|
const CAltitude newAlt(m_newSituation.getCorrectedAltitude());
|
||||||
|
Q_ASSERT_X(oldAlt.getReferenceDatum() == CAltitude::MeanSeaLevel && oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(), Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
|
||||||
|
const CAltitude altitude((newAlt - oldAlt)
|
||||||
|
* m_simulationTimeFraction
|
||||||
|
+ oldAlt,
|
||||||
|
oldAlt.getReferenceDatum());
|
||||||
|
|
||||||
|
CAircraftSituation newSituation(situation);
|
||||||
|
newSituation.setPosition(newPosition);
|
||||||
|
newSituation.setAltitude(altitude);
|
||||||
|
newSituation.setMSecsSinceEpoch(this->getInterpolatedTime());
|
||||||
|
|
||||||
|
const double oldGroundFactor = m_oldSituation.getOnGroundFactor();
|
||||||
|
const double newGroundFactor = m_newSituation.getOnGroundFactor();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (gfEqualAirborne(oldGroundFactor, newGroundFactor)) { newSituation.setOnGround(false); break; }
|
||||||
|
if (gfEqualOnGround(oldGroundFactor, newGroundFactor)) { newSituation.setOnGround(true); break; }
|
||||||
|
const double groundFactor = (newGroundFactor - oldGroundFactor) * m_simulationTimeFraction + oldGroundFactor;
|
||||||
|
newSituation.setOnGroundFactor(groundFactor);
|
||||||
|
newSituation.setOnGroundFromGroundFactorFromInterpolation();
|
||||||
|
}
|
||||||
|
while (false);
|
||||||
|
return newSituation;
|
||||||
|
}
|
||||||
|
|
||||||
CInterpolatorLinear::Interpolant CInterpolatorLinear::getInterpolant(
|
CInterpolatorLinear::Interpolant CInterpolatorLinear::getInterpolant(
|
||||||
qint64 currentTimeMsSinceEpoc,
|
qint64 currentTimeMsSinceEpoc,
|
||||||
const CInterpolationAndRenderingSetupPerCallsign &setup,
|
const CInterpolationAndRenderingSetupPerCallsign &setup,
|
||||||
@@ -58,16 +97,11 @@ namespace BlackMisc
|
|||||||
|
|
||||||
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
||||||
// so even mixing fast/slow updates shall work
|
// so even mixing fast/slow updates shall work
|
||||||
BLACK_VERIFY_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
const CAircraftSituationList validSituations = this->remoteAircraftSituations(m_callsign); // if needed, we could also copy here
|
||||||
Q_ASSERT_X(m_aircraftSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
|
BLACK_VERIFY_X(validSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
||||||
|
Q_ASSERT_X(validSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
|
||||||
// Ref T243, KB 2018-02, can be removed in future, we verify situations above
|
|
||||||
// Situations are supposed to be in correct order
|
|
||||||
// const auto end = std::is_sorted_until(m_aircraftSituations.begin(), m_aircraftSituations.end(), [](auto && a, auto && b) { return b.getAdjustedMSecsSinceEpoch() < a.getAdjustedMSecsSinceEpoch(); });
|
|
||||||
// const auto validSituations = makeRange(m_aircraftSituations.begin(), end);
|
|
||||||
|
|
||||||
// find the first situation earlier than the current time
|
// find the first situation earlier than the current time
|
||||||
const CAircraftSituationList &validSituations = m_aircraftSituations; // if needed, we could also copy here
|
|
||||||
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
|
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
|
||||||
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
|
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
|
||||||
const auto situationsOlder = makeRange(pivot, validSituations.end());
|
const auto situationsOlder = makeRange(pivot, validSituations.end());
|
||||||
@@ -147,7 +181,7 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
log.tsCurrent = currentTimeMsSinceEpoc;
|
log.tsCurrent = currentTimeMsSinceEpoc;
|
||||||
log.deltaSampleTimesMs = sampleDeltaTimeMs;
|
log.deltaSampleTimesMs = sampleDeltaTimeMs;
|
||||||
log.simulationTimeFraction = simulationTimeFraction;
|
log.simTimeFraction = simulationTimeFraction;
|
||||||
log.deltaSampleTimesMs = sampleDeltaTimeMs;
|
log.deltaSampleTimesMs = sampleDeltaTimeMs;
|
||||||
log.tsInterpolated = interpolatedTime;
|
log.tsInterpolated = interpolatedTime;
|
||||||
log.interpolationSituations.clear();
|
log.interpolationSituations.clear();
|
||||||
@@ -157,35 +191,5 @@ namespace BlackMisc
|
|||||||
|
|
||||||
return { oldSituation, newSituation, simulationTimeFraction, interpolatedTime };
|
return { oldSituation, newSituation, simulationTimeFraction, interpolatedTime };
|
||||||
}
|
}
|
||||||
|
|
||||||
CCoordinateGeodetic CInterpolatorLinear::Interpolant::interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(setup);
|
|
||||||
|
|
||||||
const std::array<double, 3> oldVec(m_oldSituation.getPosition().normalVectorDouble());
|
|
||||||
const std::array<double, 3> newVec(m_newSituation.getPosition().normalVectorDouble());
|
|
||||||
|
|
||||||
// Interpolate position: pos = (posB - posA) * t + posA
|
|
||||||
CCoordinateGeodetic currentPosition;
|
|
||||||
currentPosition.setNormalVector((newVec[0] - oldVec[0]) * m_simulationTimeFraction + oldVec[0],
|
|
||||||
(newVec[1] - oldVec[1]) * m_simulationTimeFraction + oldVec[1],
|
|
||||||
(newVec[2] - oldVec[2]) * m_simulationTimeFraction + oldVec[2]);
|
|
||||||
return currentPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
CAltitude CInterpolatorLinear::Interpolant::interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(setup);
|
|
||||||
|
|
||||||
// Interpolate altitude: Alt = (AltB - AltA) * t + AltA
|
|
||||||
// avoid underflow below ground elevation by using getCorrectedAltitude
|
|
||||||
const CAltitude oldAlt(m_oldSituation.getCorrectedAltitude());
|
|
||||||
const CAltitude newAlt(m_newSituation.getCorrectedAltitude());
|
|
||||||
Q_ASSERT_X(oldAlt.getReferenceDatum() == CAltitude::MeanSeaLevel && oldAlt.getReferenceDatum() == newAlt.getReferenceDatum(), Q_FUNC_INFO, "mismatch in reference"); // otherwise no calculation is possible
|
|
||||||
return CAltitude((newAlt - oldAlt)
|
|
||||||
* m_simulationTimeFraction
|
|
||||||
+ oldAlt,
|
|
||||||
oldAlt.getReferenceDatum());
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -29,13 +29,12 @@ namespace BlackMisc
|
|||||||
//! Linear interpolator, calculation inbetween positions
|
//! Linear interpolator, calculation inbetween positions
|
||||||
class BLACKMISC_EXPORT CInterpolatorLinear : public CInterpolator<CInterpolatorLinear>
|
class BLACKMISC_EXPORT CInterpolatorLinear : public CInterpolator<CInterpolatorLinear>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CInterpolatorLinear(const BlackMisc::Aviation::CCallsign &callsign, QObject *parent = nullptr) :
|
CInterpolatorLinear(const Aviation::CCallsign &callsign,
|
||||||
CInterpolator("CInterpolatorLinear", callsign, parent)
|
ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
|
||||||
{}
|
CInterpolationLogger *logger = nullptr) :
|
||||||
|
CInterpolator(callsign, p1, p2, p3, logger) {}
|
||||||
|
|
||||||
//! Linear function that performs the actual interpolation
|
//! Linear function that performs the actual interpolation
|
||||||
class Interpolant
|
class Interpolant
|
||||||
@@ -48,10 +47,7 @@ namespace BlackMisc
|
|||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
//! Perform the interpolation
|
//! Perform the interpolation
|
||||||
//! @{
|
Aviation::CAircraftSituation interpolatePositionAndAltitude(const Aviation::CAircraftSituation &situation) const;
|
||||||
Geo::CCoordinateGeodetic interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
|
|
||||||
Aviation::CAltitude interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
|
|
||||||
//! @}
|
|
||||||
|
|
||||||
//! Interpolator for pitch, bank, heading, groundspeed
|
//! Interpolator for pitch, bank, heading, groundspeed
|
||||||
const CInterpolatorPbh &pbh() const { return m_pbh; }
|
const CInterpolatorPbh &pbh() const { return m_pbh; }
|
||||||
@@ -71,7 +67,7 @@ namespace BlackMisc
|
|||||||
Aviation::CAircraftSituation m_newSituation;
|
Aviation::CAircraftSituation m_newSituation;
|
||||||
double m_simulationTimeFraction = 0.0; //!< 0..1
|
double m_simulationTimeFraction = 0.0; //!< 0..1
|
||||||
qint64 m_interpolatedTime = 0; //!< "Real time "of interpolated situation
|
qint64 m_interpolatedTime = 0; //!< "Real time "of interpolated situation
|
||||||
const CInterpolatorPbh m_pbh;
|
const CInterpolatorPbh m_pbh; //!< pitch, bank, ground speed and heading
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Get the interpolant for the given time point
|
//! Get the interpolant for the given time point
|
||||||
|
|||||||
@@ -17,10 +17,9 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
CInterpolatorMulti::CInterpolatorMulti(const CCallsign &callsign, QObject *parent) :
|
CInterpolatorMulti::CInterpolatorMulti(const CCallsign &callsign, ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3, CInterpolationLogger *logger) :
|
||||||
QObject(parent),
|
m_spline(callsign, p1, p2, p3, logger),
|
||||||
m_spline(callsign, this),
|
m_linear(callsign, p1, p2, p3, logger)
|
||||||
m_linear(callsign, this)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CAircraftSituation CInterpolatorMulti::getInterpolatedSituation(qint64 currentTimeSinceEpoc,
|
CAircraftSituation CInterpolatorMulti::getInterpolatedSituation(qint64 currentTimeSinceEpoc,
|
||||||
@@ -42,6 +41,7 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
{
|
{
|
||||||
|
// currently calls the same interpolation for parts
|
||||||
case ModeLinear: return m_linear.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
case ModeLinear: return m_linear.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
||||||
case ModeSpline: return m_spline.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
case ModeSpline: return m_spline.getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
||||||
default: break;
|
default: break;
|
||||||
@@ -49,38 +49,29 @@ namespace BlackMisc
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInterpolatorMulti::addAircraftSituation(const CAircraftSituation &situation)
|
CAircraftParts CInterpolatorMulti::getInterpolatedOrGuessedParts(
|
||||||
{
|
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
|
||||||
m_linear.addAircraftSituation(situation);
|
CPartsStatus &partsStatus, bool log) const
|
||||||
m_spline.addAircraftSituation(situation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CInterpolatorMulti::hasAircraftSituations() const
|
|
||||||
{
|
{
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
{
|
{
|
||||||
case ModeLinear: return m_linear.hasAircraftSituations();
|
// currently calls the same interpolation for parts
|
||||||
case ModeSpline: return m_spline.hasAircraftSituations();
|
case ModeLinear: return m_linear.getInterpolatedOrGuessedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
||||||
|
case ModeSpline: return m_spline.getInterpolatedOrGuessedParts(currentTimeSinceEpoc, setup, partsStatus, log);
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInterpolatorMulti::addAircraftParts(const CAircraftParts &parts)
|
const CAircraftSituation &CInterpolatorMulti::getLastInterpolatedSituation() const
|
||||||
{
|
|
||||||
m_linear.addAircraftParts(parts);
|
|
||||||
m_spline.addAircraftParts(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CInterpolatorMulti::hasAircraftParts() const
|
|
||||||
{
|
{
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
{
|
{
|
||||||
case ModeLinear: return m_linear.hasAircraftParts();
|
case ModeLinear: return m_linear.getLastInterpolatedSituation();
|
||||||
case ModeSpline: return m_spline.hasAircraftParts();
|
case ModeSpline: return m_spline.getLastInterpolatedSituation();
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return false;
|
return CAircraftSituation::null();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInterpolatorMulti::attachLogger(CInterpolationLogger *logger)
|
void CInterpolatorMulti::attachLogger(CInterpolationLogger *logger)
|
||||||
@@ -89,19 +80,18 @@ namespace BlackMisc
|
|||||||
m_spline.attachLogger(logger);
|
m_spline.attachLogger(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CInterpolatorMulti::initCorrespondingModel(const CAircraftModel &model)
|
||||||
|
{
|
||||||
|
m_linear.initCorrespondingModel(model);
|
||||||
|
m_spline.initCorrespondingModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
bool CInterpolatorMulti::setMode(Mode mode)
|
bool CInterpolatorMulti::setMode(Mode mode)
|
||||||
{
|
{
|
||||||
#ifdef QT_DEBUG
|
if (m_mode == mode) { return false; }
|
||||||
if (m_mode != mode)
|
|
||||||
{
|
|
||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
Q_UNUSED(mode);
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CInterpolatorMulti::setMode(const QString &mode)
|
bool CInterpolatorMulti::setMode(const QString &mode)
|
||||||
{
|
{
|
||||||
@@ -156,14 +146,9 @@ namespace BlackMisc
|
|||||||
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper()
|
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const CCallsign &callsign, QObject *parent)
|
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3, CInterpolationLogger *logger)
|
||||||
{
|
{
|
||||||
m_interpolator.reset(new CInterpolatorMulti(callsign, parent));
|
m_interpolator.reset(new CInterpolatorMulti(callsign, p1, p2, p3));
|
||||||
}
|
|
||||||
|
|
||||||
CInterpolatorMultiWrapper::CInterpolatorMultiWrapper(const CCallsign &callsign, CInterpolationLogger *logger, QObject *parent)
|
|
||||||
{
|
|
||||||
m_interpolator.reset(new CInterpolatorMulti(callsign, parent));
|
|
||||||
m_interpolator->attachLogger(logger);
|
m_interpolator->attachLogger(logger);
|
||||||
}
|
}
|
||||||
} // ns
|
} // ns
|
||||||
|
|||||||
@@ -19,43 +19,39 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
/*!
|
//! Multiplexed interpolator which allows switching between modes at runtime.
|
||||||
* Multiplexed interpolator which allows switching between modes at runtime.
|
class BLACKMISC_EXPORT CInterpolatorMulti
|
||||||
* \remark currently switching mode is only a developer feature, see https://swift-project.slack.com/archives/C04J6J76N/p1504536854000049
|
|
||||||
*/
|
|
||||||
class BLACKMISC_EXPORT CInterpolatorMulti : public QObject
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CInterpolatorMulti(const Aviation::CCallsign &callsign, QObject *parent = nullptr);
|
CInterpolatorMulti(const Aviation::CCallsign &callsign,
|
||||||
|
ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
|
||||||
|
CInterpolationLogger *logger = nullptr);
|
||||||
|
|
||||||
//! \copydoc CInterpolator::getInterpolatedSituation
|
//! \copydoc CInterpolator::getInterpolatedSituation
|
||||||
Aviation::CAircraftSituation getInterpolatedSituation(
|
Aviation::CAircraftSituation getInterpolatedSituation(
|
||||||
qint64 currentTimeSinceEpoc,
|
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
|
||||||
const CInterpolationAndRenderingSetupPerCallsign &setup,
|
|
||||||
CInterpolationStatus &status);
|
CInterpolationStatus &status);
|
||||||
|
|
||||||
//! \copydoc CInterpolator::getInterpolatedParts
|
//! \copydoc CInterpolator::getInterpolatedParts
|
||||||
Aviation::CAircraftParts getInterpolatedParts(
|
Aviation::CAircraftParts getInterpolatedParts(
|
||||||
qint64 currentTimeSinceEpoc,
|
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
|
||||||
const CInterpolationAndRenderingSetupPerCallsign &setup,
|
|
||||||
CPartsStatus &partsStatus, bool log = false) const;
|
CPartsStatus &partsStatus, bool log = false) const;
|
||||||
|
|
||||||
//! \copydoc CInterpolator::addAircraftSituation
|
//! \copydoc CInterpolator::getInterpolatedOrGuessedParts
|
||||||
void addAircraftSituation(const Aviation::CAircraftSituation &situation);
|
Aviation::CAircraftParts getInterpolatedOrGuessedParts(
|
||||||
|
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup,
|
||||||
|
CPartsStatus &partsStatus, bool log) const;
|
||||||
|
|
||||||
//! \copydoc CInterpolator::hasAircraftSituations
|
//! \copydoc CInterpolator::getLastInterpolatedSituation
|
||||||
bool hasAircraftSituations() const;
|
const Aviation::CAircraftSituation &getLastInterpolatedSituation() const;
|
||||||
|
|
||||||
//! \copydoc CInterpolator::addAircraftParts
|
|
||||||
void addAircraftParts(const Aviation::CAircraftParts &parts);
|
|
||||||
|
|
||||||
//! \copydoc CInterpolator::hasAircraftParts
|
|
||||||
bool hasAircraftParts() const;
|
|
||||||
|
|
||||||
//! \copydoc CInterpolator::attachLogger
|
//! \copydoc CInterpolator::attachLogger
|
||||||
void attachLogger(CInterpolationLogger *logger);
|
void attachLogger(CInterpolationLogger *logger);
|
||||||
|
|
||||||
|
//! \copydoc CInterpolator::initCorrespondingModel
|
||||||
|
void initCorrespondingModel(const CAircraftModel &model);
|
||||||
|
|
||||||
//! Supported interpolation modes.
|
//! Supported interpolation modes.
|
||||||
enum Mode
|
enum Mode
|
||||||
{
|
{
|
||||||
@@ -102,10 +98,10 @@ namespace BlackMisc
|
|||||||
CInterpolatorMultiWrapper();
|
CInterpolatorMultiWrapper();
|
||||||
|
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, QObject *parent = nullptr);
|
CInterpolatorMultiWrapper(
|
||||||
|
const Aviation::CCallsign &callsign,
|
||||||
//! Constructor
|
ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
|
||||||
CInterpolatorMultiWrapper(const Aviation::CCallsign &callsign, CInterpolationLogger *logger, QObject *parent = nullptr);
|
CInterpolationLogger *logger = nullptr);
|
||||||
|
|
||||||
//! Has interpolator initialized?
|
//! Has interpolator initialized?
|
||||||
bool hasInterpolator() const { return m_interpolator; }
|
bool hasInterpolator() const { return m_interpolator; }
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
#include "blackmisc/simulation/interpolatorspline.h"
|
#include "blackmisc/simulation/interpolatorspline.h"
|
||||||
#include "blackmisc/logmessage.h"
|
#include "blackmisc/logmessage.h"
|
||||||
#include "blackmisc/verify.h"
|
#include "blackmisc/verify.h"
|
||||||
|
#include "blackconfig/buildconfig.h"
|
||||||
|
|
||||||
|
using namespace BlackConfig;
|
||||||
using namespace BlackMisc::Aviation;
|
using namespace BlackMisc::Aviation;
|
||||||
using namespace BlackMisc::Geo;
|
using namespace BlackMisc::Geo;
|
||||||
using namespace BlackMisc::Math;
|
using namespace BlackMisc::Math;
|
||||||
@@ -103,70 +105,67 @@ namespace BlackMisc
|
|||||||
Q_UNUSED(setup);
|
Q_UNUSED(setup);
|
||||||
|
|
||||||
// recalculate derivatives only if they changed
|
// recalculate derivatives only if they changed
|
||||||
|
int situationsSize = -1;
|
||||||
if (currentTimeMsSinceEpoc > m_nextSampleAdjustedTime)
|
if (currentTimeMsSinceEpoc > m_nextSampleAdjustedTime)
|
||||||
{
|
{
|
||||||
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
// with the latest updates of T243 the order and the offsets are supposed to be correct
|
||||||
// so even mixing fast/slow updates shall work
|
// so even mixing fast/slow updates shall work
|
||||||
Q_ASSERT_X(m_aircraftSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
const CAircraftSituationList validSituations = this->remoteAircraftSituations(m_callsign);
|
||||||
Q_ASSERT_X(m_aircraftSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
|
situationsSize = validSituations.size();
|
||||||
|
Q_ASSERT_X(validSituations.isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
|
||||||
// Ref T243, KB 2018-02, can be removed in future, we verify situations above
|
Q_ASSERT_X(validSituations.size() <= IRemoteAircraftProvider::MaxSituationsPerCallsign, Q_FUNC_INFO, "Wrong size");
|
||||||
// Situations are supposed to be in correct order
|
|
||||||
// const auto end = std::is_sorted_until(m_aircraftSituations.begin(), m_aircraftSituations.end(), [](auto && a, auto && b) { return b.getAdjustedMSecsSinceEpoch() < a.getAdjustedMSecsSinceEpoch(); });
|
|
||||||
// const auto validSituations = makeRange(m_aircraftSituations.begin(), end);
|
|
||||||
|
|
||||||
// find the first situation earlier than the current time
|
// find the first situation earlier than the current time
|
||||||
const CAircraftSituationList &validSituations = m_aircraftSituations; // if needed, we could also copy here
|
|
||||||
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
|
const auto pivot = std::partition_point(validSituations.begin(), validSituations.end(), [ = ](auto &&s) { return s.getAdjustedMSecsSinceEpoch() > currentTimeMsSinceEpoc; });
|
||||||
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
|
const auto situationsNewer = makeRange(validSituations.begin(), pivot);
|
||||||
const auto situationsOlder = makeRange(pivot, validSituations.end());
|
const auto situationsOlder = makeRange(pivot, validSituations.end());
|
||||||
|
|
||||||
if (situationsNewer.isEmpty() || situationsOlder.size() < 2)
|
// m_s[0] .. oldest -> m_[2] .. latest
|
||||||
{
|
if (situationsNewer.isEmpty() || situationsOlder.size() < 2) { return m_interpolant; }
|
||||||
return m_interpolant;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_s = std::array<CAircraftSituation, 3> {{ *(situationsOlder.begin() + 1), *situationsOlder.begin(), *(situationsNewer.end() - 1) }};
|
m_s = std::array<CAircraftSituation, 3> {{ *(situationsOlder.begin() + 1), *situationsOlder.begin(), *(situationsNewer.end() - 1) }};
|
||||||
|
|
||||||
// - altitude unit must be the same for all three, but the unit itself does not matter
|
|
||||||
// - ground elevantion here normally is not available
|
|
||||||
// - only use elevation plane here, do not call provider
|
|
||||||
// - some info how has a plane moves: 100km/h => 1sec 27,7m => 5 secs 136m
|
|
||||||
// - on an airport the plane does not move very fast, or not at all
|
|
||||||
// - and the elevation remains (almost) constant for a wider area
|
|
||||||
// - flying the ground elevation not really matters
|
|
||||||
const CElevationPlane plane0 = this->findClosestElevationWithinRange(m_s[0], CElevationPlane::singlePointRadius());
|
|
||||||
const CElevationPlane plane1 = this->findClosestElevationWithinRange(m_s[1], CElevationPlane::singlePointRadius());
|
|
||||||
const CElevationPlane plane2 = this->findClosestElevationWithinRange(m_s[2], CElevationPlane::singlePointRadius());
|
|
||||||
|
|
||||||
// do not override existing values
|
|
||||||
m_s[0].setGroundElevationChecked(plane0);
|
|
||||||
m_s[1].setGroundElevationChecked(plane1);
|
|
||||||
m_s[2].setGroundElevationChecked(plane2);
|
|
||||||
|
|
||||||
const CLength cg = this->getCG(m_callsign);
|
|
||||||
const double a0 = m_s[0].getCorrectedAltitude(cg).value();
|
|
||||||
const double a1 = m_s[1].getCorrectedAltitude(cg).value();
|
|
||||||
const double a2 = m_s[2].getCorrectedAltitude(cg).value();
|
|
||||||
|
|
||||||
const std::array<std::array<double, 3>, 3> normals {{ m_s[0].getPosition().normalVectorDouble(), m_s[1].getPosition().normalVectorDouble(), m_s[2].getPosition().normalVectorDouble() }};
|
const std::array<std::array<double, 3>, 3> normals {{ m_s[0].getPosition().normalVectorDouble(), m_s[1].getPosition().normalVectorDouble(), m_s[2].getPosition().normalVectorDouble() }};
|
||||||
PosArray pa;
|
PosArray pa;
|
||||||
pa.x = {{ normals[0][0], normals[1][0], normals[2][0] }};
|
pa.x = {{ normals[0][0], normals[1][0], normals[2][0] }}; // oldest
|
||||||
pa.y = {{ normals[0][1], normals[1][1], normals[2][1] }};
|
pa.y = {{ normals[0][1], normals[1][1], normals[2][1] }};
|
||||||
pa.z = {{ normals[0][2], normals[1][2], normals[2][2] }};
|
pa.z = {{ normals[0][2], normals[1][2], normals[2][2] }}; // latest
|
||||||
pa.a = {{ a0, a1, a2 }};
|
|
||||||
pa.t = {{ static_cast<double>(m_s[0].getAdjustedMSecsSinceEpoch()), static_cast<double>(m_s[1].getAdjustedMSecsSinceEpoch()), static_cast<double>(m_s[2].getAdjustedMSecsSinceEpoch()) }};
|
pa.t = {{ static_cast<double>(m_s[0].getAdjustedMSecsSinceEpoch()), static_cast<double>(m_s[1].getAdjustedMSecsSinceEpoch()), static_cast<double>(m_s[2].getAdjustedMSecsSinceEpoch()) }};
|
||||||
|
|
||||||
pa.dx = getDerivatives(pa.t, pa.x);
|
pa.dx = getDerivatives(pa.t, pa.x);
|
||||||
pa.dy = getDerivatives(pa.t, pa.y);
|
pa.dy = getDerivatives(pa.t, pa.y);
|
||||||
pa.dz = getDerivatives(pa.t, pa.z);
|
pa.dz = getDerivatives(pa.t, pa.z);
|
||||||
pa.da = getDerivatives(pa.t, pa.a);
|
|
||||||
|
|
||||||
m_prevSampleAdjustedTime = situationsOlder.begin()->getAdjustedMSecsSinceEpoch();
|
// - altitude unit must be the same for all three, but the unit itself does not matter
|
||||||
m_nextSampleAdjustedTime = (situationsNewer.end() - 1)->getAdjustedMSecsSinceEpoch();
|
// - ground elevantion here normally is not available
|
||||||
m_prevSampleTime = situationsOlder.begin()->getMSecsSinceEpoch();
|
// - some info how fast a plane moves: 100km/h => 1sec 27,7m => 5 secs 136m
|
||||||
m_nextSampleTime = (situationsNewer.end() - 1)->getMSecsSinceEpoch();
|
// - on an airport the plane does not move very fast, or not at all
|
||||||
m_interpolant = Interpolant(pa, situationsOlder.begin()->getAltitude().getUnit(), { *situationsOlder.begin(), *(situationsNewer.end() - 1) });
|
// - and the elevation remains (almost) constant for a wider area
|
||||||
|
// - during flying the ground elevation not really matters
|
||||||
|
this->updateElevations();
|
||||||
|
const double a0 = m_s[0].getCorrectedAltitude(m_cg).value(); // oldest
|
||||||
|
const double a1 = m_s[1].getCorrectedAltitude(m_cg).value();
|
||||||
|
const double a2 = m_s[2].getCorrectedAltitude(m_cg).value(); // latest
|
||||||
|
pa.a = {{ a0, a1, a2 }};
|
||||||
|
pa.gnd = {{ m_s[0].getOnGroundFactor(), m_s[1].getOnGroundFactor(), m_s[2].getOnGroundFactor() }};
|
||||||
|
pa.da = getDerivatives(pa.t, pa.a);
|
||||||
|
pa.dgnd = getDerivatives(pa.t, pa.gnd);
|
||||||
|
Q_ASSERT_X(this->areAltitudeUnitsSame(), Q_FUNC_INFO, "Altitude unit mismatch");
|
||||||
|
|
||||||
|
// m_prevSampleAdjustedTime = situationsOlder.begin()->getAdjustedMSecsSinceEpoch(); // m_s[1]
|
||||||
|
// m_nextSampleAdjustedTime = (situationsNewer.end() - 1)->getAdjustedMSecsSinceEpoch(); // m_s[2]
|
||||||
|
// m_prevSampleTime = situationsOlder.begin()->getMSecsSinceEpoch(); // m_s[1]
|
||||||
|
// m_nextSampleTime = (situationsNewer.end() - 1)->getMSecsSinceEpoch(); // m_s[2]
|
||||||
|
// m_interpolant = Interpolant(pa, situationsOlder.begin()->getAltitude().getUnit(), { *situationsOlder.begin(), *(situationsNewer.end() - 1) });
|
||||||
|
|
||||||
|
m_prevSampleAdjustedTime = m_s[1].getAdjustedMSecsSinceEpoch();
|
||||||
|
m_nextSampleAdjustedTime = m_s[2].getAdjustedMSecsSinceEpoch(); // latest
|
||||||
|
m_prevSampleTime = m_s[1].getMSecsSinceEpoch();
|
||||||
|
m_nextSampleTime = m_s[2].getMSecsSinceEpoch(); // latest
|
||||||
|
m_interpolant = Interpolant(pa, m_s[2].getAltitudeUnit(), CInterpolatorPbh(m_s[1], m_s[2]));
|
||||||
|
Q_ASSERT_X(m_prevSampleAdjustedTime < m_nextSampleAdjustedTime, Q_FUNC_INFO, "Wrong time order");
|
||||||
|
|
||||||
|
// VERIFY
|
||||||
|
this->verifyInterpolationSituations(m_s[0], m_s[1], m_s[2], setup); // oldest -> latest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example:
|
// Example:
|
||||||
@@ -194,38 +193,93 @@ namespace BlackMisc
|
|||||||
|
|
||||||
if (this->hasAttachedLogger() && setup.logInterpolation())
|
if (this->hasAttachedLogger() && setup.logInterpolation())
|
||||||
{
|
{
|
||||||
|
if (situationsSize < 0) { situationsSize = this->remoteAircraftSituationsCount(m_callsign); }
|
||||||
log.interpolationSituations.push_back(m_s[0]);
|
log.interpolationSituations.push_back(m_s[0]);
|
||||||
log.interpolationSituations.push_back(m_s[1]);
|
log.interpolationSituations.push_back(m_s[1]);
|
||||||
log.interpolationSituations.push_back(m_s[2]); // latest at end
|
log.interpolationSituations.push_back(m_s[2]); // latest at end
|
||||||
log.interpolator = 's';
|
log.interpolator = 's';
|
||||||
log.deltaSampleTimesMs = dt2;
|
log.deltaSampleTimesMs = dt2;
|
||||||
log.simulationTimeFraction = timeFraction;
|
log.simTimeFraction = timeFraction;
|
||||||
log.noNetworkSituations = m_aircraftSituations.size();
|
log.noNetworkSituations = situationsSize;
|
||||||
log.tsInterpolated = interpolatedTime; // without offsets
|
log.tsInterpolated = interpolatedTime; // without offsets
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_interpolant;
|
return m_interpolant;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCoordinateGeodetic CInterpolatorSpline::Interpolant::interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const
|
bool CInterpolatorSpline::updateElevations()
|
||||||
{
|
{
|
||||||
Q_UNUSED(setup);
|
bool updated = false;
|
||||||
|
for (unsigned int i = 0; i < m_s.size(); i++)
|
||||||
const double newX = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
|
{
|
||||||
const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
|
if (m_s[i].hasGroundElevation()) { continue; } // do not override existing values
|
||||||
const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
|
const CElevationPlane plane = this->findClosestElevationWithinRange(m_s[i], CElevationPlane::singlePointRadius());
|
||||||
|
const bool u = m_s[i].setGroundElevationChecked(plane);
|
||||||
CCoordinateGeodetic currentPosition;
|
updated |= u;
|
||||||
currentPosition.setNormalVector(newX, newY, newZ);
|
}
|
||||||
return currentPosition;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAltitude CInterpolatorSpline::Interpolant::interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const
|
bool CInterpolatorSpline::areAnyElevationsMissing() const
|
||||||
{
|
{
|
||||||
Q_UNUSED(setup);
|
for (unsigned int i = 0; i < m_s.size(); i++)
|
||||||
|
{
|
||||||
|
if (!m_s[i].hasGroundElevation()) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const double newA = evalSplineInterval(m_currentTimeMsSinceEpoc, m_pa.t[1], m_pa.t[2], m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
|
bool CInterpolatorSpline::isAnySituationNearGroundRelevant() const
|
||||||
return CAltitude(newA, m_altitudeUnit);
|
{
|
||||||
|
for (unsigned int i = 0; i < m_s.size(); i++)
|
||||||
|
{
|
||||||
|
if (!m_s[i].canLikelySkipNearGroundInterpolation()) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInterpolatorSpline::areAltitudeUnitsSame(const CLengthUnit &compare) const
|
||||||
|
{
|
||||||
|
if (m_s.size() < 1) { return true; }
|
||||||
|
const CLengthUnit c = compare.isNull() ? m_s[0].getAltitudeUnit() : compare;
|
||||||
|
for (unsigned int i = 0; i < m_s.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_s[i].getAltitudeUnit() != c) { return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAircraftSituation CInterpolatorSpline::Interpolant::interpolatePositionAndAltitude(const CAircraftSituation &situation) const
|
||||||
|
{
|
||||||
|
const double t1 = m_pa.t[1];
|
||||||
|
const double t2 = m_pa.t[2];
|
||||||
|
const double newX = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.x[1], m_pa.x[2], m_pa.dx[1], m_pa.dx[2]);
|
||||||
|
const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
|
||||||
|
const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
|
||||||
|
|
||||||
|
CAircraftSituation newSituation(situation);
|
||||||
|
const std::array<double, 3> normalVector = {{ newX, newY, newZ }};
|
||||||
|
const CCoordinateGeodetic currentPosition(normalVector);
|
||||||
|
|
||||||
|
const double newA = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.a[1], m_pa.a[2], m_pa.da[1], m_pa.da[2]);
|
||||||
|
const CAltitude alt(newA, m_altitudeUnit);
|
||||||
|
|
||||||
|
newSituation.setPosition(currentPosition);
|
||||||
|
newSituation.setAltitude(alt);
|
||||||
|
newSituation.setMSecsSinceEpoch(this->getInterpolatedTime());
|
||||||
|
|
||||||
|
const double gnd1 = m_pa.gnd[1];
|
||||||
|
const double gnd2 = m_pa.gnd[2];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (gfEqualAirborne(gnd1, gnd2)) { newSituation.setOnGround(false); break; }
|
||||||
|
if (gfEqualOnGround(gnd1, gnd2)) { newSituation.setOnGround(true); break; }
|
||||||
|
const double newGnd = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, gnd1, gnd2, m_pa.dgnd[1], m_pa.dgnd[2]);
|
||||||
|
newSituation.setOnGroundFactor(newGnd);
|
||||||
|
newSituation.setOnGroundFromGroundFactorFromInterpolation();
|
||||||
|
}
|
||||||
|
while (false);
|
||||||
|
return newSituation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInterpolatorSpline::Interpolant::setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs)
|
void CInterpolatorSpline::Interpolant::setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs)
|
||||||
@@ -243,6 +297,7 @@ namespace BlackMisc
|
|||||||
a[i] = 0; t[i] = 0;
|
a[i] = 0; t[i] = 0;
|
||||||
dx[i] = 0; dy[i] = 0; dz[i] = 0;
|
dx[i] = 0; dy[i] = 0; dz[i] = 0;
|
||||||
da[i] = 0;
|
da[i] = 0;
|
||||||
|
gnd[i] = 0; dgnd[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,12 @@ namespace BlackMisc
|
|||||||
//! Cubic spline interpolator
|
//! Cubic spline interpolator
|
||||||
class BLACKMISC_EXPORT CInterpolatorSpline : public CInterpolator<CInterpolatorSpline>
|
class BLACKMISC_EXPORT CInterpolatorSpline : public CInterpolator<CInterpolatorSpline>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CInterpolatorSpline(const Aviation::CCallsign &callsign, QObject *parent = nullptr) :
|
CInterpolatorSpline(const Aviation::CCallsign &callsign,
|
||||||
CInterpolator("CInterpolatorSpline", callsign, parent)
|
ISimulationEnvironmentProvider *p1, IInterpolationSetupProvider *p2, IRemoteAircraftProvider *p3,
|
||||||
{}
|
CInterpolationLogger *logger = nullptr) :
|
||||||
|
CInterpolator(callsign, p1, p2, p3, logger) {}
|
||||||
|
|
||||||
//! Position arrays for interpolation
|
//! Position arrays for interpolation
|
||||||
struct BLACKMISC_EXPORT PosArray
|
struct BLACKMISC_EXPORT PosArray
|
||||||
@@ -43,9 +42,8 @@ namespace BlackMisc
|
|||||||
//! Zero initialized position array
|
//! Zero initialized position array
|
||||||
static const PosArray &zeroPosArray();
|
static const PosArray &zeroPosArray();
|
||||||
|
|
||||||
//! 3 coordinates for spline interpolation
|
//! 3 coordinates for spline interpolation @{
|
||||||
//! @{
|
std::array<double, 3> x, y, z, a, gnd, t, dx, dy, dz, da, dgnd;
|
||||||
std::array<double, 3> x, y, z, a, t, dx, dy, dz, da;
|
|
||||||
//! @}
|
//! @}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,10 +60,7 @@ namespace BlackMisc
|
|||||||
m_pa(pa), m_altitudeUnit(altitudeUnit), m_pbh(pbh) {}
|
m_pa(pa), m_altitudeUnit(altitudeUnit), m_pbh(pbh) {}
|
||||||
|
|
||||||
//! Perform the interpolation
|
//! Perform the interpolation
|
||||||
//! @{
|
Aviation::CAircraftSituation interpolatePositionAndAltitude(const Aviation::CAircraftSituation &situation) const;
|
||||||
Geo::CCoordinateGeodetic interpolatePosition(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
|
|
||||||
Aviation::CAltitude interpolateAltitude(const CInterpolationAndRenderingSetupPerCallsign &setup) const;
|
|
||||||
//! @}
|
|
||||||
|
|
||||||
//! Interpolator for pitch, bank, heading, groundspeed
|
//! Interpolator for pitch, bank, heading, groundspeed
|
||||||
const CInterpolatorPbh &pbh() const { return m_pbh; }
|
const CInterpolatorPbh &pbh() const { return m_pbh; }
|
||||||
@@ -83,11 +78,11 @@ namespace BlackMisc
|
|||||||
void setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs);
|
void setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PosArray m_pa;
|
PosArray m_pa; //! current positions array, latest values last
|
||||||
PhysicalQuantities::CLengthUnit m_altitudeUnit;
|
PhysicalQuantities::CLengthUnit m_altitudeUnit;
|
||||||
CInterpolatorPbh m_pbh;
|
CInterpolatorPbh m_pbh;
|
||||||
qint64 m_currentTimeMsSinceEpoc { 0 };
|
qint64 m_currentTimeMsSinceEpoc { -1 };
|
||||||
qint64 m_interpolatedTime { 0 }; //!< represented "real time" at interpolated situation
|
qint64 m_interpolatedTime { -1 }; //!< represented "real time" at interpolated situation
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Strategy used by CInterpolator::getInterpolatedSituation
|
//! Strategy used by CInterpolator::getInterpolatedSituation
|
||||||
@@ -95,11 +90,23 @@ namespace BlackMisc
|
|||||||
const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status, SituationLog &log);
|
const CInterpolationAndRenderingSetupPerCallsign &setup, CInterpolationStatus &status, SituationLog &log);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
//! Update the elevations used in CInterpolatorSpline::m_s
|
||||||
|
bool updateElevations();
|
||||||
|
|
||||||
|
//! Are any elevations missing in CInterpolatorSpline::m_s
|
||||||
|
bool areAnyElevationsMissing() const;
|
||||||
|
|
||||||
|
//! Ground relevant
|
||||||
|
bool isAnySituationNearGroundRelevant() const;
|
||||||
|
|
||||||
|
//! Are the altitude units all the same
|
||||||
|
bool areAltitudeUnitsSame(const PhysicalQuantities::CLengthUnit &compare = PhysicalQuantities::CLengthUnit::nullUnit()) const;
|
||||||
|
|
||||||
qint64 m_prevSampleAdjustedTime = 0; //!< previous sample time + offset
|
qint64 m_prevSampleAdjustedTime = 0; //!< previous sample time + offset
|
||||||
qint64 m_nextSampleAdjustedTime = 0; //!< previous sample time + offset
|
qint64 m_nextSampleAdjustedTime = 0; //!< previous sample time + offset
|
||||||
qint64 m_prevSampleTime = 0; //!< previous sample "real time"
|
qint64 m_prevSampleTime = 0; //!< previous sample "real time"
|
||||||
qint64 m_nextSampleTime = 0; //!< next sample "real time"
|
qint64 m_nextSampleTime = 0; //!< next sample "real time"
|
||||||
std::array<Aviation::CAircraftSituation, 3> m_s;
|
std::array<Aviation::CAircraftSituation, 3> m_s; //!< used situations
|
||||||
Interpolant m_interpolant;
|
Interpolant m_interpolant;
|
||||||
};
|
};
|
||||||
} // ns
|
} // ns
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ namespace BlackMiscTest
|
|||||||
{
|
{
|
||||||
CCallsign cs("SWIFT");
|
CCallsign cs("SWIFT");
|
||||||
CRemoteAircraftProviderDummy provider;
|
CRemoteAircraftProviderDummy provider;
|
||||||
CInterpolatorLinear interpolator(cs);
|
CInterpolatorLinear interpolator(cs, nullptr, nullptr, &provider);
|
||||||
interpolator.setRemoteAircraftProvider(&provider);
|
|
||||||
|
|
||||||
// fixed time so everything can be debugged
|
// fixed time so everything can be debugged
|
||||||
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch();
|
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ namespace BlackMiscTest
|
|||||||
{
|
{
|
||||||
CCallsign cs("SWIFT");
|
CCallsign cs("SWIFT");
|
||||||
CRemoteAircraftProviderDummy provider;
|
CRemoteAircraftProviderDummy provider;
|
||||||
CInterpolatorSpline interpolator(cs);
|
CInterpolatorSpline interpolator(cs, nullptr, nullptr, &provider);
|
||||||
interpolator.setRemoteAircraftProvider(&provider);
|
|
||||||
|
|
||||||
// fixed time so everything can be debugged
|
// fixed time so everything can be debugged
|
||||||
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch()
|
const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch()
|
||||||
@@ -64,12 +63,10 @@ namespace BlackMiscTest
|
|||||||
qint64 pTs = p.getAdjustedMSecsSinceEpoch();
|
qint64 pTs = p.getAdjustedMSecsSinceEpoch();
|
||||||
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
||||||
QVERIFY2(pTs == ts, "Expect latest ts");
|
QVERIFY2(pTs == ts, "Expect latest ts");
|
||||||
QCOMPARE(p.isOnGroundInterpolated(), 1.0);
|
|
||||||
p = interpolator.getInterpolatedParts(farPast, setup, status);
|
p = interpolator.getInterpolatedParts(farPast, setup, status);
|
||||||
pTs = p.getAdjustedMSecsSinceEpoch();
|
pTs = p.getAdjustedMSecsSinceEpoch();
|
||||||
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
||||||
QVERIFY2(pTs == oldestTs, "Expect oldest ts");
|
QVERIFY2(pTs == oldestTs, "Expect oldest ts");
|
||||||
QCOMPARE(p.isOnGroundInterpolated(), 1.0);
|
|
||||||
|
|
||||||
// Testing for a time >> last time
|
// Testing for a time >> last time
|
||||||
// all on ground flags true
|
// all on ground flags true
|
||||||
@@ -82,7 +79,6 @@ namespace BlackMiscTest
|
|||||||
pTs = p.getAdjustedMSecsSinceEpoch();
|
pTs = p.getAdjustedMSecsSinceEpoch();
|
||||||
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
QVERIFY2(status.isSupportingParts(), "Should support parts");
|
||||||
QVERIFY2(p.getAdjustedMSecsSinceEpoch() == pTs, "Expect latest ts");
|
QVERIFY2(p.getAdjustedMSecsSinceEpoch() == pTs, "Expect latest ts");
|
||||||
QCOMPARE(p.isOnGroundInterpolated(), 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTestInterpolatorParts::partsToSituationGndFlag()
|
void CTestInterpolatorParts::partsToSituationGndFlag()
|
||||||
|
|||||||
Reference in New Issue
Block a user