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

@@ -66,7 +66,7 @@ namespace BlackCore
return m_situationsByCallsign;
}
CAircraftPartsList IInterpolator::getAndRemovePartsBeforeOffset(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus)
CAircraftPartsList IInterpolator::getAndRemovePartsBeforeTime(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus)
{
static const CAircraftPartsList empty;
partsStatus.reset();
@@ -78,6 +78,7 @@ namespace BlackCore
}
else
{
// did we ever have parts for this callsign
partsStatus.supportsParts = m_aircraftSupportingParts.contains(callsign);
return empty;
}
@@ -99,6 +100,19 @@ namespace BlackCore
return m_situationsByCallsign[callsign];
}
CAircraftPartsList IInterpolator::getPartsForCallsign(const CCallsign &callsign) const
{
QReadLocker l(&m_lockParts);
static const CAircraftPartsList empty;
if (!m_partsByCallsign.contains(callsign)) { return empty; }
return m_partsByCallsign[callsign];
}
void IInterpolator::forceSorting(bool sort)
{
this->m_forceSortWhenAddingValues = sort;
}
void IInterpolator::ps_onAddedAircraftSituation(const CAircraftSituation &situation)
{
QWriteLocker lock(&m_lockSituations);
@@ -110,6 +124,10 @@ namespace BlackCore
// list from new to old
CAircraftSituationList &l = this->m_situationsByCallsign[callsign];
l.push_frontMaxElements(situation, MaxSituationsPerCallsign);
if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); }
// check sort order
Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch());
}
void IInterpolator::ps_onAddedAircraftParts(const CAircraftParts &parts)
@@ -123,9 +141,14 @@ namespace BlackCore
// list sorted from new to old
CAircraftPartsList &l = this->m_partsByCallsign[callsign];
l.push_frontMaxElements(parts, MaxPartsPerCallsign);
if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); }
if (m_aircraftSupportingParts.contains(callsign)) { return; }
m_aircraftSupportingParts.push_back(callsign);
m_aircraftSupportingParts.push_back(callsign); // mark as callsign which supports parts
// check sort order
Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch());
}
void IInterpolator::ps_onRemovedAircraft(const CCallsign &callsign)

View File

@@ -85,7 +85,7 @@ namespace BlackCore
//! Parts before given offset time (aka pending parts)
//! \threadsafe
virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeOffset(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset, PartsStatus &partsStatus);
virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeTime(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset, PartsStatus &partsStatus);
//! Clear all data
//! \threadsafe
@@ -95,9 +95,16 @@ namespace BlackCore
//! \threadsafe
BlackMisc::Aviation::CAircraftSituationList getSituationsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Parts for given callsign
//! \threadsafe
BlackMisc::Aviation::CAircraftPartsList getPartsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
//! Enable debug messages
void enableDebugMessages(bool enabled);
//! Force sorting (latest first), not required if order can be guaranteed
void forceSorting(bool sort);
static const qint64 TimeOffsetMs = 6000; //!< offset for interpolation
static const int MaxSituationsPerCallsign = 6; //!< How many situations per callsign
static const int MaxPartsPerCallsign = 3; //!< How many parts per callsign
@@ -120,7 +127,8 @@ namespace BlackCore
//! Constructor
IInterpolator(BlackMisc::Simulation::IRemoteAircraftProviderReadOnly *provider, const QString &workerName, QObject *parent = nullptr);
bool m_withDebugMsg = false; //!< allows to disable debug messages
bool m_withDebugMsg = false; //!< allows to disable debug messages
bool m_forceSortWhenAddingValues = false; //!< force sorting (latest first) when adding values
BlackMisc::Aviation::CCallsignList m_aircraftSupportingParts; //!< aircraft supporting parts
// hashs, because not sorted by key but keeping order

View File

@@ -30,8 +30,9 @@ namespace BlackCore
if (situationsPerCallsign)
{
if (!situationsPerCallsign->contains(callsign)) { return empty; }
if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; }
// lock free, expected that situationsPerCallsign is a copy
if (!situationsPerCallsign->contains(callsign)) { return empty; }
if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; }
splitSituations = (*situationsPerCallsign)[callsign].splitByTime(splitTimeMsSinceEpoch);
}
else
@@ -40,7 +41,7 @@ namespace BlackCore
QReadLocker lock(&m_lockSituations);
if (!m_situationsByCallsign.contains(callsign)) { return empty; }
if (m_situationsByCallsign[callsign].isEmpty()) { return empty; }
splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch);
splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch, true);
}
CAircraftSituationList &situationsNewer = splitSituations[0]; // newer part
@@ -78,8 +79,8 @@ namespace BlackCore
}
else
{
oldSituation = situationsOlder.front(); // first oldest
newSituation = situationsNewer.back(); // latest newest
oldSituation = situationsOlder.front(); // first oldest (aka newest oldest)
newSituation = situationsNewer.back(); // latest newest (aka oldest of newer block)
Q_ASSERT(oldSituation.getMSecsSinceEpoch() < newSituation.getMSecsSinceEpoch());
}
@@ -103,12 +104,12 @@ namespace BlackCore
}
}
// Interpolate latitude: Lat = (LatB - LatA) * t + LatA
const CLatitude oldLat(oldSituation.latitude());
const CLatitude newLat(newSituation.latitude());
const CLongitude oldLng(oldSituation.longitude());
const CLongitude newLng(newSituation.longitude());
// Interpolate latitude: Lat = (LatB - LatA) * t + LatA
currentPosition.setLatitude((newLat - oldLat)
* simulationTimeFraction
+ oldLat);
@@ -130,6 +131,8 @@ namespace BlackCore
if (newLat == oldLat && newLng == oldLng && oldAlt == newAlt)
{
// stop interpolation here
//! \todo Does not work for VTOL aircraft. We need a flag for VTOL aircraft
return currentSituation;
}

View File

@@ -60,7 +60,7 @@ namespace BlackMisc
CSequence(std::initializer_list<T> il) : m_pimpl(new Pimpl<QList<T>>(QList<T>(il))) {}
/*!
* \brief By QList of type <T>.
* \brief By QList of type T.
*/
CSequence(const QList<T> &list) : m_pimpl(new Pimpl<QList<T>>(QList<T>(list))) {}

View File

@@ -76,9 +76,17 @@ namespace BlackMisc
void CRemoteAircraftProviderDummy::insertNewSituation(const CAircraftSituation &situation)
{
this->m_situations.push_frontMaxElements(situation, 20);
this->m_situations.sortLatestFirst(); // like in real world, latest should be first
emit addedRemoteAircraftSituation(situation);
}
void CRemoteAircraftProviderDummy::insertNewAircraftParts(const CAircraftParts &parts)
{
this->m_parts.push_frontMaxElements(parts, 20);
this->m_parts.sortLatestFirst(); // like in real world, latest should be first
emit addedRemoteAircraftParts(parts);
}
void CRemoteAircraftProviderDummy::clear()
{
m_situations.clear();

View File

@@ -65,7 +65,10 @@ namespace BlackMisc
//! For testing, add new situation and fire signals
void insertNewSituation(const BlackMisc::Aviation::CAircraftSituation &situation);
// clear all data
//! For testing, add new parts and fire signals
void insertNewAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts);
//! Clear all data
void clear();
signals:

View File

@@ -59,10 +59,10 @@ namespace BlackMisc
}
template <class OBJ, class CONTAINER>
QList<CONTAINER> ITimestampObjectList<OBJ, CONTAINER>::splitByTime(qint64 msSinceEpoch) const
QList<CONTAINER> ITimestampObjectList<OBJ, CONTAINER>::splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst) const
{
CONTAINER newer(this->container());
newer.sortLatestFirst();
if (!alreadySortedLatestFirst) { newer.sortLatestFirst(); }
CONTAINER older;
for (auto it = newer.begin(); it != newer.end(); ++it)
{

View File

@@ -46,7 +46,7 @@ namespace BlackMisc
//! Split into 2 containers, [0] >= msSinceEpoch ("newer") [b] < msSinceEpoch ("older")
//! \note Sort order: latest elements first
QList<CONTAINER> splitByTime(qint64 msSinceEpoch) const;
QList<CONTAINER> splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst = false) const;
//! Latest value
OBJ latestValue() const;

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);