diff --git a/CMakeLists.txt b/CMakeLists.txt
index cdb9c09f..59616c09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -239,7 +239,7 @@ endif ()
if (WIN32)
# Link platform-specific libraries especially for networking
- target_link_libraries(LiveTraffic ws2_32.lib wldap32.lib advapi32.lib crypt32.lib)
+ target_link_libraries(LiveTraffic ws2_32.lib iphlpapi wldap32.lib advapi32.lib crypt32.lib)
elseif (APPLE)
# X-Plane supports OS X 10.10+, so this should ensure FlyWithLua can run on
# all supported versions.
diff --git a/Data/RealTraffic/SendTraffic.py b/Data/RealTraffic/SendTraffic.py
index 99d7beea..e4abeef4 100755
--- a/Data/RealTraffic/SendTraffic.py
+++ b/Data/RealTraffic/SendTraffic.py
@@ -62,6 +62,9 @@ def compWaitTS(ts_s: str) -> str:
print (f"Waiting for {ts-now} seconds...", end='\r')
time.sleep (ts-now)
+ # Adjust returned timestamp value for historic timestamp
+ ts -= args.historic
+
return str(ts)
""" === Handle traffic data ==="""
@@ -97,11 +100,12 @@ def sendWeatherData(ln: str) -> int:
""" === MAIN === """
# --- Handling command line argumens ---
-parser = argparse.ArgumentParser(description='SendTraffic 0.1.0: Sends air traffic tracking data from a file out on a UDP port for LiveTraffic to receive it on the RealTraffic channel',fromfile_prefix_chars='@')
+parser = argparse.ArgumentParser(description='SendTraffic 0.2.0: Sends air traffic tracking data from a file out on a UDP port for LiveTraffic to receive it on the RealTraffic channel',fromfile_prefix_chars='@')
parser.add_argument('inFile', help='Tracking data file: records in CSV format holding air traffic data in RealTraffic\'s AITraffic format and weather data.\n by default', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
parser.add_argument('-a', '--aircraft', metavar='HEX_LIST', help='List of aircraft to read, others skipped. Add one or several transponder hex id codes, separate by comma.')
parser.add_argument('-d', '--aircraftDecimal', metavar='NUM_LIST', help='Same as -a, but specify decimal values (as used in the CSV file).')
parser.add_argument('-b', '--bufPeriod', metavar='NUM', help='Buffering period: Number of seconds the first record is pushed into the past so that LiveTraffic\'s buffer fills more quickly. Recommended to be slightly less than LiveTraffic\'s buffering period.', type=int, default=0)
+parser.add_argument('--historic', metavar='NUM', help='Send historic data, ie. reduce included timestamp by this many seconds', type=int, default=0)
parser.add_argument('-l', '--loop', help='Endless loop: restart from the beginning when reaching end of file. Will only work if data contains loop with last position(s) being roughly equal to first position(s).', action='store_true')
parser.add_argument('--host', metavar='NAME_OR_IP', help='UDP target host or ip to send the data to, defaults to \'localhost\'', default='localhost')
parser.add_argument('--port', metavar='NUM', help='UDP port to send traffic data to, defaults to 49003', type=int, default=49003)
diff --git a/Include/Constants.h b/Include/Constants.h
index e54e367e..81e8e2db 100644
--- a/Include/Constants.h
+++ b/Include/Constants.h
@@ -29,7 +29,7 @@
//
// MARK: Version Information (CHANGE VERSION HERE)
//
-constexpr float VERSION_NR = 2.20f;
+constexpr float VERSION_NR = 2.30f;
constexpr bool VERSION_BETA = false;
extern float verXPlaneOrg; // version on X-Plane.org
extern int verDateXPlaneOrg; // and its date
@@ -151,6 +151,7 @@ constexpr int LT_NEW_VER_CHECK_TIME = 48; // [h] between two checks of a new
#define LT_FM_VERSION "2.2" // expected version of flight model file format
#define PLUGIN_SIGNATURE "TwinFan.plugin.LiveTraffic"
#define PLUGIN_DESCRIPTION "Create Multiplayer Aircraft based on live traffic."
+constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
#define LT_DOWNLOAD_URL "https://forums.x-plane.org/index.php?/files/file/49749-livetraffic/"
#define LT_DOWNLOAD_CH "X-Plane.org"
#define MSG_DISABLED "Disabled"
@@ -175,6 +176,7 @@ constexpr int LT_NEW_VER_CHECK_TIME = 48; // [h] between two checks of a new
#define INFO_AC_REMOVED "Removed aircraft %s"
#define INFO_AC_ALL_REMOVED "Removed all aircraft"
#define INFO_REQU_AI_RELEASE "%s requested us to release TCAS / AI control. Switch off '" MENU_HAVE_TCAS "' if you want so."
+#define INFO_REQU_AI_REMOTE "XPMP2 Remote Client requested us to release TCAS / AI control, so we do."
#define INFO_GOT_AI_CONTROL LIVE_TRAFFIC " has TCAS / AI control now"
#define INFO_RETRY_GET_AI "Another plugin released AI control, will try again to get control..."
#define INFO_AC_HIDDEN "A/c %s hidden"
@@ -354,6 +356,10 @@ constexpr int SERR_LEN = 100; // size of buffer for IO error t
#define ERR_FM_UNKNOWN_PARENT "Parent section missing in '%s', line %d: %s"
#define ERR_FM_REGEX "%s in '%s', line %d: %s"
#define ERR_FM_NOT_FOUND "Found no flight model for ICAO %s/match-string %s: will use default"
+#define ERR_TCP_LISTENACCEPT "%s: Error opening the TCP port on %s:%s: %s"
+#define ERR_SOCK_SEND_FAILED "%s: Could not send position: send operation failed"
+#define ERR_UDP_SOCKET_CREAT "%s: Error creating UDP socket for %s:%d: %s"
+#define ERR_UDP_RCVR_RCVR "%s: Error receiving UDP: %s"
constexpr int ERR_CFG_FILE_MAXWARN = 10; // maximum number of warnings while reading config file, then: dead
//MARK: Debug Texts
diff --git a/Include/DataRefs.h b/Include/DataRefs.h
index 42fb4f19..60b1d79d 100644
--- a/Include/DataRefs.h
+++ b/Include/DataRefs.h
@@ -359,6 +359,7 @@ enum dataRefsLT {
DR_CFG_HIDE_NEARBY_GND,
DR_CFG_HIDE_NEARBY_AIR,
DR_CFG_COPY_OBJ_FILES,
+ DR_CFG_REMOTE_SUPPORT,
DR_CFG_LAST_CHECK_NEW_VER,
// debug options
@@ -507,7 +508,7 @@ class DataRefs
// this is a bit ugly but avoids a wrapper union with an int
inline unsigned GetUInt() const { return *reinterpret_cast(this); }
- inline void SetUInt(int i) { *reinterpret_cast(this) = i; }
+ inline void SetUInt(unsigned i) { *reinterpret_cast(this) = i; }
inline bool operator != (const LabelCfgTy& o) const
{ return GetUInt() != o.GetUInt(); }
};
@@ -612,13 +613,14 @@ class DataRefs
int fdRefreshIntvl = DEF_FD_REFRESH_INTVL; ///< how often to fetch new flight data
int fdBufPeriod = DEF_FD_BUF_PERIOD; ///< seconds to buffer before simulating aircraft
int acOutdatedIntvl = DEF_AC_OUTDATED_INTVL; ///< a/c considered outdated if latest flight data more older than this compare to 'now'
- int netwTimeout = 90; // [s] of network request timeout
+ int netwTimeout = DEF_NETW_TIMEOUT; ///< [s] of network request timeout
int bLndLightsTaxi = false; // keep landing lights on while taxiing? (to be able to see the a/c as there is no taxi light functionality)
int hideBelowAGL = 0; // if positive: a/c visible only above this height AGL
int hideTaxiing = 0; // hide a/c while taxiing?
int hideNearbyGnd = 0; // [m] hide a/c if closer than this to user's aircraft on the ground
int hideNearbyAir = 0; // [m] hide a/c if closer than this to user's aircraft in the air
int cpyObjFiles = 1; ///< copy `.obj` files for replacing dataRefs and textures
+ int remoteSupport = 0; ///< support XPMP2 Remote Client? (3-way: -1 off, 0 auto, 1 on)
// channel config options
int ognUseRequRepl = 0; ///< OGN: Use Request/Reply instead of TCP receiver
@@ -807,6 +809,7 @@ class DataRefs
{ return hideBelowAGL > 0 || hideTaxiing != 0 ||
hideNearbyGnd > 0 || hideNearbyAir > 0; }
bool ShallCpyObjFiles () const { return cpyObjFiles != 0; }
+ int GetRemoteSupport () const { return remoteSupport; }
bool NeedNewVerCheck () const;
void SetLastCheckedNewVerNow ();
diff --git a/Include/LTADSBEx.h b/Include/LTADSBEx.h
index 27b886d2..1f68aa01 100644
--- a/Include/LTADSBEx.h
+++ b/Include/LTADSBEx.h
@@ -159,8 +159,8 @@ class ADSBExchangeConnection : public LTOnlineChannel, LTFlightDataChannel
#define ADSBEX_HIST_NAME "ADS-B Exchange Historic"
constexpr int ADSBEX_HIST_MIN_CHARS = 20; // minimum nr chars per line to be a 'reasonable' line
constexpr int ADSBEX_HIST_MAX_ERR_CNT = 5; // after that many errorneous line we stop reading
-#define ADSBEX_HIST_PATH "Custom Data/ADSB" // TODO: Move to options: relative to XP main
-#define ADSBEX_HIST_PATH_2 "Custom Data/ADSB2" // TODO: Move to options: fallback, if first one doesn't work
+#define ADSBEX_HIST_PATH "Custom Data/ADSB" // TODO : Move to options: relative to XP main
+#define ADSBEX_HIST_PATH_2 "Custom Data/ADSB2" // TODO : Move to options: fallback, if first one doesn't work
#define ADSBEX_HIST_DATE_PATH "%c%04d-%02d-%02d"
#define ADSBEX_HIST_FILE_NAME "%c%04d-%02d-%02d-%02d%02dZ.json"
#define ADSBEX_HIST_PATH_EMPTY "Historic Data Path doesn't exist or folder empty at %s"
diff --git a/Include/LTFlightData.h b/Include/LTFlightData.h
index c120697c..a893e198 100644
--- a/Include/LTFlightData.h
+++ b/Include/LTFlightData.h
@@ -283,8 +283,6 @@ class LTFlightData
bool validForAcCreate( double simTime = NAN ) const;
// a/c available for this FD?
inline bool hasAc() const { return pAc != nullptr; }
- // is the data outdated (considered too old to be useful)?
- bool outdated ( double simTime = NAN ) const;
// produce a/c label
void UpdateStaticLabel();
@@ -370,7 +368,7 @@ class LTFlightData
// access/create/destroy aircraft
bool AircraftMaintenance ( double simTime ); // returns: delete me?
bool DetermineAcModel (); ///< try interpreting model text or check for ground vehicle, last resort: default a/c type
- bool AcSlotAvailable (double simTime); ///< checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
+ bool AcSlotAvailable (); ///< checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
bool CreateAircraft ( double simTime );
void DestroyAircraft ();
LTAircraft* GetAircraft () const { return pAc; }
diff --git a/Include/LTRealTraffic.h b/Include/LTRealTraffic.h
index cb207ebc..e1e35183 100644
--- a/Include/LTRealTraffic.h
+++ b/Include/LTRealTraffic.h
@@ -47,10 +47,15 @@ constexpr double RT_VSI_AIRBORNE = 80.0; ///< if VSI is more than this then w
#define MSG_RT_STATUS "RealTraffic network status changed to: %s"
#define MSG_RT_LAST_RCVD " | last: %lds ago"
+#define MSG_RT_ADJUST " | historic traffic from %s ago"
+#define INFO_RT_REAL_TIME "RealTraffic: Tracking data is real-time again."
+#define INFO_RT_ADJUST_TS "RealTraffic: Detected tracking data from %s in the past, will adjust them to display now."
#define ERR_RT_CANTLISTEN "RealTraffic: Cannot listen to network, can't tell RealTraffic our position"
#define ERR_RT_WEATHER_QNH "RealTraffic reports unreasonable QNH %ld - ignored"
#define ERR_RT_DISCARDED_MSG "RealTraffic: Discarded invalid message: %s"
+#define ERR_SOCK_NOTCONNECTED "%s: Cannot send position: not connected"
+#define ERR_SOCK_INV_POS "%s: Cannot send position: position not fully valid"
// Traffic data format and fields
#define RT_TRAFFIC_AITFC "AITFC"
@@ -142,6 +147,10 @@ class RealTrafficConnection : public LTOnlineChannel, LTFlightDataChannel
std::map mapDatagrams;
// weather, esp. current barometric pressure to correct altitude values
std::string lastWeather; // for duplicate detection
+ /// rolling list of timestamp (diff to now) for detecting historic sending
+ std::deque dequeTS;
+ /// current timestamp adjustment
+ double tsAdjust = 0.0;
public:
RealTrafficConnection (mapLTFlightDataTy& _fdMap);
@@ -200,10 +209,14 @@ class RealTrafficConnection : public LTOnlineChannel, LTFlightDataChannel
bool ProcessRecvedTrafficData (const char* traffic);
bool ProcessRecvedWeatherData (const char* weather);
+ /// Determine timestamp adjustment necessairy in case of historic data
+ void AdjustTimestamp (double& ts);
+ /// Return a string describing the current timestamp adjustment
+ std::string GetAdjustTSText () const;
+
// UDP datagram duplicate check
// Is it a duplicate? (if not datagram is copied into a map)
bool IsDatagramDuplicate (unsigned long numId,
- double posTime,
const char* datagram);
// remove outdated entries from mapDatagrams
void CleanupMapDatagrams();
diff --git a/Include/LiveTraffic.h b/Include/LiveTraffic.h
index 87751ee7..3dd203f3 100644
--- a/Include/LiveTraffic.h
+++ b/Include/LiveTraffic.h
@@ -317,15 +317,7 @@ bool begins_with(const TContainer& input, const TContainer& match)
&& std::equal(match.cbegin(), match.cend(), input.cbegin());
}
-/// Clamps `v` between `lo` and `hi`: `lo` if `v` < `lo`, `hi` if `hi` < `v`, otherwise `v`
-/// @see C++17, https://en.cppreference.com/w/cpp/algorithm/clamp
-template
-constexpr const T& clamp( const T& v, const T& lo, const T& hi )
-{
- assert( !(hi < lo) );
- return (v < lo) ? lo : (hi < v) ? hi : v;
-}
-
+/// Is value `lo <= v <= hi`?
template
constexpr bool between( const T& v, const T& lo, const T& hi )
{
@@ -372,15 +364,11 @@ inline struct tm *localtime_s(struct tm * result, const time_t * time)
#define STRCPY_S(dest,src) strncpy_s(dest,sizeof(dest),src,sizeof(dest)-1)
#define STRCPY_ATMOST(dest,src) strncpy_s(dest,sizeof(dest),strAtMost(src,sizeof(dest)-1).c_str(),sizeof(dest)-1)
-#if APL == 1
+#if APL == 1 || LIN == 1
// XCode/Linux don't provide the _s functions, not even with __STDC_WANT_LIB_EXT1__ 1
inline int strerror_s( char *buf, size_t bufsz, int errnum )
{ return strerror_r(errnum, buf, bufsz); }
#endif
-#if LIN == 1
-inline int strerror_s( char *buf, size_t bufsz, int errnum )
-{ strncpy_s(buf,bufsz,strerror(errnum),bufsz-1); return 0; }
-#endif
// MARK: Thread names
#ifdef DEBUG
diff --git a/Include/Network.h b/Include/Network.h
index 0bcca051..1572ac9e 100644
--- a/Include/Network.h
+++ b/Include/Network.h
@@ -32,36 +32,26 @@
#else
#include
#include
-typedef int SOCKET; // Windows defines SOCKET, so we define it for non-Windows manually
+typedef int SOCKET; ///< Windows defines SOCKET, so we define it for non-Windows manually
constexpr SOCKET INVALID_SOCKET = -1;
#endif
#include
-// error messages used in derived classes
-#define ERR_TCP_LISTENACCEPT "%s: Error opening the TCP port on %s:%s: %s"
-#define ERR_SOCK_NOTCONNECTED "%s: Cannot send position: not connected"
-#define ERR_SOCK_INV_POS "%s: Cannot send position: position not fully valid"
-#define ERR_SOCK_SEND_FAILED "%s: Could not send position: send operation failed"
-#define ERR_NETW_TECH_ERROR "%s: Technical network error: %s"
-
-#define ERR_UDP_SOCKET_CREAT "%s: Error creating UDP socket for %s:%d: %s"
-#define ERR_UDP_RCVR_RCVR "%s: Error receiving UDP: %s"
-
-
-// The UDPRuntimeError exception is raised when the address
-// and port combinaison cannot be resolved or if the socket cannot be
-// opened.
-
+/// @brief Exception raised by XPMP2::SocketNetworking objects
+/// @details This exception is raised when the address
+/// and port combinaison cannot be resolved or if the socket cannot be
+/// opened.
class NetRuntimeError : public std::runtime_error
{
public:
- std::string errTxt;
- std::string fullWhat;
- NetRuntimeError(const char *w);
+ std::string errTxt; ///< OS text for what `errno` says, output of strerror_s()
+ std::string fullWhat; ///< combines `w` and `errTxt`
+ NetRuntimeError(const char *w); ///< Constructor sets the above texts
+ /// Return the full message, ie. `fullWhat`
const char* what() const noexcept override { return fullWhat.c_str(); }
};
-// Base class for socket-based networking
+/// Base class for any socket-based networking
class SocketNetworking
{
protected:
@@ -74,94 +64,101 @@ class SocketNetworking
size_t bufSize = 512;
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
SocketNetworking() {}
- SocketNetworking(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ SocketNetworking(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0, bool _bBroadcast = false);
+ /// Destructor makes sure the socket is closed
virtual ~SocketNetworking();
- // Opens the socket for listening
- virtual void Open(const std::string& _addr, int _port, size_t _bufSize,
+ /// Creates a socket and binds it to the given local address
+ virtual void Open(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0, bool _bBroadcast = false);
- // Connect to a server
+ /// Creates a socket and connects it to the given remote server
virtual void Connect(const std::string& _addr, int _port, size_t _bufSize,
unsigned _timeOut_ms = 0);
- // Close the connection(s)
+ /// Thread-safely close the connection(s) and frees the buffer
virtual void Close();
-
+ /// Is a socket open?
inline bool isOpen() const { return (f_socket != INVALID_SOCKET); }
-
+ /// (Re)Sets the buffer size (or clears it if `_bufSize==0`)
void SetBufSize (size_t _bufSize);
- std::string GetLastErr();
+ /// Returns a human-readable text for the last error
+ static std::string GetLastErr();
// attribute access
- inline SOCKET getSocket() const { return f_socket; }
- inline int getPort() const { return f_port; }
- inline std::string getAddr() const { return f_addr; }
+ SOCKET getSocket() const { return f_socket; } ///< the socket
+ int getPort() const { return f_port; } ///< the port
+ std::string getAddr() const { return f_addr; } ///< the interface address
- // return the buffer
+ /// returna the buffer
const char* getBuf () const { return buf ? buf : ""; }
- // receive messages
+ /// Waits to receive a message, ensures zero-termination in the buffer
long recv();
+ /// Waits to receive a message with timeout, ensures zero-termination in the buffer
long timedRecv(int max_wait_ms);
- // send broadcast message
+ /// Sends a broadcast message
bool broadcast (const char* msg);
- // convert addresses to string
+ /// Convert addresses to string
static std::string GetAddrString (const struct sockaddr* addr);
protected:
- // subclass tell which addresses to look for
+ /// Subclass to tell which addresses to look for
virtual void GetAddrHints (struct addrinfo& hints) = 0;
};
-// Receives UDP messages
+/// Receives UDP messages
class UDPReceiver : public SocketNetworking
{
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
UDPReceiver() : SocketNetworking() {}
- UDPReceiver(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ UDPReceiver(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0) :
SocketNetworking(_addr,_port,_bufSize,_timeOut_ms) {}
protected:
+ /// Sets flags to AI_PASSIVE, AF_INET, SOCK_DGRAM, IPPROTO_UDP
virtual void GetAddrHints (struct addrinfo& hints);
};
-// Receives TCP connections
+/// Listens to TCP connections and opens a session socket upon connect
class TCPConnection : public SocketNetworking
{
protected:
- SOCKET f_session_socket = INVALID_SOCKET;
- struct sockaddr_in f_session_addr;
+ SOCKET f_session_socket = INVALID_SOCKET; ///< session socket, ie. the socket opened when a counterparty connects
+ struct sockaddr_in f_session_addr; ///< address of the connecting counterparty
#if APL == 1 || LIN == 1
- // the self-pipe to shut down the TCP listener gracefully
+ /// the self-pipe to shut down the TCP listener gracefully
SOCKET selfPipe[2] = { INVALID_SOCKET, INVALID_SOCKET };
#endif
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
TCPConnection() : SocketNetworking() {}
- TCPConnection(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ TCPConnection(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0) :
SocketNetworking(_addr,_port,_bufSize,_timeOut_ms) {}
- virtual void Close(); // also close session connection
- void CloseListenerOnly(); // only closes the listening socket, but not a session connection
+ virtual void Close(); ///< also close session connection
+ void CloseListenerOnly(); ///< only closes the listening socket, but not a connected session
- // Listen for and accept connections
- void listen (int numConnections = 1);
- bool accept (bool bUnlisten = false);
- bool listenAccept (int numConnections = 1);
+ void listen (int numConnections = 1); ///< listen for incoming connections
+ bool accept (bool bUnlisten = false); ///< accept an incoming connections, optinally stop listening
+ bool listenAccept (int numConnections = 1); ///< combines listening and accepting
+ /// Connected to a counterparty?
bool IsConnected () const { return f_session_socket != INVALID_SOCKET; };
- // send messages on session connection
+ /// send messages on session connection
bool send(const char* msg);
protected:
diff --git a/Lib/ImGui/misc/cpp/imgui_stdlib.cpp b/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
index cb1fe174..ee694aa0 100644
--- a/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
+++ b/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
@@ -27,7 +27,7 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data)
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
std::string* str = user_data->Str;
IM_ASSERT(data->Buf == str->c_str());
- str->resize(data->BufTextLen);
+ str->resize(std::string::size_type(data->BufTextLen));
data->Buf = (char*)str->c_str();
}
else if (user_data->ChainCallback)
diff --git a/Lib/ImgWindow/ImgFontAtlas.cpp b/Lib/ImgWindow/ImgFontAtlas.cpp
index d8ee0bfa..2b0aeba3 100644
--- a/Lib/ImgWindow/ImgFontAtlas.cpp
+++ b/Lib/ImgWindow/ImgFontAtlas.cpp
@@ -46,7 +46,7 @@ ImgFontAtlas::ImgFontAtlas():
ImgFontAtlas::~ImgFontAtlas()
{
if (mTextureBound) {
- GLuint glTexNum = mGLTextureNum;
+ GLuint glTexNum = (GLuint)mGLTextureNum;
glDeleteTextures(1, &glTexNum);
mTextureBound = false;
}
diff --git a/Lib/ImgWindow/ImgWindow.cpp b/Lib/ImgWindow/ImgWindow.cpp
index 1923db08..4c64e1c0 100644
--- a/Lib/ImgWindow/ImgWindow.cpp
+++ b/Lib/ImgWindow/ImgWindow.cpp
@@ -131,7 +131,7 @@ ImgWindow::ImgWindow(
mFontTexture = (GLuint)texNum;
// upload texture.
- XPLMBindTexture2d(mFontTexture, 0);
+ XPLMBindTexture2d((int)mFontTexture, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -295,7 +295,7 @@ ImgWindow::RenderImGui(ImDrawData *draw_data)
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
- XPLMBindTexture2d((GLuint)(intptr_t)pcmd->TextureId, 0);
+ XPLMBindTexture2d((int)(intptr_t)pcmd->TextureId, 0);
// Scissors work in viewport space - must translate the coordinates from ImGui -> Boxels, then Boxels -> Native.
//FIXME: it must be possible to apply the scale+transform manually to the projection matrix so we don't need to doublestep.
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
index 306d2740..f1d2deaa 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
@@ -27,6 +27,7 @@
#include "XPLMCamera.h"
#include "XPLMMap.h"
+#include
#include
#include
#include
@@ -48,7 +49,7 @@ constexpr double M_per_FT = 0.3048; // meter per 1 foot
constexpr int M_per_NM = 1852; // meter per one nautical mile
/// The dataRefs provided by XPMP2 to the CSL models
-enum DR_VALS {
+enum DR_VALS : std::uint8_t {
V_CONTROLS_GEAR_RATIO = 0, ///< `libxplanemp/controls/gear_ratio` and \n`sim/cockpit2/tcas/targets/position/gear_deploy`
V_CONTROLS_NWS_RATIO, ///< `libxplanemp/controls/nws_ratio`, the nose wheel angle, actually in degrees
V_CONTROLS_FLAP_RATIO, ///< `libxplanemp/controls/flap_ratio` and \n`sim/cockpit2/tcas/targets/position/flap_ratio` and `...flap_ratio2`
@@ -208,7 +209,8 @@ class Aircraft {
protected:
bool bValid = true; ///< is this object valid? (Will be reset in case of exceptions)
- bool bVisible = true; ///< Shall this plane be drawn at the moment?
+ bool bVisible = true; ///< Shall this plane be drawn at the moment and be visible to TCAS/interfaces?
+ bool bRender = true; ///< Shall the CSL model be drawn in 3D world? (if !bRender && bVivile then still visible on TCAS/interfaces, Remote Client uses this for local senders' planes to take over TCAS but not drawing)
XPMP2::CSLModel* pCSLMdl = nullptr; ///< the CSL model in use
int matchQuality = -1; ///< quality of the match with the CSL model
@@ -244,20 +246,42 @@ class Aircraft {
bool bDestroyInst = false; ///< Instance to be destroyed in next flight loop callback?
public:
- /// Constructor creates a new aircraft object, which will be managed and displayed
+ /// @brief Constructor creates a new aircraft object, which will be managed and displayed
/// @exception XPMP2::XPMP2Error Mode S id invalid or duplicate, no model found during model matching
/// @param _icaoType ICAO aircraft type designator, like 'A320', 'B738', 'C172'
/// @param _icaoAirline ICAO airline code, like 'BAW', 'DLH', can be an empty string
/// @param _livery Special livery designator, can be an empty string
/// @param _modeS_id (optional) **Unique** identification of the plane [0x01..0xFFFFFF], e.g. the 24bit mode S transponder code. XPMP2 assigns an arbitrary unique number of not given
- /// @param _modelId (optional) specific model id to be used (no folder/package name, just the id as defined in the `OBJ8_AIRCRAFT` line)
+ /// @param _cslId (optional) specific unique model id to be used (package name/short id, as defined in the `OBJ8_AIRCRAFT` line)
Aircraft (const std::string& _icaoType,
const std::string& _icaoAirline,
const std::string& _livery,
XPMPPlaneID _modeS_id = 0,
- const std::string& _modelId = "");
+ const std::string& _cslId = "");
+ /// Default constructor creates an empty, invalid(!) and invisible shell; call XPMP2::Aircraft::Create() to actually create a plane
+ Aircraft ();
/// Destructor cleans up all resources acquired
virtual ~Aircraft();
+
+ /// Aircraft must not be copied as they reference non-copyable resources like XP instances
+ Aircraft (const Aircraft&) = delete;
+ /// Aircraft must not be copied as they reference non-copyable resources like XP instances
+ Aircraft& operator=(const Aircraft&) = delete;
+
+ /// @brief Creates a plane, only a valid operation if object was created using the default constructor
+ /// @exception Tried on already defined object; XPMP2::XPMP2Error Mode S id invalid or duplicate, no model found during model matching
+ /// @param _icaoType ICAO aircraft type designator, like 'A320', 'B738', 'C172'
+ /// @param _icaoAirline ICAO airline code, like 'BAW', 'DLH', can be an empty string
+ /// @param _livery Special livery designator, can be an empty string
+ /// @param _modeS_id (optional) **Unique** identification of the plane [0x01..0xFFFFFF], e.g. the 24bit mode S transponder code. XPMP2 assigns an arbitrary unique number of not given
+ /// @param _cslId (optional) specific unique model id to be used (package name/short id, as defined in the `OBJ8_AIRCRAFT` line)
+ /// @param _pCSLModel (optional) The actual model to use (no matching or search by `_cslId` if model is given this way)
+ void Create (const std::string& _icaoType,
+ const std::string& _icaoAirline,
+ const std::string& _livery,
+ XPMPPlaneID _modeS_id = 0,
+ const std::string& _cslId = "",
+ CSLModel* _pCSLModel = nullptr);
/// return the XPMP2 plane id
XPMPPlaneID GetModeS_ID () const { return modeS_id; }
@@ -295,8 +319,12 @@ class Aircraft {
/// @return match quality, the lower the better
int ReMatchModel () { return ChangeModel(acIcaoType,acIcaoAirline,acLivery); }
- /// Assigns the given model per name, returns if successful
- bool AssignModel (const std::string& _modelName);
+ /// @brief Assigns the given model
+ /// @param _cslId Search for this id (package/short)
+ /// @param _pCSLModel (optional) If given use this model and don't search
+ /// @return Successfuly found and assigned a model?
+ bool AssignModel (const std::string& _cslId,
+ CSLModel* _pCSLModel = nullptr);
/// return a pointer to the CSL model in use (Note: The CSLModel structure is not public.)
XPMP2::CSLModel* GetModel () const { return pCSLMdl; }
/// return the name of the CSL model in use
@@ -318,6 +346,11 @@ class Aircraft {
/// Is the plane visible?
bool IsVisible () const { return bVisible && bValid; }
+ /// Switch rendering of the CSL model on or off
+ virtual void SetRender (bool _bRender);
+ /// Is this plane to be rendered?
+ bool IsRendered () const { return bRender && IsVisible(); }
+
/// Distance to camera [m]
float GetCameraDist () const { return camDist; }
/// Bearing from camera [°]
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
index ff7817f7..5e5f74a4 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
@@ -262,6 +262,7 @@ constexpr XPMPPlaneID MAX_MODE_S_ID = 0x00FFFFFF;
#define XPMP_CFG_ITM_REPLTEXTURE "replace_texture" ///< Config key: Replace textures in OBJ8 files upon load if needed (specified on the OBJ8 line in xsb_aircraft.txt), creating new OBJ8 files
#define XPMP_CFG_ITM_CLAMPALL "clamp_all_to_ground" ///< Config key: Ensure no plane sinks below ground, no matter of XPMP2::Aircraft::bClampToGround
#define XPMP_CFG_ITM_HANDLE_DUP_ID "handle_dup_id" ///< Config key: Boolean: If XPMP2::Aircraft::modeS_id already exists then assign a new unique one, overwrites XPMP2::Aircraft::modeS_id
+#define XPMP_CFG_ITM_SUPPORT_REMOTE "support_remote" ///< Config key: Support remote connections? `<0` force off, `0` default: on if in a networked or multiplayer setup, `>0` force on
#define XPMP_CFG_ITM_LOGLEVEL "log_level" ///< Config key: General level of logging into `Log.txt` (0 = Debug, 1 = Info, 2 = Warning, 3 = Error, 4 = Fatal)
#define XPMP_CFG_ITM_MODELMATCHING "model_matching" ///< Config key: Write information on model matching into `Log.txt`
@@ -275,6 +276,7 @@ constexpr XPMPPlaneID MAX_MODE_S_ID = 0x00FFFFFF;
/// `models | replace_texture | int | 1 | Replace textures in OBJ8 files upon load if needed (specified on the OBJ8 line in xsb_aircraft.txt), creating new OBJ8 files`\n
/// `planes | clamp_all_to_ground | int | 1 | Ensure no plane sinks below ground, no matter of XPMP2::Aircraft::bClampToGround`\n
/// `planes | handle_dup_id | int | 0 | Boolean: If XPMP2::Aircraft::modeS_id already exists then assign a new unique one, overwrites XPMP2::Aircraft::modeS_id`\n
+/// `planes | support_remote | int | 0 | 3-state integer: Support remote connections? <0 force off, 0 default: on if in a networked or multiplayer setup, >0 force on`\n
/// `debug | log_level | int | 2 | General level of logging into Log.txt (0 = Debug, 1 = Info, 2 = Warning, 3 = Error, 4 = Fatal)`\n
/// `debug | model_matching | int | 0 | Write information on model matching into Log.txt`\n
/// @note There is no immediate requirement to check the value of `_section` in your implementation.
@@ -456,12 +458,16 @@ bool XPMPIsICAOValid(const char * inICAO);
/// in your XPMP2::Aircraft::UpdatePosition() implementation.
/// @return The functions returns the array index you need to use in the
/// XPMP2::Aircraft::v array to provide the actual value.
-/// Or `0` when the function was called while planes are active.
+/// Or `0` when an error occured.
/// @note There is a duplication check: Adding an already existing dataRef,
/// no matter if XPMP2-predefined or by the plugin will return the
/// already existing index.
/// @note Can only be called while no plane exists as it influence the size
/// of important data arrays. Returns `0` if called while planes exist.
+/// @note Can only be called from XP's main thread as XPLM SDK functions are called.
+/// Returns `0` if called from a worker thread.
+/// @note User-defined dataRefs are _not_ transfered to the XPMP2 Remote Client
+/// for display on network-connected X-Plane instances.
size_t XPMPAddModelDataRef (const std::string& dataRef);
/************************************************************************************
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
new file mode 100644
index 00000000..528d5e5e
--- /dev/null
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
@@ -0,0 +1,432 @@
+/// @file XPMPRemote.h
+/// @brief Semi-public remote network functionality for master/client operations
+/// @details Technically, this is public functionality from a library
+/// point of view. But it is intended for the "XPMP Remote Client" only,
+/// not for a standard plugin.\n
+/// Network messages are packed for space efficiency, but also to avoid
+/// layout differences between compilers/platforms.
+/// However, manual layout tries to do reasonable alignment of numeric values
+/// and 8-byte-alignment of each structure, so that arrays of structures
+/// also align well.
+/// @author Birger Hoppe
+/// @copyright (c) 2020 Birger Hoppe
+/// @copyright Permission is hereby granted, free of charge, to any person obtaining a
+/// copy of this software and associated documentation files (the "Software"),
+/// to deal in the Software without restriction, including without limitation
+/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+/// and/or sell copies of the Software, and to permit persons to whom the
+/// Software is furnished to do so, subject to the following conditions:\n
+/// The above copyright notice and this permission notice shall be included in
+/// all copies or substantial portions of the Software.\n
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+/// THE SOFTWARE.
+
+#ifndef _XPMPRemote_h_
+#define _XPMPRemote_h_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace XPMP2 {
+
+/// The signature of the XPMP2 Remote Client
+constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
+
+//
+// MARK: Global Helpers
+//
+
+/// @brief Produces a reproducible(!) hash value for strings
+/// @details Result is the same if the same string is provided, across platform
+/// and across executions, unlike what std::hash requires.\n
+/// It is implemented as a 16-bit version of the PJW hash:
+/// @see https://en.wikipedia.org/wiki/PJW_hash_function
+std::uint16_t PJWHash16 (const char *s);
+
+/// @brief Find a model by package name hash and short id
+/// @details This approach is used by the remote client to safe network bandwith.
+/// If an exact match with `pkgHash` _and_ `shortID` is not found,
+/// then a model matching the short id alone is returned if available.
+CSLModel* CSLModelByPkgShortId (std::uint16_t _pkgHash,
+ const std::string& _shortId);
+
+//
+// MARK: Network Data Definitions
+//
+
+/// Message type
+enum RemoteMsgTy : std::uint8_t {
+ RMT_MSG_INTEREST_BEACON = 0, ///< beacon sent by a remote client to signal interest in data
+ RMT_MSG_SEND, ///< internal indicator telling to send out all pending messages
+ RMT_MSG_SETTINGS, ///< a sender's id and its settings
+ RMT_MSG_AC_DETAILED, ///< aircraft full details, needed to create new a/c objects and to re-synch all remote data
+ RMT_MSG_AC_POS_UPDATE, ///< aircraft differences only
+ RMT_MSG_AC_ANIM, ///< aircraft animation values (dataRef values) only
+ RMT_MSG_AC_REMOVE, ///< aircraft is removed
+};
+
+/// Definition for how to map dataRef values to (u)int8, ie. to an integer range of 8 bits
+struct RemoteDataRefPackTy {
+ float minV = 0.0f; ///< minimum transferred value
+ float range = 0.0f; ///< range of transferred value = maxV - minV
+
+ /// Constructor sets minimum value and range
+ RemoteDataRefPackTy (float _min, float _max) : minV(_min), range(_max - _min) { assert(range != 0.0f); }
+
+ /// pack afloat value to integer
+ std::uint8_t pack (float f) const { return std::uint8_t(std::clamp(f-minV,0.0f,range) * UINT8_MAX / range); }
+ /// unpack an integer value to float
+ float unpack (std::uint8_t i) const { return minV + range*float(i)/255.0f; }
+};
+
+/// An array holding all dataRef packing definitions
+extern const std::array REMOTE_DR_DEF;
+
+//
+// MARK: Message Header (Base)
+//
+
+// To ensure best network capacity usage as well as identical alignment across platform we enforce tightly packed structures.
+// The approach is very different between Visual C++ and clang / gcc, though:
+#ifdef _MSC_VER // Visual C++
+#pragma pack(push,1) // set packing once (ie. not per struct)
+#define PACKED
+#elif defined(__clang__) || defined(__GNUC__) // Clang (Mac XCode etc) or GNU (Linux)
+#define PACKED __attribute__ ((__packed__))
+#else
+#error Unhandled Compiler!
+#endif
+
+/// Message header, identical for all message types
+struct RemoteMsgBaseTy {
+ RemoteMsgTy msgTy : 4; ///< message type
+ std::uint8_t msgVer : 4; ///< message version
+ bool bLocalSender : 1; ///< is the sender "local", ie. on same machine?
+ std::uint8_t filler1 : 7; ///< yet unsed
+ std::uint16_t pluginId = 0; ///< lower 16 bit of the sending plugin's id
+ std::uint32_t filler2 = 0; ///< yet unused, fills up to size 8
+ /// Constructor just sets the values
+ RemoteMsgBaseTy (RemoteMsgTy _ty, std::uint8_t _ver);
+} PACKED;
+
+//
+// MARK: Beacon of Interest
+//
+
+/// Interest Beacon message version number
+constexpr std::uint8_t RMT_VER_BEACON = 0;
+/// "Beacon of Interest", ie. some message on the multicast just to wake up sender
+struct RemoteMsgBeaconTy : public RemoteMsgBaseTy {
+ // don't need additional data fields
+ /// Constructor sets appropriate message type
+ RemoteMsgBeaconTy();
+} PACKED;
+
+//
+// MARK: Settings
+//
+
+/// How often to send settings? [s]
+constexpr int REMOTE_SEND_SETTINGS_INTVL = 20;
+
+/// Setttings message version number
+constexpr std::uint8_t RMT_VER_SETTINGS = 0;
+/// Settings message, identifying a sending plugin, regularly providing its settings
+struct RemoteMsgSettingsTy : public RemoteMsgBaseTy {
+ char name[16]; ///< plugin's name, not necessarily zero-terminated if using full 12 chars
+ float maxLabelDist; ///< Maximum distance for drawing labels? [m]
+ char defaultIcao[4]; ///< Default ICAO aircraft type designator if no match can be found
+ char carIcaoType[4]; ///< Ground vehicle type identifier
+ std::uint8_t logLvl :3; ///< logging level
+ bool bLogMdlMatch :1; ///< Debug model matching?
+ bool bObjReplDataRefs :1; ///< Replace dataRefs in `.obj` files on load?
+ bool bObjReplTextures :1; ///< Replace textures in `.obj` files on load if needed?
+ bool bLabelCutOffAtVisibility :1; ///< Cut off labels at XP's reported visibility mit?
+ bool bMapEnabled :1; ///< Do we feed X-Plane's maps with our aircraft positions?
+ bool bMapLabels :1; ///< Do we show labels with the aircraft icons?
+ bool bHaveTCASControl :1; ///< Do we have AI/TCAS control?
+ std::uint16_t filler; ///< yet unused, fills size up for a multiple of 8
+
+ /// Constructor sets most values to zero
+ RemoteMsgSettingsTy ();
+
+} PACKED;
+
+//
+// MARK: A/C Details
+//
+
+/// A/C detail message version number
+constexpr std::uint8_t RMT_VER_AC_DETAIL = 0;
+/// A/C details, packed into an array message
+struct RemoteAcDetailTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ char icaoType[4]; ///< icao a/c type
+ char icaoOp[4]; ///< icao airline code
+ char sShortId[20]; ///< CSL model's short id
+ std::uint16_t pkgHash; ///< hash value of package name
+ char label[23]; ///< label
+ std::uint8_t labelCol[3]; ///< label color (RGB)
+ float alt_ft; ///< [ft] current altitude
+ // ^ the above has 64 bytes, so that these doubles start on an 8-byte bounday:
+ double lat; ///< latitude
+ double lon; ///< longitude
+ std::int16_t pitch; ///< [0.01°] pitch/100
+ std::uint16_t heading; ///< [0.01°] heading/100
+ std::int16_t roll; ///< [0.01°] roll/100
+
+ std::int16_t aiPrio; ///< priority for display in limited TCAS target slots, `-1` indicates "no TCAS display"
+ std::uint16_t dTime; ///< [0.0001s] time difference to previous position in 1/10000s
+ bool bValid : 1; ///< is this object valid? (Will be reset in case of exceptions)
+ bool bVisible : 1; ///< Shall this plane be drawn at the moment?
+ bool bRender : 1; ///< Shall the CSL model be drawn in 3D world?
+
+ std::uint8_t filler[3]; ///< yet unused
+
+ ///< Array of _packed_ dataRef values for CSL model animation
+ std::uint8_t v[XPMP2::V_COUNT]; // 42
+
+ /// Default Constructor sets all to zero
+ RemoteAcDetailTy ();
+ /// A/c copy constructor fills from passed-in XPMP2::Aircraft object
+ RemoteAcDetailTy (const Aircraft& _ac, double _lat, double _lon, float _alt_ft, std::uint16_t _dTime);
+ /// Copies values from passed-in XPMP2::Aircraft object
+ void CopyFrom (const Aircraft& _ac, double _lat, double _lon, float _alt_ft, std::uint16_t _dTime);
+
+ void SetLabelCol (const float _col[4]); ///< set the label color from a float array (4th number, alpha, is always considered 1.0)
+ void GetLabelCol (float _col[4]) const; ///< writes color out into a float array
+
+ void SetPitch (float _p) { pitch = std::int16_t(_p*100.0f); } ///< sets pitch from float
+ float GetPitch () const { return float(pitch) / 100.0f; } ///< returns float pitch
+
+ /// @brief Sets heading from float
+ /// @note Only works well for `0 <= _h < 360`
+ void SetHeading (float _h);
+ float GetHeading () const { return float(heading) / 100.0f; } ///< returns float heading
+
+ void SetRoll (float _r) { roll = std::int16_t(std::lround(_r*100.0f)); } ///< sets pitch from float
+ float GetRoll () const { return float(roll) / 100.0f; } ///< returns float pitch
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcDetailTy); } ///< message size
+} PACKED;
+
+/// A/C detail message, has an inherited header plus an array of XPMP2::RemoteAcDetailTy elements
+struct RemoteMsgAcDetailTy : public RemoteMsgBaseTy {
+ RemoteAcDetailTy arr[1]; ///< basis for the array of actual details
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcDetailTy () : RemoteMsgBaseTy(RMT_MSG_AC_DETAILED, RMT_VER_AC_DETAIL) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(RemoteAcDetailTy); }
+} PACKED;
+
+//
+// MARK: A/C Position Update
+//
+
+/// A/C Position update message version number
+constexpr std::uint8_t RMT_VER_AC_POS_UPDATE = 0;
+
+/// What is the maximum difference a RemoteAcPosUpdateTy can hold?
+constexpr double REMOTE_DEGREE_RES = 0.00000001; ///< resolution of degree updates
+constexpr double REMOTE_MAX_DIFF_DEGREE = REMOTE_DEGREE_RES * INT16_MAX;///< maximum degree difference that can be represented in a pos update msg
+constexpr double REMOTE_ALT_FT_RES = 0.01; ///< resolution of altitude[ft] updates
+constexpr double REMOTE_MAX_DIFF_ALT_FT = REMOTE_ALT_FT_RES * INT16_MAX;///< maximum altitude[ft] difference that can be represented in a pos update msg
+constexpr float REMOTE_TIME_RES = 0.0001f; ///< resolution of time difference
+constexpr float REMOTE_MAX_DIFF_TIME = REMOTE_TIME_RES * UINT16_MAX; ///< maximum time difference thatn can be represented in a pos update msg
+
+/// @brief A/C Position updates based on global coordinates
+/// @details for space efficiency only deltas to last msg are given in
+/// 0.0000001 degree lat/lon (roughly 1 centimeter resolution) and
+/// 0.01 ft altitude
+struct RemoteAcPosUpdateTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ std::int16_t dLat; ///< [0.0000001 degrees] latitude position difference
+ std::int16_t dLon; ///< [0.0000001 degrees] longitude position difference
+ std::int16_t dAlt_ft; ///< [0.01 ft] altitude difference
+ std::uint16_t dTime; ///< [0.0001s] time difference to previous position in 1/10000s
+ std::int16_t pitch; ///< [0.01 degree] pitch/100
+ std::uint16_t heading; ///< [0.01 degree] heading/100
+ std::int16_t roll; ///< [0.01 degree] roll/100
+ std::uint16_t filler1; ///< not yet used (for 4-byte alignment)
+
+ /// Default Constructor sets all 0
+ RemoteAcPosUpdateTy () { std::memset(this,0,sizeof(*this)); }
+ /// Constructor sets all values
+ RemoteAcPosUpdateTy (XPMPPlaneID _modeS_id,
+ std::int16_t _dLat, std::int16_t _dLon,
+ std::int16_t _dAlt_ft, std::uint16_t _dTime,
+ float _pitch, float _heading, float _roll);
+
+ void SetPitch (float _p) { pitch = std::int16_t(_p*100.0f); } ///< sets pitch from float
+ float GetPitch () const { return float(pitch) / 100.0f; } ///< returns float pitch
+
+ /// @brief Sets heading from float
+ /// @note Only works well for `0 <= _h < 360`
+ void SetHeading (float _h);
+ float GetHeading () const { return float(heading) / 100.0f; } ///< returns float heading
+
+ void SetRoll (float _r) { roll = std::int16_t(std::lround(_r*100.0f)); }///< sets pitch from float
+ float GetRoll () const { return float(roll) / 100.0f; } ///< returns float pitch
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcPosUpdateTy); } ///< message size
+} PACKED;
+
+/// A/C detail message, has an inherited header plus an array of XPMP2::RemoteAcDetailTy elements
+struct RemoteMsgAcPosUpdateTy : public RemoteMsgBaseTy {
+ RemoteAcPosUpdateTy arr[1]; ///< basis for the array of actual position updates
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcPosUpdateTy () : RemoteMsgBaseTy(RMT_MSG_AC_POS_UPDATE, RMT_VER_AC_POS_UPDATE) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(RemoteAcPosUpdateTy); }
+} PACKED;
+
+//
+// MARK: A/C animation dataRefs
+//
+
+/// A/C Position update message version number
+constexpr std::uint8_t RMT_VER_AC_ANIM = 0;
+
+/// @brief A/C animation dataRef changes
+/// @details This structure has variable length depending on the number of
+/// actual dataRef values to carry. And several of these variable
+/// length structures are added into one variable length network message.
+/// @note Structure must stay aligned with XPMP2::RmtDataAcAnimTy
+struct RemoteAcAnimTy {
+ std::uint32_t modeS_id = 0; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ std::uint8_t numVals = 0; ///< number of dataRef values in the following array
+ std::uint8_t filler = 0; ///< not yet used
+
+ /// dataRef animation types and value
+ struct DataRefValTy {
+ DR_VALS idx; ///< index into XPMP2::Aircraft::v
+ std::uint8_t v; ///< dataRef animation value
+ } v[1]; ///< array of dataRef animation types and value
+
+ /// Constructor
+ RemoteAcAnimTy (XPMPPlaneID _id) : modeS_id(_id) { v[0].idx = DR_VALS(0); v[0].v = 0; }
+
+ /// message size assuming `num` array elements
+ static constexpr size_t msgSize (std::uint8_t num)
+ { return sizeof(RemoteAcAnimTy) + (num-1) * sizeof(DataRefValTy); }
+ /// current message size
+ size_t msgSize() const { return msgSize(numVals); }
+} PACKED;
+
+/// A/C animation dataRef message, has an inherited header plus an array of _variable sized_ XPMP2::RemoteAcAnimTy elements
+struct RemoteMsgAcAnimTy : public RemoteMsgBaseTy {
+ RemoteAcAnimTy animData; ///< message data starts here but extend beyond this point!
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcAnimTy () : RemoteMsgBaseTy(RMT_MSG_AC_ANIM, RMT_VER_AC_ANIM), animData(0) {}
+
+ /// Returns a pointer to the first/next animation data element in the message
+ const RemoteAcAnimTy* next (size_t _msgLen, const RemoteAcAnimTy* pCurr = nullptr) const;
+} PACKED;
+
+
+//
+// MARK: A/C Removal
+//
+
+
+/// A/C removal message version number
+constexpr std::uint8_t RMT_VER_AC_REMOVE = 0;
+
+/// A/C Removal only includes the plane id, structure required for msgSize() function
+struct RemoteAcRemoveTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+
+ /// Constructor sets plane id
+ RemoteAcRemoveTy (XPMPPlaneID _id = 0) : modeS_id(_id) {}
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcRemoveTy); } ///< message size
+} PACKED;
+
+/// A/C removal message, an array of plane ids
+struct RemoteMsgAcRemoveTy : public RemoteMsgBaseTy {
+ RemoteAcRemoveTy arr[1]; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcRemoveTy () : RemoteMsgBaseTy(RMT_MSG_AC_REMOVE, RMT_VER_AC_REMOVE) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(arr[0]); }
+} PACKED;
+
+#ifdef _MSC_VER // Visual C++
+#pragma pack(pop) // reseting packing
+#endif
+
+// A few static validations just to make sure that no compiler fiddles with my network message layout.
+static_assert(sizeof(RemoteMsgBaseTy) == 8, "RemoteMsgBaseTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgSettingsTy) == 40, "RemoteMsgSettingsTy doesn't have expected size");
+static_assert(sizeof(RemoteAcDetailTy) == 94+42, "RemoteAcDetailTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcDetailTy) == 102+42, "RemoteMsgAcDetailTy doesn't have expected size");
+static_assert(sizeof(RemoteAcPosUpdateTy) == 20, "RemoteAcPosUpdateTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcPosUpdateTy)== 28, "RemoteMsgAcPosUpdateTy doesn't have expected size");
+static_assert(sizeof(RemoteAcAnimTy) == 8, "RemoteAcAnimTy doesn't have expected size");
+static_assert(RemoteAcAnimTy::msgSize(V_COUNT) == 90, "RemoteAcAnimTy for V_COUNT dataRefs doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcAnimTy) == 16, "RemoteMsgAcAnimTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcRemoveTy) == 12, "RemoteMsgAcRemoveTy doesn't have expected size");
+
+//
+// MARK: Miscellaneous
+//
+
+/// Function prototypes for callback functions to handle the received messages
+struct RemoteCBFctTy {
+ /// Called in flight loop before processing first aircraft
+ void (*pfBeforeFirstAc)() = nullptr;
+ /// Called in flight loop after processing last aircraft
+ void (*pfAfterLastAc)() = nullptr;
+ /// Callback for processing Settings messages
+ void (*pfMsgSettings) (const std::uint32_t from[4],
+ const std::string& sFrom,
+ const RemoteMsgSettingsTy&) = nullptr;
+ /// Callback for processing A/C Details messages
+ void (*pfMsgACDetails) (const std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcDetailTy&) = nullptr;
+ /// Callback for processing A/C Details messages
+ void (*pfMsgACPosUpdate) (const std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcPosUpdateTy&) = nullptr;
+ /// Callback for processing A/C Animation dataRef messages
+ void (*pfMsgACAnim) (const std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcAnimTy&) = nullptr;
+ /// Callback for processing A/C Removal messages
+ void (*pfMsgACRemove) (const std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcRemoveTy&) = nullptr;
+};
+
+/// State of remote communcations
+enum RemoteStatusTy : unsigned {
+ REMOTE_OFF = 0, ///< no remote connectivtiy, not listening, not sending
+ REMOTE_SEND_WAITING, ///< listening for a request to send data, but not actively sending data
+ REMOTE_SENDING, ///< actively sending aircraft data out to the network
+ REMOTE_RECV_WAITING, ///< waiting to receive data, periodically sending a token of interest
+ REMOTE_RECEIVING, ///< actively receiving data
+};
+
+/// Returns the current Remote status
+RemoteStatusTy RemoteGetStatus();
+
+/// Starts the listener, will call provided callback functions with received messages
+void RemoteRecvStart (const RemoteCBFctTy& _rmtCBFcts);
+
+/// Stops the receiver
+void RemoteRecvStop ();
+
+}
+
+
+#endif
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
index b69ce493..8732bee0 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
@@ -3,7 +3,7 @@
BuildMachineOSBuild
- 19H2
+ 19H15CFBundleDevelopmentRegionenCFBundleExecutable
@@ -27,19 +27,19 @@
DTCompilercom.apple.compilers.llvm.clang.1_0DTPlatformBuild
- 12A7300
+ 12B45bDTPlatformNamemacosxDTPlatformVersion
- 10.15.6
+ 11.0DTSDKBuild
- 19G68
+ 20A2408DTSDKName
- macosx10.15
+ macosx11.0DTXcode
- 1201
+ 1220DTXcodeBuild
- 12A7300
+ 12B45bLSMinimumSystemVersion10.12NSHumanReadableCopyright
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 b/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2
index 7904b851..7600b704 100644
Binary files a/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 and b/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 differ
diff --git a/Lib/XPMP2/XPMP2.lib b/Lib/XPMP2/XPMP2.lib
index a3a972bd..bd3a8202 100644
Binary files a/Lib/XPMP2/XPMP2.lib and b/Lib/XPMP2/XPMP2.lib differ
diff --git a/Lib/XPMP2/libXPMP2.a b/Lib/XPMP2/libXPMP2.a
index 96e2609a..7f44cd78 100644
Binary files a/Lib/XPMP2/libXPMP2.a and b/Lib/XPMP2/libXPMP2.a differ
diff --git a/LiveTraffic.rc b/LiveTraffic.rc
index 7e9e2b07..ebbad27e 100644
Binary files a/LiveTraffic.rc and b/LiveTraffic.rc differ
diff --git a/LiveTraffic.vcxproj b/LiveTraffic.vcxproj
index d6c0d658..58e7ccd0 100644
--- a/LiveTraffic.vcxproj
+++ b/LiveTraffic.vcxproj
@@ -143,7 +143,7 @@
Lib/XPMP2;Lib/SDK/Libraries/Win;Lib/CURL;%(AdditionalLibraryDirectories)
- Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)
+ Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)Console
@@ -159,10 +159,12 @@
$(IntDir)$(TargetName).lib
- copy /Y "$(TargetDir)$(TargetName)$(TargetExt)" "C:\X-Plane\11\Resources\plugins\LiveTraffic\win_x64"
+
+
- Copying win.xpl into my X-Plane plugin paths
+
+
@@ -191,7 +193,7 @@
Lib/XPMP2;Lib/SDK/Libraries/Win;Lib/CURL;%(AdditionalLibraryDirectories)
- Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)
+ Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)ConsoleUseLinkTimeCodeGeneration
@@ -205,8 +207,10 @@
$(IntDir)$(TargetName).lib
- copy /Y "$(TargetDir)$(TargetName)$(TargetExt)" "C:\X-Plane\11\Resources\plugins\LiveTraffic\win_x64"
- Copying win.xpl into my X-Plane plugin paths
+
+
+
+
diff --git a/LiveTraffic.xcodeproj/project.pbxproj b/LiveTraffic.xcodeproj/project.pbxproj
index 3d608574..66518327 100755
--- a/LiveTraffic.xcodeproj/project.pbxproj
+++ b/LiveTraffic.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -41,7 +41,7 @@
25C59465207ABDC700E52073 /* LTMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25C59464207ABDC700E52073 /* LTMain.cpp */; };
25E9C2AF207D5B8100D3C642 /* LTFlightData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25E9C2AE207D5B8100D3C642 /* LTFlightData.cpp */; };
25EFC76E24959662005DB0A6 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25EFC76D24959662005DB0A6 /* ApplicationServices.framework */; };
- 25F3471C24D2C7DE004574E7 /* parson.c in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471B24D2C7DE004574E7 /* parson.c */; };
+ 25F3471C24D2C7DE004574E7 /* parson.c in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471B24D2C7DE004574E7 /* parson.c */; settings = {COMPILER_FLAGS = "-Wno-sign-conversion"; }; };
25F3471F24D2CB20004574E7 /* ACTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471E24D2CB20004574E7 /* ACTable.cpp */; };
25FEB7B9224D7B10002A051F /* LTForeFlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25FEB7B8224D7B10002A051F /* LTForeFlight.cpp */; };
D67297EB0F9E0FCC00CFD1FA /* LiveTraffic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D67297EA0F9E0FCC00CFD1FA /* LiveTraffic.cpp */; };
@@ -519,6 +519,7 @@
buildPhases = (
D607B19609A556E400699BC3 /* Sources */,
D607B19709A556E400699BC3 /* Frameworks */,
+ 255C337E2544DA0C002C166F /* ShellScript */,
);
buildRules = (
);
@@ -538,7 +539,7 @@
KnownAssetTags = (
New,
);
- LastUpgradeCheck = 1200;
+ LastUpgradeCheck = 1210;
};
buildConfigurationList = D607B16209A5563100699BC3 /* Build configuration list for PBXProject "LiveTraffic" */;
compatibilityVersion = "Xcode 3.2";
@@ -558,6 +559,27 @@
};
/* End PBXProject section */
+/* Begin PBXShellScriptBuildPhase section */
+ 255C337E2544DA0C002C166F /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "TAGS=\"TODO:|FIXME:\"\necho \"searching ${SRCROOT} for ${TAGS}\"\nfind {\"${SRCROOT}/Include\",\"${SRCROOT}/Src\"} \\( -name \"*.cpp\" -or -name \"*.h\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($TAGS).*\\$\" | perl -p -e \"s/($TAGS)/ warning: \\$1/\"\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
D607B19609A556E400699BC3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -614,6 +636,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
@@ -633,6 +656,7 @@
DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ EXCLUDED_ARCHS = arm64;
EXECUTABLE_EXTENSION = xpl;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -707,6 +731,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
@@ -724,6 +749,7 @@
DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ EXCLUDED_ARCHS = arm64;
EXECUTABLE_EXTENSION = xpl;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -787,6 +813,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_IDENTITY = "-";
DEPLOYMENT_LOCATION = YES;
+ EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Lib/XPMP2",
@@ -819,6 +846,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_IDENTITY = "-";
DEPLOYMENT_LOCATION = YES;
+ EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Lib/XPMP2",
diff --git a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
index 9cddb523..9d244fa6 100644
--- a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
+++ b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
@@ -59,13 +59,13 @@
contextName = "Apt::FindEdgesForHeading(double, double, std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >&, TaxiEdge::edgeTy) const:LTApt.cpp">
+ value = "vecTaxiEdges[222]">
+ value = "lst[1385]">
+ value = "vecTaxiEdgesIdxHead[3321]">
@@ -106,10 +106,10 @@
contextName = "LTFlightData::AddDynData(LTFlightData::FDDynamicData const&, int, int, positionTy*):LTFlightData.cpp">
+ value = "posDeque">
+ value = "dynDataDeque">
@@ -237,10 +237,10 @@
value = "cull">
+ value = "iter->second">
+ value = "tcas">
@@ -278,14 +278,6 @@
-
-
-
-
-
-
@@ -405,14 +397,11 @@
contextName = "GLOBAL">
+ value = "dataRefs">
-
-
+ value = "from">
@@ -434,7 +423,7 @@
value = "ppos">
+ value = "turn">
@@ -532,10 +521,10 @@
value = "mapFd">
+ value = "sizeof(bool)">
+ value = "dataRefs">
@@ -547,14 +536,6 @@
-
-
-
-
-
-
@@ -918,6 +899,9 @@
+
+
@@ -1072,10 +1056,10 @@
value = "_startTime">
+ value = "_targetTime">
+ value = "_deltaDist">
@@ -1088,6 +1072,14 @@
+
+
+
+
+
+
diff --git a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
index 4e00739b..01159e44 100644
--- a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
+++ b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
@@ -1,6 +1,6 @@
second.GetAircraft();
if (dr == DR_AC_BULK_QUICK)
- ac.CopyBulkData ((LTAPIAircraft::LTAPIBulkData*)pOut, size);
+ ac.CopyBulkData ((LTAPIAircraft::LTAPIBulkData*)pOut, (size_t)size);
else
- ac.CopyBulkData((LTAPIAircraft::LTAPIBulkInfoTexts*)pOut, size);
+ ac.CopyBulkData((LTAPIAircraft::LTAPIBulkInfoTexts*)pOut, (size_t)size);
}
// how many bytes copied?
@@ -1830,7 +1832,7 @@ bool DataRefs::LoadConfigFile()
}
// the second part is again an array of icao types, separated by space or comma or so
- aFlarmToIcaoAcTy[std::stoi(ln[0])] = str_tokenize(ln[1], " ,;/");
+ aFlarmToIcaoAcTy[std::stoul(ln[0])] = str_tokenize(ln[1], " ,;/");
}
}
diff --git a/Src/LTAircraft.cpp b/Src/LTAircraft.cpp
index f646ef33..66ff1c95 100644
--- a/Src/LTAircraft.cpp
+++ b/Src/LTAircraft.cpp
@@ -1564,8 +1564,8 @@ bool LTAircraft::CalcPPos()
// nearly no drag, so try calculating the flight path angle and use
// it also as pitch
if ( vsi > mdl.VSI_STABLE ) {
- toPitch = clamp(vsi2deg(vec.speed, vec.vsi),
- mdl.PITCH_MIN, mdl.PITCH_MAX);
+ toPitch = std::clamp(vsi2deg(vec.speed, vec.vsi),
+ mdl.PITCH_MIN, mdl.PITCH_MAX);
}
// add some degrees in case flaps will be set
diff --git a/Src/LTApt.cpp b/Src/LTApt.cpp
index 854f2252..e90cfc94 100644
--- a/Src/LTApt.cpp
+++ b/Src/LTApt.cpp
@@ -419,7 +419,7 @@ class Apt {
double bestDist2 = HUGE_VAL;
const vecTaxiNodesTy::const_iterator dontIter =
dontCombineWith == ULONG_MAX ? vecTaxiNodes.cend() :
- std::next(vecTaxiNodes.cbegin(),dontCombineWith);
+ std::next(vecTaxiNodes.cbegin(),(long)dontCombineWith);
vecTaxiNodesTy::const_iterator bestIter = vecTaxiNodes.cend();
for (auto iter = vecTaxiNodes.cbegin(); iter != vecTaxiNodes.cend(); ++iter)
{
@@ -439,7 +439,7 @@ class Apt {
// Found something?
return (bestIter != vecTaxiNodes.cend() ?
- std::distance(vecTaxiNodes.cbegin(), bestIter) :
+ (size_t)std::distance(vecTaxiNodes.cbegin(), bestIter) :
ULONG_MAX);
}
diff --git a/Src/LTChannel.cpp b/Src/LTChannel.cpp
index 7ce43e3e..e32d2a24 100644
--- a/Src/LTChannel.cpp
+++ b/Src/LTChannel.cpp
@@ -583,7 +583,6 @@ bool LTFlightDataEnable()
if ( dataRefs.GetUseHistData() ) {
// load historic data readers
listFDC.emplace_back(new ADSBExchangeHistorical);
- // TODO: master data readers for historic data, like reading CSV file
} else {
// load live feed readers (in order of priority)
listFDC.emplace_back(new RealTrafficConnection(mapFd));
diff --git a/Src/LTFlightData.cpp b/Src/LTFlightData.cpp
index 1e041b09..fb4ea95c 100644
--- a/Src/LTFlightData.cpp
+++ b/Src/LTFlightData.cpp
@@ -385,44 +385,22 @@ bool LTFlightData::validForAcCreate(double simTime) const
if (std::isnan(simTime))
simTime = dataRefs.GetSimTime();
- // so we have two positions...one in the past, one in the future?
- return
- posDeque.front().ts() <= simTime && simTime < posDeque[1].ts();
-}
-
-// is the data outdated (considered too old to be useful)?
-bool LTFlightData::outdated (double simTime) const
-{
- // general re-init necessary?
- if (dataRefs.IsReInitAll())
- return true;
-
- // a/c turned invalid (due to exceptions)?
- if (pAc && !pAc->IsValid())
- return true;
-
- // flown out of sight? -> outdated, remove
- if (pAc &&
- pAc->GetVecView().dist > dataRefs.GetFdStdDistance_m())
- return true;
+ // so we have two positions...
+ // if it is _not_ one in the past, one in the future, then bail
+ if (!(posDeque.front().ts() <= simTime && simTime < posDeque[1].ts()))
+ return false;
- // cover the special case of finishing landing and roll-out without live positions
- // i.e. during approach and landing we don't destroy the aircraft
- // if it is approaching some runway
- // until it finally stopped on the runway
- if (pAc &&
- (pAc->GetFlightPhase() >= FPH_LANDING ||
- (pAc->GetFlightPhase() >= FPH_APPROACH && posRwy.isNormal())) &&
- pAc->GetFlightPhase() < FPH_STOPPED_ON_RWY)
- {
+ // So first pos is in the past, second in the future, great...
+ // both are within limits in terms of distance?
+ if (CoordDistance(dataRefs.GetViewPos(), posDeque.front()) > dataRefs.GetFdStdDistance_m() ||
+ CoordDistance(dataRefs.GetViewPos(), posDeque[1] ) > dataRefs.GetFdStdDistance_m())
return false;
- }
- // youngestTS longer ago than allowed? -> remove
- return
- youngestTS + dataRefs.GetAcOutdatedIntvl() < (std::isnan(simTime) ? dataRefs.GetSimTime() : simTime);
+ // All checks passed
+ return true;
}
+
#define ADD_LABEL(b,txt) if (b && !txt.empty()) { labelStat += txt; labelStat += ' '; }
// update static data parts of the a/c label for reuse for performance reasons
void LTFlightData::UpdateStaticLabel()
@@ -786,10 +764,8 @@ void LTFlightData::DataSmoothing (bool& bChanged)
}
// we went one too far...so how far did we go into the deque?
--itLast;
- const size_t numPos = std::distance(posDeque.begin(), itLast);
-
// not far enough for any smoothing?
- if (numPos < 2)
+ if (std::distance(posDeque.begin(), itLast) < 2)
return;
// what is the total distance travelled between first and last?
@@ -1687,7 +1663,7 @@ void LTFlightData::AppendNewPos()
// and for that we need 2 positions before insert
const positionTy* pBefore = nullptr;
double heading = NAN;
- size_t idx = std::distance(posDeque.begin(), i);
+ size_t idx = (size_t)std::distance(posDeque.begin(), i);
if (idx >= 2) {
pBefore = &(posDeque[idx-1]);
heading = posDeque[idx-2].angle(*pBefore);
@@ -2288,32 +2264,62 @@ bool LTFlightData::AircraftMaintenance ( double simTime )
if ( !lock ) // we didn't get the lock, just return w/o deletion
return false;
- // if the a/c became invalid then remove the aircraft object,
- // but retain the remaining flight data
- if (pAc && !pAc->IsValid())
- DestroyAircraft();
-
- // if outdated just return 'delete me'
- if ( outdated(simTime) )
- return true;
-
// do we need to recalc the static part of the a/c label due to config change?
if (dataRefs.GetLabelCfg() != labelCfg)
UpdateStaticLabel();
- // doesn't yet have an associated aircraft but two positions?
- if ( !hasAc() && posDeque.size() >= 2 ) {
- // is already valid for a/c creation?
- if ( validForAcCreate(simTime) )
- // then do create the aircraft
- CreateAircraft(simTime);
- else // not yet valid
- // but the oldest position is at or before current simTime?
- // then chances are good that we can calculate positions
- if ( posDeque.front().ts() <= simTime)
- // start thread for position calculation...next time we might be valid for creation
- TriggerCalcNewPos(NAN);
+ // general re-init necessary?
+ if (dataRefs.IsReInitAll())
+ return true;
+
+ // Tests on an existing aircraft object
+ if (hasAc())
+ {
+ // if the a/c became invalid or has flown out of sight
+ // then remove the aircraft object,
+ // but retain the remaining flight data
+ if (!pAc->IsValid() ||
+ pAc->GetVecView().dist > dataRefs.GetFdStdDistance_m())
+ DestroyAircraft();
+ else {
+ // cover the special case of finishing landing and roll-out without live positions
+ // i.e. during approach and landing we don't destroy the aircraft
+ // if it is approaching some runway
+ // until it finally stopped on the runway
+ if ((pAc->GetFlightPhase() >= FPH_LANDING ||
+ (pAc->GetFlightPhase() >= FPH_APPROACH && posRwy.isNormal())) &&
+ pAc->GetFlightPhase() < FPH_STOPPED_ON_RWY)
+ {
+ return false;
+ }
+ }
}
+ // Tests when not (yet) having an aircraft object
+ else {
+ // Remove position from the beginning for as long as there is past data,
+ // i.e.: Only the .front pos may be in the past
+ while (posDeque.size() >= 2 && posDeque[1].ts() < simTime)
+ posDeque.pop_front();
+
+ // Have at least two positions?
+ if (posDeque.size() >= 2 ) {
+ // is already valid for a/c creation?
+ if ( validForAcCreate(simTime) )
+ // then do create the aircraft
+ CreateAircraft(simTime);
+ else // not yet valid
+ // but the oldest position is at or before current simTime?
+ // then chances are good that we can calculate positions
+ if ( posDeque.front().ts() <= simTime)
+ // start thread for position calculation...next time we might be valid for creation
+ TriggerCalcNewPos(NAN);
+ }
+ }
+
+ // youngestTS longer ago than allowed? -> remove the entire FD object
+ if (youngestTS + dataRefs.GetAcOutdatedIntvl() <
+ (std::isnan(simTime) ? dataRefs.GetSimTime() : simTime))
+ return true;
// don't delete me
return false;
@@ -2392,49 +2398,50 @@ bool LTFlightData::DetermineAcModel()
// checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
/// @warning Caller must own `mapFdMutex`!
-bool LTFlightData::AcSlotAvailable (double simTime)
+bool LTFlightData::AcSlotAvailable ()
{
// time we had shown the "Too many a/c" warning last:
- static double tTooManyAcMsgShown = 0.0;
+ static float tTooManyAcMsgShown = 0.0;
- // Haven't reach the limit in terms of number of a/c yet?
- if (dataRefs.GetNumAc() < dataRefs.GetMaxNumAc())
- return true;
-
// Have no positions? (Need one to determine distance to camera)
if (posDeque.empty())
return false;
- // Now we need to see if we are closer to the camera than other a/c.
- // If so remove the farest a/c to make room for us.
- LTFlightData* pFarestAc = nullptr;
-
- // NOTE: We can loop mapFd without lock only because we assume that
- // calling function owns mapFdMutex already!
- // find the farest a/c...if it is further away than us:
- double farestDist = CoordDistance(dataRefs.GetViewPos(), posDeque.front());
- for (mapLTFlightDataTy::value_type& p: mapFd)
+ // If we have too many aircraft show message (at most every 5 minutes)
+ if (dataRefs.GetNumAc() >= dataRefs.GetMaxNumAc()) {
+ if (CheckEverySoOften(tTooManyAcMsgShown, 300.0f))
+ SHOW_MSG(logWARN,MSG_TOO_MANY_AC,dataRefs.GetMaxNumAc());
+ }
+
+ // As long as there are too many a/c remove the ones farest away
+ while (dataRefs.GetNumAc() >= dataRefs.GetMaxNumAc())
{
- LTFlightData& fd = p.second;
- if (fd.hasAc() && fd.pAc->GetVecView().dist > farestDist) {
- farestDist = fd.pAc->GetVecView().dist;
- pFarestAc = &fd;
+ // Now we need to see if we are closer to the camera than other a/c.
+ // If so remove the farest a/c to make room for us.
+ LTFlightData* pFarestAc = nullptr;
+
+ // NOTE: We can loop mapFd without lock only because we assume that
+ // calling function owns mapFdMutex already!
+ // find the farest a/c...if it is further away than us:
+ double farestDist = CoordDistance(dataRefs.GetViewPos(), posDeque.front());
+ for (mapLTFlightDataTy::value_type& p: mapFd)
+ {
+ LTFlightData& fd = p.second;
+ if (fd.hasAc() && fd.pAc->GetVecView().dist > farestDist) {
+ farestDist = fd.pAc->GetVecView().dist;
+ pFarestAc = &fd;
+ }
}
- }
- // So we definitely have too many aircraft!
- if (tTooManyAcMsgShown + 180.0 < simTime) { // Show message at most every 3 minutes
- SHOW_MSG(logWARN,MSG_TOO_MANY_AC,dataRefs.GetMaxNumAc());
- tTooManyAcMsgShown = simTime;
- }
-
- // If we didn't find an active a/c farther away than us then bail with message
- if (!pFarestAc) {
- return false;
+ // If we didn't find an active a/c farther away than us then bail
+ if (!pFarestAc)
+ return false;
+
+ // We found the a/c farest away...remove it to make room for us!
+ pFarestAc->DestroyAircraft();
}
- // We found the a/c farest away...remove it!
- pFarestAc->DestroyAircraft();
+ // There is a slot now - either there was already or we made room
return true;
}
@@ -2447,7 +2454,7 @@ bool LTFlightData::CreateAircraft ( double simTime )
if ( hasAc() ) return true;
// exit if too many a/c shown and this one wouldn't be one of the nearest ones
- if (!AcSlotAvailable(simTime))
+ if (!AcSlotAvailable())
return false;
try {
diff --git a/Src/LTImgWindow.cpp b/Src/LTImgWindow.cpp
index 67a9b21e..2df253a1 100644
--- a/Src/LTImgWindow.cpp
+++ b/Src/LTImgWindow.cpp
@@ -271,7 +271,7 @@ IMGUI_API bool SliderDr(const char* label, dataRefsLT idx,
if (v_step > 1)
iV = (iV+(v_step/2))/v_step * v_step;
// When entering manually [Ctrl+Click], values aren't clamped, so we take care of it
- cfgSet(idx, clamp(iV, v_min, v_max)); // set dataRef value
+ cfgSet(idx, std::clamp(iV, v_min, v_max)); // set dataRef value
return true;
}
else
diff --git a/Src/LTMain.cpp b/Src/LTMain.cpp
index 61ac24f2..434a54b1 100644
--- a/Src/LTMain.cpp
+++ b/Src/LTMain.cpp
@@ -201,7 +201,7 @@ bool FileRecLookup (std::ifstream& f, size_t& n,
// Determine number of records if not (yet) known
if (f && n == 0) {
f.seekg(0, std::ios_base::end);
- n = f.tellg() / recLen;
+ n = size_t(f.tellg()) / recLen;
if (!f) return false;
}
@@ -218,15 +218,15 @@ bool FileRecLookup (std::ifstream& f, size_t& n,
m = L + (size_t)std::floor(float(key-Al)/float(Ar-Al) * (R-L));
// test if record at m is less than the key
- f.seekg(m * recLen);
- f.read((char*)outRec, recLen);
+ f.seekg((long long)(m * recLen));
+ f.read((char*)outRec, (std::streamsize)recLen);
if (!f) return false;
if (*pAm == key) {
return true;
}
else if (*pAm < key) {
L = m+1; // move to _next_ record as we are too small
- f.read((char*)outRec, recLen);
+ f.read((char*)outRec, (std::streamsize)recLen);
Al = *(unsigned long*)outRec;
if (Al == key) // that next record is our value?
return true;
@@ -631,6 +631,9 @@ int MPIntPrefsFunc (const char*, const char* key, int iDefault)
if (!strcmp(key, XPMP_CFG_ITM_REPLDATAREFS) ||
!strcmp(key, XPMP_CFG_ITM_REPLTEXTURE))
return dataRefs.ShallCpyObjFiles();
+ // Support XPMP2 Remote Clinet?
+ if (!strcmp(key, XPMP_CFG_ITM_SUPPORT_REMOTE))
+ return dataRefs.GetRemoteSupport();
// dont' know/care about the option, return the default value
return iDefault;
diff --git a/Src/LTOpenGlider.cpp b/Src/LTOpenGlider.cpp
index a8411cf9..65a8b299 100644
--- a/Src/LTOpenGlider.cpp
+++ b/Src/LTOpenGlider.cpp
@@ -141,7 +141,8 @@ std::string OpenGliderConnection::GetURL (const positionTy& pos)
// We only return a URL if we are to use the request/reply procedure
if (bFailoverToHttp || DataRefs::GetCfgInt(DR_CFG_OGN_USE_REQUREPL)) {
APRSClose(); // make sure the ARPS connection is off, will return quickly if not even running
- boundingBoxTy box (pos, dataRefs.GetFdStdDistance_m());
+ // Bounding box the size of the configured distance...plus 10% so we have data in hand once the plane comes close enough
+ boundingBoxTy box (pos, double(dataRefs.GetFdStdDistance_m()) * 1.10 );
char url[128] = "";
snprintf(url, sizeof(url),
OPGLIDER_URL,
@@ -152,7 +153,7 @@ std::string OpenGliderConnection::GetURL (const positionTy& pos)
return std::string(url);
} else {
// otherwise (and by default) we are to use the direct APRS connection
- APRSStartUpdate(pos, dataRefs.GetFdStdDistance_km());
+ APRSStartUpdate(pos, (unsigned)dataRefs.GetFdStdDistance_km());
return std::string();
}
}
@@ -196,7 +197,7 @@ bool OpenGliderConnection::ProcessFetchedData (mapLTFlightDataTy& fdMap)
}
// then this is the marker definition to work on
- const std::string sMarker (sPos, sEnd-sPos);
+ const std::string sMarker (sPos, std::string::size_type(sEnd-sPos));
std::vector tok = str_tokenize(sMarker, ",", false);
if (tok.size() != GNF_COUNT) {
LOG_MSG(logERR, ERR_OGN_WRONG_NUM_FIELDS, sMarker.c_str());
@@ -244,8 +245,8 @@ bool OpenGliderConnection::ProcessFetchedData (mapLTFlightDataTy& fdMap)
fd.SetKey(fdKey);
// Aircraft type converted from Flarm AcftType
- const FlarmAircraftTy acTy = (FlarmAircraftTy)clamp(std::stoi(tok[GNF_FLARM_ACFT_TYPE]),
- FAT_UNKNOWN, FAT_STATIC_OBJ);
+ const FlarmAircraftTy acTy = (FlarmAircraftTy)std::clamp(std::stoi(tok[GNF_FLARM_ACFT_TYPE]),
+ FAT_UNKNOWN, FAT_STATIC_OBJ);
stat.catDescr = OGNGetAcTypeName(acTy);
// If we still have no accurate ICAO type then we need to fall back to some configured defaults
@@ -608,7 +609,7 @@ bool OpenGliderConnection::APRSProcessLine (const std::string& ln)
dyn.gnd = false; // there is no GND indicator in OGN data
dyn.heading = std::stod(m.str(M_HEAD));
dyn.spd = std::stod(m.str(M_SPEED));
- if (m.size() > M_VSI)
+ if (m.size() > M_VSI && !m.str(M_VSI).empty())
dyn.vsi = std::stod(m.str(M_VSI));
dyn.pChannel = this;
@@ -796,7 +797,7 @@ static void OGNAcListOneLine (OGNCbHandoverTy& ho, std::string::size_type posEnd
if (tok[0][0] == '#') {
tok[0].erase(0,1); // remove the #
// walk the tokens and remember the column indexes per field
- for (int i = 0; i < (int)tok.size(); i++) {
+ for (unsigned i = 0; i < (unsigned)tok.size(); i++) {
if (tok[i] == "DEVICE_ID") ho.devIdIdx = i;
else if (tok[i] == "DEVICE_TYPE") ho.devTypeIdx = i;
else if (tok[i] == "AIRCRAFT_MODEL") ho.mdlIdx = i;
@@ -991,7 +992,7 @@ const std::string& OGNGetIcaoAcType (FlarmAircraftTy _acTy)
if (icaoTypes.empty()) return dataRefs.GetDefaultAcIcaoType();
if (icaoTypes.size() == 1) return icaoTypes.front();
// more than one type defined, take a random pick
- const size_t i = randoml(0, long(icaoTypes.size())-1);
+ const size_t i = (size_t)randoml(0, long(icaoTypes.size())-1);
assert(0 <= i && i < icaoTypes.size());
return icaoTypes[i];
}
diff --git a/Src/LTOpenSky.cpp b/Src/LTOpenSky.cpp
index 206b242c..8b478a0f 100644
--- a/Src/LTOpenSky.cpp
+++ b/Src/LTOpenSky.cpp
@@ -44,7 +44,8 @@ LTFlightDataChannel()
// put together the URL to fetch based on current view position
std::string OpenSkyConnection::GetURL (const positionTy& pos)
{
- boundingBoxTy box (pos, dataRefs.GetFdStdDistance_m());
+ // we add 10% to the bounding box to have some data ready once the plane is close enough for display
+ boundingBoxTy box (pos, double(dataRefs.GetFdStdDistance_m()) * 1.10);
char url[128] = "";
snprintf(url, sizeof(url),
OPSKY_URL_ALL,
diff --git a/Src/LTRealTraffic.cpp b/Src/LTRealTraffic.cpp
index c8305c77..c69e8499 100644
--- a/Src/LTRealTraffic.cpp
+++ b/Src/LTRealTraffic.cpp
@@ -243,9 +243,16 @@ std::string RealTrafficConnection::GetStatusWithTimeStr() const
std::string s (GetStatusStr());
if (IsConnected() && lastReceivedTime > 0.0) {
char sIntvl[50];
+ // add when the last msg was received
long intvl = std::lround(dataRefs.GetSimTime() - lastReceivedTime);
snprintf(sIntvl,sizeof(sIntvl),MSG_RT_LAST_RCVD,intvl);
s += sIntvl;
+ // if receiving historic traffic say so
+ if (tsAdjust > 1.0) {
+ snprintf(sIntvl, sizeof(sIntvl), MSG_RT_ADJUST,
+ GetAdjustTSText().c_str());
+ s += sIntvl;
+ }
}
return s;
}
@@ -303,6 +310,9 @@ bool RealTrafficConnection::StopConnections()
// stop both TCP and UDP
StopTcpConnection();
StopUdpConnection();
+
+ // Clear the list of historic time stamp differences
+ dequeTS.clear();
// stopped
SetStatus(RT_STATUS_NONE);
@@ -649,32 +659,37 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
if (numId == 0)
return true; // ignore silently
+ // RealTraffic sends bursts of data every 10s, but that doesn't necessarily
+ // mean that anything really moved. Data could be stale.
+ // So here we just completely ignore data which looks exactly like the previous datagram
+ if (IsDatagramDuplicate(numId, traffic))
+ return true; // ignore silently
+
// *** position time ***
- using namespace std::chrono;
// There are 2 possibilities:
// 1. As of v7.0.55 RealTraffic can send a timestamp (when configured
// to use the "LiveTraffic" as Simulator in use, I assume)
// 2. Before that or with other settings there is no timestamp
- // so we assume 'now', corrected by network time offset
+ // so we assume 'now'
- const double posTime =
+ double posTime;
// Timestamp included?
- (tfc[RT_TFC_MSG_TYPE] == RT_TRAFFIC_AITFC &&
- tfc.size() >= RT_TFC_TIMESTAMP+1) ?
- // use that delivered timestamp
- std::stod(tfc[RT_TFC_TIMESTAMP]) :
- // system time in microseconds
- double(duration_cast(system_clock::now().time_since_epoch()).count())
- // divided by 1000000 to create seconds with fractionals
- / 1000000.0;
-
- // check for duplicate data
- // RealTraffic sends bursts of data every 10s, but that doesn't necessarily
- // mean that anything really moved. Data could be stale.
- // But as data doesn't come with a timestamp we have no means of identifying it.
- // So here we just completely ignore data which looks exactly like the previous datagram
- if (IsDatagramDuplicate(numId, posTime, traffic))
- return true; // ignore silently
+ if (tfc[RT_TFC_MSG_TYPE] == RT_TRAFFIC_AITFC && tfc.size() >= RT_TFC_TIMESTAMP+1)
+ {
+ // use that delivered timestamp and (potentially) adjust it if it is in the past
+ posTime = std::stod(tfc[RT_TFC_TIMESTAMP]);
+ AdjustTimestamp(posTime);
+ }
+ else
+ {
+ // No Timestamp provided: assume 'now'
+ using namespace std::chrono;
+ posTime =
+ // system time in microseconds
+ double(duration_cast(system_clock::now().time_since_epoch()).count())
+ // divided by 1000000 to create seconds with fractionals
+ / 1000000.0;
+ }
// *** Process received data ***
@@ -701,10 +716,6 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
return false;
}
- // is position close enough to current pos?
- if (posCamera.dist(pos) > dataRefs.GetFdStdDistance_m())
- return true; // ignore silently, no error
-
try {
// from here on access to fdMap guarded by a mutex
// until FD object is inserted and updated
@@ -807,9 +818,77 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
}
+// Determine timestamp adjustment necessairy in case of historic data
+void RealTrafficConnection::AdjustTimestamp (double& ts)
+{
+ // the assumed 'now' is simTime + buffering period
+ const double now = dataRefs.GetSimTime() + dataRefs.GetFdBufPeriod();
+
+ // *** Keep the rolling list of timestamps diffs, max length: 11 ***
+ dequeTS.push_back(now - ts);
+ while (dequeTS.size() > 11)
+ dequeTS.pop_front();
+
+ // *** Determine Median of timestamp differences ***
+ double medianTs;
+ if (dequeTS.size() >= 3)
+ {
+ // To find the (lower) Median while at the same time preserve the deque in its order,
+ // we do a partial sort into another array
+ std::vector v((dequeTS.size()+1)/2);
+ std::partial_sort_copy(dequeTS.cbegin(), dequeTS.cend(),
+ v.begin(), v.end());
+ medianTs = v.back();
+ }
+ // with less than 3 sample we just pick the last
+ else {
+ medianTs = dequeTS.back();
+ }
+
+ // *** Need to change the timestamp adjustment?
+ // Priority has to change back to zero if we are half the buffering period away from "now"
+ const int halfBufPeriod = dataRefs.GetFdBufPeriod()/2;
+ if (medianTs < 0.0 ||
+ std::abs(medianTs) <= halfBufPeriod) {
+ if (tsAdjust > 0.0) {
+ tsAdjust = 0.0;
+ SHOW_MSG(logINFO, INFO_RT_REAL_TIME);
+ }
+ }
+ // ...if that median is more than half the buffering period away from current adjustment
+ else if (std::abs(medianTs - tsAdjust) > halfBufPeriod)
+ {
+ // new adjustment is that median, rounded to 10 seconds
+ tsAdjust = std::round(medianTs / 10.0) * 10.0;
+ SHOW_MSG(logINFO, INFO_RT_ADJUST_TS, GetAdjustTSText().c_str());
+ }
+
+ // Adjust the passed-in timestamp by the determined adjustment
+ ts += tsAdjust;
+}
+
+
+// Return a string describing the current timestamp adjustment
+std::string RealTrafficConnection::GetAdjustTSText () const
+{
+ char timeTxt[25];
+ if (tsAdjust < 300.0) // less than 5 minutes: tell seconds
+ snprintf(timeTxt, sizeof(timeTxt), "%.0fs", tsAdjust);
+ else if (tsAdjust < 86400.0) // less than 1 day
+ snprintf(timeTxt, sizeof(timeTxt), "%ld:%02ldh",
+ long(tsAdjust/3600.0), // hours
+ long(tsAdjust/60.0) % 60); // minutes
+ else
+ snprintf(timeTxt, sizeof(timeTxt), "%ldd %ld:%02ldh",
+ long(tsAdjust/86400), // days
+ long(tsAdjust/3600.0) % 24, // hours
+ long(tsAdjust/60.0) % 60); // minutes
+ return std::string(timeTxt);
+}
+
+
// Is it a duplicate? (if not datagram is copied into a map)
bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
- double posTime,
const char* datagram)
{
// access is guarded by a lock
@@ -819,7 +898,9 @@ bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
auto it = mapDatagrams.find(numId);
if (it == mapDatagrams.end()) {
// add the datagram the first time for this plane
- mapDatagrams.emplace(numId, RTUDPDatagramTy(posTime,datagram));
+ mapDatagrams.emplace(std::piecewise_construct,
+ std::forward_as_tuple(numId),
+ std::forward_as_tuple(dataRefs.GetSimTime(),datagram));
// no duplicate
return false;
}
@@ -830,7 +911,7 @@ bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
return true;
// plane known, but data different, replace data in map
- d.posTime = posTime;
+ d.posTime = dataRefs.GetSimTime();
d.datagram = datagram;
// no duplicate
diff --git a/Src/LiveTraffic.cpp b/Src/LiveTraffic.cpp
index bc916627..7cdb7d41 100755
--- a/Src/LiveTraffic.cpp
+++ b/Src/LiveTraffic.cpp
@@ -578,8 +578,18 @@ PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * /*i
if (inMsg == XPLM_MSG_RELEASE_PLANES)
{
char who[256] = "?";
- XPLMGetPluginInfo(inFrom, who, NULL, NULL, NULL);
- SHOW_MSG(logINFO, INFO_REQU_AI_RELEASE, who);
+ char signature[256] = "";
+ XPLMGetPluginInfo(inFrom, who, NULL, signature, NULL);
+
+ // We do cease control to the XPMP2 Remote Control plugin
+ // because that one will display our traffic, too:
+ if (strncmp(signature, REMOTE_SIGNATURE, sizeof(signature)) == 0) {
+ LOG_MSG(logINFO, INFO_REQU_AI_REMOTE);
+ LTMainReleaseAIAircraft();
+ MenuUpdateAllItemStatus();
+ } else {
+ SHOW_MSG(logINFO, INFO_REQU_AI_RELEASE, who);
+ }
return;
}
diff --git a/Src/Network.cpp b/Src/Network.cpp
index 6ba8fa1f..b43788e7 100644
--- a/Src/Network.cpp
+++ b/Src/Network.cpp
@@ -25,6 +25,7 @@
// All includes are collected in one header
#include "LiveTraffic.h"
+
#if IBM
#include
#include
@@ -32,6 +33,7 @@
#define errno WSAGetLastError() // https://docs.microsoft.com/en-us/windows/desktop/WinSock/error-codes-errno-h-errno-and-wsagetlasterror-2
#define close closesocket
typedef USHORT in_port_t;
+typedef int socklen_t; // in Winsock, an int is passed as length argument
#else
#include
#include
@@ -135,7 +137,7 @@ void SocketNetworking::Open(const std::string& _addr, int _port,
}
// bind the socket to the address:port
- r = bind(f_socket, addrinfo->ai_addr, (int)addrinfo->ai_addrlen);
+ r = bind(f_socket, addrinfo->ai_addr, (socklen_t)addrinfo->ai_addrlen);
if(r != 0)
throw NetRuntimeError(("could not bind UDP socket with: \"" + f_addr + ":" + decimal_port + "\"").c_str());
@@ -199,7 +201,7 @@ void SocketNetworking::Connect(const std::string& _addr, int _port,
#endif
// actually connect
- r = connect(f_socket, addrinfo->ai_addr, (int)addrinfo->ai_addrlen);
+ r = connect(f_socket, addrinfo->ai_addr, (socklen_t)addrinfo->ai_addrlen);
if(r != 0)
throw NetRuntimeError(("could not connect to: \"" + f_addr + ":" + decimal_port + "\"").c_str());
@@ -303,7 +305,7 @@ long SocketNetworking::recv()
return -1;
}
- long ret = ::recv(f_socket, buf, (int)bufSize-1, 0);
+ long ret = ::recv(f_socket, buf, (socklen_t)bufSize-1, 0);
if (ret >= 0) { // we did receive something
buf[ret] = 0; // zero-termination
} else {
@@ -383,7 +385,7 @@ bool SocketNetworking::broadcast (const char* msg)
s.sin_addr.s_addr = htonl(INADDR_BROADCAST);
while (index(dataRefs.GetRemoteSupport(),-1,1); // this turns the order around: 0 - on, 1 - Auto, 2 - Off
+ if (ImGui::Combo("##RemoteSupport", &n, "Always On\0Auto Detect (default)\0Off\0", 5))
+ DATA_REFS_LT[DR_CFG_REMOTE_SUPPORT].setData(1-n);
+ ImGui::TableNextCell();
+ }
if (!*sFilter) ImGui::TreePop();
}
@@ -698,7 +707,7 @@ void LTSettingsUI::buildInterface()
DataRefs::vecCSLPaths& vec = dataRefs.GetCSLPaths();
for (int i = 0; (size_t)i < vec.size(); ++i)
{
- DataRefs::CSLPathCfgTy& pathCfg = vec[i];
+ DataRefs::CSLPathCfgTy& pathCfg = vec[size_t(i)];
ImGui::PushID(pathCfg.getPath().c_str());
// Enable Checkbox / Load Button
diff --git a/docs/readme.html b/docs/readme.html
index 6d179944..0415037d 100755
--- a/docs/readme.html
+++ b/docs/readme.html
@@ -25,41 +25,13 @@
Documentation
and, certainly,
- do the setup.
+ do the setup,
+ which includes not only copying the plugin itself,
+ but also the installation of CSL models.
-
Upgrade from v1.5 to v2.0
-
-
Please follow
- upgrade instructions
- for upgrading an existing LiveTraffic installation to version 2.0.
-
-
-
Essential Installation
-
-
LiveTraffic plugin
-
-
- The LiveTraffic plugin folder is to be copied into X-Plane's
- Resources/plugins folder, the same as with other plugins.
-
-
-
CSL Packages
-
- You must install CSL packages, otherwise there will be no
- aircraft models to display and LiveTraffic will fail to
- start up.
-
-
- Recommendation is the "Bluebell" OBJ8 CSL packages by Oktalist found at the
- X-Plane.org forums.
- Follow their instructions. Also download and install the
- BB_IVAO_vert_offsets_* files in the downloads as recommended
- for the X-IvAp users. LiveTraffic can handle them, too.
-
-
Resulting Directory Structure
...under X-Plane's Resources/plugins folder:
@@ -82,7 +54,44 @@
Resulting Directory Structure
Release Notes
-
The number-link refers to the issue in GitHub with (often technical) details.
+
The issue number links refer to issue on GitHub with (often technical) details.
+
+
v2.3
+
+
v2.30
+
+
For updating coming from v2.20
+
+
copy lin|mac|win_x64/LiveTraffic.xpl.
+
+
+
+
Change log:
+
+
+
ADDED support for XPMP2 Remote Client and TCAS Concentrator (XPMP2 issues
+ #22 and
+ #16):
+ After installing XPMP2 Remote Client, LiveTraffic's planes are
+ synchronized across X-Plane instances in the network, e.g. supporting
+ External Visuals, Networked Multiplayer.
+ Locally installed, the Remote Client combines planes from several
+ XPMP2-based clients for one combined view on TCAS.
+ See documentation
+ for more.
+
ADDED issue #199
+ support for RealTraffic's historic data feed:
+ LiveTraffic recognizes
+ when fed with data from the past and adjusts the data so that it displays now.
+ Use the time slider in the RealTraffic app to go back in time.
+
FIXED issue #201,
+ avoiding creation and immediate destruction of planes when max number
+ of configured planes is reached. Also, available tracking data is
+ kept in memory if plane is removed for whatever reason, so it is
+ faster available again when coming back in reach.
+
FIXED a potential crash related to unexpectedly formatted
+ Open Glider Network data.