Skip to content

Commit

Permalink
Implement performance warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
kebekus committed Nov 22, 2024
1 parent 4b6d2ec commit 6b67fbc
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 63 deletions.
10 changes: 9 additions & 1 deletion src/qml/dialogs/WeatherReport.qml
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,19 @@ CenteringDialog {
Label { // Derived Data
Layout.fillWidth: true
visible: text !== ""
text: (weatherStation != null) && weatherStation.hasMETAR ? weatherStation.metar.derivedData(Navigator.aircraft) : ""
text: (weatherStation != null) && weatherStation.hasMETAR ? weatherStation.metar.derivedData(Navigator.aircraft, Global.warnMETARPerformance, Global.showMETARPerformanceExplanation) : ""
wrapMode: Text.WordWrap
textFormat: Text.RichText
bottomPadding: 0.2*font.pixelSize
topPadding: 0.4*font.pixelSize

onLinkActivated: (linkText) =>
{
if (linkText === "hideExplanation")
Global.showMETARPerformanceExplanation = false
if (linkText === "hidePerformanceWarning")
Global.warnMETARPerformance = false
}
}

Label { // title: "TAF"
Expand Down
4 changes: 4 additions & 0 deletions src/qml/items/Global.qml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Item {

// Warning
property bool warnNOTAMLocation: true
property bool warnMETARPerformance: true
property bool showMETARPerformanceExplanation: true

property LocationPermission locationPermission: LocationPermission {
id: locationPermission
Expand All @@ -51,6 +53,8 @@ Item {
Settings {
category: "GUIWarnings"
property alias warnNOTAMLocation: global.warnNOTAMLocation
property alias warnMETARPerformance: global.warnMETARPerformance
property alias showMETARPerformanceExplanation: global.showMETARPerformanceExplanation
}


Expand Down
11 changes: 9 additions & 2 deletions src/qml/pages/SettingsPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,27 @@ Page {
}

WordWrappingItemDelegate {
id: showAllWarning

Layout.fillWidth: true
icon.source: "/icons/material/ic_warning.svg"
text: qsTr("Show All Warnings")
onClicked: {
PlatformAdaptor.vibrateBrief()
GlobalSettings.alwaysOpenExternalWebsites = false
Global.warnNOTAMLocation = true
Global.warnMETARPerformance = true
Global.showMETARPerformanceExplanation = true
Global.toast.doToast(qsTr("Warnings will be shown again."))
}
visible: GlobalSettings.alwaysOpenExternalWebsites || !Global.warnNOTAMLocation
visible: GlobalSettings.alwaysOpenExternalWebsites
|| !Global.warnNOTAMLocation
|| !Global.warnMETARPerformance
|| !Global.showMETARPerformanceExplanation
}
ToolButton {
icon.source: "/icons/material/ic_info_outline.svg"
visible: GlobalSettings.alwaysOpenExternalWebsites || !Global.warnNOTAMLocation
visible: showAllWarning.visible
onClicked: {
PlatformAdaptor.vibrateBrief()
helpDialog.title = qsTr("Show All Warnings")
Expand Down
130 changes: 71 additions & 59 deletions src/weather/METAR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,35 +263,13 @@ QString Weather::METAR::summary(const Navigation::Aircraft& aircraft, const QDat
}

// Wind and Gusts
if (m_gust.toKN() > 15)
if (m_gust.isFinite() && m_gust.toKN() > 15)
{
switch (aircraft.horizontalDistanceUnit())
{
case Navigation::Aircraft::Kilometer:
resultList << tr("gusts of %1 km/h").arg( qRound(m_gust.toKMH()) );
break;
case Navigation::Aircraft::StatuteMile:
resultList << tr("gusts of %1 mph").arg( qRound(m_gust.toMPH()) );
break;
case Navigation::Aircraft::NauticalMile:
resultList << tr("gusts of %1 kn").arg( qRound(m_gust.toKN()) );
break;
}
resultList << tr("gusts of %1").arg(aircraft.horizontalSpeedToString(m_gust));
}
else if (m_wind.toKN() > 10)
else if (m_wind.isFinite() && m_wind.toKN() > 10)
{
switch (aircraft.horizontalDistanceUnit())
{
case Navigation::Aircraft::Kilometer:
resultList << tr("wind at %1 km/h").arg( qRound(m_wind.toKMH()) );
break;
case Navigation::Aircraft::StatuteMile:
resultList << tr("wind at %1 mph").arg( qRound(m_wind.toMPH()) );
break;
case Navigation::Aircraft::NauticalMile:
resultList << tr("wind at %1 kn").arg( qRound(m_wind.toKN()) );
break;
}
resultList << tr("wind at %1").arg(aircraft.horizontalSpeedToString(m_wind));
}

// Weather
Expand All @@ -310,53 +288,70 @@ QString Weather::METAR::summary(const Navigation::Aircraft& aircraft, const QDat
}


QString Weather::METAR::derivedData(const Navigation::Aircraft& aircraft) const
QString Weather::METAR::derivedData(const Navigation::Aircraft& aircraft, bool showPerformanceWarning, bool explainPerformanceWarning) const
{
QStringList items;

// Density altitude
if (m_densityAltitude.isFinite())
{
Units::Distance const altitude = Units::Distance::fromM(m_location.altitude());
items += tr("Density Altitude: %1 (Δ %2)").arg(
aircraft.verticalDistanceToString(m_densityAltitude),
aircraft.verticalDistanceToString(m_densityAltitude - altitude, /*forceSign=*/ true)
);

// koch chart approximation formulas based on https://groups.google.com/g/rec.aviation.piloting/c/SDlMioBVqaA/m/8_x9GYsnGwAJ
const double densityAltitudeInFt = m_densityAltitude.toFeet();
int takeoffDistIncPercentage = 100 * (densityAltitudeInFt / 1000) * .15;
takeoffDistIncPercentage = (takeoffDistIncPercentage > 5) ? (((takeoffDistIncPercentage + 9) / 10) * 10) : 0; // round up to next group of ten; less than 6 percent considered as 0
int rateOfClimbDecreasePercentage = 100 * (densityAltitudeInFt / 1000) * .075;
rateOfClimbDecreasePercentage = (rateOfClimbDecreasePercentage > 5) ? (((rateOfClimbDecreasePercentage + 9) / 10) * 10) : 0;

if (takeoffDistIncPercentage > 0)
auto result = tr("Density Altitude: %1").arg(aircraft.verticalDistanceToString(m_densityAltitude));
if (altitude.isFinite())
{
if (takeoffDistIncPercentage <= 100)
{
items += tr("Takeoff distance increases by ≈ %1\%").arg(takeoffDistIncPercentage);
}
else
{
items += tr("Takeoff distance increases by > 100\%");
}
}

if (rateOfClimbDecreasePercentage > 0)
result += u" (Δ %2)"_s.arg(aircraft.verticalDistanceToString(m_densityAltitude - altitude, /*forceSign=*/ true));
}
items += result;
}

// Relative humidity
auto relativeHumidity = Navigation::Atmosphere::relativeHumidity(m_temperature, m_dewpoint);
if (!std::isnan(relativeHumidity))
{
items += tr("Relative Humidity: %1%").arg(qRound(relativeHumidity));
}

// Performance warnings
bool performanceWarningsShown = false;
if (m_densityAltitude.isFinite() && showPerformanceWarning)
{
// Koch chart approximation formulas based on https://groups.google.com/g/rec.aviation.piloting/c/SDlMioBVqaA/m/8_x9GYsnGwAJ
const auto densityAltitudeInFt = m_densityAltitude.toFeet();

// Estimate 15% increase in takeoff distance per 1000ft density height
auto takeoffDistIncPercentage = 0.015 * densityAltitudeInFt;

// Estimate 7.5% decrease in climb rate per 1000ft density height
auto rateOfClimbDecreasePercentage = 0.0075 * densityAltitudeInFt;

if (takeoffDistIncPercentage > 25)
{
if (rateOfClimbDecreasePercentage <= 100)
performanceWarningsShown = true;
items += "<strong>"
+ tr("Performance")
+ ":</strong> "
+ tr("Expect %1\% increase in takeoff distance").arg(qRound(takeoffDistIncPercentage));
}

if (rateOfClimbDecreasePercentage > 25)
{
performanceWarningsShown = true;
if (rateOfClimbDecreasePercentage <= 90)
{
items += tr("Rate of climb decreases by ≈ %1\%").arg(rateOfClimbDecreasePercentage);
items += "<strong>"
+ tr("Performance")
+ ":</strong> "
+ tr("Expect %1\% decrease in climb rate").arg(qRound(rateOfClimbDecreasePercentage));
}
else
{
items += tr("Rate of climb decreases by > 100\%");
items += "<strong>"
+ tr("Performance")
+ ":</strong> "
+ tr("Expect drastic decrease in climb rate. Flying might be inadvisable.");
}
}
}
auto relativeHumidity = Navigation::Atmosphere::relativeHumidity(m_temperature, m_dewpoint);
if (!std::isnan(relativeHumidity))
{
items += tr("Relative Humidity: %1%").arg(qRound(relativeHumidity));
}

if (items.isEmpty())
{
Expand All @@ -373,6 +368,23 @@ QString Weather::METAR::derivedData(const Navigation::Aircraft& aircraft) const
result += u"</li>"_s;
}
result += QStringLiteral("</ul>");

if (performanceWarningsShown)
{
if (explainPerformanceWarning)
{
result += u"<p>"_s
+ tr("Percentages are rough estimates, comparing performance of typical SEP aircraft at density altitude to standard sea level values. "
"Runway conditions might further degrade performance. "
"Always consult the flight manual for exact values.")
+ u" <a href='hideExplanation'>"_s + tr("Hide this explanation.") + u"</a>"_s
+ u"</p>"_s;
}
else
{
result += u"<p><a href='hidePerformanceWarning'>"_s + tr("Hide performance warnings.") + u"</a></p>"_s;
}
}
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion src/weather/METAR.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class METAR : public Decoder {
*
* @returns Human-readable, translated rich text
*/
[[nodiscard]] Q_INVOKABLE QString derivedData(const Navigation::Aircraft& aircraft) const;
[[nodiscard]] Q_INVOKABLE QString derivedData(const Navigation::Aircraft& aircraft, bool showPerformanceWarning, bool explainPerformanceWarning) const;

/*! \brief One-line summary of the METAR
*
Expand Down

0 comments on commit 6b67fbc

Please sign in to comment.