refs #386, performance issues

* keep split per callsign map in IInterpolator (so it is available for all interpolators)
* added signals to provider to add split situations / callsigns
* adjustments to airspace / context for those signals
* thread safe access to those from interpolator
* renamed from rendered to remote aircraft as discussed
* adjust samples
* removed no longer required functions in timestampobjectlist
This commit is contained in:
Klaus Basan
2015-02-19 02:14:20 +01:00
parent f8bebf5ffa
commit 190e2c3757
8 changed files with 224 additions and 110 deletions

View File

@@ -126,16 +126,16 @@ namespace BlackCore
signals:
//--- signals for the provider, work locally only (not in DBus
//--- signals for the provider, work locally only (not in DBus)
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftSituation
void addedRemoteAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation) override;
void addedRemoteAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! \copydoc IRemoteAircraftProviderReadOnly::addedRemoteAircraftPart
void addedRemoteAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts) override;
void addedRemoteAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);
//! \copydoc IRemoteAircraftProviderReadOnly::removedAircraft
void removedAircraft(const BlackMisc::Aviation::CCallsign &callsign) override;
void removedRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign);
//! Online ATC stations were changed
void changedAtcStationsOnline();
@@ -172,6 +172,7 @@ namespace BlackCore
CVatsimDataFileReader *m_vatsimDataFileReader = nullptr;
CAirspaceWatchdog m_atcWatchdog;
CAirspaceWatchdog m_aircraftWatchdog;
bool m_serverSupportsNameQuery = false; //!< not all servers support name query
// TODO FIXME (MS) should be in INetwork
void sendFsipiCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;

View File

@@ -29,7 +29,7 @@ namespace BlackCore
bool c = provider->connectRemoteAircraftProviderSignals(
std::bind(&IInterpolator::ps_onAddedAircraftSituation, this, std::placeholders::_1),
std::bind(&IInterpolator::ps_onAddedAircraftParts, this, std::placeholders::_1),
std::bind(&IInterpolator::ps_onRemoveAircraft, this, std::placeholders::_1)
std::bind(&IInterpolator::ps_onRemovedAircraft, this, std::placeholders::_1)
);
Q_ASSERT(c);
Q_UNUSED(c);
@@ -63,18 +63,19 @@ namespace BlackCore
return partsList.latestValue();
}
void IInterpolator::requestSituationsCalculationsForAllCallsigns(int requestId)
void IInterpolator::syncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch)
{
QReadLocker l(&m_situationsLock);
Q_ASSERT(requestId >= 0);
const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> situationsCopy = m_situationsByCallsign;
const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> situationsCopy(m_situationsByCallsign);
l.unlock();
CAircraftSituationList latestInterpolations;
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
for (const CCallsign &cs : situationsCopy.keys())
{
bool ok;
CAircraftSituation situation = getCurrentInterpolatedSituation(situationsCopy, cs, &ok);
bool ok = false;
CAircraftSituation situation = getCurrentInterpolatedSituation(situationsCopy, cs, currentTimeMsSinceEpoch, &ok);
if (ok)
{
latestInterpolations.push_back(situation);
@@ -92,15 +93,33 @@ namespace BlackCore
m_requestedInterpolations.erase(--m_requestedInterpolations.end());
}
m_requestedInterpolations.insert(requestId, latestInterpolations); // new to old
if (m_withDebugMsg)
{
CLogMessage(this).debug() << "Added request" << requestId << "with" << latestInterpolations.size() << "interpolation(s)";
}
}
void IInterpolator::asyncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch)
{
Q_ASSERT(requestId >= 0);
QMetaObject::invokeMethod(this, "syncRequestSituationsCalculationsForAllCallsigns",
Qt::QueuedConnection, Q_ARG(int, requestId), Q_ARG(qint64, currentTimeMsSinceEpoch));
}
QHash<CCallsign, CAircraftSituationList> IInterpolator::getSituationsByCallsign() const
{
QReadLocker l(&m_requestedInterpolationsLock);
QReadLocker l(&m_situationsLock);
return m_situationsByCallsign;
}
CAircraftSituationList IInterpolator::getSituationsForCallsign(const CCallsign &callsign) const
{
QReadLocker l(&m_situationsLock);
static const CAircraftSituationList empty;
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
return m_situationsByCallsign[callsign];
}
int IInterpolator::latestFinishedRequestId() const
{
QReadLocker l(&m_requestedInterpolationsLock);
@@ -132,41 +151,37 @@ namespace BlackCore
void IInterpolator::ps_onAddedAircraftSituation(const CAircraftSituation &situation)
{
QWriteLocker lock(&m_situationsLock);
Q_ASSERT(!situation.getCallsign().isEmpty());
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << situation; }
const CCallsign callsign(situation.getCallsign());
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << situation.getCallsign() << situation.getMSecsSinceEpoch(); }
// list from new to old
CAircraftSituationList &l = this->m_situationsByCallsign[situation.getCallsign()];
if (l.size() >= MaxSituationsPerCallsign - 1)
{
l.truncate(MaxSituationsPerCallsign - 1);
}
l.insert(situation);
CAircraftSituationList &l = this->m_situationsByCallsign[callsign];
l.insertTimestampObject(situation, MaxSituationsPerCallsign);
}
void IInterpolator::ps_onAddedAircraftParts(const CAircraftParts &parts)
{
QWriteLocker lock(&m_partsLock);
Q_ASSERT(!parts.getCallsign().isEmpty());
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << parts; }
const CCallsign callsign(parts.getCallsign());
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << parts.getCallsign() << parts.getMSecsSinceEpoch(); }
// list from new to old
CAircraftPartsList &l = this->m_partsByCallsign[parts.getCallsign()];
if (l.size() >= MaxPartsPerCallsign - 1)
{
l.truncate(MaxPartsPerCallsign - 1);
}
l.insert(parts);
CAircraftPartsList &l = this->m_partsByCallsign[callsign];
l.insertTimestampObject(parts, MaxPartsPerCallsign);
}
void IInterpolator::ps_onRemoveAircraft(const CCallsign &callsign)
void IInterpolator::ps_onRemovedAircraft(const CCallsign &callsign)
{
QWriteLocker ls(&m_situationsLock);
QWriteLocker lp(&m_partsLock);
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
if (callsign.isEmpty()) { return; }
this->m_partsByCallsign.remove(callsign);
this->m_situationsByCallsign.remove(callsign);
}

View File

@@ -32,28 +32,30 @@ namespace BlackCore
//! Virtual destructor
virtual ~IInterpolator() {}
//! Log category
static QString getMessageCategory() { return "swift.iinterpolator"; }
//! Has situations?
//! \deprecated Try no to use, it would be more efficient to directly getting the values and decide then
//! \deprecated Try not to use, it would be more efficient to directly getting the values and decide then
//! \threadsafe
virtual bool hasEnoughAircraftSituations(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Current interpolated situation
//! \threadsafe
virtual BlackMisc::Aviation::CAircraftSituation getCurrentInterpolatedSituation(const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> &allSituations, const BlackMisc::Aviation::CCallsign &callsign, bool *ok = nullptr) const = 0;
virtual BlackMisc::Aviation::CAircraftSituation getCurrentInterpolatedSituation(const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> &allSituations, const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc = -1, bool *ok = nullptr) const = 0;
//! Latest parts before time - offset
//! \threadsafe
BlackMisc::Aviation::CAircraftParts getLatestPartsBeforeOffset(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset = TimeOffsetMs, bool *ok = nullptr) const;
//! Do a complete calculation for all know callsigns in background.
//! Only use positive numbers.
//! \threadsafe
void requestSituationsCalculationsForAllCallsigns(int requestId);
//! The situations per callsign
//! \threadsafe
QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> getSituationsByCallsign() const;
//! Situations for given callsign
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList getSituationsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Last finished request id, -1 means none
//! \threadsafe
int latestFinishedRequestId() const;
@@ -66,11 +68,26 @@ namespace BlackCore
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList getRequest(int requestId, bool *ok = nullptr) const;
//! Enable debug messages
void enableDebugMessages(bool enabled);
static const qint64 TimeOffsetMs = 6000; //!< offset for interpolation
static const int MaxSituationsPerCallsign = 6; //!< How many situations per callsign
static const int MaxPartsPerCallsign = 3; //!< How many parts per callsign
static const int MaxKeptInterpolationRequests = 3; //!< How many requests are stored
public slots:
//! Do a complete calculation for all know callsigns in background.
//! Only use positive numbers.
//! \param currentTimeMsSinceEpoch if no value is passed current time is used
//! \threadsafe
void syncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch = -1);
//! Do a complete calculation for all know callsigns in background.
//! Non blocking call of \syncRequestSituationsCalculationsForAllCallsigns
//! \threadsafe
void asyncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch = -1);
private slots:
//! New situation got added
//! \threadsafe
@@ -82,7 +99,7 @@ namespace BlackCore
//! Removed aircraft
//! \threadsafe
void ps_onRemoveAircraft(const BlackMisc::Aviation::CCallsign &callsign);
void ps_onRemovedAircraft(const BlackMisc::Aviation::CCallsign &callsign);
protected:
//! Constructor
@@ -94,7 +111,7 @@ namespace BlackCore
//! \deprecated For first version
QList<BlackMisc::Aviation::CAircraftSituationList> getSituationsTimeSplit(const BlackMisc::Aviation::CCallsign &callsign, qint64 splitTimeMsSinceEpoch) const;
bool m_withDebugMsg = true;
bool m_withDebugMsg = false; //!< allows to disable debug messages
private:
mutable QReadWriteLock m_situationsLock;

View File

@@ -9,8 +9,10 @@
#include "interpolator_linear.h"
#include "blackmisc/avaircraftsituation.h"
#include "blackmisc/logmessage.h"
#include <QDateTime>
using namespace BlackMisc;
using namespace BlackMisc::Geo;
using namespace BlackMisc::Math;
using namespace BlackMisc::PhysicalQuantities;
@@ -18,64 +20,96 @@ using namespace BlackMisc::Aviation;
namespace BlackCore
{
CAircraftSituation CInterpolatorLinear::getCurrentInterpolatedSituation(const QHash<CCallsign, CAircraftSituationList> &allSituations, const CCallsign &callsign, bool *ok) const
CAircraftSituation CInterpolatorLinear::getCurrentInterpolatedSituation(const QHash<CCallsign, CAircraftSituationList> &allSituations, const CCallsign &callsign, qint64 currentTimeMsSinceEpoc, bool *ok) const
{
const static CAircraftSituation empty;
qint64 splitTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch() - TimeOffsetMs;
if (ok) { *ok = false; }
if (!allSituations.contains(callsign)) { return empty; }
if (allSituations[callsign].isEmpty()) { return empty; }
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
qint64 splitTimeMsSinceEpoch = currentTimeMsSinceEpoc - TimeOffsetMs;
QList<CAircraftSituationList> splitSituations = allSituations[callsign].splitByTime(splitTimeMsSinceEpoch);
CAircraftSituationList &situationsBefore = splitSituations[0];
CAircraftSituationList &situationsAfter = splitSituations[1];
if (situationsBefore.isEmpty())
{
if (ok) { *ok = false; }
return empty;
}
CAircraftSituationList &situationsNewer = splitSituations[0]; // latest first
CAircraftSituationList &situationsOlder = splitSituations[1]; // latest first
CAircraftSituation beginSituation;
CAircraftSituation endSituation;
// interpolation situations
CAircraftSituation oldSituation;
CAircraftSituation newSituation;
int situationsNewerNo = situationsNewer.size();
int situationsOlderNo = situationsOlder.size();
// The first condition covers a situation, when there is now future packet.
// So we have to extrapolate.
if (situationsAfter.isEmpty())
// latest first, now 00:26 -> 00:26 - 6000ms -> 00:20 split time
// time pos
// 00:25 10 newer
// 00:20 11 newer
// <----- split
// 00:15 12 older
// 00:10 13 older
// 00:05 14 older
// The first condition covers a situation, when there are no before / after situations.
// We just place at he last position until we get before / after situations
if (situationsOlderNo < 1 || situationsNewerNo < 1)
{
beginSituation = situationsBefore[situationsBefore.size() - 2];
endSituation = situationsBefore[situationsBefore.size() - 1];
if (ok) { *ok = true; }
// no after situations
if (situationsOlderNo < 1) { return situationsNewer.back(); } // oldest newest
// no before situations
if (situationsOlder.size() < 2) { return situationsOlder.front(); } // latest older
// this will lead to extrapolation
oldSituation = situationsOlder[1]; // before newest
newSituation = situationsOlder.front(); // newest
}
else
{
beginSituation = situationsBefore.back();
endSituation = situationsAfter.front();
oldSituation = situationsOlder.front(); // first oldest
newSituation = situationsNewer.back(); // latest newest
Q_ASSERT(oldSituation.getMSecsSinceEpoch() < newSituation.getMSecsSinceEpoch());
}
CAircraftSituation currentSituation;
CAircraftSituation currentSituation(oldSituation);
CCoordinateGeodetic currentPosition;
// Time between start and end packet
double deltaTime = beginSituation.msecsToAbs(endSituation);
double deltaTime = oldSituation.absMsecsTo(newSituation);
// Fraction of the deltaTime [0.0 - 1.0]
double simulationTimeFraction = (beginSituation.getMSecsSinceEpoch() - splitTimeMsSinceEpoch) / deltaTime;
// Fraction of the deltaTime, ideally [0.0 - 1.0]
// < 0 should not happen due to the split, > 1 can happen if new values are delayed beyond split time
// 1) values > 1 mean extrapolation
// 2) values > 2 mean no new situations coming in
double simulationTimeFraction = 1 - ((newSituation.getMSecsSinceEpoch() - splitTimeMsSinceEpoch) / deltaTime);
if (simulationTimeFraction > 1.5)
{
if (this->m_withDebugMsg)
{
CLogMessage(this).warning("Extrapolation, fraction > 1: %1 for callsign: %2") << simulationTimeFraction << callsign;
}
}
// Interpolate latitude: Lat = (LatB - LatA) * t + LatA
currentPosition.setLatitude((endSituation.getPosition().latitude() - beginSituation.getPosition().latitude())
currentPosition.setLatitude((newSituation.getPosition().latitude() - oldSituation.getPosition().latitude())
* simulationTimeFraction
+ beginSituation.getPosition().latitude());
+ oldSituation.getPosition().latitude());
// Interpolate latitude: Lon = (LonB - LonA) * t + LonA
currentPosition.setLongitude((endSituation.getPosition().longitude() - beginSituation.getPosition().longitude())
currentPosition.setLongitude((newSituation.getPosition().longitude() - oldSituation.getPosition().longitude())
* simulationTimeFraction
+ beginSituation.getPosition().longitude());
+ oldSituation.getPosition().longitude());
currentSituation.setPosition(currentPosition);
// Interpolate altitude: Alt = (AltB - AltA) * t + AltA
currentSituation.setAltitude(CAltitude((endSituation.getAltitude() - beginSituation.getAltitude())
currentSituation.setAltitude(CAltitude((newSituation.getAltitude() - oldSituation.getAltitude())
* simulationTimeFraction
+ beginSituation.getAltitude(),
beginSituation.getAltitude().getReferenceDatum()));
+ oldSituation.getAltitude(),
oldSituation.getAltitude().getReferenceDatum()));
// Interpolate heading: HDG = (HdgB - HdgA) * t + HdgA
CHeading headingBegin = beginSituation.getHeading();
CHeading headingEnd = endSituation.getHeading();
CHeading headingBegin = oldSituation.getHeading();
CHeading headingEnd = newSituation.getHeading();
if ((headingEnd - headingBegin).value(CAngleUnit::deg()) < -180)
{
@@ -93,8 +127,8 @@ namespace BlackCore
headingBegin.getReferenceNorth()));
// Interpolate Pitch: Pitch = (PitchB - PitchA) * t + PitchA
CAngle pitchBegin = beginSituation.getPitch();
CAngle pitchEnd = endSituation.getPitch();
CAngle pitchBegin = oldSituation.getPitch();
CAngle pitchEnd = newSituation.getPitch();
CAngle pitch = (pitchEnd - pitchBegin) * simulationTimeFraction + pitchBegin;
// TODO: According to the specification, pitch above horizon should be negative.
@@ -103,8 +137,8 @@ namespace BlackCore
currentSituation.setPitch(pitch);
// Interpolate bank: Bank = (BankB - BankA) * t + BankA
CAngle bankBegin = beginSituation.getBank();
CAngle bankEnd = endSituation.getBank();
CAngle bankBegin = oldSituation.getBank();
CAngle bankEnd = newSituation.getBank();
CAngle bank = (bankEnd - bankBegin) * simulationTimeFraction + bankBegin;
// TODO: According to the specification, banks to the right should be negative.
@@ -112,10 +146,11 @@ namespace BlackCore
bank *= -1;
currentSituation.setBank(bank);
currentSituation.setGroundspeed((endSituation.getGroundSpeed() - beginSituation.getGroundSpeed())
currentSituation.setGroundspeed((newSituation.getGroundSpeed() - oldSituation.getGroundSpeed())
* simulationTimeFraction
+ beginSituation.getGroundSpeed());
+ oldSituation.getGroundSpeed());
if (ok) { *ok = true; }
Q_ASSERT(currentSituation.getCallsign() == callsign);
return currentSituation;
}

View File

@@ -30,7 +30,11 @@ namespace BlackCore
{}
//! \copydoc IInterpolator::getCurrentInterpolatedSituation
virtual BlackMisc::Aviation::CAircraftSituation getCurrentInterpolatedSituation(const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> &allSituations, const BlackMisc::Aviation::CCallsign &callsign, bool *ok = nullptr) const override;
virtual BlackMisc::Aviation::CAircraftSituation getCurrentInterpolatedSituation(const QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> &allSituations, const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc = -1, bool *ok = nullptr) const override;
//! Log category
static QString getMessageCategory() { return "swift.interpolatorlinear"; }
};
} // namespace BlackCore