Skip to content

Commit

Permalink
Feat/RT/Weather: historic weather, stability fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
TwinFan committed Aug 25, 2024
1 parent b473269 commit 4d8f2e5
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 68 deletions.
Binary file added Data/RealTraffic/RT_API.sjson/746312457.328537
Binary file not shown.
Binary file modified Data/RealTraffic/RT_API.sjson/data
Binary file not shown.
Binary file modified Data/RealTraffic/RT_API.sjson/metaData
Binary file not shown.
6 changes: 3 additions & 3 deletions Include/LTRealTraffic.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,14 @@ class RealTrafficConnection : public LTFlightDataChannel
std::string ICAO = RT_METAR_UNKN; ///< ICAO code of METAR station
float dist = NAN; ///< distance to station
float brgTo = NAN; ///< bearing to station
std::string METAR; ///< the actual METAR report

NearestMETAR() {} ///< Standard constructor, all empty
NearestMETAR(const JSON_Object* pObj) { Parse (pObj); } ///< Fill from JSON

void clear() { *this = NearestMETAR(); } ///< reset to defaults
bool Parse (const JSON_Object* pObj); ///< parse RT's NearestMETAR response array entry, reutrns if valid
bool isValid () const ///< valid, ie. all fields properly set?
{ return !ICAO.empty() && ICAO != RT_METAR_UNKN && !std::isnan(dist) && !std::isnan(brgTo) && !METAR.empty(); }
{ return !ICAO.empty() && ICAO != RT_METAR_UNKN && !std::isnan(dist) && !std::isnan(brgTo); }
};

/// Weather data
Expand Down Expand Up @@ -339,7 +338,8 @@ class RealTrafficConnection : public LTFlightDataChannel
bool DoHoverDetection () const override { return true; }

// Status
std::string GetStatusText () const override; ///< return a human-readable status
std::string GetStatusText () const override; ///< return a human-readable status
bool isHistoric () const { return curr.tOff > 0; } ///< serving historic data?

protected:
void Main () override; ///< virtual thread main function
Expand Down
10 changes: 8 additions & 2 deletions Include/LTWeather.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ bool WeatherIsXPRealWeather ();
/// Thread-safely store weather information to be set in X-Plane in the main thread later
void WeatherSet (const LTWeather& w);
/// Thread-safely store weather information to be set in X-Plane in the main thread later
void WeatherSet (const std::string& metar);
void WeatherSet (const std::string& metar, const std::string& metarIcao);
/// Actually update X-Plane's weather if there is anything to do (called from main thread)
void WeatherUpdate ();
/// Reset weather settings to what they were before X-Plane took over
Expand All @@ -47,6 +47,8 @@ void WeatherReset ();
/// Log current weather
void WeatherLogCurrent (const std::string& msg);

/// Current METAR in use for weather generation
const std::string& WeatherGetMETAR ();
/// Return a human readable string on the weather source, is "LiveTraffic" if WeatherInControl()
std::string WeatherGetSource ();

Expand Down Expand Up @@ -75,7 +77,7 @@ class LTWeather
float w = 1.0f; ///< weight on lower index' value, other weight is 1.0f-w
};

positionTy pos; ///< position the weather refers to
positionTy pos; ///< position the weather refers to, effectively the camera view pos, including its altitude

float visibility_reported_sm = NAN; ///< float y statute_miles = 0. The reported visibility (e.g. what the METAR/weather window says).
float sealevel_pressure_pas = NAN; ///< float y pascals Pressure at sea level, current planet
Expand Down Expand Up @@ -108,7 +110,11 @@ class LTWeather
int change_mode = -1; ///< int y enum How the weather is changing. 0 = Rapidly Improving, 1 = Improving, 2 = Gradually Improving, 3 = Static, 4 = Gradually Deteriorating, 5 = Deteriorating, 6 = Rapidly Deteriorating, 7 = Using Real Weather
int weather_source = -1; ///< int n enum What system is currently controlling the weather. 0 = Preset, 1 = Real Weather, 2 = Controlpad, 3 = Plugin.
int weather_preset = -1; ///< int y enum Read the UI weather preset that is closest to the current conditions, or set an UI preset. Clear(0), VFR Few(1), VFR Scattered(2), VFR Broken(3), VFR Marginal(4), IFR Non-precision(5), IFR Precision(6), Convective(7), Large-cell Storms(8)

// METAR
std::string metar; ///< METAR, if filled combine METAR data into weather generation
std::string metarFieldIcao; ///< METAR field's ICAO code
positionTy posMetarField; ///< position of the field the METAR refers to

public:
LTWeather(); ///< Constructor sets all arrays to all `NAN`
Expand Down
4 changes: 2 additions & 2 deletions Src/DataRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1784,7 +1784,7 @@ bool DataRefs::SetCfgValue (void* p, int val)
WeatherReset();
break;
case WC_METAR_XP: // ...by METAR pass on the last METAR now
WeatherSet(lastWeatherMETAR);
WeatherSet(lastWeatherMETAR, lastWeatherStationId);
break;
case WC_REAL_TRAFFIC: // ...by RealTraffic do nothing...RT will do
break;
Expand Down Expand Up @@ -2766,7 +2766,7 @@ float DataRefs::SetWeather (float hPa, float lat, float lon,

// If we are to set weather based on METAR, then this is it
if (dataRefs.GetWeatherControl() == WC_METAR_XP)
WeatherSet(lastWeatherMETAR);
WeatherSet(lastWeatherMETAR, lastWeatherStationId);

return lastWeatherHPA;
}
Expand Down
9 changes: 9 additions & 0 deletions Src/InfoListWnd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,15 @@ void InfoListWnd::buildInterface()
if (ImGui::TableSetColumnIndex(0)) ImGui::TextUnformatted("Weather Source");
if (ImGui::TableSetColumnIndex(1)) ImGui::TextUnformatted(WeatherGetSource().c_str());

// If generated weather's METAR deviates from live weather, then display the generation source, too
const std::string& MetarForWeatherGeneration = WeatherGetMETAR();
if (!MetarForWeatherGeneration.empty() &&
MetarForWeatherGeneration != weatherMETAR) {
ImGui::TableNextRow();
if (ImGui::TableSetColumnIndex(0)) ImGui::TextUnformatted("Weather METAR");
if (ImGui::TableSetColumnIndex(1)) ImGui::TextUnformatted(MetarForWeatherGeneration.c_str());
}

ImGui::TableNextRow();
if (ImGui::TableSetColumnIndex(0)) ImGui::TextUnformatted("Live Weather");
if (ImGui::TableSetColumnIndex(1)) {
Expand Down
72 changes: 38 additions & 34 deletions Src/LTRealTraffic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ std::string RealTrafficConnection::GetStatusText () const
curr.eRequType == CurrTy::RT_REQU_NEAREST_METAR ? "Fetching weather..." :
curr.eRequType == CurrTy::RT_REQU_WEATHER ? "Fetching weather..." :
LTChannel::GetStatusText();
if (tsAdjust > 1.0) { // historic data?
if (isHistoric()) { // historic data?
snprintf(sIntvl, sizeof(sIntvl), MSG_RT_ADJUST,
GetAdjustTSText().c_str());
s += sIntvl;
}
if (lTotalFlights == 0) { // RealTraffic has no data at all???
s += " | RealTraffic has no traffic at all! ";
s += (curr.tOff > 0 ? "Maybe requested historic data too far in the past?" : "(full_count=0)");
s += (isHistoric() ? "Maybe requested historic data too far in the past?" : "(full_count=0)");
}
return s;
}
Expand Down Expand Up @@ -302,17 +302,19 @@ void RealTrafficConnection::SetRequType (const positionTy& _pos)
curr.eRequType = CurrTy::RT_REQU_AUTH;
else if (curr.eRequType == CurrTy::RT_REQU_NEAREST_METAR) // previous request was METAR location?
curr.eRequType = CurrTy::RT_REQU_WEATHER;
else if (std::isnan(rtWx.QNH) || // no Weather, or wrong time offset, or outdated, or moved too far away?
std::labs(curr.tOff - rtWx.tOff) > 120 ||
// too far? (we use half the max. METAR distance
// FIXME: Must distinguish between weather's position and 'nearest METAR locations's' position
rtWx.pos.distRoughSqr(curr.pos) > (sqr(dataRefs.GetWeatherMaxMetarDist_m()/2.0)))
else if (rtWx.nErr < RT_DRCT_MAX_WX_ERR && // not yet seen too many weather request errors? _AND_
(std::isnan(rtWx.QNH) || // no Weather, or wrong time offset, or outdated, or moved too far away?
std::labs(curr.tOff - rtWx.tOff) > 120 ||
// too far? (we use half the max. METAR distance
// FIXME: Must distinguish between weather's position and 'nearest METAR locations's' position
rtWx.pos.distRoughSqr(curr.pos) > (sqr(dataRefs.GetWeatherMaxMetarDist_m()/2.0))))
{
curr.eRequType = CurrTy::RT_REQU_NEAREST_METAR;
if (std::labs(curr.tOff - rtWx.tOff) > 120) // if changing the timeoffset (request other historic data) then we must have new weather before proceeding
rtWx.QNH = NAN;
}
else if (std::chrono::steady_clock::now() >= rtWx.next) // just time for a weather update
else if (rtWx.nErr < RT_DRCT_MAX_WX_ERR && // not yet seen too many weather request errors? _AND_
std::chrono::steady_clock::now() >= rtWx.next) // just time for a weather update
{
curr.eRequType = CurrTy::RT_REQU_WEATHER;
}
Expand Down Expand Up @@ -537,9 +539,13 @@ bool RealTrafficConnection::ProcessFetchedData ()

// Error in locWX data?
std::string s = jog_s(pObj, "data.locWX.Error"); // sometimes errors are given in a specific field
if (s.empty() &&
!strcmp(jog_s(pObj, "data.locWX.Info"), "TinyDelta")) // if we request too often then Info is 'TinyDelta'
s = "TinyDelta";
if (s.empty()) { // and at other times there is something in the 'Info' field...not very consistent
s = jog_s(pObj, "data.locWX.Info");
if (!s.empty() &&
s != "TinyDelta" && // if we request too often then Info is 'TinyDelta', and we let it sit in 's'
s.substr(0,6) != "error:") // any error starts with "error:" and we let it sit in 's'
s.clear();
}

// Any error, either explicitely or because local pressure is bogus?
if (!s.empty() || std::isnan(wxQNH) || wxQNH < 800.0)
Expand Down Expand Up @@ -573,40 +579,37 @@ bool RealTrafficConnection::ProcessFetchedData ()
return false;
}

// Store METAR for later processing
std::string metar;
// If we have METAR info pass that on, too
s = jog_s(pObj, "data.ICAO");
if (!s.empty() && s != "UNKN") // ignore empty or UNKN response
metar = jog_s(pObj, "data.METAR");
else
s.clear();

if (s != rtWx.nearestMETAR.ICAO)
rtWx.nearestMETAR.dist = rtWx.nearestMETAR.brgTo = NAN;
rtWx.nearestMETAR.ICAO = std::move(s);
rtWx.nearestMETAR.METAR = std::move(metar);
std::string metar = jog_s(pObj, "data.METAR");

if (s.empty() || s == "UNKN") { // ignore no/unknown METAR
s.clear();
metar.clear();
}

// If this is live data, not historic, then we can use it instead of separately querying METAR
if (curr.tOff == 0) {
if (!isHistoric()) {
rtWx.w.qnh_pas = dataRefs.SetWeather((float)wxQNH,
(float)rtWx.pos.lat(), (float)rtWx.pos.lon(),
rtWx.nearestMETAR.ICAO,
rtWx.nearestMETAR.METAR);
s, metar);
}
// historic data
else {
// Try reading QNH from METAR
// TODO: Test weather for historical RT data
rtWx.w.qnh_pas = WeatherQNHfromMETAR(rtWx.nearestMETAR.METAR);
rtWx.w.qnh_pas = WeatherQNHfromMETAR(metar);
}

// Successfully received local pressure information
rtWx.set(std::isnan(rtWx.w.qnh_pas) ? wxQNH : double(rtWx.w.qnh_pas), curr); // Save new QNH
LOG_MSG(logDEBUG, "Received RealTraffic Weather with QNH = %.1f", rtWx.QNH);

// If requested to set X-Plane's weather based on detailed weather data
if (dataRefs.GetWeatherControl() == WC_REAL_TRAFFIC)
if (dataRefs.GetWeatherControl() == WC_REAL_TRAFFIC) {
ProcessWeather (json_object_get_object(pObj, "data"));
if (std::isnan(rtWx.w.qnh_pas))
rtWx.w.qnh_pas = float(rtWx.QNH);
}

return true;
}
Expand Down Expand Up @@ -879,7 +882,6 @@ bool RealTrafficConnection::NearestMETAR::Parse (const JSON_Object* pObj)
ICAO = jog_s(pObj, "ICAO");
dist = (float)jog_n_nan(pObj, "Dist");
brgTo = (float)jog_n_nan(pObj, "BrgTo");
METAR = jog_s(pObj, "METAR");

return isValid();
}
Expand Down Expand Up @@ -916,10 +918,9 @@ void RealTrafficConnection::ProcessNearestMETAR (const JSON_Array* pData)
}

// TODO: Check for better matching station in direction of flight
LOG_MSG(logDEBUG, "Using Nearest METAR: %s (%.1fnm, %.0fdeg), '%s'",
LOG_MSG(logDEBUG, "Using Nearest METAR location %s (%.1fnm, %.0fdeg)",
rtWx.nearestMETAR.ICAO.c_str(),
rtWx.nearestMETAR.dist, rtWx.nearestMETAR.brgTo,
rtWx.nearestMETAR.METAR.c_str());
rtWx.nearestMETAR.dist, rtWx.nearestMETAR.brgTo);
}


Expand Down Expand Up @@ -950,8 +951,11 @@ void RealTrafficConnection::ProcessWeather(const JSON_Object* pData)
}

rtWx.w.pos = curr.pos;
// Forward METAR for weather processing, too
rtWx.w.metar = rtWx.nearestMETAR.METAR;

// METAR
rtWx.w.metar = jog_s(pData, "METAR");
rtWx.w.metarFieldIcao = jog_s(pData, "ICAO");
rtWx.w.posMetarField = positionTy(); // field's location is to be determined later in main thread

rtWx.w.visibility_reported_sm = float(jog_n_nan(pLocWX, "SVis") / M_per_SM);
rtWx.w.sealevel_pressure_pas = float(jog_n_nan(pLocWX, "SLP") * 100.0);
Expand Down
Loading

0 comments on commit 4d8f2e5

Please sign in to comment.