diff --git a/Data/RealTraffic/RTAPI_example.py b/Data/RealTraffic/RTAPI_example.py
index 08b9763..e9df886 100755
--- a/Data/RealTraffic/RTAPI_example.py
+++ b/Data/RealTraffic/RTAPI_example.py
@@ -90,18 +90,18 @@ def UDPbcast(ip, bcast, port, data):
# Example area: Heidiland
traffic_payload = { "GUID": "%s" % GUID,
"querytype": "locationtraffic",
- "top": 47.8,
- "bottom": 45.6,
- "left": 5.7,
- "right": 10.7,
+ "top": 52.4, # Around EDLE
+ "bottom": 50.4,
+ "left": 5.9,
+ "right": 7.9,
"toffset": 0 }
weather_payload = { "GUID": "%s" % GUID,
"querytype": "locwx",
- "lat": 47.8,
- "lon": 7.7,
- "alt": 36000,
- "airports": "LSZB|LSZH|LSGG|LFSB",
+ "lat": 51.4069, # EDLE
+ "lon": 6.9391,
+ "alt": 0,
+ "airports": "UNKN", # Don't need airport METAR
"toffset": 0 }
###############################################################
@@ -110,6 +110,7 @@ def UDPbcast(ip, bcast, port, data):
# fetch weather
response = requests.post(weather_url, weather_payload, headers=header)
+ print(response.text)
try:
json_data = response.json()
except Exception as e:
diff --git a/Data/RealTraffic/RT_API.sjson/725717638.083122 b/Data/RealTraffic/RT_API.sjson/725717638.083122
new file mode 100644
index 0000000..13e5477
Binary files /dev/null and b/Data/RealTraffic/RT_API.sjson/725717638.083122 differ
diff --git a/Data/RealTraffic/RT_API.sjson/data b/Data/RealTraffic/RT_API.sjson/data
index 66da90a..e0e0b0f 100644
Binary files a/Data/RealTraffic/RT_API.sjson/data and b/Data/RealTraffic/RT_API.sjson/data differ
diff --git a/Data/RealTraffic/RT_API.sjson/metaData b/Data/RealTraffic/RT_API.sjson/metaData
index c28b275..7969afc 100644
Binary files a/Data/RealTraffic/RT_API.sjson/metaData and b/Data/RealTraffic/RT_API.sjson/metaData differ
diff --git a/Include/CoordCalc.h b/Include/CoordCalc.h
index f0a94ff..f792396 100644
--- a/Include/CoordCalc.h
+++ b/Include/CoordCalc.h
@@ -445,6 +445,7 @@ struct positionTy {
// short-cuts to coord functions
inline double angle (const positionTy& pos2 ) const { return CoordAngle ( *this, pos2); }
inline double dist (const positionTy& pos2 ) const { return CoordDistance ( *this, pos2); }
+ double distRoughSqr (const positionTy& pos2) const { return DistLatLonSqr(lat(), lon(), pos2.lat(), pos2.lon()); }
inline vectorTy between (const positionTy& pos2 ) const { return CoordVectorBetween ( *this, pos2); }
inline positionTy destPos (const vectorTy& vec ) const { return CoordPlusVector ( *this, vec); }
inline positionTy operator+ (const vectorTy& vec ) const { return destPos (vec); }
diff --git a/Include/LTChannel.h b/Include/LTChannel.h
index 2f9cf63..99468ab 100644
--- a/Include/LTChannel.h
+++ b/Include/LTChannel.h
@@ -400,12 +400,12 @@ inline long jog_sl (const JSON_Object *object, const char *name)
// access to JSON number field (just a shorter name, returns 0 if not a number)
inline double jog_n (const JSON_Object *object, const char *name)
{
- return json_object_get_number (object, name);
+ return json_object_dotget_number (object, name);
}
inline long jog_l (const JSON_Object *object, const char *name)
{
- return std::lround(json_object_get_number (object, name));
+ return std::lround(json_object_dotget_number (object, name));
}
// access to JSON number with 'null' returned as 'NAN'
@@ -415,9 +415,9 @@ double jog_sn_nan (const JSON_Object *object, const char *name);
// access to JSON boolean field (replaces -1 with false)
inline bool jog_b (const JSON_Object *object, const char *name)
{
- // json_object_get_boolean returns -1 if field doesn't exit, so we
+ // json_object_dotget_boolean returns -1 if field doesn't exit, so we
// 'convert' -1 and 0 both to false with the following comparison:
- return json_object_get_boolean (object, name) > 0;
+ return json_object_dotget_boolean (object, name) > 0;
}
// interprets a string-encapsulated number "0" as false, all else as true
@@ -444,7 +444,7 @@ double jag_n_nan (const JSON_Array *array, size_t idx);
// access to JSON array boolean field (replaces -1 with false)
inline bool jag_b (const JSON_Array *array, size_t idx)
{
- // json_object_get_boolean returns -1 if field doesn't exit, so we
+ // json_array_get_boolean returns -1 if field doesn't exit, so we
// 'convert' -1 and 0 both to false with the following comparison:
return json_array_get_boolean (array, idx) > 0;
}
diff --git a/Include/LTRealTraffic.h b/Include/LTRealTraffic.h
index ff18e4c..f23688d 100644
--- a/Include/LTRealTraffic.h
+++ b/Include/LTRealTraffic.h
@@ -41,9 +41,12 @@
#define RT_AUTH_URL "https://rtw.flyrealtraffic.com/v3/auth"
#define RT_AUTH_POST "license=%s&software=%s"
+#define RT_WEATHER_URL "https://rtw.flyrealtraffic.com/v3/weather"
+#define RT_WEATHER_POST "GUID=%s&lat=%.2f&lon=%.2f&alt=%ld&airports=UNKN&querytype=locwx&toffset=%ld"
#define RT_TRAFFIC_URL "https://rtw.flyrealtraffic.com/v3/traffic"
#define RT_TRAFFIC_POST "GUID=%s&top=%.2f&bottom=%.2f&left=%.2f&right=%.2f&querytype=locationtraffic&toffset=%ld"
+
#define RT_LOCALHOST "0.0.0.0"
constexpr size_t RT_NET_BUF_SIZE = 8192;
@@ -69,6 +72,13 @@ constexpr double RT_VSI_AIRBORNE = 80.0; ///< if VSI is more than this then w
#define RT_TRAFFIC_XATTPSX "XATTPSX"
#define RT_TRAFFIC_XGPSPSX "XGPSPSX"
+// Constant for direct connection
+constexpr long RT_DRCT_DEFAULT_WAIT = 8000L; ///< [ms] Default wait time between traffic requests
+constexpr std::chrono::seconds RT_DRCT_ERR_WAIT = std::chrono::seconds(5); ///< standard wait between errors
+constexpr std::chrono::minutes RT_DRCT_WX_WAIT = std::chrono::minutes(10); ///< How often to update weather?
+constexpr long RT_DRCT_WX_DIST = 10L * M_per_NM; ///< Distance for which weather is considered valid, greater than that and we re-request
+constexpr int RT_DRCT_MAX_WX_ERR = 10; ///< Max number of consecutive errors during weather requests we wait for...before not asking for weather any longer
+
/// Fields in a response of a direct connection's request
enum RT_DIRECT_FIELDS_TY {
RT_DRCT_HexId = 0, ///< hexid (7c68a1)
@@ -228,17 +238,39 @@ class RealTrafficConnection : public LTFlightDataChannel
// RealTraffic connection status
volatile rtStatusTy status = RT_STATUS_NONE;
- /// UID returned by RealTraffic upon authentication, valid for 10s only
- std::string sGUID;
/// RealTraffic License type
enum RTLicTypeTy : int {
RT_LIC_UNKNOWN = 0,
RT_LIC_STANDARD = 1, ///< Standard RealTraffic license
RT_LIC_PROFESSIONAL = 2, ///< Professional RT license, allowing for historical data
} eLicType = RT_LIC_UNKNOWN;
+ /// QNH to use for altitude correction (will be historic QNH in case of historic data)
+ /// Data for the current request
+ struct CurrTy {
+ /// Which kind of call do we need next?
+ enum RTRequestTypeTy : int {
+ RT_REQU_AUTH = 1, ///< Perform Authentication request
+ RT_REQU_WEATHER, ///< Perform Weather request
+ RT_REQU_TRAFFIC, ///< Perform Traffic request
+ } eRequType = RT_REQU_AUTH; ///< Which type of request is being performed now?
+ std::string sGUID; ///< UID returned by RealTraffic upon authentication, valid for 10s only
+ positionTy pos; ///< viewer position for which we receive Realtraffic data
+ long tOff = 0; ///< time offset for which we request data
+ } curr; ///< Data for the current request
/// How long to wait before making the next request?
std::chrono::milliseconds rrlWait = std::chrono::milliseconds(0);
+ /// Weather data
+ struct WxTy {
+ double QNH = NAN; ///< baro pressure
+ std::chrono::steady_clock::time_point time; ///< time when RealTraffic weather was received
+ positionTy pos; ///< viewer position for which we received Realtraffic weather
+ long tOff = 0; ///< time offset for which we requested weather
+ int nErr = 0; ///< How many errors did we have during weather requests?
+
+ WxTy& operator = (const CurrTy& o); ///< fill from `current` data
+ } rtWx; ///< Data with which latest weather was requested
+
// TCP connection to send current position
std::thread thrTcpServer; ///< thread of the TCP listening thread (short-lived)
TCPConnection tcpPosSender; ///< TCP connection to communicate with RealTraffic
@@ -282,6 +314,7 @@ class RealTrafficConnection : public LTFlightDataChannel
// MARK: Direct Connection by Request/Reply
protected:
void MainDirect (); ///< thread main function for the direct connection
+ void SetRequType (const positionTy& pos); ///< Which request do we need now?
public:
std::string GetURL (const positionTy&) override; ///< in direct mode return URL and set
void ComputeBody (const positionTy& pos) override; ///< in direct mode puts together the POST request with the position data etc.
diff --git a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index 27c8aae..5c01291 100644
--- a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -7,49 +7,33 @@
+
+
+
+
-
-
-
-
-
-
diff --git a/Src/LTChannel.cpp b/Src/LTChannel.cpp
index 249e9ef..d5f404b 100644
--- a/Src/LTChannel.cpp
+++ b/Src/LTChannel.cpp
@@ -52,7 +52,7 @@ listPtrLTChannelTy listFDC;
// tests for 'null', return ptr to value if wanted
bool jog_is_null (const JSON_Object *object, const char *name, JSON_Value** ppValue)
{
- JSON_Value* pJSONVal = json_object_get_value(object, name);
+ JSON_Value* pJSONVal = json_object_dotget_value(object, name);
if (ppValue)
*ppValue = pJSONVal;
return !pJSONVal || json_type(pJSONVal) == JSONNull;
@@ -71,7 +71,7 @@ bool jag_is_null (const JSON_Array *array,
// access to JSON string fields, with NULL or text "null" replaced by ""
const char* jog_s (const JSON_Object *object, const char *name)
{
- const char* s = json_object_get_string ( object, name );
+ const char* s = json_object_dotget_string ( object, name );
return
!s ? "" : // found nothing
std::strcmp(s, "null") ? s : ""; // test if text is "null"
@@ -80,7 +80,7 @@ const char* jog_s (const JSON_Object *object, const char *name)
// access to JSON number fields, encapsulated as string, with NULL replaced by 0
double jog_sn (const JSON_Object *object, const char *name)
{
- const char* s = json_object_get_string ( object, name );
+ const char* s = json_object_dotget_string ( object, name );
return s ? strtod(s,NULL) : 0.0;
}
@@ -96,7 +96,7 @@ double jog_n_nan (const JSON_Object *object, const char *name)
double jog_sn_nan (const JSON_Object *object, const char *name)
{
- const char* s = json_object_get_string ( object, name );
+ const char* s = json_object_dotget_string ( object, name );
return s ? strtod(s,NULL) : NAN;
}
@@ -669,6 +669,12 @@ void LTOnlineChannel::DebugLogRaw(const char *data, long httpCode, bool bHeader)
const double now = GetSysTime();
outRaw.precision(2);
if (bHeader) {
+
+ // Empty line before a (new) SENDING request
+ if (httpCode == HTTP_FLAG_SENDING)
+ outRaw << "\n";
+
+ // Actual header
outRaw
<< std::fixed << now << ' ' << ts2string(now,2)
<< " - SimTime "
diff --git a/Src/LTRealTraffic.cpp b/Src/LTRealTraffic.cpp
index b87467e..025280c 100644
--- a/Src/LTRealTraffic.cpp
+++ b/Src/LTRealTraffic.cpp
@@ -34,6 +34,15 @@
// MARK: RealTraffic Connection
//
+// fill from `current` data
+RealTrafficConnection::WxTy& RealTrafficConnection::WxTy::operator=(const CurrTy& o)
+{
+ pos = o.pos;
+ tOff = o.tOff;
+ time = std::chrono::steady_clock::now();
+ return *this;
+}
+
// Constructor doesn't do much
RealTrafficConnection::RealTrafficConnection () :
LTFlightDataChannel(DR_CHANNEL_REAL_TRAFFIC_ONLINE, REALTRAFFIC_NAME)
@@ -162,17 +171,22 @@ void RealTrafficConnection::MainDirect ()
eConnType = RT_CONN_REQU_REPL;
// Clear the list of historic time stamp differences
dequeTS.clear();
+ // Some more data resets to make sure we start over with the series of requests
+ curr.sGUID.clear();
+ rtWx.QNH = NAN;
+ rtWx.nErr = 0;
while ( shallRun() ) {
// LiveTraffic Top Level Exception Handling
try {
// where are we right now?
const positionTy pos (dataRefs.GetViewPos());
- rrlWait = std::chrono::seconds(5); // Standard is: retry in 5s
+ rrlWait = RT_DRCT_ERR_WAIT; // Standard is: retry in 5s
// If the camera position is valid we can request data around it
if (pos.isNormal()) {
- // fetch data and process it
+ // determine the type of request, fetch data and process it
+ SetRequType(pos);
if (FetchAllData(pos) && ProcessFetchedData())
// reduce error count if processed successfully
// as a chance to appear OK in the long run
@@ -202,6 +216,40 @@ void RealTrafficConnection::MainDirect ()
}
}
+// Which request do we need now?
+void RealTrafficConnection::SetRequType (const positionTy& _pos)
+{
+ // Position as passed in
+ curr.pos = _pos;
+
+ // Time offset: in minutes compared to now
+ curr.tOff = 0L;
+ if (dataRefs.GetRTSTC() != STC_NO_CTRL && // Configured to send any offset
+ !dataRefs.IsUsingSystemTime()) // and not actually using system time
+ {
+ // Simulated 'now' in seconds since the epoch
+ const time_t simNow = time_t(dataRefs.GetXPSimTime_ms() / 1000LL);
+ const time_t now = time(nullptr);
+ if (simNow < now) {
+ // offset between older 'simNow' and current 'now' in minutes
+ curr.tOff = (now - simNow) / 60L;
+ }
+ }
+
+ if (curr.sGUID.empty()) // have no GUID? Need authentication
+ curr.eRequType = CurrTy::RT_REQU_AUTH;
+ else if ((rtWx.nErr < RT_DRCT_MAX_WX_ERR) && // not seen too many errors yet
+ (std::isnan(rtWx.QNH) || // no Weather, or wrong time offset, or outdated, or moved too far away?
+ std::labs(curr.tOff - rtWx.tOff) > 120 ||
+ std::chrono::steady_clock::now() - rtWx.time > RT_DRCT_WX_WAIT ||
+ rtWx.pos.distRoughSqr(curr.pos) > (RT_DRCT_WX_DIST*RT_DRCT_WX_DIST)))
+ curr.eRequType = CurrTy::RT_REQU_WEATHER;
+ else
+ // in all other cases we ask for traffic data
+ curr.eRequType = CurrTy::RT_REQU_TRAFFIC;
+}
+
+
// in direct mode return URL and set
std::string RealTrafficConnection::GetURL (const positionTy&)
{
@@ -213,45 +261,48 @@ std::string RealTrafficConnection::GetURL (const positionTy&)
ret, curl_errtxt);
}
- // Need to login or can we request actual data?
- if (sGUID.empty())
- return RT_AUTH_URL;
- else
- return RT_TRAFFIC_URL;
+ // What kind of request do we need next?
+ switch (curr.eRequType) {
+ case CurrTy::RT_REQU_AUTH:
+ return RT_AUTH_URL;
+ case CurrTy::RT_REQU_WEATHER:
+ return RT_WEATHER_URL;
+ case CurrTy::RT_REQU_TRAFFIC:
+ return RT_TRAFFIC_URL;
+ }
+ return RT_TRAFFIC_URL;
}
// in direct mode puts together the POST request with the position data etc.
-void RealTrafficConnection::ComputeBody (const positionTy& pos)
+void RealTrafficConnection::ComputeBody (const positionTy&)
{
- char s[256];
+ char s[256] = "";
- // Need to login or can we request actual data?
- if (sGUID.empty()) {
- snprintf(s,sizeof(s), RT_AUTH_POST,
- dataRefs.GetRTLicense().c_str(),
- HTTP_USER_AGENT);
- } else {
- // we add 10% to the bounding box to have some data ready once the plane is close enough for display
- const boundingBoxTy box (pos, double(dataRefs.GetFdStdDistance_m()) * 1.10);
- // Time offset: in minutes compared to now
- long tOff = 0L;
- if (dataRefs.GetRTSTC() != STC_NO_CTRL && // Configured to send any offset
- !dataRefs.IsUsingSystemTime()) // and not actually using system time
+ // What kind of request will we need?
+ switch (curr.eRequType) {
+ case CurrTy::RT_REQU_AUTH:
+ snprintf(s,sizeof(s), RT_AUTH_POST,
+ dataRefs.GetRTLicense().c_str(),
+ HTTP_USER_AGENT);
+ break;
+ case CurrTy::RT_REQU_WEATHER:
+ snprintf(s, sizeof(s), RT_WEATHER_POST,
+ curr.sGUID.c_str(),
+ curr.pos.lat(), curr.pos.lon(), 0L,
+ curr.tOff);
+ break;
+ case CurrTy::RT_REQU_TRAFFIC:
{
- // Simulated 'now' in seconds since the epoch
- const time_t simNow = time_t(dataRefs.GetXPSimTime_ms() / 1000LL);
- const time_t now = time(nullptr);
- if (simNow < now) {
- // offset between older 'simNow' and current 'now' in minutes
- tOff = (now - simNow) / 60L;
- }
+ // we add 10% to the bounding box to have some data ready once the plane is close enough for display
+ const boundingBoxTy box (curr.pos, double(dataRefs.GetFdStdDistance_m()) * 1.10);
+
+ snprintf(s,sizeof(s), RT_TRAFFIC_POST,
+ curr.sGUID.c_str(),
+ box.nw.lat(), box.se.lat(),
+ box.nw.lon(), box.se.lon(),
+ curr.tOff);
+ break;
}
-
- snprintf(s,sizeof(s), RT_TRAFFIC_POST,
- sGUID.c_str(),
- box.nw.lat(), box.se.lat(),
- box.nw.lon(), box.se.lon(),
- tOff);
}
requBody = s;
@@ -281,14 +332,22 @@ bool RealTrafficConnection::ProcessFetchedData ()
std::string rMsg = jog_s(pObj, "message");
// --- Error processing ---
- rrlWait = std::chrono::seconds(5); // Standard is: retry in 5s
+ rrlWait = RT_DRCT_ERR_WAIT; // Standard is: retry in 5s
+
+ // For failed weather requests keep a separate counter
+ if (curr.eRequType == CurrTy::RT_REQU_WEATHER && rStatus != HTTP_OK) {
+ if (++rtWx.nErr >= RT_DRCT_MAX_WX_ERR) { // Too many WX errors?
+ SHOW_MSG(logERR, "Too many errors trying to fetch RealTraffic weather, will continue without; planes may appear at slightly wrong altitude.");
+ }
+ }
+
switch (rStatus) {
case HTTP_OK:
break; // All good, just continue
case HTTP_PAYMENT_REQU:
case HTTP_NOT_FOUND:
- if (sGUID.empty()) {
+ if (curr.eRequType == CurrTy::RT_REQU_AUTH) {
SHOW_MSG(logERR, "RealTraffic license invalid: %s", rMsg.c_str());
SetValid(false,true); // set invalid, stop trying
return false;
@@ -298,16 +357,16 @@ bool RealTrafficConnection::ProcessFetchedData ()
return false;
}
- case HTTP_METH_NOT_ALLWD: // Send for "too many sessions"
+ case HTTP_METH_NOT_ALLWD: // Send for "too many sessions" / "request rate violation"
LOG_MSG(logERR, "RealTraffic: %s", rMsg.c_str());
IncErrCnt();
rrlWait = std::chrono::seconds(10); // documentation says "wait 10 seconds"
- sGUID.clear(); // force re-login
+ curr.sGUID.clear(); // force re-login
return false;
case HTTP_UNAUTHORIZED: // means our GUID expired
LOG_MSG(logDEBUG, "Session expired");
- sGUID.clear(); // re-login immediately
+ curr.sGUID.clear(); // re-login immediately
rrlWait = std::chrono::milliseconds(0);
return false;
@@ -324,22 +383,53 @@ bool RealTrafficConnection::ProcessFetchedData ()
}
// All good, process the request
+
+ // Wait till next request?
long l = jog_l(pObj, "rrl"); // Wait time till next request
- if ((!sGUID.empty() && l < 8000L) || !l)
- l = 8000L; // By default we wait 8s, or more if RealTraffic instructs us so
+ switch (curr.eRequType) {
+ case CurrTy::RT_REQU_AUTH: // after an AUTH request we take the rrl unchanged, ie. as quickly as possible
+ break;
+ case CurrTy::RT_REQU_WEATHER: // unfortunately, no `rrl` in weather requests...
+ l = 300; // we just continue 300ms later
+ break;
+ case CurrTy::RT_REQU_TRAFFIC: // By default we wait at least 8s, or more if RealTraffic instructs us so
+ if (l < RT_DRCT_DEFAULT_WAIT)
+ l = RT_DRCT_DEFAULT_WAIT;
+ break;
+ }
rrlWait = std::chrono::milliseconds(l);
// --- Authorization ---
- if (sGUID.empty()) {
+ if (curr.eRequType == CurrTy::RT_REQU_AUTH) {
eLicType = RTLicTypeTy(jog_l(pObj, "type"));
- sGUID = jog_s(pObj, "GUID");
- if (sGUID.empty()) {
- LOG_MSG(logERR, "Did not actually receive a GUID: %s", netData);
+ curr.sGUID = jog_s(pObj, "GUID");
+ if (curr.sGUID.empty()) {
+ LOG_MSG(logERR, "Did not actually receive a GUID:\n%s", netData);
IncErrCnt();
return false;
}
LOG_MSG(logDEBUG, "Authenticated: type=%d, GUID=%s",
- eLicType, sGUID.c_str());
+ eLicType, curr.sGUID.c_str());
+ return true;
+ }
+
+ // --- Weather ---
+ if (curr.eRequType == CurrTy::RT_REQU_WEATHER) {
+ // We are interested in just a single value: local Pressure
+ const double wxSLP = jog_n_nan(pObj, "data.locWX.SLP");
+ if (std::isnan(wxSLP) || wxSLP < 800.0) {
+ LOG_MSG(logERR, "RealTraffic returned no or invalid local pressure %.1f:\n%s",
+ wxSLP, netData);
+ rrlWait = std::chrono::seconds(2); // isn't exactly clear how quickly we can repeat weather requests!
+ if (++rtWx.nErr >= RT_DRCT_MAX_WX_ERR) { // Too many WX errors?
+ SHOW_MSG(logERR, "Too many errors trying to fetch RealTraffic weather, will continue without; planes may appear at slightly wrong altitude.");
+ }
+ return false;
+ }
+ LOG_MSG(logDEBUG, "Received RealTraffic locWX.SLP = %.1f", wxSLP);
+ rtWx.QNH = wxSLP; // Save new QNH
+ rtWx = curr; // and how it was requested
+ rtWx.nErr = 0; // reset the error counter as we now received good data
return true;
}
@@ -371,6 +461,19 @@ bool RealTrafficConnection::ProcessFetchedData ()
}
}
}
+
+ // If RealTraffic returns `full_count = 0` then something's wrong...
+ // like data requested too far in the past
+ l = jog_l(pObj, "full_count");
+ if (l == 0) {
+ static std::chrono::steady_clock::time_point lastWarn;
+ const auto now = std::chrono::steady_clock::now();
+ if (now - lastWarn > std::chrono::minutes(5)) {
+ SHOW_MSG(logWARN, "RealTraffic has no traffic at all! %s",
+ curr.tOff > 0 ? "Maybe requested historic data too far in the past?" : "(full_count=0)");
+ lastWarn = now;
+ }
+ }
// any a/c filter defined for debugging purposes?
const std::string acFilter ( dataRefs.GetDebugAcFilter() );
@@ -437,9 +540,12 @@ bool RealTrafficConnection::ProcessFetchedData ()
pos.f.onGrnd = GND_ON;
else {
pos.f.onGrnd = GND_OFF;
- double d = jag_n(pJAc, RT_DRCT_BaroAlt); // prefer (already corrected) baro altitude
- if (d > 0.0)
+ double d = jag_n(pJAc, RT_DRCT_BaroAlt); // prefer baro altitude
+ if (d > 0.0) {
+ if (!std::isnan(rtWx.QNH))
+ d = BaroAltToGeoAlt_ft(d, rtWx.QNH);
pos.SetAltFt(d);
+ }
else // else try geo altitude
pos.SetAltFt(jag_n(pJAc, RT_DRCT_GeoAlt));
}
diff --git a/Src/SettingsUI.cpp b/Src/SettingsUI.cpp
index e8cf2d2..ee22a2b 100644
--- a/Src/SettingsUI.cpp
+++ b/Src/SettingsUI.cpp
@@ -562,22 +562,6 @@ void LTSettingsUI::buildInterface()
ImGui::TableNextCell();
}
- // RealTraffic traffic port number
- if (ImGui::FilteredLabel("Traffic Port", sFilter)) {
- ImGui::SetNextItemWidth(fSmallWidth);
- ImGui::InputText("", &sRTPort, ImGuiInputTextFlags_CharsDecimal);
- // if changed then set (then re-read) the value
- if (ImGui::IsItemDeactivatedAfterEdit()) {
- dataRefs.SetRTTrafficPort(std::stoi(sRTPort));
- sRTPort = std::to_string(DataRefs::GetCfgInt(DR_CFG_RT_TRAFFIC_PORT));
- }
- else if (ImGui::IsItemActive()) {
- ImGui::SameLine();
- ImGui::TextUnformatted("[Enter] to save. Default is 49005. Effective after restart.");
- }
- ImGui::TableNextCell();
- }
-
if (ImGui::FilteredLabel("Simulator Time Control", sFilter)) {
const float cbWidth = ImGui::CalcTextSize("Send Sim Time plus Buffering Period (default)_____").x;
ImGui::SetNextItemWidth(cbWidth);