refs #386, further improvements on interpolation

* extended unit tests for interpolator / parts testing
* allow to skip sorting when splitting by time
* update ot aircraft to FSX in own member function
* Skip time sync parts (FSX) when disabled
This commit is contained in:
Klaus Basan
2015-03-03 00:45:38 +01:00
parent 5bad11dcca
commit f31445e873
18 changed files with 260 additions and 119 deletions

View File

@@ -552,6 +552,9 @@ namespace BlackSimPlugin
hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluDay, "ZULU_DAY_SET");
hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluHours, "ZULU_HOURS_SET");
hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluMinutes, "ZULU_MINUTES_SET");
hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleTaxiLights, "TOGGLE_TAXI_LIGHTS");
if (hr != S_OK)
{
CLogMessage(this).error("FSX plugin error: %1") << "SimConnect_MapClientEventToSimEvent failed";
@@ -617,13 +620,14 @@ namespace BlackSimPlugin
{
const CCallsign callsign(simObj.getCallsign());
IInterpolator::InterpolationStatus interpolatorStatus;
IInterpolator::PartsStatus partsStatus;
if (simObj.getObjectId() == 0) { continue; }
CAircraftSituation interpolatedSituation = this->m_interpolator->getInterpolatedSituation(callsign, currentTimestamp, interpolatorStatus);
// 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);
// having the onGround flag in parts forces me to obtain parts here
// which is not the smartest thing regarding performance
IInterpolator::PartsStatus partsStatus;
CAircraftPartsList parts = this->m_interpolator->getAndRemovePartsBeforeTime(callsign, currentTimestamp - IInterpolator::TimeOffsetMs, partsStatus);
if (interpolatorStatus.allTrue())
{
// update situation
@@ -648,87 +652,104 @@ namespace BlackSimPlugin
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; }
} // interpolation data
if (interpolatorStatus.interpolationSucceeded)
{
// aircraft parts
// inside interpolator if, as no parts can be sent without position
updateRemoteAircraftParts(simObj, parts, partsStatus, interpolatedSituation, isOnGround); // update and retrieve parts in the same step
}
} // all callsigns
qint64 dt = QDateTime::currentMSecsSinceEpoch() - currentTimestamp;
m_statsUpdateAircraftTimeTotal += dt;
m_statsUpdateAircraftCount++;
m_statsUpdateAircraftTimeAvg = m_statsUpdateAircraftTimeTotal / m_statsUpdateAircraftCount;
}
// set parts
DataDefinitionRemoteAircraftParts ddRemoteAircraftParts;
if (partsStatus.supportsParts)
bool CSimulatorFsx::updateRemoteAircraftParts(const CSimConnectObject &simObj, const CAircraftPartsList &parts, IInterpolator::PartsStatus partsStatus, const CAircraftSituation &interpolatedSituation, bool isOnGround) const
{
// set parts
DataDefinitionRemoteAircraftParts ddRemoteAircraftParts;
if (partsStatus.supportsParts)
{
// parts is supported, but do we need to update?
if (parts.isEmpty()) { return false; }
// we have parts
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
{
// mode is guessing parts
if (this->m_interpolationRequest % 20 != 0) { return false; } // only update every 20th cycle
ddRemoteAircraftParts.gearHandlePosition = isOnGround ? 1 : 0;
// when first detected moving, lights on
if (isOnGround)
{
// parts is supported, but do we need to update?
if (parts.isEmpty()) { continue; }
// ddRemoteAircraftParts.lightTaxi = 1.0;
ddRemoteAircraftParts.lightBeacon = 1.0;
ddRemoteAircraftParts.lightNav = 1.0;
// we have parts
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
{
// 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 (isOnGround)
double gskmh = interpolatedSituation.getGroundSpeed().value(CSpeedUnit::km_h());
if (gskmh > 7.5)
{
// mode taxi
// 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;
}
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.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;
ddRemoteAircraftParts.lightLanding = 0.0;
}
}
else
{
// 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::DataRemoteAircraftParts,
simObj.getObjectId(), 0, 0,
sizeof(DataDefinitionRemoteAircraftParts), &ddRemoteAircraftParts);
if (hr != S_OK) { CLogMessage(this).warning("Failed so set parts on SimObject %1 callsign: %2") << simObj.getObjectId() << callsign; }
Q_ASSERT(m_hSimConnect);
HRESULT hr = S_OK;
hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts,
simObj.getObjectId(), 0, 0,
sizeof(DataDefinitionRemoteAircraftParts), &ddRemoteAircraftParts);
} // all callsigns
if (hr != S_OK) { CLogMessage(this).warning("Failed so set parts on SimObject %1 callsign: %2") << simObj.getObjectId() << simObj.getCallsign(); }
return hr == S_OK;
}
SIMCONNECT_DATA_INITPOSITION CSimulatorFsx::aircraftSituationToFsxInitPosition(const CAircraftSituation &situation)
@@ -746,8 +767,8 @@ namespace BlackSimPlugin
void CSimulatorFsx::synchronizeTime(const CTime &zuluTimeSim, const CTime &localTimeSim)
{
if (!this->m_simTimeSynced) return;
if (!this->isConnected()) return;
if (!this->m_simTimeSynced) { return; }
if (!this->isConnected()) { return; }
if (m_syncDeferredCounter > 0)
{
--m_syncDeferredCounter;
@@ -766,7 +787,7 @@ namespace BlackSimPlugin
int targetMins = myTime.hour() * 60 + myTime.minute();
int simMins = zuluTimeSim.valueRounded(CTimeUnit::min());
int diffMins = qAbs(targetMins - simMins);
if (diffMins < 2) return;
if (diffMins < 2) { return; }
HRESULT hr = S_OK;
hr += SimConnect_TransmitClientEvent(m_hSimConnect, 0, EventSetTimeZuluHours, h, SIMCONNECT_GROUP_PRIORITY_STANDARD, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);
hr += SimConnect_TransmitClientEvent(m_hSimConnect, 0, EventSetTimeZuluMinutes, m, SIMCONNECT_GROUP_PRIORITY_STANDARD, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);

View File

@@ -62,7 +62,8 @@ namespace BlackSimPlugin
EventSetTimeZuluYear,
EventSetTimeZuluDay,
EventSetTimeZuluHours,
EventSetTimeZuluMinutes
EventSetTimeZuluMinutes,
EventToggleTaxiLights
};
//! FSX Simulator Implementation
@@ -165,9 +166,13 @@ namespace BlackSimPlugin
//! Initialize SimConnect data definitions
HRESULT initDataDefinitionsWhenConnected();
//! Update other aircrafts
//! Update remote aircraft
void updateRemoteAircraft();
//! Update remote airacraft parts (send to FSX)
bool updateRemoteAircraftParts(const CSimConnectObject &simObj, const BlackMisc::Aviation::CAircraftPartsList &parts,
BlackCore::IInterpolator::PartsStatus partsStatus, const BlackMisc::Aviation::CAircraftSituation &interpolatedSituation, bool isOnGround) const;
//! Format conversion
SIMCONNECT_DATA_INITPOSITION aircraftSituationToFsxInitPosition(const BlackMisc::Aviation::CAircraftSituation &situation);
@@ -189,6 +194,11 @@ namespace BlackSimPlugin
BlackCore::IInterpolator *m_interpolator = nullptr; //!< interpolator instance
QHash<BlackMisc::Aviation::CCallsign, CSimConnectObject> m_simConnectObjects;
QFutureWatcher<bool> m_watcherConnect;
// statistics
qint64 m_statsUpdateAircraftTimeTotal = 0;
qint64 m_statsUpdateAircraftTimeAvg = 0;
int m_statsUpdateAircraftCount = 0;
};
}

View File

@@ -146,13 +146,16 @@ namespace BlackSimPlugin
case CSimConnectDefinitions::RequestSimEnvironment:
{
DataDefinitionSimEnvironment *simEnv = (DataDefinitionSimEnvironment *) &pObjData->dwData;
qint32 zh = simEnv->zuluTimeSeconds / 3600;
qint32 zm = (simEnv->zuluTimeSeconds - (zh * 3600)) / 60;
CTime zulu(zh, zm);
qint32 lh = simEnv->localTimeSeconds / 3600;
qint32 lm = (simEnv->localTimeSeconds - (lh * 3600)) / 60;
CTime local(lh, lm);
simulatorFsx->synchronizeTime(zulu, local);
if (simulatorFsx->isTimeSynchronized())
{
int zh = simEnv->zuluTimeSeconds / 3600;
int zm = (simEnv->zuluTimeSeconds - (zh * 3600)) / 60;
CTime zulu(zh, zm);
int lh = simEnv->localTimeSeconds / 3600;
int lm = (simEnv->localTimeSeconds - (lh * 3600)) / 60;
CTime local(lh, lm);
simulatorFsx->synchronizeTime(zulu, local);
}
break;
}
default:
@@ -171,7 +174,7 @@ namespace BlackSimPlugin
if (!pFacilityAirport) break;
const QString icao(pFacilityAirport->Icao);
if (icao.isEmpty()) { continue; } // airfield without ICAO code
if (!CAirportIcao::isValidIcaoDesignator(icao)) continue; // tiny airfields in SIM
if (!CAirportIcao::isValidIcaoDesignator(icao)) { continue; } // tiny airfields in SIM
CCoordinateGeodetic pos(pFacilityAirport->Latitude, pFacilityAirport->Longitude, pFacilityAirport->Altitude);
CAirport airport(CAirportIcao(icao), pos);
CLength d = airport.calculcateDistanceAndBearingToOwnAircraft(posAircraft);