Reflecting discussed changes for interpolation performance refs #386

(based on FSX testing)
* Only send changed situation to SIM
* Split sending of parts / situations
* Only send parts with a reduced frequency (means slower as positions)
* Mark geodetic height as null for default values (the value is usually unavailable)
* Fixed altitude to MSL for network data
* Trace which aircrafts support aircraft parts via network
* Renamed insert_fron push_front (as proposed by Roland)

Status quo / lessons learnt
* On slower PCs jitter is still noticed for interpolated aircraft.
* Running interpolation in an independent process (aka core, not in GUI) reduced load dependencies
  => it seems to make sense to run driver in own thread
* The onGround flag in parts seems clumsy as it required to retrieve parts for position updates
* In interpolation performance really matters
This commit is contained in:
Klaus Basan
2015-02-26 21:47:28 +01:00
parent ca6cd9c063
commit eca8c5b637
21 changed files with 376 additions and 320 deletions

View File

@@ -120,9 +120,13 @@ namespace BlackCore
std::function<void(const CCallsign &)> removedAircraftSlot
)
{
bool s1 = connect(this, &CAirspaceMonitor::addedRemoteAircraftSituation, situationSlot);
// bool s1 = connect(this, &CAirspaceMonitor::addedRemoteAircraftSituation, situationSlot);
bool s1 = connect(this->m_network, &INetwork::aircraftPositionUpdate, situationSlot);
Q_ASSERT(s1);
bool s2 = connect(this, &CAirspaceMonitor::addedRemoteAircraftParts, partsSlot);
Q_ASSERT(s2);
bool s3 = connect(this, &CAirspaceMonitor::removedRemoteAircraft, removedAircraftSlot);
Q_ASSERT(s3);
return s1 && s2 && s3;
}
@@ -747,16 +751,18 @@ namespace BlackCore
if (c > 0) { ps_sendReadyForModelMatching(callsign, 1); }
}
void CAirspaceMonitor::ps_aircraftUpdateReceived(const CCallsign &callsign, const CAircraftSituation &situation, const CTransponder &transponder)
void CAirspaceMonitor::ps_aircraftUpdateReceived(const CAircraftSituation &situation, const CTransponder &transponder)
{
Q_ASSERT(BlackCore::isCurrentThreadCreatingThread(this));
if (!this->m_connected) { return; }
CCallsign callsign(situation.getCallsign());
Q_ASSERT(!callsign.isEmpty());
// store situation history
CAircraftSituation situationWithCallsign(situation);
situationWithCallsign.setCallsign(callsign);
// this->m_aircraftSituations.insert_front(situationWithCallsign);
// this->m_aircraftSituations.insert_front(situation);
// this->m_aircraftSituations.removeOlderThanNowMinusOffset(AircraftSituationsRemovedOffsetMs);
emit this->addedRemoteAircraftSituation(situation);
bool exists = this->m_aircraftInRange.containsCallsign(callsign);
if (!exists)
@@ -829,7 +835,6 @@ namespace BlackCore
this->m_aircraftWatchdog.resetCallsign(callsign);
}
emit this->addedRemoteAircraftSituation(situationWithCallsign);
emit this->changedAircraftInRange();
}

View File

@@ -201,7 +201,7 @@ namespace BlackCore
private slots:
//! Create aircraft in range, this is the only place where a new aircraft should be added
void ps_aircraftUpdateReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);
void ps_aircraftUpdateReceived(const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);
void ps_realNameReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &realname);
void ps_capabilitiesReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, quint32 flags);

View File

@@ -35,48 +35,19 @@ namespace BlackCore
Q_UNUSED(c);
}
QList<CAircraftSituationList> IInterpolator::getSituationsTimeSplit(const CCallsign &callsign, qint64 splitTimeMsSinceEpoch) const
CAircraftSituationList IInterpolator::getInterpolatedSituations(qint64 currentTimeMsSinceEpoch)
{
QReadLocker l(&m_situationsLock);
static const QList<CAircraftSituationList> empty({ CAircraftSituationList(), CAircraftSituationList() });
if (!this->m_situationsByCallsign.contains(callsign)) { return empty; }
return this->m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch);
}
bool IInterpolator::hasEnoughAircraftSituations(const CCallsign &callsign) const
{
QReadLocker l(&m_situationsLock);
if (!this->m_situationsByCallsign.contains(callsign)) { return false; }
return this->m_situationsByCallsign[callsign].findBeforeNowMinusOffset(TimeOffsetMs).size() > 0;
}
CAircraftParts IInterpolator::getLatestPartsBeforeOffset(const CCallsign &callsign, qint64 timeOffset, bool *ok) const
{
QReadLocker l(&m_partsLock);
static const CAircraftParts empty;
if (ok) { *ok = false; }
if (this->m_partsByCallsign.contains(callsign)) { return empty; }
CAircraftPartsList partsList = this->m_partsByCallsign[callsign].findBeforeNowMinusOffset(timeOffset);
l.unlock();
if (partsList.isEmpty()) { return empty; }
if (ok) { *ok = true; }
return partsList.latestValue();
}
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);
QReadLocker l(&m_lockSituations);
const CSituationsPerCallsign situationsCopy(m_situationsByCallsign);
l.unlock();
CAircraftSituationList latestInterpolations;
if (currentTimeMsSinceEpoch < 0) { currentTimeMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
for (const CCallsign &cs : situationsCopy.keys())
{
bool ok = false;
CAircraftSituation situation = getCurrentInterpolatedSituation(situationsCopy, cs, currentTimeMsSinceEpoch, &ok);
if (ok)
InterpolationStatus status;
CAircraftSituation situation = getInterpolatedSituation(cs, currentTimeMsSinceEpoch, status, &situationsCopy);
if (status.allTrue())
{
latestInterpolations.push_back(situation);
}
@@ -86,71 +57,51 @@ namespace BlackCore
// further logging could go here
}
}
QWriteLocker wl(&m_requestedInterpolationsLock);
while (m_requestedInterpolations.size() >= MaxKeptInterpolationRequests - 1)
{
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)";
}
return latestInterpolations;
}
void IInterpolator::asyncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch)
IInterpolator::CSituationsPerCallsign IInterpolator::getSituationsByCallsign() const
{
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_situationsLock);
QReadLocker l(&m_lockSituations);
return m_situationsByCallsign;
}
CAircraftPartsList IInterpolator::getAndRemovePartsBeforeOffset(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus)
{
static const CAircraftPartsList empty;
partsStatus.reset();
QWriteLocker l(&m_lockParts);
if (this->m_partsByCallsign.contains(callsign))
{
partsStatus.supportsParts = true;
return this->m_partsByCallsign[callsign].findBeforeAndRemove(cutoffTime);
}
else
{
partsStatus.supportsParts = m_aircraftSupportingParts.contains(callsign);
return empty;
}
}
void IInterpolator::clear()
{
QWriteLocker s(&m_lockSituations);
QWriteLocker p(&m_lockParts);
m_situationsByCallsign.clear();
m_partsByCallsign.clear();
}
CAircraftSituationList IInterpolator::getSituationsForCallsign(const CCallsign &callsign) const
{
QReadLocker l(&m_situationsLock);
QReadLocker l(&m_lockSituations);
static const CAircraftSituationList empty;
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
return m_situationsByCallsign[callsign];
}
int IInterpolator::latestFinishedRequestId() const
{
QReadLocker l(&m_requestedInterpolationsLock);
if (m_requestedInterpolations.isEmpty()) { return -1; }
return m_requestedInterpolations.keys().first();
}
CAircraftSituationList IInterpolator::latestFinishedRequest() const
{
QReadLocker l(&m_requestedInterpolationsLock);
static const CAircraftSituationList empty;
if (m_requestedInterpolations.isEmpty()) { return empty; }
return m_requestedInterpolations.values().first();
}
CAircraftSituationList IInterpolator::getRequest(int requestId, bool *ok) const
{
QReadLocker l(&m_requestedInterpolationsLock);
static const CAircraftSituationList empty;
if (!m_requestedInterpolations.contains(requestId))
{
if (ok) { *ok = false; }
return empty;
}
if (ok) { *ok = true; }
return m_requestedInterpolations[requestId];
}
void IInterpolator::ps_onAddedAircraftSituation(const CAircraftSituation &situation)
{
QWriteLocker lock(&m_situationsLock);
QWriteLocker lock(&m_lockSituations);
const CCallsign callsign(situation.getCallsign());
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
@@ -158,32 +109,57 @@ namespace BlackCore
// list from new to old
CAircraftSituationList &l = this->m_situationsByCallsign[callsign];
l.insertTimestampObject(situation, MaxSituationsPerCallsign);
l.push_frontMaxElements(situation, MaxSituationsPerCallsign);
}
void IInterpolator::ps_onAddedAircraftParts(const CAircraftParts &parts)
{
QWriteLocker lock(&m_partsLock);
QWriteLocker lock(&m_lockParts);
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
// list sorted from new to old
CAircraftPartsList &l = this->m_partsByCallsign[callsign];
l.insertTimestampObject(parts, MaxPartsPerCallsign);
l.push_frontMaxElements(parts, MaxPartsPerCallsign);
if (m_aircraftSupportingParts.contains(callsign)) { return; }
m_aircraftSupportingParts.push_back(callsign);
}
void IInterpolator::ps_onRemovedAircraft(const CCallsign &callsign)
{
QWriteLocker ls(&m_situationsLock);
QWriteLocker lp(&m_partsLock);
QWriteLocker ls(&m_lockSituations);
QWriteLocker lp(&m_lockParts);
Q_ASSERT(!callsign.isEmpty());
if (callsign.isEmpty()) { return; }
if (this->m_withDebugMsg) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << callsign; }
this->m_partsByCallsign.remove(callsign);
this->m_situationsByCallsign.remove(callsign);
this->m_aircraftSupportingParts.remove(callsign);
}
bool IInterpolator::InterpolationStatus::allTrue() const
{
return interpolationSucceeded && changedPosition;
}
void IInterpolator::InterpolationStatus::reset()
{
changedPosition = false;
interpolationSucceeded = false;
}
bool IInterpolator::PartsStatus::allTrue() const
{
return supportsParts;
}
void IInterpolator::PartsStatus::reset()
{
supportsParts = false;
}
} // namespace

View File

@@ -29,45 +29,72 @@ namespace BlackCore
Q_OBJECT
public:
//! Situations per callsign
typedef QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> CSituationsPerCallsign;
//! Parts per callsign
typedef QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftPartsList> CPartsPerCallsign;
//! Virtual destructor
virtual ~IInterpolator() {}
//! Log category
static QString getMessageCategory() { return "swift.iinterpolator"; }
//! Has situations?
//! \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;
//! Status of interpolation
struct InterpolationStatus
{
public:
bool changedPosition = false; //!< position was changed
bool interpolationSucceeded = false; //!< interpolation succeeded (means enough values, etc.)
//! all OK
bool allTrue() const;
//! Reset to default values
void reset();
};
//! Status regarding parts
struct PartsStatus
{
bool supportsParts = false; //!< supports parts for given callsign
//! all OK
bool allTrue() const;
//! Reset to default values
void reset();
};
//! Current interpolated situation
//! \threadsafe
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;
virtual BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(
const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc,
InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign = nullptr) const = 0;
//! Latest parts before time - offset
//! Do a complete calculation for all know callsigns.
//! \param currentTimeMsSinceEpoch if no value is passed current time is used
//! \threadsafe
BlackMisc::Aviation::CAircraftParts getLatestPartsBeforeOffset(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset = TimeOffsetMs, bool *ok = nullptr) const;
//!
virtual BlackMisc::Aviation::CAircraftSituationList getInterpolatedSituations(qint64 currentTimeMsSinceEpoch = -1);
//! The situations per callsign
//! \threadsafe
QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> getSituationsByCallsign() const;
CSituationsPerCallsign getSituationsByCallsign() const;
//! Parts before given offset time (aka pending parts)
//! \threadsafe
virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeOffset(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset, PartsStatus &partsStatus);
//! Clear all data
//! \threadsafe
virtual void clear();
//! 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;
//! Latest calculation
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList latestFinishedRequest() const;
//! Calculation by id
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList getRequest(int requestId, bool *ok = nullptr) const;
//! Enable debug messages
void enableDebugMessages(bool enabled);
@@ -76,20 +103,6 @@ namespace BlackCore
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 requestId
//! \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 \sa syncRequestSituationsCalculationsForAllCallsigns
//! \threadsafe
void asyncRequestSituationsCalculationsForAllCallsigns(int requestId, qint64 currentTimeMsSinceEpoch = -1);
private slots:
//! New situation got added
//! \threadsafe
@@ -107,23 +120,17 @@ namespace BlackCore
//! Constructor
IInterpolator(BlackMisc::Simulation::IRemoteAircraftProviderReadOnly *provider, const QString &workerName, QObject *parent = nullptr);
//! Situations for times before / after
//! \sa ITimestampObjectList::splitByTime
//! \threadsafe
//! \deprecated For first version
QList<BlackMisc::Aviation::CAircraftSituationList> getSituationsTimeSplit(const BlackMisc::Aviation::CCallsign &callsign, qint64 splitTimeMsSinceEpoch) const;
bool m_withDebugMsg = false; //!< allows to disable debug messages
private:
mutable QReadWriteLock m_situationsLock;
mutable QReadWriteLock m_partsLock;
mutable QReadWriteLock m_requestedInterpolationsLock;
BlackMisc::Aviation::CCallsignList m_aircraftSupportingParts; //!< aircraft supporting parts
// hashs, because not sorted by key but keeping order
QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituationList> m_situationsByCallsign;
QHash<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftPartsList> m_partsByCallsign;
QHash<int, BlackMisc::Aviation::CAircraftSituationList> m_requestedInterpolations;
CSituationsPerCallsign m_situationsByCallsign; //!< situations
CPartsPerCallsign m_partsByCallsign; //!< parts
// locks
mutable QReadWriteLock m_lockSituations; //!< lock for situations
mutable QReadWriteLock m_lockParts; //!< lock for parts
};
} // namespace

View File

@@ -20,18 +20,31 @@ using namespace BlackMisc::Aviation;
namespace BlackCore
{
CAircraftSituation CInterpolatorLinear::getCurrentInterpolatedSituation(const QHash<CCallsign, CAircraftSituationList> &allSituations, const CCallsign &callsign, qint64 currentTimeMsSinceEpoc, bool *ok) const
CAircraftSituation CInterpolatorLinear::getInterpolatedSituation(const CCallsign &callsign, qint64 currentTimeMsSinceEpoc, InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign) const
{
const static CAircraftSituation empty;
if (ok) { *ok = false; }
if (!allSituations.contains(callsign)) { return empty; }
if (allSituations[callsign].isEmpty()) { return empty; }
status.reset();
QList<CAircraftSituationList> splitSituations;
if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); }
qint64 splitTimeMsSinceEpoch = currentTimeMsSinceEpoc - TimeOffsetMs;
QList<CAircraftSituationList> splitSituations = allSituations[callsign].splitByTime(splitTimeMsSinceEpoch);
CAircraftSituationList &situationsNewer = splitSituations[0]; // latest first
CAircraftSituationList &situationsOlder = splitSituations[1]; // latest first
if (situationsPerCallsign)
{
if (!situationsPerCallsign->contains(callsign)) { return empty; }
if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; }
splitSituations = (*situationsPerCallsign)[callsign].splitByTime(splitTimeMsSinceEpoch);
}
else
{
// only part where it is locked
QReadLocker lock(&m_lockSituations);
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
if (m_situationsByCallsign[callsign].isEmpty()) { return empty; }
splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch);
}
CAircraftSituationList &situationsNewer = splitSituations[0]; // newer part
CAircraftSituationList &situationsOlder = splitSituations[1]; // older part
// interpolation situations
CAircraftSituation oldSituation;
@@ -52,7 +65,6 @@ namespace BlackCore
// We just place at he last position until we get before / after situations
if (situationsOlderNo < 1 || situationsNewerNo < 1)
{
if (ok) { *ok = true; }
// no after situations
if (situationsOlderNo < 1) { return situationsNewer.back(); } // oldest newest
@@ -73,6 +85,7 @@ namespace BlackCore
CAircraftSituation currentSituation(oldSituation);
CCoordinateGeodetic currentPosition;
status.interpolationSucceeded = true;
// Time between start and end packet
double deltaTime = oldSituation.absMsecsTo(newSituation);
@@ -82,7 +95,7 @@ namespace BlackCore
// 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 (simulationTimeFraction > 2.0)
{
if (this->m_withDebugMsg)
{
@@ -91,21 +104,34 @@ namespace BlackCore
}
// Interpolate latitude: Lat = (LatB - LatA) * t + LatA
currentPosition.setLatitude((newSituation.getPosition().latitude() - oldSituation.getPosition().latitude())
const CLatitude oldLat(oldSituation.latitude());
const CLatitude newLat(newSituation.latitude());
const CLongitude oldLng(oldSituation.longitude());
const CLongitude newLng(newSituation.longitude());
currentPosition.setLatitude((newLat - oldLat)
* simulationTimeFraction
+ oldSituation.getPosition().latitude());
+ oldLat);
// Interpolate latitude: Lon = (LonB - LonA) * t + LonA
currentPosition.setLongitude((newSituation.getPosition().longitude() - oldSituation.getPosition().longitude())
currentPosition.setLongitude((newLng - oldLng)
* simulationTimeFraction
+ oldSituation.getPosition().longitude());
+ oldLng);
currentSituation.setPosition(currentPosition);
// Interpolate altitude: Alt = (AltB - AltA) * t + AltA
currentSituation.setAltitude(CAltitude((newSituation.getAltitude() - oldSituation.getAltitude())
const CAltitude oldAlt(oldSituation.getAltitude());
const CAltitude newAlt(newSituation.getAltitude());
Q_ASSERT(oldAlt.getReferenceDatum() == newAlt.getReferenceDatum()); // otherwise no calculation is possible
currentSituation.setAltitude(CAltitude((newAlt - oldAlt)
* simulationTimeFraction
+ oldSituation.getAltitude(),
oldSituation.getAltitude().getReferenceDatum()));
+ oldAlt,
oldAlt.getReferenceDatum()));
if (newLat == oldLat && newLng == oldLng && oldAlt == newAlt)
{
return currentSituation;
}
// Interpolate heading: HDG = (HdgB - HdgA) * t + HdgA
CHeading headingBegin = oldSituation.getHeading();
@@ -143,13 +169,13 @@ namespace BlackCore
// TODO: According to the specification, banks to the right should be negative.
// But somehow we get positive banks from the network.
bank *= -1;
bank *= -1.0;
currentSituation.setBank(bank);
currentSituation.setGroundspeed((newSituation.getGroundSpeed() - oldSituation.getGroundSpeed())
* simulationTimeFraction
+ oldSituation.getGroundSpeed());
if (ok) { *ok = true; }
status.changedPosition = true;
Q_ASSERT(currentSituation.getCallsign() == callsign);
return currentSituation;
}

View File

@@ -30,7 +30,7 @@ namespace BlackCore
{}
//! \copydoc IInterpolator::getCurrentInterpolatedSituation
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;
virtual BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(const BlackMisc::Aviation::CCallsign &callsign, qint64 currentTimeSinceEpoc, InterpolationStatus &status, const CSituationsPerCallsign *situationsPerCallsign = nullptr) const override;
//! Log category
static QString getMessageCategory() { return "swift.interpolatorlinear"; }

View File

@@ -430,8 +430,9 @@ namespace BlackCore
/*!
* We received a notification of the state of another aircraft on the network.
* Corresponding callsign in \sa CAircraftSituation::getCallsign .
*/
void aircraftPositionUpdate(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &situation,
void aircraftPositionUpdate(const BlackMisc::Aviation::CAircraftSituation &situation,
const BlackMisc::Aviation::CTransponder &transponder);
/*!

View File

@@ -648,16 +648,17 @@ namespace BlackCore
{
const CCallsign callsign(callsignChar);
const CAircraftSituation situation(
callsign,
CCoordinateGeodetic(position->latitude, position->longitude, 0.0),
CAltitude(position->altitudeTrue, CAltitude::AboveGround, CLengthUnit::ft()),
CAltitude(position->altitudeTrue, CAltitude::MeanSeaLevel, CLengthUnit::ft()),
CHeading(position->heading, CHeading::True, CAngleUnit::deg()),
CAngle(position->pitch, CAngleUnit::deg()),
CAngle(position->bank, CAngleUnit::deg()),
CSpeed(position->groundSpeed, CSpeedUnit::kts())
);
QString tn("transponder ");
tn.append(callsign.asString());
QString transponderName("transponder ");
transponderName.append(callsign.asString());
CTransponder::TransponderMode mode = CTransponder::StateStandby;
switch (position->transponderMode)
{
@@ -675,19 +676,21 @@ namespace BlackCore
break;
}
// I did have a situation where I got wrong transponger codes (KB)
// I did have a situation where I got wrong transponder codes (KB)
// So I now check for a valid code in order to detect such codes
CTransponder transponder(tn, 0, mode);
CTransponder transponder;
if (CTransponder::isValidTransponderCode(position->transponderCode))
{
transponder = CTransponder(tn, position->transponderCode, mode);
transponder = CTransponder(transponderName, position->transponderCode, mode);
}
else
{
// TODO: how do with log this
qDebug() << "Wrong transponder code" << position->transponderMode << callsign;
CLogMessage(static_cast<CNetworkVatlib *>(nullptr)).warning("Wrong transponder code %1 for %2") << position->transponderCode << callsign;
// default
transponder = CTransponder(transponderName, 7000, mode);
}
emit cbvar_cast(cbvar)->aircraftPositionUpdate(callsign, situation, transponder);
emit cbvar_cast(cbvar)->aircraftPositionUpdate(situation, transponder);
}
void CNetworkVatlib::onAircraftConfigReceived(VatSessionID, const char *callsign, const char *aircraftConfig, void *cbvar)

View File

@@ -27,7 +27,9 @@ namespace BlackCore
/*!
* Implementation of INetwork using the vatlib shim
*/
class CNetworkVatlib : public INetwork, public BlackMisc::Simulation::COwnAircraftProviderSupport
class CNetworkVatlib :
public INetwork,
public BlackMisc::Simulation::COwnAircraftProviderSupport // network vatlib consumes own aircraft data and sets ICAO/callsign data
{
Q_OBJECT

View File

@@ -111,16 +111,16 @@ namespace BlackMisc
CLength heightAboveGround(this->getHeightAboveGround());
if (!heightAboveGround.isNull())
{
return heightAboveGround.value(CLengthUnit::m()) < 2.0;
return heightAboveGround.value(CLengthUnit::m()) < 1.0;
}
// we guess on pitch an bank
if (qAbs(this->getPitch().value(CAngleUnit::deg())) > 10) { return false; }
if (qAbs(this->getBank().value(CAngleUnit::deg())) > 10) { return false; }
if (qAbs(this->getBank().value(CAngleUnit::deg())) > 10) { return false; }
if (this->getGroundSpeed().value(CSpeedUnit::km_h()) > 80) { return false; }
// not sure, but his is a guess
// not sure, but this is a guess
return true;
}

View File

@@ -183,7 +183,7 @@ namespace BlackMisc
BLACK_ENABLE_TUPLE_CONVERSION(CCoordinateGeodetic)
BlackMisc::Geo::CLatitude m_latitude; //!< Latitude
BlackMisc::Geo::CLongitude m_longitude; //!< Longitude
BlackMisc::PhysicalQuantities::CLength m_geodeticHeight; //!< height, ellipsoidal or geodetic height
BlackMisc::PhysicalQuantities::CLength m_geodeticHeight { 0, BlackMisc::PhysicalQuantities::CLengthUnit::nullUnit() }; //!< height, ellipsoidal or geodetic height
};
} // namespace

View File

@@ -234,6 +234,12 @@ namespace BlackMisc
*/
void push_back(const T &value) { Q_ASSERT(pimpl()); pimpl()->push_back(value); }
/*!
* \brief Insert as first element.
* \pre The sequence must be initialized.
*/
void push_front(const T &value) { insert(begin(), value); }
/*!
* \brief Move-appends an element at the end of the sequence.
* \pre The sequence must be initialized.
@@ -272,12 +278,6 @@ namespace BlackMisc
*/
void insert(T &&value) { push_back(std::move(value)); }
/*!
* \brief Insert as first element.
* \pre The sequence must be initialized.
*/
void insert_front(const T &value) { insert(begin(), value); }
/*!
* \brief Synonym for push_back.
* \pre The sequence must be initialized.
@@ -376,7 +376,7 @@ namespace BlackMisc
*/
int applyIf(const CPropertyIndexVariantMap &pattern, const CPropertyIndexVariantMap &newValues, bool skipEqualValues = false)
{
return applyIf([ & ](const T &value) { return value == pattern; }, newValues, skipEqualValues);
return applyIf([ & ](const T & value) { return value == pattern; }, newValues, skipEqualValues);
}
/*!

View File

@@ -75,7 +75,7 @@ namespace BlackMisc
void CRemoteAircraftProviderDummy::insertNewSituation(const CAircraftSituation &situation)
{
this->m_situations.insertTimestampObject(situation, 20);
this->m_situations.push_frontMaxElements(situation, 20);
emit addedRemoteAircraftSituation(situation);
}

View File

@@ -55,6 +55,17 @@ namespace BlackMisc
return this->findBy(Predicates::MemberValid(&CSimulatedAircraft::getPilot)).transform(Predicates::MemberTransform(&CSimulatedAircraft::getPilot));
}
CCallsignList CSimulatedAircraftList::getCallsignsWithSyncronizedParts() const
{
CCallsignList csl;
for (const CSimulatedAircraft &aircraft : (*this))
{
if (!aircraft.isPartsSynchronized()) { continue; }
csl.push_back(aircraft.getCallsign());
}
return csl;
}
CAircraftList CSimulatedAircraftList::toAircraftList() const
{
CAircraftList al;

View File

@@ -43,6 +43,9 @@ namespace BlackMisc
//! All pilots (with valid data)
BlackMisc::Network::CUserList getPilots() const;
//! Callsigns of aircraft with synchronized parts
BlackMisc::Aviation::CCallsignList getCallsignsWithSyncronizedParts() const;
//! \copydoc CValueObject::toQVariant
virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); }

View File

@@ -29,6 +29,14 @@ namespace BlackMisc
});
}
template <class OBJ, class CONTAINER>
CONTAINER ITimestampObjectList<OBJ, CONTAINER>::findBeforeAndRemove(qint64 msSinceEpoch)
{
CONTAINER result(findBefore(msSinceEpoch));
this->removeBefore(msSinceEpoch);
return result;
}
template <class OBJ, class CONTAINER>
CONTAINER ITimestampObjectList<OBJ, CONTAINER>::findBeforeNowMinusOffset(qint64 msOffset) const
{
@@ -75,18 +83,18 @@ namespace BlackMisc
OBJ ITimestampObjectList<OBJ, CONTAINER>::latestValue() const
{
if (this->container().isEmpty()) { return OBJ(); }
CONTAINER container(container()); // copy
container.sortLatestFirst();
return container.front();
CONTAINER copy(container()); // copy
copy.sortLatestFirst();
return copy.front();
}
template <class OBJ, class CONTAINER>
OBJ ITimestampObjectList<OBJ, CONTAINER>::oldestValue() const
{
if (this->container().isEmpty()) { return OBJ(); }
CONTAINER container(container()); // copy
container.sortLatestFirst();
return container.back();
CONTAINER copy(container()); // copy
copy.sortLatestFirst();
return copy.back();
}
template <class OBJ, class CONTAINER>
@@ -134,14 +142,14 @@ namespace BlackMisc
}
template <class OBJ, class CONTAINER>
void ITimestampObjectList<OBJ, CONTAINER>::insertTimestampObject(const OBJ &object, int maxElements)
void ITimestampObjectList<OBJ, CONTAINER>::push_frontMaxElements(const OBJ &object, int maxElements)
{
Q_ASSERT(maxElements > 1);
if (this->container().size() >= (maxElements - 1))
{
this->container().truncate(maxElements - 1);
}
this->container().insert_front(object);
this->container().push_front(object);
}
// see here for the reason of thess forward instantiations

View File

@@ -32,6 +32,9 @@ namespace BlackMisc
//! List of objects before msSinceEpoch
CONTAINER findBefore(qint64 msSinceEpoch) const;
//! Get objects before msSinceEpoch and remove those
CONTAINER findBeforeAndRemove(qint64 msSinceEpoch);
//! List of objects before now - offset
CONTAINER findBeforeNowMinusOffset(qint64 msOffset) const;
@@ -67,7 +70,7 @@ namespace BlackMisc
void sortOldestFirst();
//! Inserts as first object by keeping max. elements
void insertTimestampObject(const OBJ &object, int maxElements);
void push_frontMaxElements(const OBJ &object, int maxElements);
protected:
//! Constructor

View File

@@ -81,31 +81,14 @@ namespace BlackSimPlugin
if (m_clientStatus == Disconnected) { return; }
bool ok;
CAircraftSituation situation = this->m_interpolator->getCurrentInterpolatedSituation(this->m_interpolator->getSituationsByCallsign(), m_callsign, -1, &ok);
if (!ok) { return; }
MPPositionSlewMode positionSlewMode = aircraftSituationToFS9(situation);
IInterpolator::InterpolationStatus status;
CAircraftSituation situation = this->m_interpolator->getInterpolatedSituation(m_callsign, -1, status);
QByteArray positionMessage;
MultiPlayerPacketParser::writeType(positionMessage, CFs9Sdk::MULTIPLAYER_PACKET_ID_POSITION_SLEWMODE);
MultiPlayerPacketParser::writeSize(positionMessage, positionSlewMode.size());
positionSlewMode.packet_index = m_packetIndex;
++m_packetIndex;
positionMessage = MultiPlayerPacketParser::writeMessage(positionMessage, positionSlewMode);
sendMessage(positionMessage);
QByteArray paramMessage;
MPParam param;
MultiPlayerPacketParser::writeType(paramMessage, CFs9Sdk::MULTIPLAYER_PACKET_ID_PARAMS);
MultiPlayerPacketParser::writeSize(paramMessage, param.size());
param.packet_index = m_packetIndex;
++m_packetIndex;
paramMessage = MultiPlayerPacketParser::writeMessage(paramMessage, param);
sendMessage(paramMessage);
m_lastAircraftSituation = situation;
// Test only for successful interpolation. FS9 requires constant positions
if (!status.interpolationSucceeded) return;
sendMultiplayerPosition(situation);
sendMultiplayerParamaters();
}
void CFs9Client::initialize()

View File

@@ -71,31 +71,32 @@ namespace BlackSimPlugin
HRESULT CSimConnectDefinitions::initRemoteAircraft(const HANDLE hSimConnect)
{
HRESULT hr = S_OK;
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "Initial Position", NULL, SIMCONNECT_DATATYPE_INITPOSITION);
// Position
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPosition, "Initial Position", NULL, SIMCONNECT_DATATYPE_INITPOSITION);
// Lights
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT STROBE", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT LANDING", "Bool");
// hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT TAXI", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT BEACON", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT NAV", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LIGHT LOGO", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT STROBE", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT LANDING", "Bool");
// hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT TAXI", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT BEACON", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT NAV", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LIGHT LOGO", "Bool");
// Flaps
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LEADING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "LEADING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "TRAILING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "TRAILING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LEADING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "LEADING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "TRAILING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "TRAILING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
// Gear & Spoiler
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "GEAR HANDLE POSITION", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "SPOILERS HANDLE POSITION", "Percent Over 100");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GEAR HANDLE POSITION", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "SPOILERS HANDLE POSITION", "Percent Over 100");
// Engines
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "GENERAL ENG COMBUSTION:1", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "GENERAL ENG COMBUSTION:2", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "GENERAL ENG COMBUSTION:3", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraft, "GENERAL ENG COMBUSTION:4", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GENERAL ENG COMBUSTION:1", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GENERAL ENG COMBUSTION:2", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GENERAL ENG COMBUSTION:3", "Bool");
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GENERAL ENG COMBUSTION:4", "Bool");
if (hr != S_OK)
{

View File

@@ -70,13 +70,12 @@ namespace BlackSimPlugin
char title[256]; //!< Aircraft model string
};
//! Data struct of remote aircraft
struct DataDefinitionRemoteAircraft
//! Data struct of remote aircraft parts
struct DataDefinitionRemoteAircraftParts
{
SIMCONNECT_DATA_INITPOSITION position; //!< Aircrafts position
double lightStrobe; //!< Is strobe light on?
double lightLanding; //!< Is landing light on?
// double lightTaxi; //!< Is taxi light on?
// double lightTaxi; //!< Is taxi light on?
double lightBeacon; //!< Is beacon light on?
double lightNav; //!< Is nav light on?
double lightLogo; //!< Is logo light on?
@@ -141,7 +140,8 @@ namespace BlackSimPlugin
{
DataOwnAircraft,
DataOwnAircraftTitle,
DataRemoteAircraft,
DataRemoteAircraftParts,
DataRemoteAircraftPosition,
DataSimEnvironment,
DataClientAreaSb, //!< whole SB area
DataClientAreaSbIdent, //!< ident single value

View File

@@ -597,111 +597,138 @@ namespace BlackSimPlugin
void CSimulatorFsx::updateRemoteAircraft()
{
static_assert(sizeof(DataDefinitionRemoteAircraft) == 176, "DataDefinitionRemoteAircraft has an incorrect size.");
static_assert(sizeof(DataDefinitionRemoteAircraftParts) == 120, "DataDefinitionRemoteAircraftParts has an incorrect size.");
Q_ASSERT(this->m_interpolator);
Q_ASSERT_X(this->m_interpolator->thread() != this->thread(), "updateOtherAircraft", "interpolator should run in its own thread");
// nothing to do, reset request id and exit
if (this->isPaused()) { return; } // no interpolation while paused
int remoteAircraftNo = this->remoteAircraft().size();
if (remoteAircraftNo < 1) { m_interpolationRequest = 0; return; }
// initial request, and bye. First time we have aircraft
if (m_interpolationRequest == 0)
{
m_interpolator->syncRequestSituationsCalculationsForAllCallsigns(++m_interpolationRequest);
return;
}
// interpolate and send to SIM
m_interpolationRequest++;
// try to get old request
bool lastRequestAvailable = false;
CAircraftSituationList interpolations = m_interpolator->getRequest(m_interpolationRequest, &lastRequestAvailable);
if (!lastRequestAvailable)
{
// warning the 1st and every 10th time
bool warning = m_interpolationsSkipped % 10;
m_interpolationsSkipped++;
if (warning)
{
CLogMessage(this).warning("Skipped interpolation %1 time(s)") << m_interpolationsSkipped;
}
return;
}
// values used for position and parts
bool isOnGround = false;
// non blocking calculations in background
m_interpolator->syncRequestSituationsCalculationsForAllCallsigns(++m_interpolationRequest);
// now send to sim
for (const CAircraftSituation &currentSituation : interpolations)
qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
for (const CSimConnectObject &simObj : m_simConnectObjects)
{
const CCallsign callsign(currentSituation.getCallsign());
if (!m_simConnectObjects.contains(callsign)) { continue; } // only if aircraft is already available
bool hasParts;
const CSimConnectObject &simObj = m_simConnectObjects[callsign];
const CCallsign callsign(simObj.getCallsign());
IInterpolator::InterpolationStatus interpolatorStatus;
IInterpolator::PartsStatus partsStatus;
if (simObj.getObjectId() == 0) { continue; }
SIMCONNECT_DATA_INITPOSITION position = aircraftSituationToFsxInitPosition(currentSituation);
CAircraftParts parts = m_interpolator->getLatestPartsBeforeOffset(callsign, IInterpolator::TimeOffsetMs, &hasParts);
CAircraftSituation interpolatedSituation = this->m_interpolator->getInterpolatedSituation(callsign, currentTimestamp, interpolatorStatus);
DataDefinitionRemoteAircraft ddRemoteAircraft;
if (hasParts)
// having the on gground flag in parts forces me to obtain parts here
// which is not hte smartest thing regarding performance
CAircraftPartsList parts = this->m_interpolator->getAndRemovePartsBeforeOffset(callsign, currentTimestamp - IInterpolator::TimeOffsetMs, partsStatus);
if (interpolatorStatus.allTrue())
{
// update situation
SIMCONNECT_DATA_INITPOSITION position = aircraftSituationToFsxInitPosition(interpolatedSituation);
//! \todo The onGround in parts is nuts, as already mentioned in the discussion
// a) I am forced to read parts even if i just want to update position
// b) Unlike the other values it is not a fire aforget value, as I need it again in the next cycle
if (partsStatus.supportsParts && !parts.isEmpty())
{
// we have parts, and use the closest ground
isOnGround = parts.front().isOnGround();
}
else
{
isOnGround = interpolatedSituation.isOnGroundGuessed();
}
position.OnGround = isOnGround ? 1 : 0;
HRESULT hr = S_OK;
hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPosition,
simObj.getObjectId(), 0, 0,
sizeof(SIMCONNECT_DATA_INITPOSITION), &position);
if (hr != S_OK) { CLogMessage(this).warning("Failed so set position on SimObject %1 callsign: %2") << simObj.getObjectId() << callsign; }
}
// set parts
DataDefinitionRemoteAircraftParts ddRemoteAircraftParts;
if (partsStatus.supportsParts)
{
// parts is supported, but do we need to update?
if (parts.isEmpty()) { continue; }
// we have parts
position.OnGround = parts.isOnGround() ? 1 : 0;
ddRemoteAircraft.position = position;
ddRemoteAircraft.lightStrobe = parts.getLights().isStrobeOn() ? 1.0 : 0.0;
ddRemoteAircraft.lightLanding = parts.getLights().isLandingOn() ? 1.0 : 0.0;
// ddRemoteAircraft.lightTaxi = parts.getLights().isTaxiOn() ? 1.0 : 0.0;
ddRemoteAircraft.lightBeacon = parts.getLights().isBeaconOn() ? 1.0 : 0.0;
ddRemoteAircraft.lightNav = parts.getLights().isNavOn() ? 1.0 : 0.0;
ddRemoteAircraft.lightLogo = parts.getLights().isLogoOn() ? 1.0 : 0.0;
ddRemoteAircraft.flapsLeadingEdgeLeftPercent = parts.getFlapsPercent() / 100.0;
ddRemoteAircraft.flapsLeadingEdgeRightPercent = parts.getFlapsPercent() / 100.0;
ddRemoteAircraft.flapsTrailingEdgeLeftPercent = parts.getFlapsPercent() / 100.0;
ddRemoteAircraft.flapsTrailingEdgeRightPercent = parts.getFlapsPercent() / 100.0;
ddRemoteAircraft.spoilersHandlePosition = parts.isSpoilersOut() ? 1.0 : 0.0;
ddRemoteAircraft.gearHandlePosition = parts.isGearDown() ? 1 : 0;
ddRemoteAircraft.engine1Combustion = parts.isEngineOn(1) ? 1 : 0;
ddRemoteAircraft.engine2Combustion = parts.isEngineOn(2) ? 1 : 0;;
ddRemoteAircraft.engine3Combustion = parts.isEngineOn(3) ? 1 : 0;
ddRemoteAircraft.engine4Combustion = parts.isEngineOn(4) ? 1 : 0;
CAircraftParts newestParts = parts.front();
ddRemoteAircraftParts.lightStrobe = newestParts.getLights().isStrobeOn() ? 1.0 : 0.0;
ddRemoteAircraftParts.lightLanding = newestParts.getLights().isLandingOn() ? 1.0 : 0.0;
// ddRemoteAircraftParts.lightTaxi = newestParts.getLights().isTaxiOn() ? 1.0 : 0.0;
ddRemoteAircraftParts.lightBeacon = newestParts.getLights().isBeaconOn() ? 1.0 : 0.0;
ddRemoteAircraftParts.lightNav = newestParts.getLights().isNavOn() ? 1.0 : 0.0;
ddRemoteAircraftParts.lightLogo = newestParts.getLights().isLogoOn() ? 1.0 : 0.0;
ddRemoteAircraftParts.flapsLeadingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0;
ddRemoteAircraftParts.flapsLeadingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0;
ddRemoteAircraftParts.flapsTrailingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0;
ddRemoteAircraftParts.flapsTrailingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0;
ddRemoteAircraftParts.spoilersHandlePosition = newestParts.isSpoilersOut() ? 1.0 : 0.0;
ddRemoteAircraftParts.gearHandlePosition = newestParts.isGearDown() ? 1 : 0;
ddRemoteAircraftParts.engine1Combustion = newestParts.isEngineOn(1) ? 1 : 0;
ddRemoteAircraftParts.engine2Combustion = newestParts.isEngineOn(2) ? 1 : 0;;
ddRemoteAircraftParts.engine3Combustion = newestParts.isEngineOn(3) ? 1 : 0;
ddRemoteAircraftParts.engine4Combustion = newestParts.isEngineOn(4) ? 1 : 0;
}
else
{
//! \todo interpolator, set data without parts by educated guessing whatsoever
bool onGround = currentSituation.isOnGroundGuessed();
position.OnGround = onGround ? 1 : 0;
ddRemoteAircraft.position = position;
ddRemoteAircraft.gearHandlePosition = onGround ? 1 : 0;
// mode is guessing parts
if (this->m_interpolationRequest % 20 != 0) { continue; } // only update every 20th cycle
if (!interpolatorStatus.allTrue()) { continue; } // no position, no really guess possible
ddRemoteAircraftParts.gearHandlePosition = isOnGround ? 1 : 0;
// when first detected moving, lights on
if (onGround && currentSituation.getGroundSpeed().value(CSpeedUnit::km_h()) > 15)
if (isOnGround)
{
// ddRemoteAircraft.light = 1.0;
ddRemoteAircraft.lightBeacon = 1.0;
ddRemoteAircraft.lightNav = 1.0;
ddRemoteAircraft.lightLanding = 0.0;
// ddRemoteAircraftParts.lightTaxi = 1.0;
ddRemoteAircraftParts.lightBeacon = 1.0;
ddRemoteAircraftParts.lightNav = 1.0;
double gskmh = interpolatedSituation.getGroundSpeed().value(CSpeedUnit::km_h());
if (gskmh > 7.5)
{
// mode taxi
// ddRemoteAircraftParts.lightTaxi = 1.0;
ddRemoteAircraftParts.lightLanding = 0.0;
}
else if (gskmh > 25)
{
// mode accelaration for takeoff
// ddRemoteAircraftParts.lightTaxi = 0.0;
ddRemoteAircraftParts.lightLanding = 1.0;
}
else
{
// slow movements or parking
// ddRemoteAircraftParts.lightTaxi = 0.0;
ddRemoteAircraftParts.lightLanding = 0.0;
}
}
else if (onGround)
else
{
// ddRemoteAircraft.lightTaxi = 0.0;
ddRemoteAircraft.lightBeacon = 1.0;
ddRemoteAircraft.lightNav = 1.0;
ddRemoteAircraft.lightLanding = 0.0;
}
else if (!onGround)
{
// ddRemoteAircraft.lightTaxi = 0.0;
ddRemoteAircraft.lightBeacon = 1.0;
ddRemoteAircraft.lightNav = 1.0;
// ddRemoteAircraftParts.lightTaxi = 0.0;
ddRemoteAircraftParts.lightBeacon = 1.0;
ddRemoteAircraftParts.lightNav = 1.0;
// landing lights for < 10000ft (normally MSL, here ignored)
ddRemoteAircraftParts.lightLanding = (interpolatedSituation.getAltitude().value(CLengthUnit::ft()) < 10000) ? 1.0 : 0;
}
}
HRESULT hr = S_OK;
hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraft,
hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts,
simObj.getObjectId(), 0, 0,
sizeof(DataDefinitionRemoteAircraft), &ddRemoteAircraft);
sizeof(DataDefinitionRemoteAircraftParts), &ddRemoteAircraftParts);
if (hr != S_OK) { CLogMessage(this).warning("Failed so set parts on SimObject %1 callsign: %2") << simObj.getObjectId() << callsign; }
if (hr != S_OK) { CLogMessage(this).warning("Failed so set data on SimObject"); }
} // all situations
} // all callsigns
}
SIMCONNECT_DATA_INITPOSITION CSimulatorFsx::aircraftSituationToFsxInitPosition(const CAircraftSituation &situation)