Move X-Plane interpolation into driver

ref T259
This commit is contained in:
Roland Winklmeier
2018-02-18 12:42:54 +01:00
committed by Klaus Basan
parent f3b48f75a1
commit 4db0b83310
8 changed files with 792 additions and 77 deletions

View File

@@ -79,7 +79,6 @@ namespace BlackSimPlugin
{
namespace XPlane
{
CSimulatorXPlane::CSimulatorXPlane(const BlackMisc::Simulation::CSimulatorPluginInfo &info,
IOwnAircraftProvider *ownAircraftProvider,
IRemoteAircraftProvider *remoteAircraftProvider,
@@ -247,6 +246,7 @@ namespace BlackSimPlugin
connect(m_service, &CXSwiftBusServiceProxy::aircraftModelChanged, this, &CSimulatorXPlane::ps_emitOwnAircraftModelChanged);
connect(m_service, &CXSwiftBusServiceProxy::airportsInRangeUpdated, this, &CSimulatorXPlane::ps_setAirportsInRange);
m_service->updateAirportsInRange();
connect(m_traffic, &CXSwiftBusTrafficProxy::simFrame, this, &CSimulatorXPlane::updateRemoteAircraft);
if (m_watcher) { m_watcher->setConnection(m_conn); }
loadCslPackages();
emitSimulatorCombinedStatus();
@@ -375,8 +375,25 @@ namespace BlackSimPlugin
{
if (!isConnected()) { return false; }
m_traffic->setInterpolatorMode(callsign.asString(), mode == CInterpolatorMulti::ModeSpline);
return true;
if(c_driverInterpolation)
{
if (mode == CInterpolatorMulti::ModeUnknown) { return false; }
if (callsign.isEmpty())
{
const int c = m_xplaneAircrafts.setInterpolatorModes(mode);
return c > 0;
}
else
{
if (!m_xplaneAircrafts.contains(callsign)) { return false; }
return m_xplaneAircrafts[callsign].setInterpolatorMode(mode);
}
}
else
{
m_traffic->setInterpolatorMode(callsign.asString(), mode == CInterpolatorMulti::ModeSpline);
return true;
}
}
QDBusConnection CSimulatorXPlane::connectionFromString(const QString &str)
@@ -398,8 +415,15 @@ namespace BlackSimPlugin
bool CSimulatorXPlane::isPhysicallyRenderedAircraft(const CCallsign &callsign) const
{
//! \todo XP implement isRenderedAircraft correctly. This is a workaround, but not telling me if a callsign is really(!) visible in simulator
return getAircraftInRangeForCallsign(callsign).isRendered();
if (c_driverInterpolation)
{
return m_xplaneAircrafts.contains(callsign);
}
else
{
//! \todo XP implement isRenderedAircraft correctly. This is a workaround, but not telling me if a callsign is really(!) visible in simulator
return getAircraftInRangeForCallsign(callsign).isRendered();
}
}
bool CSimulatorXPlane::updateOwnSimulatorCockpit(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, const CIdentifier &originator)
@@ -487,90 +511,174 @@ namespace BlackSimPlugin
bool CSimulatorXPlane::physicallyAddRemoteAircraft(const CSimulatedAircraft &newRemoteAircraft)
{
Q_ASSERT(isConnected());
//! \todo XPlane driver check if already exists, how?
//! \todo XPlane driver set correct return value
if (c_driverInterpolation)
{
// entry checks
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread");
Q_ASSERT_X(!newRemoteAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "empty callsign");
Q_ASSERT_X(newRemoteAircraft.hasModelString(), Q_FUNC_INFO, "missing model string");
CAircraftModel aircraftModel = newRemoteAircraft.getModel();
QString livery = aircraftModel.getLivery().getCombinedCode(); //! \todo livery resolution for XP
m_traffic->addPlane(newRemoteAircraft.getCallsign().asString(), aircraftModel.getModelString(),
newRemoteAircraft.getAircraftIcaoCode().getDesignator(),
newRemoteAircraft.getAirlineIcaoCode().getDesignator(),
livery);
m_xplaneAircrafts.insert(newRemoteAircraft.getCallsign(), CXPlaneMPAircraft(newRemoteAircraft, &m_interpolationLogger));
CAircraftModel aircraftModel = newRemoteAircraft.getModel();
QString livery = aircraftModel.getLivery().getCombinedCode(); //! \todo livery resolution for XP
m_traffic->addPlane(newRemoteAircraft.getCallsign().asString(), aircraftModel.getModelString(),
newRemoteAircraft.getAircraftIcaoCode().getDesignator(),
newRemoteAircraft.getAirlineIcaoCode().getDesignator(),
livery);
CLogMessage(this).info("XP: Added aircraft %1") << newRemoteAircraft.getCallsign().toQString();
CLogMessage(this).info("XP: Added aircraft %1") << newRemoteAircraft.getCallsign().toQString();
bool rendered = true;
updateAircraftRendered(newRemoteAircraft.getCallsign(), rendered);
bool rendered = true;
updateAircraftRendered(newRemoteAircraft.getCallsign(), rendered);
CSimulatedAircraft remoteAircraftCopy(newRemoteAircraft);
remoteAircraftCopy.setRendered(rendered);
emit aircraftRenderingChanged(remoteAircraftCopy);
return true;
CSimulatedAircraft remoteAircraftCopy(newRemoteAircraft);
remoteAircraftCopy.setRendered(rendered);
emit aircraftRenderingChanged(remoteAircraftCopy);
return true;
}
else
{
CAircraftModel aircraftModel = newRemoteAircraft.getModel();
QString livery = aircraftModel.getLivery().getCombinedCode(); //! \todo livery resolution for XP
m_traffic->addPlane(newRemoteAircraft.getCallsign().asString(), aircraftModel.getModelString(),
newRemoteAircraft.getAircraftIcaoCode().getDesignator(),
newRemoteAircraft.getAirlineIcaoCode().getDesignator(),
livery);
CLogMessage(this).info("XP: Added aircraft %1") << newRemoteAircraft.getCallsign().toQString();
bool rendered = true;
updateAircraftRendered(newRemoteAircraft.getCallsign(), rendered);
CSimulatedAircraft remoteAircraftCopy(newRemoteAircraft);
remoteAircraftCopy.setRendered(rendered);
emit aircraftRenderingChanged(remoteAircraftCopy);
return true;
}
}
void CSimulatorXPlane::onRemoteProviderAddedAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation)
{
Q_ASSERT(isConnected());
using namespace BlackMisc::PhysicalQuantities;
m_traffic->addPlanePosition(situation.getCallsign().asString(),
situation.latitude().value(CAngleUnit::deg()),
situation.longitude().value(CAngleUnit::deg()),
situation.getAltitude().value(CLengthUnit::ft()),
situation.getPitch().value(CAngleUnit::deg()),
situation.getBank().value(CAngleUnit::deg()),
situation.getHeading().value(CAngleUnit::deg()),
situation.getMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(),
situation.getTimeOffsetMs());
if (! isRemoteAircraftSupportingParts(situation.getCallsign()))
if (c_driverInterpolation)
{
// if aircraft not supporting parts then guess the basics (onGround, gear, lights)
//! \todo not working for vtol
BlackMisc::Aviation::CAircraftParts parts;
parts.setMSecsSinceEpoch(situation.getMSecsSinceEpoch());
parts.setTimeOffsetMs(situation.getTimeOffsetMs());
if (situation.getGroundSpeed() < CSpeed(50, CSpeedUnit::kts()))
if (m_xplaneAircrafts.contains(situation.getCallsign()))
{
const auto nearestAirport = std::min_element(m_airportsInRange.cbegin(), m_airportsInRange.cend(), [&situation](auto &&a, auto &&b)
{
return calculateEuclideanDistanceSquared(situation, a) < calculateEuclideanDistanceSquared(situation, b);
});
if (nearestAirport != m_airportsInRange.cend() && situation.getAltitude() - nearestAirport->getElevation() < CLength(50, CLengthUnit::ft()))
{
parts.setOnGround(true);
parts.setGearDown(true);
}
m_xplaneAircrafts[situation.getCallsign()].addAircraftSituation(situation);
}
if (situation.getAltitude() < CAltitude(10000, CLengthUnit::ft()))
{
parts.setLights({ true, true, true, true, true, true, true, true });
}
else
{
parts.setLights({ true, false, false, true, true, true, true, true });
}
onRemoteProviderAddedAircraftParts(situation.getCallsign(), parts);
}
else
{
using namespace BlackMisc::PhysicalQuantities;
m_traffic->addPlanePosition(situation.getCallsign().asString(),
situation.latitude().value(CAngleUnit::deg()),
situation.longitude().value(CAngleUnit::deg()),
situation.getAltitude().value(CLengthUnit::ft()),
situation.getPitch().value(CAngleUnit::deg()),
situation.getBank().value(CAngleUnit::deg()),
situation.getHeading().value(CAngleUnit::deg()),
situation.getMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(),
situation.getTimeOffsetMs());
if (! isRemoteAircraftSupportingParts(situation.getCallsign()))
{
// if aircraft not supporting parts then guess the basics (onGround, gear, lights)
//! \todo not working for vtol
BlackMisc::Aviation::CAircraftParts parts;
parts.setMSecsSinceEpoch(situation.getMSecsSinceEpoch());
parts.setTimeOffsetMs(situation.getTimeOffsetMs());
if (situation.getGroundSpeed() < CSpeed(50, CSpeedUnit::kts()))
{
const auto nearestAirport = std::min_element(m_airportsInRange.cbegin(), m_airportsInRange.cend(), [&situation](auto &&a, auto &&b)
{
return calculateEuclideanDistanceSquared(situation, a) < calculateEuclideanDistanceSquared(situation, b);
});
if (nearestAirport != m_airportsInRange.cend() && situation.getAltitude() - nearestAirport->getElevation() < CLength(50, CLengthUnit::ft()))
{
parts.setOnGround(true);
parts.setGearDown(true);
}
}
if (situation.getAltitude() < CAltitude(10000, CLengthUnit::ft()))
{
parts.setLights({ true, true, true, true, true, true, true, true });
}
else
{
parts.setLights({ true, false, false, true, true, true, true, true });
}
onRemoteProviderAddedAircraftParts(situation.getCallsign(), parts);
}
}
}
void CSimulatorXPlane::onRemoteProviderAddedAircraftParts(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftParts &parts)
{
Q_ASSERT(isConnected());
m_traffic->addPlaneSurfaces(callsign.asString(), parts.isGearDown() ? 1 : 0,
parts.getFlapsPercent() / 100.0, parts.isSpoilersOut() ? 1 : 0, parts.isSpoilersOut() ? 1 : 0, parts.getFlapsPercent() / 100.0,
0, parts.isAnyEngineOn() ? 0 : 0.75, 0, 0, 0,
parts.getLights().isLandingOn(), parts.getLights().isBeaconOn(), parts.getLights().isStrobeOn(), parts.getLights().isNavOn(),
0, parts.isOnGround(), parts.getMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(), parts.getTimeOffsetMs());
m_traffic->setPlaneTransponder(callsign.asString(), 2000, true, false);
if (c_driverInterpolation)
{
if (m_xplaneAircrafts.contains(callsign))
{
m_xplaneAircrafts[callsign].addAircraftParts(parts);
}
}
else
{
m_traffic->addPlaneSurfaces(callsign.asString(), parts.isGearDown() ? 1 : 0,
parts.getFlapsPercent() / 100.0, parts.isSpoilersOut() ? 1 : 0, parts.isSpoilersOut() ? 1 : 0, parts.getFlapsPercent() / 100.0,
0, parts.isAnyEngineOn() ? 0 : 0.75, 0, 0, 0,
parts.getLights().isLandingOn(), parts.getLights().isBeaconOn(), parts.getLights().isStrobeOn(), parts.getLights().isNavOn(),
0, parts.isOnGround(), parts.getMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(), parts.getTimeOffsetMs());
m_traffic->setPlaneTransponder(callsign.asString(), 2000, true, false);
}
}
bool CSimulatorXPlane::physicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign)
{
Q_ASSERT(isConnected());
m_traffic->removePlane(callsign.asString());
updateAircraftRendered(callsign, false);
CLogMessage(this).info("XP: Removed aircraft %1") << callsign.toQString();
return true;
if (c_driverInterpolation)
{
// only remove from sim
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "wrong thread");
if (callsign.isEmpty()) { return false; } // can happen if an object is not an aircraft
// clean up anyway
m_hints.remove(callsign);
// really remove from simulator
if (!m_xplaneAircrafts.contains(callsign)) { return false; } // already fully removed or not yet added
CXPlaneMPAircraft &xplaneAircraft = m_xplaneAircrafts[callsign];
// avoid further data from simulator
// this->stopRequestingDataForSimObject(simObject);
m_traffic->removePlane(callsign.asString());
m_xplaneAircrafts.remove(callsign);
// mark in provider
const bool updated = this->updateAircraftRendered(callsign, false);
if (updated)
{
CSimulatedAircraft aircraft(xplaneAircraft.getAircraft());
aircraft.setRendered(false);
emit this->aircraftRenderingChanged(aircraft);
}
// bye
return true;
}
else
{
m_traffic->removePlane(callsign.asString());
updateAircraftRendered(callsign, false);
CLogMessage(this).info("XP: Removed aircraft %1") << callsign.toQString();
return true;
}
}
int CSimulatorXPlane::physicallyRemoveAllRemoteAircraft()
@@ -578,11 +686,26 @@ namespace BlackSimPlugin
Q_ASSERT(isConnected());
//! \todo XP driver obtain number of removed aircraft
resetHighlighting();
int r = getAircraftInRangeCount();
m_traffic->removeAllPlanes();
updateMarkAllAsNotRendered();
CLogMessage(this).info("XP: Removed all aircraft");
return r;
if (c_driverInterpolation)
{
// remove one by one
int r = 0;
const CCallsignSet callsigns = m_xplaneAircrafts.getAllCallsigns();
for (const CCallsign &cs : callsigns)
{
if (this->physicallyRemoveRemoteAircraft(cs)) { r++; }
}
return r;
}
else
{
int r = getAircraftInRangeCount();
m_traffic->removeAllPlanes();
updateMarkAllAsNotRendered();
CLogMessage(this).info("XP: Removed all aircraft");
return r;
}
}
CCallsignSet CSimulatorXPlane::physicallyRenderedAircraft() const
@@ -701,6 +824,187 @@ namespace BlackSimPlugin
m_weather->setThunderstormRatio(0.0);
}
void CSimulatorXPlane::updateRemoteAircraft()
{
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread");
const int remoteAircraftNo = this->getAircraftInRangeCount();
if (remoteAircraftNo < 1) { m_interpolationRequest = 0; return; }
// interpolate and send to simulator
m_interpolationRequest++;
const bool enableAircraftParts = m_interpolationRenderingSetup.isAircraftPartsEnabled();
const CCallsignSet aircraftWithParts = enableAircraftParts ? this->remoteAircraftSupportingParts() : CCallsignSet(); // optimization, fetch all parts supporting aircraft in one step (one lock)
// values used for position and parts
const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
const CCallsignSet callsignsToLog(m_interpolationRenderingSetup.getLogCallsigns());
// interpolation for all remote aircraft
const QList<CXPlaneMPAircraft> xplaneAircrafts(m_xplaneAircrafts.values());
for (const CXPlaneMPAircraft &xplaneAircraft : xplaneAircrafts)
{
const CCallsign callsign(xplaneAircraft.getCallsign());
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "missing callsign");
// fetch parts, as they are needed for ground interpolation
const bool useAircraftParts = enableAircraftParts && aircraftWithParts.contains(callsign);
const bool logInterpolationAndParts = callsignsToLog.contains(callsign);
const CInterpolationAndRenderingSetup setup(this->getInterpolationAndRenderingSetup());
CPartsStatus partsStatus(useAircraftParts);
const CAircraftParts parts = useAircraftParts ? xplaneAircraft.getInterpolatedParts(currentTimestamp, setup, partsStatus, logInterpolationAndParts) : CAircraftParts();
// get interpolated situation
CInterpolationStatus interpolatorStatus;
CInterpolationHints hints(m_hints[callsign]);
hints.setAircraftParts(useAircraftParts ? parts : CAircraftParts(), useAircraftParts);
hints.setLoggingInterpolation(logInterpolationAndParts);
const CAircraftSituation interpolatedSituation = xplaneAircraft.getInterpolatedSituation(currentTimestamp, setup, hints, interpolatorStatus);
if (interpolatorStatus.hasValidSituation())
{
// update situation
if (!xplaneAircraft.isSameAsSent(interpolatedSituation))
{
m_xplaneAircrafts[xplaneAircraft.getCallsign()].setPositionAsSent(interpolatedSituation);
m_traffic->setPlanePosition(interpolatedSituation.getCallsign().asString(),
interpolatedSituation.latitude().value(CAngleUnit::deg()),
interpolatedSituation.longitude().value(CAngleUnit::deg()),
interpolatedSituation.getAltitude().value(CLengthUnit::ft()),
interpolatedSituation.getPitch().value(CAngleUnit::deg()),
interpolatedSituation.getBank().value(CAngleUnit::deg()),
interpolatedSituation.getHeading().value(CAngleUnit::deg()));
}
}
else
{
CLogMessage(this).warning("Invalid situation for callsign: '%1' info: '%2'")
<< callsign
<< interpolatorStatus.toQString();
}
if (useAircraftParts)
{
this->updateRemoteAircraftParts(xplaneAircraft, parts, partsStatus);
}
else
{
// guess on position, but not every frame
if (m_interpolationRequest % GuessRemoteAircraftPartsCycle == 0)
{
this->guessAndUpdateRemoteAircraftParts(xplaneAircraft, interpolatedSituation, interpolatorStatus);
}
}
} // all callsigns
const qint64 dt = QDateTime::currentMSecsSinceEpoch() - currentTimestamp;
m_statsUpdateAircraftTimeTotalMs += dt;
m_statsUpdateAircraftCountMs++;
m_statsUpdateAircraftTimeAvgMs = m_statsUpdateAircraftTimeTotalMs / m_statsUpdateAircraftCountMs;
}
bool CSimulatorXPlane::updateRemoteAircraftParts(const CXPlaneMPAircraft &xplaneAircraft, const CAircraftParts &parts, const CPartsStatus &partsStatus)
{
if (!partsStatus.isSupportingParts()) { return false; }
return this->sendRemoteAircraftPartsToSimulator(xplaneAircraft, parts);
}
bool CSimulatorXPlane::guessAndUpdateRemoteAircraftParts(const CXPlaneMPAircraft &xplaneAircraft, const CAircraftSituation &interpolatedSituation, const CInterpolationStatus &interpolationStatus)
{
if (!interpolationStatus.isInterpolated()) { return false; }
CAircraftLights lights;
CAircraftParts parts; // init members
const bool isOnGround = interpolatedSituation.isOnGround() == CAircraftSituation::OnGround;
const double gsKts = interpolatedSituation.getGroundSpeed().value(CSpeedUnit::kts());
parts.setEngines({ true, true, true, true });
lights.setCabinOn(true);
lights.setRecognitionOn(true);
// when first detected moving, lights on
if (isOnGround)
{
parts.setGearDown(true);
lights.setTaxiOn(true);
lights.setBeaconOn(true);
lights.setNavOn(true);
if (gsKts > 5)
{
// mode taxi
lights.setTaxiOn(true);
lights.setLandingOn(false);
}
else if (gsKts > 30)
{
// mode accelaration for takeoff
lights.setTaxiOn(false);
lights.setLandingOn(true);
}
else
{
// slow movements or parking
lights.setTaxiOn(false);
lights.setLandingOn(false);
parts.setEngines({ false, false, false, false });
}
}
else
{
// not on ground
parts.setGearDown(false);
lights.setTaxiOn(false);
lights.setBeaconOn(true);
lights.setNavOn(true);
// landing lights for < 10000ft (normally MSL, here ignored)
lights.setLandingOn(interpolatedSituation.getAltitude().value(CLengthUnit::ft()) < 10000);
if (!xplaneAircraft.isVtol() && interpolatedSituation.hasGroundElevation())
{
if (interpolatedSituation.getHeightAboveGround().value(CLengthUnit::ft()) < 1000)
{
parts.setGearDown(true);
parts.setFlapsPercent(25);
}
else if (interpolatedSituation.getHeightAboveGround().value(CLengthUnit::ft()) < 2000)
{
parts.setGearDown(true);
parts.setFlapsPercent(10);
}
}
}
parts.setLights(lights);
return this->sendRemoteAircraftPartsToSimulator(xplaneAircraft, parts);
}
bool CSimulatorXPlane::sendRemoteAircraftPartsToSimulator(const CXPlaneMPAircraft &xplaneAircraft, const CAircraftParts &parts)
{
// same as in simulator or same as already send to simulator?
if (xplaneAircraft.getPartsAsSent() == parts)
{
return true;
}
m_traffic->setPlaneSurfaces(xplaneAircraft.getCallsign().asString(),
parts.isGearDown() ? 1 : 0,
parts.getFlapsPercent() / 100.0,
parts.isSpoilersOut() ? 1 : 0,
parts.isSpoilersOut() ? 1 : 0,
parts.getFlapsPercent() / 100.0,
0, parts.isAnyEngineOn() ? 0 : 0.75,
0, 0, 0,
parts.getLights().isLandingOn(), parts.getLights().isBeaconOn(), parts.getLights().isStrobeOn(), parts.getLights().isNavOn(),
0, parts.isOnGround());
CAircraftLights lights = parts.getLights();
lights.setRecognitionOn(parts.isAnyEngineOn());
lights.setCabinOn(parts.isAnyEngineOn());
return true;
}
BlackCore::ISimulator *CSimulatorXPlaneFactory::create(const CSimulatorPluginInfo &info,
IOwnAircraftProvider *ownAircraftProvider,
IRemoteAircraftProvider *remoteAircraftProvider,

View File

@@ -12,6 +12,7 @@
#ifndef BLACKSIMPLUGIN_SIMULATOR_XPLANE_H
#define BLACKSIMPLUGIN_SIMULATOR_XPLANE_H
#include "xplanempaircraft.h"
#include "blackcore/simulator.h"
#include "blackcore/simulatorcommon.h"
#include "plugins/simulator/xplaneconfig/simulatorxplaneconfig.h"
@@ -143,6 +144,25 @@ namespace BlackSimPlugin
void loadCslPackages();
QString findCslPackage(const QString &modelFileName);
//! Update remote aircraft
//! \remark this is where the interpolated data are set
void updateRemoteAircraft();
//! Update remote aircraft parts (send to XSwiftBus)
bool updateRemoteAircraftParts(const CXPlaneMPAircraft &xplaneAircraft,
const BlackMisc::Aviation::CAircraftParts &parts, const BlackMisc::Simulation::CPartsStatus &partsStatus);
//! Update remote aircraft parts by guessing (send to XSwiftBus)
bool guessAndUpdateRemoteAircraftParts(const CXPlaneMPAircraft &xplaneAircraft,
const BlackMisc::Aviation::CAircraftSituation &interpolatedSituation, const BlackMisc::Simulation::CInterpolationStatus &interpolationStatus);
//! Send parts to simulator
bool sendRemoteAircraftPartsToSimulator(const CXPlaneMPAircraft &xplaneAircraft, const BlackMisc::Aviation::CAircraftParts &parts);
static constexpr bool c_driverInterpolation = true;
static constexpr int GuessRemoteAircraftPartsCycle = 20; //!< guess every n-th cycle
// XSwiftBus interpolation
QDBusConnection m_conn { "default" };
QDBusServiceWatcher *m_watcher { nullptr };
CXSwiftBusServiceProxy *m_service { nullptr };
@@ -153,6 +173,10 @@ namespace BlackSimPlugin
BlackMisc::Aviation::CAirportList m_airportsInRange; //!< aiports in range of own aircraft
BlackMisc::CData<BlackMisc::Simulation::Data::TModelSetCacheXP> m_modelSet { this };
// Driver Interpolation
CXPlaneMPAircrafts m_xplaneAircrafts; //!< XPlane multiplayer aircrafts
int m_interpolationRequest = 0; //!< current interpolation request
//! \todo Add units to members? pitchDeg?, altitudeFt?
struct // data is written by DBus async method callbacks
{

View File

@@ -0,0 +1,124 @@
/* Copyright (C) 2018
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#include "xplanempaircraft.h"
#include "blackmisc/simulation/interpolatormulti.h"
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Simulation;
namespace BlackSimPlugin
{
namespace XPlane
{
CXPlaneMPAircraft::CXPlaneMPAircraft()
{ }
CXPlaneMPAircraft::CXPlaneMPAircraft(const CSimulatedAircraft &aircraft,
CInterpolationLogger *logger) :
m_aircraft(aircraft),
m_interpolator(QSharedPointer<CInterpolatorMulti>::create(aircraft.getCallsign()))
{
m_interpolator->attachLogger(logger);
// if available set situation and parts
if (aircraft.isPartsSynchronized()) { this->addAircraftParts(aircraft.getParts()); }
if (aircraft.getSituation().hasValidTimestamp()) { this->addAircraftSituation(aircraft.getSituation()); }
}
CXPlaneMPAircraft::CXPlaneMPAircraft(const CAircraftSituation &situation) :
m_interpolator(QSharedPointer<CInterpolatorMulti>::create(situation.getCallsign()))
{
if (situation.hasValidTimestamp()) { this->addAircraftSituation(situation); }
}
CXPlaneMPAircraft::CXPlaneMPAircraft(const CAircraftParts &parts, const CCallsign &callsign) :
m_interpolator(QSharedPointer<CInterpolatorMulti>::create(callsign))
{
if (parts.hasValidTimestamp()) { this->addAircraftParts(parts); }
}
void CXPlaneMPAircraft::addAircraftParts(const CAircraftParts &parts)
{
Q_ASSERT(m_interpolator);
Q_ASSERT(parts.hasValidTimestamp());
m_interpolator->addAircraftParts(parts);
m_aircraft.setParts(parts);
}
void CXPlaneMPAircraft::addAircraftSituation(const CAircraftSituation &situation)
{
Q_ASSERT(m_interpolator);
Q_ASSERT(situation.hasValidTimestamp());
m_interpolator->addAircraftSituation(situation);
m_aircraft.setSituation(situation); // update with last situation
}
bool CXPlaneMPAircraft::isSameAsSent(const CAircraftSituation &position) const
{
return m_positionAsSent == position;
}
void CXPlaneMPAircraft::toggleInterpolatorMode()
{
Q_ASSERT(m_interpolator);
m_interpolator->toggleMode();
}
bool CXPlaneMPAircraft::setInterpolatorMode(CInterpolatorMulti::Mode mode)
{
Q_ASSERT(m_interpolator);
return m_interpolator->setMode(mode);
}
QString CXPlaneMPAircraft::getInterpolatorInfo() const
{
Q_ASSERT(m_interpolator);
return m_interpolator->getInterpolatorInfo();
}
void CXPlaneMPAircraft::attachInterpolatorLogger(CInterpolationLogger *logger)
{
Q_ASSERT(m_interpolator);
return m_interpolator->attachLogger(logger);
}
CAircraftSituation CXPlaneMPAircraft::getInterpolatedSituation(
qint64 currentTimeSinceEpoc,
const CInterpolationAndRenderingSetup &setup,
const CInterpolationHints &hints, CInterpolationStatus &status) const
{
Q_ASSERT(m_interpolator);
return m_interpolator->getInterpolatedSituation(currentTimeSinceEpoc, setup, hints, status);
}
CAircraftParts CXPlaneMPAircraft::getInterpolatedParts(
qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetup &setup,
CPartsStatus &partsStatus, bool log) const
{
Q_ASSERT(m_interpolator);
return m_interpolator->getInterpolatedParts(currentTimeSinceEpoc, setup, partsStatus, log);
}
CCallsignSet CXPlaneMPAircrafts::getAllCallsigns() const
{
return CCallsignSet(this->keys());
}
int CXPlaneMPAircrafts::setInterpolatorModes(CInterpolatorMulti::Mode mode)
{
int c = 0;
for (const CCallsign &cs : this->keys())
{
if ((*this)[cs].setInterpolatorMode(mode)) c++;
}
return c;
}
} // namespace
} // namespace

View File

@@ -0,0 +1,136 @@
/* Copyright (C) 2018
* swift project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKSIMPLUGIN_XPLANE_XPLANEMPAIRCRAFT_H
#define BLACKSIMPLUGIN_XPLANE_XPLANEMPAIRCRAFT_H
#include "blackmisc/simulation/simulatedaircraft.h"
#include "blackmisc/simulation/interpolatormulti.h"
#include <QSharedPointer>
#include <QStringList>
namespace BlackSimPlugin
{
namespace XPlane
{
//! Class representing a X-Plane multiplayer aircraft
class CXPlaneMPAircraft
{
public:
//! Constructor
CXPlaneMPAircraft();
//! Constructor providing initial situation/parts
CXPlaneMPAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraft,
BlackMisc::Simulation::CInterpolationLogger *logger);
//! Constructor providing initial situation
CXPlaneMPAircraft(const BlackMisc::Aviation::CAircraftSituation &situation);
//! Constructor providing initial parts
CXPlaneMPAircraft(const BlackMisc::Aviation::CAircraftParts &parts, const BlackMisc::Aviation::CCallsign &callsign);
//! Destructor
~CXPlaneMPAircraft() {}
//! Get callsign
const BlackMisc::Aviation::CCallsign &getCallsign() const { return m_aircraft.getCallsign(); }
//! Simulated aircraft (as added)
const BlackMisc::Simulation::CSimulatedAircraft &getAircraft() const { return m_aircraft; }
//! Simulated aircraft model string
const QString &getAircraftModelString() const { return m_aircraft.getModelString(); }
//! Set the aircraft
void setAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraft) { m_aircraft = aircraft; }
//! Add parts for interpolator
void addAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);
//! Add situation for interpolator
void addAircraftSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
//! Parts as sent to simulator
const BlackMisc::Aviation::CAircraftParts &getPartsAsSent() const { return m_partsAsSent; }
//! Parts as sent to simulator
void setPartsAsSent(const BlackMisc::Aviation::CAircraftParts &parts) { m_partsAsSent = parts; }
//! Position as sent
void setPositionAsSent(const BlackMisc::Aviation::CAircraftSituation &position) { m_positionAsSent = position; }
//! Same as sent
bool isSameAsSent(const BlackMisc::Aviation::CAircraftSituation &position) const;
//! VTOL?
bool isVtol() const { return m_aircraft.isVtol(); }
//! \copydoc BlackMisc::Simulation::CInterpolatorMulti::toggleMode
void toggleInterpolatorMode();
//! \copydoc BlackMisc::Simulation::CInterpolatorMulti::setMode
bool setInterpolatorMode(BlackMisc::Simulation::CInterpolatorMulti::Mode mode);
//! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolatorInfo
QString getInterpolatorInfo() const;
//! \copydoc BlackMisc::Simulation::CInterpolator::attachLogger
void attachInterpolatorLogger(BlackMisc::Simulation::CInterpolationLogger *logger);
//! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolatedSituation
BlackMisc::Aviation::CAircraftSituation getInterpolatedSituation(
qint64 currentTimeSinceEpoc,
const BlackMisc::Simulation::CInterpolationAndRenderingSetup &setup,
const BlackMisc::Simulation::CInterpolationHints &hints, BlackMisc::Simulation::CInterpolationStatus &status) const;
//! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolatedParts
BlackMisc::Aviation::CAircraftParts getInterpolatedParts(
qint64 currentTimeSinceEpoc,
const BlackMisc::Simulation::CInterpolationAndRenderingSetup &setup,
BlackMisc::Simulation::CPartsStatus &partsStatus, bool log) const;
//! Interpolator
BlackMisc::Simulation::CInterpolatorMulti *getInterpolator() const { return m_interpolator.data(); }
private:
BlackMisc::Simulation::CSimulatedAircraft m_aircraft; //!< corresponding aircraft
QSharedPointer<BlackMisc::Simulation::CInterpolatorMulti> m_interpolator; //!< shared pointer because CSimConnectObject can be copied
BlackMisc::Aviation::CAircraftSituation m_positionAsSent;
BlackMisc::Aviation::CAircraftParts m_partsAsSent;
};
//! Simulator objects (aka AI aircraft)
class CXPlaneMPAircrafts : public QHash<BlackMisc::Aviation::CCallsign, CXPlaneMPAircraft>
{
public:
//! Get all callsigns
BlackMisc::Aviation::CCallsignSet getAllCallsigns() const;
//! Get all callsign strings
QStringList getAllCallsignStrings(bool sorted = false) const;
//! Get all callsign strings as string
QString getAllCallsignStringsAsString(bool sorted = false, const QString &separator = ", ") const;
//! Toggle interpolator modes
void toggleInterpolatorModes();
//! Toggle interpolator modes
void toggleInterpolatorMode(const BlackMisc::Aviation::CCallsign &callsign);
//! Set interpolator modes
int setInterpolatorModes(BlackMisc::Simulation::CInterpolatorMulti::Mode mode);
};
} // namespace
} // namespace
#endif // guard

View File

@@ -86,6 +86,11 @@ namespace BlackSimPlugin
m_dbusInterface->callDBus(QLatin1String("addPlanePosition"), callsign, latitude, longitude, altitude, pitch, roll, heading, relativeTime, timeOffset);
}
void CXSwiftBusTrafficProxy::setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading)
{
m_dbusInterface->callDBus(QLatin1String("setPlanePosition"), callsign, latitude, longitude, altitude, pitch, roll, heading);
}
void CXSwiftBusTrafficProxy::addPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround, qint64 relativeTime, qint64 timeOffset)
{
@@ -93,6 +98,13 @@ namespace BlackSimPlugin
landLight, beaconLight, strobeLight, navLight, lightPattern, onGround, relativeTime, timeOffset);
}
void CXSwiftBusTrafficProxy::setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround)
{
m_dbusInterface->callDBus(QLatin1String("setPlaneSurfaces"), callsign, gear, flap, spoiler, speedBrake, slat, wingSweep, thrust, elevator, rudder, aileron,
landLight, beaconLight, strobeLight, navLight, lightPattern, onGround);
}
void CXSwiftBusTrafficProxy::setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident)
{
m_dbusInterface->callDBus(QLatin1String("setPlaneTransponder"), callsign, code, modeC, ident);

View File

@@ -61,6 +61,10 @@ namespace BlackSimPlugin
private:
BlackMisc::CGenericDBusInterface *m_dbusInterface = nullptr;
signals:
//! \copydoc XSwiftBus::CTraffic::simFrame
void simFrame();
public slots:
//! \copydoc XSwiftBus::CTraffic::initialize
bool initialize();
@@ -98,10 +102,17 @@ namespace BlackSimPlugin
//! \copydoc XSwiftBus::CTraffic::addPlanePosition
void addPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading, qint64 relativeTime, qint64 timeOffset);
//! \copydoc XSwiftBus::CTraffic::setPlanePosition
void setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading);
//! \copydoc XSwiftBus::CTraffic::addPlaneSurfaces
void addPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround, qint64 relativeTime, qint64 timeOffset);
//! \copydoc XSwiftBus::CTraffic::setPlaneSurfaces
void setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround);
//! \copydoc XSwiftBus::CTraffic::setPlaneTransponder
void setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident);

View File

@@ -21,6 +21,7 @@
#include <XPLM/XPLMProcessing.h>
#include <XPLM/XPLMUtilities.h>
#include <QDateTime>
#include <QDebug>
#include <QStringList>
#include <cstring>
#include <cmath>
@@ -74,6 +75,7 @@ namespace XSwiftBus
CTraffic::CTraffic(QObject *parent) :
QObject(parent)
{
XPLMRegisterDrawCallback(CTraffic::drawCallback, xplm_Phase_Airplanes, 0, this);
}
CTraffic::~CTraffic()
@@ -124,6 +126,21 @@ namespace XSwiftBus
m_initialized = false;
XPMPMultiplayerCleanup();
}
XPLMUnregisterDrawCallback(CTraffic::drawCallback, xplm_Phase_Airplanes, 0, this);
}
void CTraffic::emitSimFrame()
{
qint64 currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
// The draw callback is called twice for unknown reasons each frame. We can filter the second one by
// requiring a minimum offset of 5 ms (equal to 200 fps).
if (currentMSecsSinceEpoch > m_timestampLastSimFrame + 5)
{
emit simFrame();
m_timestampLastSimFrame = currentMSecsSinceEpoch;
}
}
int g_maxPlanes = 100;
@@ -258,6 +275,17 @@ namespace XSwiftBus
}
}
void CTraffic::setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading)
{
const auto plane = m_planesByCallsign.value(callsign, nullptr);
plane->position.lat = latitude;
plane->position.lon = longitude;
plane->position.elevation = altitude;
plane->position.pitch = static_cast<float>(pitch);
plane->position.roll = static_cast<float>(roll);
plane->position.heading = static_cast<float>(heading);
}
void CTraffic::addPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround, qint64 relativeTime, qint64 timeOffset)
{
@@ -295,6 +323,32 @@ namespace XSwiftBus
}
}
void CTraffic::setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround)
{
Q_UNUSED(onGround);
const auto plane = m_planesByCallsign.value(callsign, nullptr);
if (plane)
{
plane->hasSurfaces = true;
plane->targetGearPosition = gear;
plane->surfaces.flapRatio = flap;
plane->surfaces.spoilerRatio = spoiler;
plane->surfaces.speedBrakeRatio = speedBrake;
plane->surfaces.slatRatio = slat;
plane->surfaces.wingSweep = wingSweep;
plane->surfaces.thrust = thrust;
plane->surfaces.yokePitch = elevator;
plane->surfaces.yokeHeading = rudder;
plane->surfaces.yokeRoll = aileron;
plane->surfaces.lights.landLights = landLight;
plane->surfaces.lights.bcnLights = beaconLight;
plane->surfaces.lights.strbLights = strobeLight;
plane->surfaces.lights.navLights = navLight;
plane->surfaces.lights.flashPattern = lightPattern;
}
}
void CTraffic::setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident)
{
const auto plane = m_planesByCallsign.value(callsign, nullptr);
@@ -342,6 +396,20 @@ namespace XSwiftBus
switch (dataType)
{
case xpmpDataType_Position:
{
if (c_driverInterpolation)
{
const auto io_position = static_cast<XPMPPlanePosition_t *>(io_data);
io_position->lat = plane->position.lat;
io_position->lon = plane->position.lon;
io_position->elevation = plane->position.elevation;
io_position->pitch = plane->position.pitch;
io_position->roll = plane->position.roll;
io_position->heading = plane->position.heading;
std::strncpy(io_position->label, plane->label, sizeof(plane->label)); // fixme don't need to copy on every frame
return xpmpData_NewData;
}
else
{
BlackMisc::Simulation::CInterpolationAndRenderingSetup setup;
BlackMisc::Simulation::CInterpolationStatus status;
@@ -364,16 +432,23 @@ namespace XSwiftBus
return xpmpData_NewData;
}
}
case xpmpDataType_Surfaces:
if (plane->hasSurfaces)
{
const auto currentTime = QDateTime::currentMSecsSinceEpoch();
while (! plane->pendingSurfaces.isEmpty() && plane->pendingSurfaces.constFirst().first <= currentTime)
if (! c_driverInterpolation)
{
//! \todo if gear is currently retracted, look ahead and pull gear position from pendingSurfaces up to 5 seconds in the future
plane->pendingSurfaces.constFirst().second(plane);
plane->pendingSurfaces.pop_front();
while (! plane->pendingSurfaces.isEmpty() && plane->pendingSurfaces.constFirst().first <= currentTime)
{
//! \todo if gear is currently retracted, look ahead and pull gear position from pendingSurfaces up to 5 seconds in the future
plane->pendingSurfaces.constFirst().second(plane);
plane->pendingSurfaces.pop_front();
}
}
if (plane->surfaces.gearPosition != plane->targetGearPosition)
{
// interpolate gear position
@@ -412,6 +487,15 @@ namespace XSwiftBus
}
}
int CTraffic::drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon)
{
Q_UNUSED(phase);
Q_UNUSED(isBefore);
CTraffic *traffic = static_cast<CTraffic *>(refcon);
traffic->emitSimFrame();
return 1;
}
}
//! \endcond

View File

@@ -22,6 +22,7 @@
#include <QVector>
#include <QStringList>
#include "XPMPMultiplayer.h"
#include <XPLM/XPLMDisplay.h>
#include <functional>
#include <utility>
@@ -65,6 +66,10 @@ namespace XSwiftBus
//! Called by XPluginStart
static void initLegacyData();
signals:
//! Signal emitted for each simulator rendering frame
void simFrame();
public slots:
//! Initialize the multiplayer planes rendering and return true if successful
bool initialize();
@@ -99,13 +104,20 @@ namespace XSwiftBus
//! Remove all traffic aircraft
void removeAllPlanes();
//! Set the position of a traffic aircraft
//! Add the position of a traffic aircraft
void addPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading, qint64 relativeTime, qint64 timeOffset);
//! Set the flight control surfaces and lights of a traffic aircraft
//! Set the position of a traffic aircraft
void setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading);
//! Add the flight control surfaces and lights of a traffic aircraft
void addPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround, qint64 relativeTime, qint64 timeOffset);
//! Set the flight control surfaces and lights of a traffic aircraft
void setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern, bool onGround);
//! Set the transponder of a traffic aircraft
void setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident);
@@ -116,6 +128,10 @@ namespace XSwiftBus
bool m_initialized = false;
bool m_enabled = false;
static constexpr bool c_driverInterpolation = true;
void emitSimFrame();
static int preferences(const char *section, const char *name, int def);
static float preferences(const char *section, const char *name, float def);
@@ -137,16 +153,20 @@ namespace XSwiftBus
float targetGearPosition = 0;
qint64 prevSurfacesLerpTime = 0;
XPMPPlaneRadar_t xpdr;
XPMPPlanePosition_t position;
Plane(void *id_, QString callsign_, QString aircraftIcao_, QString airlineIcao_, QString livery_);
};
QHash<QString, Plane *> m_planesByCallsign;
QHash<void *, Plane *> m_planesById;
qint64 m_timestampLastSimFrame = QDateTime::currentMSecsSinceEpoch();
int getPlaneData(void *id, int dataType, void *io_data);
static int getPlaneData(void *id, int dataType, void *io_data, void *self)
{
return static_cast<CTraffic *>(self)->getPlaneData(id, dataType, io_data);
}
static int drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon);
};
}