Ref T786, FSX/P3D METAR (weather inject)

* allow to select which layers are converted to METAR string
* allow to pass sim.flag (FSX/P3D)
* winds aloft seem to crash FSX, disabled(!)
This commit is contained in:
Klaus Basan
2020-04-20 00:07:26 +02:00
committed by Mat Sutcliffe
parent 17cebfb8d3
commit f9bb71c8ee
3 changed files with 61 additions and 44 deletions

View File

@@ -7,6 +7,7 @@
*/ */
#include "blackmisc/simulation/fsx/simconnectutilities.h" #include "blackmisc/simulation/fsx/simconnectutilities.h"
#include "blackmisc/stringutils.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile> #include <QFile>
@@ -19,6 +20,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QStringBuilder> #include <QStringBuilder>
using namespace BlackMisc;
using namespace BlackMisc::Aviation; using namespace BlackMisc::Aviation;
using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Weather; using namespace BlackMisc::Weather;
@@ -205,20 +207,18 @@ namespace BlackMisc
return lightMask; return lightMask;
} }
QString CSimConnectUtilities::convertToSimConnectMetar(const CGridPoint &gridPoint) QString CSimConnectUtilities::convertToSimConnectMetar(const CGridPoint &gridPoint, bool isFSX, bool useWindLayers, bool useVisibilityLayers, bool useCloudLayers, bool useTempLayers)
{ {
// STATION ID // STATION ID
Q_ASSERT(!gridPoint.getIdentifier().isEmpty()); Q_ASSERT(!gridPoint.getIdentifier().isEmpty());
QString simconnectMetar = gridPoint.getIdentifier(); QString simconnectMetar = gridPoint.getIdentifier();
// SURFACE WINDS/WINDS ALOFT // SURFACE WINDS/WINDS ALOFT
CWindLayerList windLayers = gridPoint.getWindLayers(); const CWindLayerList windLayers = useWindLayers ? gridPoint.getWindLayers().sortedBy(&CWindLayer::getLevel) : CWindLayerList();
windLayers.sortBy(&CWindLayer::getLevel); simconnectMetar += windsToSimConnectMetar(windLayers, isFSX);
simconnectMetar += windsToSimConnectMetar(windLayers);
// VISIBILITY // VISIBILITY
CVisibilityLayerList visibilityLayers = gridPoint.getVisibilityLayers(); const CVisibilityLayerList visibilityLayers = useVisibilityLayers ? gridPoint.getVisibilityLayers().sortedBy(&CVisibilityLayer::getBase) : CVisibilityLayerList();
visibilityLayers.sortBy(&CVisibilityLayer::getBase);
simconnectMetar += visibilitiesToSimConnectMetar(visibilityLayers); simconnectMetar += visibilitiesToSimConnectMetar(visibilityLayers);
// PRESENT CONDITIONS // PRESENT CONDITIONS
@@ -228,27 +228,25 @@ namespace BlackMisc
// todo // todo
// SKY CONDITIONS // SKY CONDITIONS
CCloudLayerList cloudLayers = gridPoint.getCloudLayers(); const CCloudLayerList cloudLayers = useCloudLayers ? gridPoint.getCloudLayers().sortedBy(&CCloudLayer::getBase) : CCloudLayerList();
cloudLayers.sortBy(&CCloudLayer::getBase);
simconnectMetar += cloudsToSimConnectMetar(cloudLayers); simconnectMetar += cloudsToSimConnectMetar(cloudLayers);
// TEMPERATURE // TEMPERATURE
const CTemperatureLayerList temperatureLayers = useTempLayers ? gridPoint.getTemperatureLayers().sortedBy(&CTemperatureLayer::getLevel) : CTemperatureLayerList();
CTemperatureLayerList temperatureLayers = gridPoint.getTemperatureLayers();
temperatureLayers.sortBy(&CTemperatureLayer::getLevel);
simconnectMetar += temperaturesToSimConnectMetar(temperatureLayers); simconnectMetar += temperaturesToSimConnectMetar(temperatureLayers);
// ALTIMETER // ALTIMETER
// Format: // Format:
// QNNNN // QNNNN
// Q = specifier for altimeter in millibars // Q = specifier for altimeter in millibars
simconnectMetar += QLatin1String(" Q");
// NNNN = altimeter in millibars // NNNN = altimeter in millibars
static const QString arg1s("%1"); if (!gridPoint.getPressureAtMsl().isNull())
const auto altimeter = gridPoint.getPressureAtMsl().valueInteger(CPressureUnit::mbar()); {
simconnectMetar += arg1s.arg(altimeter, 4, 10, QLatin1Char('0')); static const QString arg1s(" Q%1");
const int altimeter = gridPoint.getPressureAtMsl().valueInteger(CPressureUnit::mbar());
return simconnectMetar; simconnectMetar += arg1s.arg(altimeter, 4, 10, QLatin1Char('0'));
}
return simconnectMetar.simplified();
} }
void CSimConnectUtilities::registerMetadata() void CSimConnectUtilities::registerMetadata()
@@ -257,14 +255,15 @@ namespace BlackMisc
qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_SURFACE>(); qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_SURFACE>();
} }
QString CSimConnectUtilities::windsToSimConnectMetar(const CWindLayerList &windLayers) QString CSimConnectUtilities::windsToSimConnectMetar(const CWindLayerList &windLayers, bool isFSX)
{ {
static const QString arg1s("%1"); static const QString arg1s("%1");
QString simconnectWinds;
QString simConnectWinds;
bool surface = true; bool surface = true;
for (const CWindLayer &windLayer : windLayers) for (const CWindLayer &windLayer : windLayers)
{ {
simconnectWinds += QLatin1Char(' '); simConnectWinds += QLatin1Char(' ');
// Format: // Format:
// DDDSSSUUU (steady) // DDDSSSUUU (steady)
@@ -272,22 +271,25 @@ namespace BlackMisc
if (windLayer.isDirectionVariable()) if (windLayer.isDirectionVariable())
{ {
// DDD = VRB for variable // DDD = VRB for variable
simconnectWinds += QLatin1String("VRB"); simConnectWinds += QLatin1String("VRB");
} }
else else
{ {
const int speed = windLayer.getSpeed().valueInteger(CSpeedUnit::kts()); if (!windLayer.getSpeed().isNull() && !windLayer.getDirection().isNull())
const int direction = windLayer.getDirection().valueInteger(CAngleUnit::deg()); {
const int speedKts = windLayer.getSpeed().valueInteger(CSpeedUnit::kts());
const int directionDeg = windLayer.getDirection().valueInteger(CAngleUnit::deg());
simconnectWinds += arg1s.arg(direction, 3, 10, QLatin1Char('0')) % // DDD = Direction (0-360 degrees) simConnectWinds += arg1s.arg(directionDeg, 3, 10, QLatin1Char('0')) % // DDD = Direction (0-360 degrees)
arg1s.arg(speed, 3, 10, QLatin1Char('0')); // SSS = Speed arg1s.arg(speedKts, 3, 10, QLatin1Char('0')); // SSS = Speed
}
} }
// XX = Gust speed // XX = Gust speed
const int gustSpeed = windLayer.getGustSpeed().valueInteger(CSpeedUnit::kts()); const int gustSpeedKts = windLayer.getGustSpeed().valueInteger(CSpeedUnit::kts());
if (gustSpeed > 0) { simconnectWinds += u'G' % arg1s.arg(gustSpeed, 2, 10, QLatin1Char('0')); } if (gustSpeedKts > 0) { simConnectWinds += u'G' % arg1s.arg(gustSpeedKts, 2, 10, QLatin1Char('0')); }
// UUU = Speed units // UUU = Speed units
simconnectWinds += QStringLiteral("KT"); simConnectWinds += QStringLiteral("KT");
if (surface) if (surface)
{ {
@@ -297,24 +299,31 @@ namespace BlackMisc
"&D" // D = specifier for surface layer "&D" // D = specifier for surface layer
"305" // Surface default depth is 1000 feet or 305m "305" // Surface default depth is 1000 feet or 305m
"NG"; // We don't have turbulence or wind shear information, hence we use the defaults "NG"; // We don't have turbulence or wind shear information, hence we use the defaults
simconnectWinds += surfaceWinds; simConnectWinds += surfaceWinds;
surface = false; surface = false;
} }
else else
{ {
auto altitude = windLayer.getLevel(); CAltitude altitude = windLayer.getLevel();
altitude.toMeanSeaLevel();
int altitudeValue = altitude.valueInteger(CLengthUnit::m());
// Winds aloft extension: // this seems to crash FSX, P3D works
// &ANNNNTS // https://www.fsdeveloper.com/forum/threads/setting-winds-aloft.18862/
simconnectWinds += if (!altitude.isNull() && !isFSX)
u"&A" % // A = specifier for altitude above mean sea-level (MSL) {
arg1s.arg(altitudeValue, 4, 10, QLatin1Char('0')) % // NNNN = depth (height) in meters. altitude.toMeanSeaLevel();
u"NG"; // We don't have turbulence or wind shear information, hence we use the defaults int altitudeValueMeters = altitude.valueInteger(CLengthUnit::m());
// Winds aloft extension:
// &ANNNNTS
simConnectWinds +=
u"&A" % // A = specifier for altitude above mean sea-level (MSL)
arg1s.arg(altitudeValueMeters, 4, 10, QLatin1Char('0')) % // NNNN = depth (height) in meters.
u"NG"; // We don't have turbulence or wind shear information, hence we use the defaults
}
} }
} }
return simconnectWinds;
return QStringLiteral(" ") % simConnectWinds.simplified();
} }
QString CSimConnectUtilities::visibilitiesToSimConnectMetar(const CVisibilityLayerList &visibilityLayers) QString CSimConnectUtilities::visibilitiesToSimConnectMetar(const CVisibilityLayerList &visibilityLayers)

View File

@@ -237,7 +237,7 @@ namespace BlackMisc
static int lightsToLightStates(const Aviation::CAircraftLights &lights); static int lightsToLightStates(const Aviation::CAircraftLights &lights);
//! Converts the weather at gridPoint to a SimConnect METAR string //! Converts the weather at gridPoint to a SimConnect METAR string
static QString convertToSimConnectMetar(const Weather::CGridPoint &gridPoint); static QString convertToSimConnectMetar(const Weather::CGridPoint &gridPoint, bool isFSX, bool useWindLayers = true, bool useVisibilityLayers = true, bool useCloudLayers = true, bool useTempLayers = true);
//! Get info about SimConnect DLL //! Get info about SimConnect DLL
static BlackMisc::CWinDllUtils::DLLInfo simConnectDllInfo(); static BlackMisc::CWinDllUtils::DLLInfo simConnectDllInfo();
@@ -256,7 +256,7 @@ namespace BlackMisc
//! \return enum element's name //! \return enum element's name
static QString resolveEnumToString(const DWORD id, const char *enumName); static QString resolveEnumToString(const DWORD id, const char *enumName);
static QString windsToSimConnectMetar(const BlackMisc::Weather::CWindLayerList &windLayers); static QString windsToSimConnectMetar(const BlackMisc::Weather::CWindLayerList &windLayers, bool isFSX);
static QString visibilitiesToSimConnectMetar(const BlackMisc::Weather::CVisibilityLayerList &visibilityLayers); static QString visibilitiesToSimConnectMetar(const BlackMisc::Weather::CVisibilityLayerList &visibilityLayers);
static QString cloudsToSimConnectMetar(const BlackMisc::Weather::CCloudLayerList &cloudLayers); static QString cloudsToSimConnectMetar(const BlackMisc::Weather::CCloudLayerList &cloudLayers);
static QString temperaturesToSimConnectMetar(const BlackMisc::Weather::CTemperatureLayerList &temperatureLayers); static QString temperaturesToSimConnectMetar(const BlackMisc::Weather::CTemperatureLayerList &temperatureLayers);

View File

@@ -2410,13 +2410,21 @@ namespace BlackSimPlugin
} }
// So far, there is only global weather // So far, there is only global weather
auto glob = weatherGrid.frontOrDefault(); const bool isFSX = this->getSimulatorPluginInfo().getSimulatorInfo().isFSX();
CGridPoint glob = weatherGrid.frontOrDefault();
glob.setIdentifier("GLOB"); glob.setIdentifier("GLOB");
const QString metar = CSimConnectUtilities::convertToSimConnectMetar(glob); const QString metar = CSimConnectUtilities::convertToSimConnectMetar(glob, isFSX);
const QByteArray metarBa = toFsxChar(metar); const QByteArray metarBa = toFsxChar(metar);
// send
SimConnect_WeatherSetModeCustom(m_hSimConnect); SimConnect_WeatherSetModeCustom(m_hSimConnect);
SimConnect_WeatherSetModeGlobal(m_hSimConnect); SimConnect_WeatherSetModeGlobal(m_hSimConnect);
SimConnect_WeatherSetObservation(m_hSimConnect, 0, metarBa.constData());
if (!metarBa.isEmpty())
{
// Q_ASSERT_X(metarBa.back() == 0, Q_FUNC_INFO, "Need 0 terminated string");
SimConnect_WeatherSetObservation(m_hSimConnect, 0, metarBa.constData());
}
} }
bool CSimulatorFsxCommon::requestPositionDataForSimObject(const CSimConnectObject &simObject, SIMCONNECT_PERIOD period) bool CSimulatorFsxCommon::requestPositionDataForSimObject(const CSimConnectObject &simObject, SIMCONNECT_PERIOD period)