Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ endif()
set(CACHE{CMAKE_BUILD_TYPE} TYPE STRING VALUE "RelWithDebInfo")

project(LiveTraffic
VERSION 4.3.5
VERSION 4.4.0
DESCRIPTION "LiveTraffic X-Plane plugin")
set(VERSION_BETA 0)

Expand Down
Binary file not shown.
Binary file added Data/AirplanesLive/AirplanesLive.sjson/data
Binary file not shown.
Binary file added Data/AirplanesLive/AirplanesLive.sjson/metaData
Binary file not shown.
3 changes: 3 additions & 0 deletions Include/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
#define CFG_DEFAULT_CAR_TYPE "DEFAULT_CAR_TYPE"
#define CFG_DEFAULT_AC_TYP_INFO "Default a/c type is '%s'"
#define CFG_DEFAULT_CAR_TYP_INFO "Default car type is '%s'"
#define CFG_SOUND_DEVICE "Sound_Device"
#define CFG_SND_NO_DEVICE "(no change)"
#define CFG_OPENSKY_CLIENT "OpenSky_Client"
#define CFG_OPENSKY_SECRET "OpenSky_Secret"
#define CFG_ADSBEX_API_KEY "ADSBEX_API_KEY"
Expand Down Expand Up @@ -266,6 +268,7 @@ constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
#define HELP_SET_CH_OPENSKY "setup/installation/opensky"
#define HELP_SET_CH_ADSBHUB "setup/installation/adsbhub"
#define HELP_SET_CH_ADSBEX "setup/installation/ads-b-exchange"
#define HELP_SET_CH_AIRPLANES "setup/installation/airplanes.live"
#define HELP_SET_CH_ADSBFI "setup/installation/adsb.fi"
#define HELP_SET_CH_OPENGLIDER "setup/installation/ogn"
#define HELP_SET_CH_REALTRAFFIC "setup/installation/realtraffic-connectivity"
Expand Down
10 changes: 10 additions & 0 deletions Include/CoordCalc.h
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,16 @@ struct boundingBoxTy {
positionTy sw () const { return positionTy(se.lat(), nw.lon()); }
/// north-east corner ("maximum")
positionTy ne () const { return positionTy(nw.lat(), se.lon()); }

double& top () { return nw.lat(); }
double& bottom () { return se.lat(); }
double& left () { return nw.lon(); }
double& right () { return se.lon(); }

double top () const { return nw.lat(); }
double bottom () const { return se.lat(); }
double left () const { return nw.lon(); }
double right () const { return se.lon(); }

// standard string for any output purposes
operator std::string() const;
Expand Down
18 changes: 17 additions & 1 deletion Include/DataRefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ enum dataRefsLT {
DR_SIM_DATE,
DR_SIM_TIME,

DR_CAMERA_CONTROL, ///< Does LiveTraffic have camera control?

DR_LT_VER, ///< LiveTraffic's version number, like 201 for v2.01
DR_LT_VER_DATE, ///< LiveTraffic's version date, like 20200430 for 30-APR-2020

Expand Down Expand Up @@ -449,8 +451,9 @@ enum dataRefsLT {
DR_CHANNEL_OPEN_SKY_ONLINE,
DR_CHANNEL_OPEN_SKY_AC_MASTERDATA,
DR_CHANNEL_OPEN_SKY_AC_MASTERFILE,
DR_CHANNEL_ADSB_FI_ONLINE,
DR_CHANNEL_ADSB_EXCHANGE_ONLINE,
DR_CHANNEL_ADSB_FI_ONLINE,
DR_CHANNEL_AIRPLANES_LIVE,
DR_CHANNEL_REAL_TRAFFIC_ONLINE, // currently highest-prio channel
// always last, number of elements:
CNT_DATAREFS_LT
Expand Down Expand Up @@ -768,6 +771,7 @@ class DataRefs

std::string sDefaultAcIcaoType = CSL_DEFAULT_ICAO_TYPE;
std::string sDefaultCarIcaoType = CSL_CAR_ICAO_TYPE;
std::string sSoundDevice = CFG_SND_NO_DEVICE; ///< Output sound device name
std::string sOpenSkyClient; ///< OpenSky Network Client ID
std::string sOpenSkySecret; ///< OpenSky Network Client Secret
std::string sADSBExAPIKey; ///< ADS-B Exchange API key
Expand All @@ -783,6 +787,10 @@ class DataRefs
std::string keyAc; // key (transpIcao) for a/c whose data is returned
const LTAircraft* pAc = nullptr; // ptr to that a/c

// Track camera control
const int MAX_CYCLE_NO_CAMERA_CB = 6;
int nCycleWithoutCameraCB = MAX_CYCLE_NO_CAMERA_CB; ///< How many flight loop cycles did we do without receiving a camera callback? (Anything larger than 5 is considered "No camera control")

// Weather
float lastWeatherAttempt = 0.0f; ///< last time we _tried_ to update the weather
float lastWeatherUpd = 0.0f; ///< last time the weather was updated? (in XP's network time)
Expand Down Expand Up @@ -918,7 +926,11 @@ class DataRefs
static float LTGetAcInfoF(void* p);

void SetCameraAc(const LTAircraft* pCamAc); ///< sets the data of the shared datarefs to point to `ac` as the current aircraft under the camera
void CntCyclesWithoutCamera(); ///< Count flight loop callbacks without camera callback
void CntCameraCallback(); ///< Count the fact that there was a camera callback -> resets `nCycleWithoutCameraCB`
static void ClearCameraAc(void*); ///< shared dataRef callback: Whenever someone else writes to the shared dataRef we clear our a/c camera information
// livetraffic/camera/control
static int LTHasCameraControl(void*); ///< Does LT have camera control?

// seconds since epoch including fractionals
double GetSimTime() const { return lastSimTime; }
Expand Down Expand Up @@ -963,6 +975,10 @@ class DataRefs
inline bool GetAutoStart() const { return bAutoStart != 0; }
int GetVolumeMaster() const { return volMaster; }
bool ShallForceFmodInstance() const { return sndForceFmodInstance != 0; }
const std::string& GetSoundDevice () const { return sSoundDevice; }
bool SetSoundDevice (const std::string& dev);
std::vector<std::string> GetAllSoundDeviceNames (bool bForceIncludeCurrent) const;;
void SetSound (); ///< Set sound according to volMaster and sSoundDevice
inline bool IsAIonRequest() const { return bAIonRequest != 0; }
bool IsAINotOnGnd() const { return bAINotOnGnd != 0; }
static int HaveAIUnderControl(void* =NULL) { return XPMPHasControlOfAIAircraft(); }
Expand Down
35 changes: 31 additions & 4 deletions Include/LTADSBEx.h
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
/// @file LTADSBEx.h
/// @brief ADS-B Exchange and adsb.fi: Requests and processes live tracking data
/// @see Airplanes.live: https://airplanes.live/api-guide/
/// @see adsb.fi: https://github.com/adsbfi/opendata
/// @see ADSBEx: https://www.adsbexchange.com/
/// RAPID API: https://rapidapi.com/adsbx/api/adsbexchange-com1
/// RAPID API Endpoint: https://rapidapi.com/adsbx/api/adsbexchange-com1/playground/endpoint_7dee5835-86b3-40ce-a402-f1ab43240884
/// ADSBEx v2 API documentation:
/// ...on Swagger: https://adsbexchange.com/api/aircraft/v2/docs
/// ...fields: https://www.adsbexchange.com/version-2-api-wip/
/// @see adsb.fi: https://github.com/adsbfi/opendata
/// @details Defines a base class handling the ADSBEx data format,
/// which is shared by both ADS-B Exchange and adsb.fi.
/// @details Defines AirplanesLiveConnection:\n
/// - Provides a proper REST-conform URL
/// @details Defines ADSBfiConnection:\n
/// - Provides a proper REST-conform URL
/// @details Defines ADSBExchangeConnection:\n
/// - Handles the API key\n
/// - Provides a proper REST-conform URL for both the original sevrer as well as for the Rapid API server.
/// @details Defines ADSBfiConnection:\n
/// - Provides a proper REST-conform URL
/// @author Birger Hoppe
/// @copyright (c) 2018-2024 Birger Hoppe
/// @copyright Permission is hereby granted, free of charge, to any person obtaining a
Expand Down Expand Up @@ -166,6 +169,30 @@ class ADSBExchangeConnection : public ADSBBase
static size_t DoTestADSBExAPIKeyCB (char *ptr, size_t, size_t nmemb, void* userdata);
};

//
// MARK: Airplanes.live
//

#define AIRPLANES_CHECK_NAME "Airplanes.live Map"
#define AIRPLANES_CHECK_URL "https://globe.airplanes.live/?lat=%.3f&lon=%.3f"
#define AIRPLANES_SLUG_BASE "https://globe.airplanes.live/?icao=" // + icao24 hex code
#define AIRPLANES_CHECK_POPUP "Check Airplane.live's coverage"

#define AIRPLANES_NAME "Airplanes.live"
#define AIRPLANES_URL "https://api.airplanes.live/v2/point/%.3f/%.3f/%d" // lat/lon/radius

class AirplanesLiveConnection : public ADSBBase
{
public:
AirplanesLiveConnection (); ///< Constructor
std::string GetURL (const positionTy& pos) override; ///< Compile Airplanes.live request URL

protected:
void Main () override; ///< virtual thread main function
bool ProcessErrors (const JSON_Object*) override ///< No specific error processing for Airplanes.live
{ return true; }
};

//
// MARK: adsb.fi
//
Expand All @@ -176,7 +203,7 @@ class ADSBExchangeConnection : public ADSBBase
#define ADSBFI_CHECK_POPUP "Check adsb.fi's coverage"

#define ADSBFI_NAME "adsb.fi"
#define ADSBFI_URL "https://opendata.adsb.fi/api/v2/lat/%f/lon/%f/dist/%d/"
#define ADSBFI_URL "https://opendata.adsb.fi/api/v2/lat/%.3f/lon/%.3f/dist/%d/"

#define ADSBFI_AIRCRAFT_ARR "aircraft"

Expand Down
55 changes: 34 additions & 21 deletions Include/LTRealTraffic.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#define RT_CHECK_NAME "RealTraffic Web Site"
#define RT_CHECK_URL "https://www.flyrealtraffic.com/"
#define RT_CHECK_POPUP "Open RealTraffic's web site"
#define RT_SLUG "https://www.flyrealtraffic.com/livemap/?hex=%lx"

#define REALTRAFFIC_NAME "RealTraffic"

Expand All @@ -56,7 +57,7 @@
#define RT_WEATHER_POST "GUID=%s&lat=%.2f&lon=%.2f&alt=%ld&airports=%s&querytype=locwx&toffset=%ld"
#define RT_TRAFFIC_URL RT_BASE_URL "/traffic"
#define RT_TRAFFIC_POST "GUID=%s&top=%.2f&bottom=%.2f&left=%.2f&right=%.2f&querytype=locationtraffic&toffset=%ld"
#define RT_TRAFFIC_POST_BUFFER "GUID=%s&top=%.2f&bottom=%.2f&left=%.2f&right=%.2f&querytype=locationtraffic&toffset=%ld&buffercount=%d&buffertime=10"
#define RT_TRAFFIC_POST_BUFFER "GUID=%s&top=%.2f&bottom=%.2f&left=%.2f&right=%.2f&querytype=locationtraffic&toffset=%ld&buffercount=%d&buffertime=%d"
#define RT_TRAFFIC_POST_PARKED "GUID=%s&top=%.2f&bottom=%.2f&left=%.2f&right=%.2f&querytype=parkedtraffic&toffset=%ld"

#define RT_LOCALHOST "0.0.0.0"
Expand Down Expand Up @@ -90,7 +91,8 @@ constexpr std::chrono::seconds RT_DRCT_ERR_WAIT = std::chrono::seconds(5); ///<
constexpr std::chrono::seconds RT_DRCT_ERR_RATE = std::chrono::seconds(10); ///< wait in case of rate violations, too many sessions
constexpr std::chrono::minutes RT_DRCT_WX_WAIT = std::chrono::minutes(1); ///< How often to update weather?
constexpr int RT_DRCT_MAX_WX_ERR = 5; ///< Max number of consecutive errors during initial weather requests we wait for...before not asking for weather any longer
constexpr int RT_CNT_SEND_TIMING = 240; ///< RT App: After how many position position message also to send a timing message? (with 250ms period, 240 means: every minute)
constexpr int RT_CNT_SEND_TIMING = 240; ///< RT App: After how many position messages also to send a timing message? (with 250ms period, 240 means: every minute)
constexpr int RT_BUFFER_PERIOD = 10; ///< [s] When requesting buffered traffic, how much time between two buffers?

/// Fields in a response of a direct connection's request
enum RT_DIRECT_FIELDS_TY {
Expand Down Expand Up @@ -254,7 +256,7 @@ class RealTrafficConnection : public LTFlightDataChannel
RT_STATUS_CONNECTED_FULL, // both connected to, and have received UDP data
RT_STATUS_STOPPING
};

protected:
// general lock to synch thread access to object members
std::recursive_mutex rtMutex;
Expand Down Expand Up @@ -282,18 +284,18 @@ class RealTrafficConnection : public LTFlightDataChannel
positionTy pos; ///< viewer position for which we receive Realtraffic data
long tOff = 0; ///< [min] time offset for which we request data
} curr; ///< Data for the current request

/// What's the next time we could send a traffic request?
std::chrono::time_point<std::chrono::steady_clock> tNextTraffic;
/// What's the next time we could send a weather request?
std::chrono::time_point<std::chrono::steady_clock> tNextWeather;

/// METAR entry in the NearestMETAR response
struct NearestMETAR {
std::string ICAO = RT_METAR_UNKN; ///< ICAO code of METAR station
double dist = NAN; ///< distance to station
double brgTo = NAN; ///< bearing to station

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

Expand Down Expand Up @@ -328,46 +330,52 @@ class RealTrafficConnection : public LTFlightDataChannel
long lTotalFlights = -1;
/// Shall we check for parked traffic next time around? (Set from main thread after airport data updates)
bool bDoParkedTraffic = false;

// TCP connection to send current position
std::thread thrTcpServer; ///< thread of the TCP listening thread (short-lived)
XPMP2::TCPConnection tcpPosSender; ///< TCP connection to communicate with RealTraffic
/// Status of the separate TCP listening thread
volatile ThrStatusTy eTcpThrStatus = THR_NONE;

// UDP sockets
XPMP2::UDPReceiver udpTrafficData; ///< UDP receiver for traffic data (port 49005)
XPMP2::UDPReceiver udpWeatherData; ///< UDP receiver for weather data (port 49004)
#if APL == 1 || LIN == 1
// the self-pipe to shut down the UDP listener thread gracefully
SOCKET udpPipe[2] = { INVALID_SOCKET, INVALID_SOCKET };
#endif
double lastReceivedTime = 0.0; // copy of simTime
/// last simtime that we received UDP traffic
double lastReceivedTime = 0.0;
/// last known position to detect fast movement (to request buffered traffic and the like)
positionTy lastKnownViewPos;
/// Expecting buffered traffic first?
bool bWaitForBuffers = true;
/// expected bu
// map of last received datagrams for duplicate detection
std::map<unsigned long,RTUDPDatagramTy> mapDatagrams;
/// rolling list of timestamp (diff to now) for detecting historic sending
std::deque<double> dequeTS;
/// [s] current timestamp adjustment
double tsAdjust = 0.0;

public:
RealTrafficConnection ();

void Stop (bool bWaitJoin) override; ///< Stop the UDP listener gracefully

// interface called from LTChannel
// SetValid also sets internal status
void SetValid (bool _valid, bool bMsg = true) override;

/// Have connection read traffic data at next chance
void DoReadParkedTraffic () { bDoParkedTraffic = true; }

// // shall data of this channel be subject to LTFlightData::DataSmoothing?
// bool DoDataSmoothing (double& gndRange, double& airbRange) const override
// { gndRange = RT_SMOOTH_GROUND; airbRange = RT_SMOOTH_AIRBORNE; return true; }
// // shall data of this channel be subject to LTFlightData::DataSmoothing?
// bool DoDataSmoothing (double& gndRange, double& airbRange) const override
// { gndRange = RT_SMOOTH_GROUND; airbRange = RT_SMOOTH_AIRBORNE; return true; }
// shall data of this channel be subject to hovering flight detection?
bool DoHoverDetection () const override { return true; }

// Status
std::string GetStatusText () const override; ///< return a human-readable status
bool isHistoric () const { return curr.tOff > 0; } ///< serving historic data?
Expand All @@ -382,7 +390,8 @@ class RealTrafficConnection : public LTFlightDataChannel
/// Which request do we need next and when can we send it?
std::chrono::time_point<std::chrono::steady_clock> SetRequType (const positionTy& pos);
public:
bool IsFirstTrafficRequ () const { return lTotalFlights < 0; } ///< Have not received any traffic data before?
int GetNumTrafficBuffers () const ///< How many buffers of buffered traffic would we request?
{ return std::min<int>(10, dataRefs.GetFdBufPeriod() / RT_BUFFER_PERIOD); }
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.
bool ProcessFetchedData () override; ///< in direct mode process the received data
Expand Down Expand Up @@ -415,17 +424,21 @@ class RealTrafficConnection : public LTFlightDataChannel
void SendXPSimTime(bool bForce); ///< Send XP's current simulated time to RealTraffic, adapted to "today or earlier", every once in a while, or if `bForce`
void SendPos (const positionTy& pos, double speed_m); ///< Send position/speed info for own ship to RealTraffic
void SendUsersPlanePos(); ///< Send user's plane's position/speed to RealTraffic
void RequestBufferTraffic(); ///< Send request for initial traffic for buffering
void RequestBufferTraffic(const positionTy& pos,
double radius_m); ///< Send request for initial traffic for buffering

// MARK: Data Processing
// Process received datagrams
bool ProcessRecvedTrafficData (const char* traffic);
bool ProcessRTTFC (LTFlightData::FDKeyTy& fdKey, const std::vector<std::string>& tfc); ///< Process a RTTFC type message
bool ProcessAITFC (LTFlightData::FDKeyTy& fdKey, const std::vector<std::string>& tfc); ///< Process a AITFC or XTRAFFICPSX type message
/// Process a RTTFC type message
bool ProcessRTTFC (LTFlightData::FDKeyTy& fdKey, const std::vector<std::string>& tfc, int nBuffer);
///< Process a AITFC or XTRAFFICPSX type message
bool ProcessAITFC (LTFlightData::FDKeyTy& fdKey, const std::vector<std::string>& tfc, int nBuffer);
bool ProcessRecvedWeatherData (const char* weather); ///< Process UDP weather JSON from RT Application
std::string GetSlug (unsigned long hex) const; ///< returns a slug string for a given hex id

/// Determine timestamp adjustment necessary in case of historic data
void AdjustTimestamp (double& ts);
void AdjustTimestamp (double& ts, int nBuffer);
/// Return a string describing the current timestamp adjustment
std::string GetAdjustTSText () const;

Expand Down
4 changes: 4 additions & 0 deletions Include/SettingsUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class LTSettingsUI : public LTImgWindow
std::string gndVehicleEntry; ///< edit buffer for ground vehicle
int gndVehicleOK = 0; ///< -1 error, 0 untested, 1 OK

// Advanced
std::vector<std::string> vecSndDevs; ///< List of possible sound devices
float tsSndDevsLastUpd = 0.0f; ///< when was that list updated last?

// Debug options
std::string txtDebugFilter; ///< filter for single aircraft
std::string txtFixAcType; ///< fixed aircraft type
Expand Down
Loading