From eca8c5b637ba1edc018a13465892ba2e8f29cfc8 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Thu, 26 Feb 2015 21:47:28 +0100 Subject: [PATCH] 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 --- src/blackcore/airspace_monitor.cpp | 17 +- src/blackcore/airspace_monitor.h | 2 +- src/blackcore/interpolator.cpp | 158 +++++++-------- src/blackcore/interpolator.h | 103 +++++----- src/blackcore/interpolator_linear.cpp | 64 +++++-- src/blackcore/interpolator_linear.h | 2 +- src/blackcore/network.h | 3 +- src/blackcore/network_vatlib.cpp | 21 +- src/blackcore/network_vatlib.h | 4 +- src/blackmisc/avaircraftsituation.cpp | 6 +- src/blackmisc/coordinategeodetic.h | 2 +- src/blackmisc/sequence.h | 14 +- .../simdirectaccessremoteaircraftdummy.cpp | 2 +- .../simulation/simulatedaircraftlist.cpp | 11 ++ .../simulation/simulatedaircraftlist.h | 3 + src/blackmisc/timestampobjectlist.cpp | 24 ++- src/blackmisc/timestampobjectlist.h | 5 +- src/plugins/simulator/fs9/fs9_client.cpp | 29 +-- .../fsx/simconnect_datadefinition.cpp | 35 ++-- .../simulator/fsx/simconnect_datadefinition.h | 10 +- src/plugins/simulator/fsx/simulator_fsx.cpp | 181 ++++++++++-------- 21 files changed, 376 insertions(+), 320 deletions(-) diff --git a/src/blackcore/airspace_monitor.cpp b/src/blackcore/airspace_monitor.cpp index 7f0267c54..a507c5bd6 100644 --- a/src/blackcore/airspace_monitor.cpp +++ b/src/blackcore/airspace_monitor.cpp @@ -120,9 +120,13 @@ namespace BlackCore std::function 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(); } diff --git a/src/blackcore/airspace_monitor.h b/src/blackcore/airspace_monitor.h index e60ed3fb3..ffdfeb190 100644 --- a/src/blackcore/airspace_monitor.h +++ b/src/blackcore/airspace_monitor.h @@ -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); diff --git a/src/blackcore/interpolator.cpp b/src/blackcore/interpolator.cpp index 1ab5f8d89..811469f1e 100644 --- a/src/blackcore/interpolator.cpp +++ b/src/blackcore/interpolator.cpp @@ -35,48 +35,19 @@ namespace BlackCore Q_UNUSED(c); } - QList IInterpolator::getSituationsTimeSplit(const CCallsign &callsign, qint64 splitTimeMsSinceEpoch) const + CAircraftSituationList IInterpolator::getInterpolatedSituations(qint64 currentTimeMsSinceEpoch) { - QReadLocker l(&m_situationsLock); - static const QList 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 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 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 diff --git a/src/blackcore/interpolator.h b/src/blackcore/interpolator.h index deb11fce4..0a3857cd5 100644 --- a/src/blackcore/interpolator.h +++ b/src/blackcore/interpolator.h @@ -29,45 +29,72 @@ namespace BlackCore Q_OBJECT public: + //! Situations per callsign + typedef QHash CSituationsPerCallsign; + + //! Parts per callsign + typedef QHash 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 &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 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 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 m_situationsByCallsign; - QHash m_partsByCallsign; - QHash 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 diff --git a/src/blackcore/interpolator_linear.cpp b/src/blackcore/interpolator_linear.cpp index eff90fd25..a507b70bf 100644 --- a/src/blackcore/interpolator_linear.cpp +++ b/src/blackcore/interpolator_linear.cpp @@ -20,18 +20,31 @@ using namespace BlackMisc::Aviation; namespace BlackCore { - CAircraftSituation CInterpolatorLinear::getCurrentInterpolatedSituation(const QHash &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 splitSituations; if (currentTimeMsSinceEpoc < 0) { currentTimeMsSinceEpoc = QDateTime::currentMSecsSinceEpoch(); } qint64 splitTimeMsSinceEpoch = currentTimeMsSinceEpoc - TimeOffsetMs; - QList 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; } diff --git a/src/blackcore/interpolator_linear.h b/src/blackcore/interpolator_linear.h index ff003f267..7f8df1cb9 100644 --- a/src/blackcore/interpolator_linear.h +++ b/src/blackcore/interpolator_linear.h @@ -30,7 +30,7 @@ namespace BlackCore {} //! \copydoc IInterpolator::getCurrentInterpolatedSituation - virtual BlackMisc::Aviation::CAircraftSituation getCurrentInterpolatedSituation(const QHash &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"; } diff --git a/src/blackcore/network.h b/src/blackcore/network.h index 48de0bad8..a5613a9b0 100644 --- a/src/blackcore/network.h +++ b/src/blackcore/network.h @@ -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); /*! diff --git a/src/blackcore/network_vatlib.cpp b/src/blackcore/network_vatlib.cpp index 570a91dcd..06ab5db0d 100644 --- a/src/blackcore/network_vatlib.cpp +++ b/src/blackcore/network_vatlib.cpp @@ -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(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) diff --git a/src/blackcore/network_vatlib.h b/src/blackcore/network_vatlib.h index 716163c4d..2fe8ec5ca 100644 --- a/src/blackcore/network_vatlib.h +++ b/src/blackcore/network_vatlib.h @@ -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 diff --git a/src/blackmisc/avaircraftsituation.cpp b/src/blackmisc/avaircraftsituation.cpp index 7230ea038..05a9ead74 100644 --- a/src/blackmisc/avaircraftsituation.cpp +++ b/src/blackmisc/avaircraftsituation.cpp @@ -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; } diff --git a/src/blackmisc/coordinategeodetic.h b/src/blackmisc/coordinategeodetic.h index 50b3c1a7c..7dc35bb78 100644 --- a/src/blackmisc/coordinategeodetic.h +++ b/src/blackmisc/coordinategeodetic.h @@ -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 diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 6afcda90d..3976407ff 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -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); } /*! diff --git a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp index 6f436d14b..f5c4b1e16 100644 --- a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp +++ b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp @@ -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); } diff --git a/src/blackmisc/simulation/simulatedaircraftlist.cpp b/src/blackmisc/simulation/simulatedaircraftlist.cpp index 525a12c46..b9c83c97a 100644 --- a/src/blackmisc/simulation/simulatedaircraftlist.cpp +++ b/src/blackmisc/simulation/simulatedaircraftlist.cpp @@ -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; diff --git a/src/blackmisc/simulation/simulatedaircraftlist.h b/src/blackmisc/simulation/simulatedaircraftlist.h index b1b3e35c9..df097089d 100644 --- a/src/blackmisc/simulation/simulatedaircraftlist.h +++ b/src/blackmisc/simulation/simulatedaircraftlist.h @@ -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); } diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index 9801276a2..694ec5565 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -29,6 +29,14 @@ namespace BlackMisc }); } + template + CONTAINER ITimestampObjectList::findBeforeAndRemove(qint64 msSinceEpoch) + { + CONTAINER result(findBefore(msSinceEpoch)); + this->removeBefore(msSinceEpoch); + return result; + } + template CONTAINER ITimestampObjectList::findBeforeNowMinusOffset(qint64 msOffset) const { @@ -75,18 +83,18 @@ namespace BlackMisc OBJ ITimestampObjectList::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 OBJ ITimestampObjectList::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 @@ -134,14 +142,14 @@ namespace BlackMisc } template - void ITimestampObjectList::insertTimestampObject(const OBJ &object, int maxElements) + void ITimestampObjectList::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 diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 60f721484..660adf8b5 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -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 diff --git a/src/plugins/simulator/fs9/fs9_client.cpp b/src/plugins/simulator/fs9/fs9_client.cpp index c605eb748..263baee25 100644 --- a/src/plugins/simulator/fs9/fs9_client.cpp +++ b/src/plugins/simulator/fs9/fs9_client.cpp @@ -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() diff --git a/src/plugins/simulator/fsx/simconnect_datadefinition.cpp b/src/plugins/simulator/fsx/simconnect_datadefinition.cpp index 2587302cd..f7b3d11ed 100644 --- a/src/plugins/simulator/fsx/simconnect_datadefinition.cpp +++ b/src/plugins/simulator/fsx/simconnect_datadefinition.cpp @@ -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) { diff --git a/src/plugins/simulator/fsx/simconnect_datadefinition.h b/src/plugins/simulator/fsx/simconnect_datadefinition.h index 40db6bf98..2a906a35d 100644 --- a/src/plugins/simulator/fsx/simconnect_datadefinition.h +++ b/src/plugins/simulator/fsx/simconnect_datadefinition.h @@ -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 diff --git a/src/plugins/simulator/fsx/simulator_fsx.cpp b/src/plugins/simulator/fsx/simulator_fsx.cpp index 398b28761..16e6afc6b 100644 --- a/src/plugins/simulator/fsx/simulator_fsx.cpp +++ b/src/plugins/simulator/fsx/simulator_fsx.cpp @@ -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 ¤tSituation : 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)