From 44fa02aa5ac51b5112e11f3690ad8d77dd60003f Mon Sep 17 00:00:00 2001
From: DonLakeFlyer <don@thegagnes.com>
Date: Fri, 15 Nov 2019 18:04:19 -0800
Subject: [PATCH] ADSB server support provided through new ADSBVehicleManager
 implemenation

---
 qgroundcontrol.pro                            |   9 +-
 qgroundcontrol.qrc                            |   1 +
 src/ADSB/ADSBVehicle.cc                       |  68 +++++++
 src/{Vehicle => ADSB}/ADSBVehicle.h           |  30 ++-
 src/ADSB/ADSBVehicleManager.cc                | 188 ++++++++++++++++++
 src/ADSB/ADSBVehicleManager.h                 |  73 +++++++
 src/FlightDisplay/FlightDisplayViewMap.qml    |   3 +-
 src/FlightMap/MapItems/VehicleMapItem.qml     |  12 +-
 src/QGCLoggingCategory.cc                     |   1 +
 src/QGCLoggingCategory.h                      |   1 +
 src/QGCToolbox.cc                             |   3 +
 src/QGCToolbox.h                              |   3 +
 src/QmlControls/QGroundControlQmlGlobal.cc    |   1 +
 src/QmlControls/QGroundControlQmlGlobal.h     |   4 +
 .../ADSBVehicleManager.SettingsGroup.json     |  24 +++
 src/Settings/ADSBVehicleManagerSettings.cc    |  22 ++
 src/Settings/ADSBVehicleManagerSettings.h     |  24 +++
 src/Settings/SettingsManager.cc               |  24 ++-
 src/Settings/SettingsManager.h                |   4 +
 src/Vehicle/ADSBVehicle.cc                    |  98 ---------
 src/Vehicle/Vehicle.cc                        |  64 +++---
 src/Vehicle/Vehicle.h                         |   9 +-
 src/ui/preferences/GeneralSettings.qml        |  53 +++++
 23 files changed, 545 insertions(+), 174 deletions(-)
 create mode 100644 src/ADSB/ADSBVehicle.cc
 rename src/{Vehicle => ADSB}/ADSBVehicle.h (75%)
 create mode 100644 src/ADSB/ADSBVehicleManager.cc
 create mode 100644 src/ADSB/ADSBVehicleManager.h
 create mode 100644 src/Settings/ADSBVehicleManager.SettingsGroup.json
 create mode 100644 src/Settings/ADSBVehicleManagerSettings.cc
 create mode 100644 src/Settings/ADSBVehicleManagerSettings.h
 delete mode 100644 src/Vehicle/ADSBVehicle.cc

diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index 471c6dc367..d6d26c247e 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -398,6 +398,7 @@ INCLUDEPATH += .
 INCLUDEPATH += \
     include/ui \
     src \
+    src/ADSB \
     src/api \
     src/AnalyzeView \
     src/Camera \
@@ -566,6 +567,8 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin {
 # Main QGC Headers and Source files
 
 HEADERS += \
+    src/ADSB/ADSBVehicle.h \
+    src/ADSB/ADSBVehicleManager.h \
     src/AnalyzeView/LogDownloadController.h \
     src/AnalyzeView/PX4LogParser.h \
     src/AnalyzeView/ULogParser.h \
@@ -652,6 +655,7 @@ HEADERS += \
     src/QmlControls/RCChannelMonitorController.h \
     src/QmlControls/ScreenToolsController.h \
     src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \
+    src/Settings/ADSBVehicleManagerSettings.h \
     src/Settings/AppSettings.h \
     src/Settings/AutoConnectSettings.h \
     src/Settings/BrandImageSettings.h \
@@ -669,7 +673,6 @@ HEADERS += \
     src/SHPFileHelper.h \
     src/Terrain/TerrainQuery.h \
     src/TerrainTile.h \
-    src/Vehicle/ADSBVehicle.h \
     src/Vehicle/GPSRTKFactGroup.h \
     src/Vehicle/MAVLinkLogManager.h \
     src/Vehicle/MultiVehicleManager.h \
@@ -800,6 +803,8 @@ AndroidBuild {
 }
 
 SOURCES += \
+    src/ADSB/ADSBVehicle.cc \
+    src/ADSB/ADSBVehicleManager.cc \
     src/AnalyzeView/LogDownloadController.cc \
     src/AnalyzeView/PX4LogParser.cc \
     src/AnalyzeView/ULogParser.cc \
@@ -882,6 +887,7 @@ SOURCES += \
     src/QmlControls/RCChannelMonitorController.cc \
     src/QmlControls/ScreenToolsController.cc \
     src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \
+    src/Settings/ADSBVehicleManagerSettings.cc \
     src/Settings/AppSettings.cc \
     src/Settings/AutoConnectSettings.cc \
     src/Settings/BrandImageSettings.cc \
@@ -899,7 +905,6 @@ SOURCES += \
     src/SHPFileHelper.cc \
     src/Terrain/TerrainQuery.cc \
     src/TerrainTile.cc\
-    src/Vehicle/ADSBVehicle.cc \
     src/Vehicle/GPSRTKFactGroup.cc \
     src/Vehicle/MAVLinkLogManager.cc \
     src/Vehicle/MultiVehicleManager.cc \
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index fa5de8820d..e82ba2e387 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -234,6 +234,7 @@
     <qresource prefix="/json">
         <file alias="APMMavlinkStreamRate.SettingsGroup.json">src/Settings/APMMavlinkStreamRate.SettingsGroup.json</file>
         <file alias="BreachReturn.FactMetaData.json">src/MissionManager/BreachReturn.FactMetaData.json</file>
+        <file alias="ADSBVehicleManager.SettingsGroup.json">src/Settings/ADSBVehicleManager.SettingsGroup.json</file>
         <file alias="App.SettingsGroup.json">src/Settings/App.SettingsGroup.json</file>
         <file alias="AutoConnect.SettingsGroup.json">src/Settings/AutoConnect.SettingsGroup.json</file>
         <file alias="BrandImage.SettingsGroup.json">src/Settings/BrandImage.SettingsGroup.json</file>
diff --git a/src/ADSB/ADSBVehicle.cc b/src/ADSB/ADSBVehicle.cc
new file mode 100644
index 0000000000..571b083016
--- /dev/null
+++ b/src/ADSB/ADSBVehicle.cc
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#include "ADSBVehicle.h"
+#include "QGCLoggingCategory.h"
+
+#include <QDebug>
+#include <QtMath>
+
+ADSBVehicle::ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent)
+    : QObject       (parent)
+    , _icaoAddress  (vehicleInfo.icaoAddress)
+    , _altitude     (qQNaN())
+    , _heading      (qQNaN())
+    , _alert        (false)
+{
+    update(vehicleInfo);
+}
+
+void ADSBVehicle::update(const VehicleInfo_t& vehicleInfo)
+{
+    if (_icaoAddress != vehicleInfo.icaoAddress) {
+        qCWarning(ADSBVehicleManagerLog) << "ICAO address mismatch expected:actual" << _icaoAddress << vehicleInfo.icaoAddress;
+        return;
+    }
+    if (vehicleInfo.availableFlags & CallsignAvailable) {
+        if (vehicleInfo.callsign != _callsign) {
+            _callsign = vehicleInfo.callsign;
+            emit callsignChanged();
+        }
+    }
+    if (vehicleInfo.availableFlags & LocationAvailable) {
+        if (_coordinate != vehicleInfo.location) {
+            _coordinate = vehicleInfo.location;
+            emit coordinateChanged();
+        }
+    }
+    if (vehicleInfo.availableFlags & AltitudeAvailable) {
+        if (!(qIsNaN(vehicleInfo.altitude) && qIsNaN(_altitude)) && !qFuzzyCompare(vehicleInfo.altitude, _altitude)) {
+            _altitude = vehicleInfo.altitude;
+            emit altitudeChanged();
+        }
+    }
+    if (vehicleInfo.availableFlags & HeadingAvailable) {
+        if (!(qIsNaN(vehicleInfo.heading) && qIsNaN(_heading)) && !qFuzzyCompare(vehicleInfo.heading, _heading)) {
+            _heading = vehicleInfo.heading;
+            emit headingChanged();
+        }
+    }
+    if (vehicleInfo.availableFlags & AlertAvailable) {
+        if (vehicleInfo.alert != _alert) {
+            _alert = vehicleInfo.alert;
+            emit alertChanged();
+        }
+    }
+    _lastUpdateTimer.restart();
+}
+
+bool ADSBVehicle::expired()
+{
+    return _lastUpdateTimer.hasExpired(expirationTimeoutMs);
+}
diff --git a/src/Vehicle/ADSBVehicle.h b/src/ADSB/ADSBVehicle.h
similarity index 75%
rename from src/Vehicle/ADSBVehicle.h
rename to src/ADSB/ADSBVehicle.h
index 5982aa49b6..602009e401 100644
--- a/src/Vehicle/ADSBVehicle.h
+++ b/src/ADSB/ADSBVehicle.h
@@ -20,9 +20,25 @@ class ADSBVehicle : public QObject
     Q_OBJECT
 
 public:
-    ADSBVehicle(mavlink_adsb_vehicle_t& adsbVehicle, QObject* parent = nullptr);
+    enum {
+        CallsignAvailable =     1 << 1,
+        LocationAvailable =     1 << 2,
+        AltitudeAvailable =     1 << 3,
+        HeadingAvailable =      1 << 4,
+        AlertAvailable =        1 << 5,
+    };
 
-    ADSBVehicle(const QGeoCoordinate& location, float heading, bool alert = false, QObject* parent = nullptr);
+    typedef struct {
+        uint32_t        icaoAddress;    // Required
+        QString         callsign;
+        QGeoCoordinate  location;
+        double          altitude;
+        double          heading;
+        bool            alert;
+        uint32_t        availableFlags;
+    } VehicleInfo_t;
+
+    ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent);
 
     Q_PROPERTY(int              icaoAddress READ icaoAddress    CONSTANT)
     Q_PROPERTY(QString          callsign    READ callsign       NOTIFY callsignChanged)
@@ -31,17 +47,14 @@ public:
     Q_PROPERTY(double           heading     READ heading        NOTIFY headingChanged)      // NaN for not available
     Q_PROPERTY(bool             alert       READ alert          NOTIFY alertChanged)        // Collision path
 
-    int             icaoAddress (void) const { return _icaoAddress; }
+    int             icaoAddress (void) const { return static_cast<int>(_icaoAddress); }
     QString         callsign    (void) const { return _callsign; }
     QGeoCoordinate  coordinate  (void) const { return _coordinate; }
     double          altitude    (void) const { return _altitude; }
     double          heading     (void) const { return _heading; }
     bool            alert       (void) const { return _alert; }
 
-    /// Update the vehicle with new information
-    void update(mavlink_adsb_vehicle_t& adsbVehicle);
-
-    void update(bool alert, const QGeoCoordinate& location, float heading);
+    void update(const VehicleInfo_t& vehicleInfo);
 
     /// check if the vehicle is expired and should be removed
     bool expired();
@@ -69,3 +82,6 @@ private:
 
     QElapsedTimer   _lastUpdateTimer;
 };
+
+Q_DECLARE_METATYPE(ADSBVehicle::VehicleInfo_t)
+
diff --git a/src/ADSB/ADSBVehicleManager.cc b/src/ADSB/ADSBVehicleManager.cc
new file mode 100644
index 0000000000..5e128df6ba
--- /dev/null
+++ b/src/ADSB/ADSBVehicleManager.cc
@@ -0,0 +1,188 @@
+/****************************************************************************
+ *
+ *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#include "ADSBVehicleManager.h"
+#include "QGCLoggingCategory.h"
+#include "QGCApplication.h"
+#include "SettingsManager.h"
+#include "ADSBVehicleManagerSettings.h"
+
+#include <QDebug>
+
+ADSBVehicleManager::ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox)
+    : QGCTool(app, toolbox)
+{
+}
+
+void ADSBVehicleManager::setToolbox(QGCToolbox* toolbox)
+{
+    QGCTool::setToolbox(toolbox);
+
+    connect(&_adsbVehicleCleanupTimer, &QTimer::timeout, this, &ADSBVehicleManager::_cleanupStaleVehicles);
+    _adsbVehicleCleanupTimer.setSingleShot(false);
+    _adsbVehicleCleanupTimer.start(1000);
+
+    ADSBVehicleManagerSettings* settings = qgcApp()->toolbox()->settingsManager()->adsbVehicleManagerSettings();
+    if (settings->adsbServerConnectEnabled()->rawValue().toBool()) {
+        _tcpLink = new ADSBTCPLink(this);
+        connect(_tcpLink, &ADSBTCPLink::adsbVehicleUpdate, this, &ADSBVehicleManager::adsbVehicleUpdate, Qt::QueuedConnection);
+    }
+}
+
+void ADSBVehicleManager::_cleanupStaleVehicles()
+{
+    // Remove all expired ADSB vehicles
+    for (int i=_adsbVehicles.count()-1; i>=0; i--) {
+        ADSBVehicle* adsbVehicle = _adsbVehicles.value<ADSBVehicle*>(i);
+        if (adsbVehicle->expired()) {
+            _adsbVehicles.removeAt(i);
+            adsbVehicle->deleteLater();
+        }
+    }
+}
+
+void ADSBVehicleManager::adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo)
+{
+    uint32_t icaoAddress = vehicleInfo.icaoAddress;
+
+    if (_adsbICAOMap.contains(icaoAddress)) {
+        _adsbICAOMap[icaoAddress]->update(vehicleInfo);
+    } else {
+        if (vehicleInfo.availableFlags & ADSBVehicle::LocationAvailable) {
+            ADSBVehicle* adsbVehicle = new ADSBVehicle(vehicleInfo, this);
+            _adsbICAOMap[icaoAddress] = adsbVehicle;
+            _adsbVehicles.append(adsbVehicle);
+        }
+    }
+}
+
+ADSBTCPLink::ADSBTCPLink(QObject* parent)
+    : QThread(parent)
+{
+    _settings = qgcApp()->toolbox()->settingsManager()->adsbVehicleManagerSettings();
+    moveToThread(this);
+    start();
+}
+
+ADSBTCPLink::~ADSBTCPLink(void)
+{
+    if (_socket) {
+        _socket->disconnectFromHost();
+        _socket->waitForDisconnected();
+        _socket->deleteLater();
+        _socket = nullptr;
+    }
+    quit();
+    wait();
+}
+
+void ADSBTCPLink::run(void)
+{
+    _hardwareConnect();
+    exec();
+}
+
+void ADSBTCPLink::_hardwareConnect()
+{
+    _socket = new QTcpSocket();
+
+    QObject::connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
+
+    _socket->connectToHost(_settings->adsbServerHostAddress()->rawValue().toString(), _settings->adsbServerPort()->rawValue().toInt());
+
+    // Give the socket a second to connect to the other side otherwise error out
+    if (!_socket->waitForConnected(1000)) {
+        qCDebug(ADSBVehicleManagerLog) << "ADSB Socket failed to connect";
+        delete _socket;
+        _socket = nullptr;
+        return;
+    }
+
+    qCDebug(ADSBVehicleManagerLog) << "ADSB Socket connected";
+}
+
+void ADSBTCPLink::_readBytes(void)
+{
+    if (_socket) {
+        QByteArray bytes = _socket->readLine();
+        _parseLine(QString::fromLocal8Bit(bytes));
+    }
+}
+
+void ADSBTCPLink::_socketError(QAbstractSocket::SocketError socketError)
+{
+    QString error = _socket->errorString();
+    qDebug() << _socket->errorString();
+}
+
+
+void ADSBTCPLink::_parseLine(const QString& line)
+{
+    if (line.startsWith(QStringLiteral("MSG"))) {
+        qCDebug(ADSBVehicleManagerLog) << "ADSB SBS-1" << line;
+
+        QStringList values = line.split(QStringLiteral(","));
+
+        if (values[1] == QStringLiteral("3")) {
+            bool icaoOk, altOk, latOk, lonOk;
+
+            uint32_t    icaoAddress =   values[4].toUInt(&icaoOk, 16);
+            int         modeCAltitude = values[11].toInt(&altOk);
+            double      lat =           values[14].toDouble(&latOk);
+            double      lon =           values[15].toDouble(&lonOk);
+            QString     callsign =      values[10];
+
+            if (!icaoOk || !altOk || !latOk || !lonOk) {
+                return;
+            }
+            if (lat == 0 && lon == 0) {
+                return;
+            }
+
+            double altitude = modeCAltitude / 3.048;
+            QGeoCoordinate location(lat, lon);
+
+            ADSBVehicle::VehicleInfo_t adsbInfo;
+            adsbInfo.icaoAddress = icaoAddress;
+            adsbInfo.callsign = callsign;
+            adsbInfo.location = location;
+            adsbInfo.altitude = altitude;
+            adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable | ADSBVehicle::LocationAvailable | ADSBVehicle::AltitudeAvailable;
+            emit adsbVehicleUpdate(adsbInfo);
+        } else if (values[1] == QStringLiteral("4")) {
+            bool icaoOk, headingOk;
+
+            uint32_t    icaoAddress =   values[4].toUInt(&icaoOk, 16);
+            double      heading =       values[13].toDouble(&headingOk);
+
+            if (!icaoOk || !headingOk) {
+                return;
+            }
+
+            ADSBVehicle::VehicleInfo_t adsbInfo;
+            adsbInfo.icaoAddress = icaoAddress;
+            adsbInfo.heading = heading;
+            adsbInfo.availableFlags = ADSBVehicle::HeadingAvailable;
+            emit adsbVehicleUpdate(adsbInfo);
+        } else if (values[1] == QStringLiteral("1")) {
+            bool icaoOk;
+
+            uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
+            if (!icaoOk) {
+                return;
+            }
+
+            ADSBVehicle::VehicleInfo_t adsbInfo;
+            adsbInfo.icaoAddress = icaoAddress;
+            adsbInfo.callsign = values[10];
+            adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable;
+            emit adsbVehicleUpdate(adsbInfo);
+        }
+    }
+}
diff --git a/src/ADSB/ADSBVehicleManager.h b/src/ADSB/ADSBVehicleManager.h
new file mode 100644
index 0000000000..3bb56fc4d1
--- /dev/null
+++ b/src/ADSB/ADSBVehicleManager.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+ *
+ *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#pragma once
+
+#include "QGCToolbox.h"
+#include "QmlObjectListModel.h"
+#include "ADSBVehicle.h"
+
+#include <QThread>
+#include <QTcpSocket>
+#include <QTimer>
+#include <QGeoCoordinate>
+
+class ADSBVehicleManagerSettings;
+
+class ADSBTCPLink : public QThread
+{
+    Q_OBJECT
+
+public:
+    ADSBTCPLink(QObject* parent);
+    ~ADSBTCPLink();
+
+signals:
+    void adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo);
+
+protected:
+    void run(void) final;
+
+private slots:
+    void _readBytes(void);
+    void _socketError(QAbstractSocket::SocketError socketError);
+
+private:
+    void _hardwareConnect(void);
+    void _parseLine(const QString& line);
+
+    QTcpSocket*                 _socket =   nullptr;
+    ADSBVehicleManagerSettings* _settings = nullptr;
+};
+
+class ADSBVehicleManager : public QGCTool {
+    Q_OBJECT
+    
+public:
+    ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox);
+
+    Q_PROPERTY(QmlObjectListModel* adsbVehicles READ adsbVehicles CONSTANT)
+
+    QmlObjectListModel* adsbVehicles(void) { return &_adsbVehicles; }
+
+    // QGCTool overrides
+    void setToolbox(QGCToolbox* toolbox) final;
+
+public slots:
+    void adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo);
+
+private slots:
+    void _cleanupStaleVehicles(void);
+
+private:
+    QmlObjectListModel              _adsbVehicles;
+    QMap<uint32_t, ADSBVehicle*>    _adsbICAOMap;
+    QTimer                          _adsbVehicleCleanupTimer;
+    ADSBTCPLink*                    _tcpLink = nullptr;
+};
diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml
index ac4b35bdf8..55bb0a5e5a 100644
--- a/src/FlightDisplay/FlightDisplayViewMap.qml
+++ b/src/FlightDisplay/FlightDisplayViewMap.qml
@@ -214,8 +214,7 @@ FlightMap {
 
     // Add ADSB vehicles to the map
     MapItemView {
-        model: activeVehicle ? activeVehicle.adsbVehicles : []
-        property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
+        model: QGroundControl.adsbVehicleManager.adsbVehicles
         delegate: VehicleMapItem {
             coordinate:     object.coordinate
             altitude:       object.altitude
diff --git a/src/FlightMap/MapItems/VehicleMapItem.qml b/src/FlightMap/MapItems/VehicleMapItem.qml
index 5c8855ec66..61ebf0bc30 100644
--- a/src/FlightMap/MapItems/VehicleMapItem.qml
+++ b/src/FlightMap/MapItems/VehicleMapItem.qml
@@ -80,7 +80,7 @@ MapQuickItem {
             anchors.horizontalCenter:   parent.horizontalCenter
             map:                        _map
             text:                       vehicleLabelText
-            font.pointSize:             ScreenTools.smallFontPointSize
+            font.pointSize:             _adsbVehicle ? ScreenTools.defaultFontPointSize : ScreenTools.smallFontPointSize
             visible:                    _adsbVehicle ? !isNaN(altitude) : _multiVehicle
             property string vehicleLabelText: visible ?
                                                   (_adsbVehicle ?
@@ -89,15 +89,5 @@ MapQuickItem {
                                                   ""
 
         }
-
-        QGCMapLabel {
-            anchors.top:                vehicleLabel.bottom
-            anchors.horizontalCenter:   parent.horizontalCenter
-            map:                        _map
-            text:                       vehicleLabelText
-            font.pointSize:             ScreenTools.smallFontPointSize
-            visible:                    _adsbVehicle ? !isNaN(altitude) : _multiVehicle
-            property string vehicleLabelText: visible && _adsbVehicle ? callsign : ""
-        }
     }
 }
diff --git a/src/QGCLoggingCategory.cc b/src/QGCLoggingCategory.cc
index 15b8c7f329..9e6c8f9736 100644
--- a/src/QGCLoggingCategory.cc
+++ b/src/QGCLoggingCategory.cc
@@ -24,6 +24,7 @@ QGC_LOGGING_CATEGORY(ParameterManagerLog,           "ParameterManagerLog")
 QGC_LOGGING_CATEGORY(GeotaggingLog,                 "GeotaggingLog")
 QGC_LOGGING_CATEGORY(RTKGPSLog,                     "RTKGPSLog")
 QGC_LOGGING_CATEGORY(GuidedActionsControllerLog,    "GuidedActionsControllerLog")
+QGC_LOGGING_CATEGORY(ADSBVehicleManagerLog,         "ADSBVehicleManagerLog")
 
 QGCLoggingCategoryRegister* _instance = nullptr;
 const char* QGCLoggingCategoryRegister::_filterRulesSettingsGroup = "LoggingFilters";
diff --git a/src/QGCLoggingCategory.h b/src/QGCLoggingCategory.h
index 15683c9a54..62ca9be9f0 100644
--- a/src/QGCLoggingCategory.h
+++ b/src/QGCLoggingCategory.h
@@ -26,6 +26,7 @@ Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog)
 Q_DECLARE_LOGGING_CATEGORY(GeotaggingLog)
 Q_DECLARE_LOGGING_CATEGORY(RTKGPSLog)
 Q_DECLARE_LOGGING_CATEGORY(GuidedActionsControllerLog)
+Q_DECLARE_LOGGING_CATEGORY(ADSBVehicleManagerLog)
 
 /// @def QGC_LOGGING_CATEGORY
 /// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a
diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc
index a4675c0210..569e6dd583 100644
--- a/src/QGCToolbox.cc
+++ b/src/QGCToolbox.cc
@@ -30,6 +30,7 @@
 #include "QGCOptions.h"
 #include "SettingsManager.h"
 #include "QGCApplication.h"
+#include "ADSBVehicleManager.h"
 #if defined(QGC_ENABLE_PAIRING)
 #include "PairingManager.h"
 #endif
@@ -73,6 +74,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app)
     _followMe               = new FollowMe                  (app, this);
     _videoManager           = new VideoManager              (app, this);
     _mavlinkLogManager      = new MAVLinkLogManager         (app, this);
+    _adsbVehicleManager     = new ADSBVehicleManager        (app, this);
 #if defined(QGC_ENABLE_PAIRING)
     _pairingManager         = new PairingManager            (app, this);
 #endif
@@ -116,6 +118,7 @@ void QGCToolbox::setChildToolboxes(void)
     _videoManager->setToolbox(this);
     _mavlinkLogManager->setToolbox(this);
     _airspaceManager->setToolbox(this);
+    _adsbVehicleManager->setToolbox(this);
 #if defined(QGC_GST_TAISYNC_ENABLED)
     _taisyncManager->setToolbox(this);
 #endif
diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h
index eaef87334a..bb65623e15 100644
--- a/src/QGCToolbox.h
+++ b/src/QGCToolbox.h
@@ -33,6 +33,7 @@ class MAVLinkLogManager;
 class QGCCorePlugin;
 class SettingsManager;
 class AirspaceManager;
+class ADSBVehicleManager;
 #if defined(QGC_ENABLE_PAIRING)
 class PairingManager;
 #endif
@@ -67,6 +68,7 @@ public:
     QGCCorePlugin*              corePlugin              () { return _corePlugin; }
     SettingsManager*            settingsManager         () { return _settingsManager; }
     AirspaceManager*            airspaceManager         () { return _airspaceManager; }
+    ADSBVehicleManager*         adsbVehicleManager      () { return _adsbVehicleManager; }
 #if defined(QGC_ENABLE_PAIRING)
     PairingManager*             pairingManager          () { return _pairingManager; }
 #endif
@@ -106,6 +108,7 @@ private:
     QGCCorePlugin*              _corePlugin             = nullptr;
     SettingsManager*            _settingsManager        = nullptr;
     AirspaceManager*            _airspaceManager        = nullptr;
+    ADSBVehicleManager*         _adsbVehicleManager     = nullptr;
 #if defined(QGC_ENABLE_PAIRING)
     PairingManager*             _pairingManager         = nullptr;
 #endif
diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc
index 8ec4f55a33..23b30e1420 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.cc
+++ b/src/QmlControls/QGroundControlQmlGlobal.cc
@@ -66,6 +66,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox)
     _settingsManager        = toolbox->settingsManager();
     _gpsRtkFactGroup        = qgcApp()->gpsRtkFactGroup();
     _airspaceManager        = toolbox->airspaceManager();
+    _adsbVehicleManager     = toolbox->adsbVehicleManager();
 #if defined(QGC_ENABLE_PAIRING)
     _pairingManager         = toolbox->pairingManager();
 #endif
diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h
index 0740290ea9..6ec3dd9ca8 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.h
+++ b/src/QmlControls/QGroundControlQmlGlobal.h
@@ -23,6 +23,7 @@
 #include "QGCLoggingCategory.h"
 #include "AppSettings.h"
 #include "AirspaceManager.h"
+#include "ADSBVehicleManager.h"
 #if defined(QGC_ENABLE_PAIRING)
 #include "PairingManager.h"
 #endif
@@ -73,6 +74,7 @@ public:
     Q_PROPERTY(SettingsManager*     settingsManager     READ settingsManager        CONSTANT)
     Q_PROPERTY(FactGroup*           gpsRtk              READ gpsRtkFactGroup        CONSTANT)
     Q_PROPERTY(AirspaceManager*     airspaceManager     READ airspaceManager        CONSTANT)
+    Q_PROPERTY(ADSBVehicleManager*  adsbVehicleManager  READ adsbVehicleManager     CONSTANT)
     Q_PROPERTY(bool                 airmapSupported     READ airmapSupported        CONSTANT)
     Q_PROPERTY(TaisyncManager*      taisyncManager      READ taisyncManager         CONSTANT)
     Q_PROPERTY(bool                 taisyncSupported    READ taisyncSupported       CONSTANT)
@@ -179,6 +181,7 @@ public:
     SettingsManager*        settingsManager     ()  { return _settingsManager; }
     FactGroup*              gpsRtkFactGroup     ()  { return _gpsRtkFactGroup; }
     AirspaceManager*        airspaceManager     ()  { return _airspaceManager; }
+    ADSBVehicleManager*     adsbVehicleManager  ()  { return _adsbVehicleManager; }
 #if defined(QGC_ENABLE_PAIRING)
     bool                    supportsPairing     ()  { return true; }
     PairingManager*         pairingManager      ()  { return _pairingManager; }
@@ -269,6 +272,7 @@ private:
     AirspaceManager*        _airspaceManager        = nullptr;
     TaisyncManager*         _taisyncManager         = nullptr;
     MicrohardManager*       _microhardManager       = nullptr;
+    ADSBVehicleManager*     _adsbVehicleManager     = nullptr;
 #if defined(QGC_ENABLE_PAIRING)
     PairingManager*         _pairingManager         = nullptr;
 #endif
diff --git a/src/Settings/ADSBVehicleManager.SettingsGroup.json b/src/Settings/ADSBVehicleManager.SettingsGroup.json
new file mode 100644
index 0000000000..62500d7b4b
--- /dev/null
+++ b/src/Settings/ADSBVehicleManager.SettingsGroup.json
@@ -0,0 +1,24 @@
+[
+{
+    "name":                 "adsbServerConnectEnabled",
+    "shortDescription":     "Connect to ADSB SBS server",
+    "longDescription":      "Connect to ADSB SBS-1 server using specified address/port",
+    "type":                 "bool",
+    "defaultValue":         false,
+    "qgcRebootRequired":    true
+},
+{
+    "name":                 "adsbServerHostAddress",
+    "shortDescription":     "Host address",
+    "type":                 "string",
+    "defaultValue":         "127.0.0.1",
+    "qgcRebootRequired":    true
+},
+{
+    "name":                 "adsbServerPort",
+    "shortDescription":     "Server port",
+    "type":                 "string",
+    "defaultValue":         30003,
+    "qgcRebootRequired":    true
+}
+]
diff --git a/src/Settings/ADSBVehicleManagerSettings.cc b/src/Settings/ADSBVehicleManagerSettings.cc
new file mode 100644
index 0000000000..a9b94bf2ae
--- /dev/null
+++ b/src/Settings/ADSBVehicleManagerSettings.cc
@@ -0,0 +1,22 @@
+/****************************************************************************
+ *
+ *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#include "ADSBVehicleManagerSettings.h"
+
+#include <QQmlEngine>
+#include <QtQml>
+
+DECLARE_SETTINGGROUP(ADSBVehicleManager, "ADSBVehicleManager")
+{
+    qmlRegisterUncreatableType<ADSBVehicleManagerSettings>("QGroundControl.SettingsManager", 1, 0, "ADSBVehicleManagerSettings", "Reference only");
+}
+
+DECLARE_SETTINGSFACT(ADSBVehicleManagerSettings, adsbServerConnectEnabled)
+DECLARE_SETTINGSFACT(ADSBVehicleManagerSettings, adsbServerHostAddress)
+DECLARE_SETTINGSFACT(ADSBVehicleManagerSettings, adsbServerPort)
diff --git a/src/Settings/ADSBVehicleManagerSettings.h b/src/Settings/ADSBVehicleManagerSettings.h
new file mode 100644
index 0000000000..89e4081e80
--- /dev/null
+++ b/src/Settings/ADSBVehicleManagerSettings.h
@@ -0,0 +1,24 @@
+/****************************************************************************
+ *
+ *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#pragma once
+
+#include "SettingsGroup.h"
+
+class ADSBVehicleManagerSettings : public SettingsGroup
+{
+    Q_OBJECT
+public:
+    ADSBVehicleManagerSettings(QObject* parent = nullptr);
+    DEFINE_SETTING_NAME_GROUP()
+
+    DEFINE_SETTINGFACT(adsbServerConnectEnabled)
+    DEFINE_SETTINGFACT(adsbServerHostAddress)
+    DEFINE_SETTINGFACT(adsbServerPort)
+};
diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc
index 4b6968fd1a..82202d01e2 100644
--- a/src/Settings/SettingsManager.cc
+++ b/src/Settings/SettingsManager.cc
@@ -28,6 +28,7 @@ SettingsManager::SettingsManager(QGCApplication* app, QGCToolbox* toolbox)
     , _brandImageSettings           (nullptr)
     , _offlineMapsSettings          (nullptr)
     , _firmwareUpgradeSettings      (nullptr)
+    , _adsbVehicleManagerSettings   (nullptr)
 #if !defined(NO_ARDUPILOT_DIALECT)
     , _apmMavlinkStreamRateSettings (nullptr)
 #endif
@@ -41,17 +42,18 @@ void SettingsManager::setToolbox(QGCToolbox *toolbox)
     QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
     qmlRegisterUncreatableType<SettingsManager>("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only");
 
-    _unitsSettings =                new UnitsSettings           (this);        // Must be first since AppSettings references it
-    _appSettings =                  new AppSettings             (this);
-    _autoConnectSettings =          new AutoConnectSettings     (this);
-    _videoSettings =                new VideoSettings           (this);
-    _flightMapSettings =            new FlightMapSettings       (this);
-    _rtkSettings =                  new RTKSettings             (this);
-    _flyViewSettings =              new FlyViewSettings         (this);
-    _planViewSettings =             new PlanViewSettings        (this);
-    _brandImageSettings =           new BrandImageSettings      (this);
-    _offlineMapsSettings =          new OfflineMapsSettings     (this);
-    _firmwareUpgradeSettings =      new FirmwareUpgradeSettings (this);
+    _unitsSettings =                new UnitsSettings               (this);        // Must be first since AppSettings references it
+    _appSettings =                  new AppSettings                 (this);
+    _autoConnectSettings =          new AutoConnectSettings         (this);
+    _videoSettings =                new VideoSettings               (this);
+    _flightMapSettings =            new FlightMapSettings           (this);
+    _rtkSettings =                  new RTKSettings                 (this);
+    _flyViewSettings =              new FlyViewSettings             (this);
+    _planViewSettings =             new PlanViewSettings            (this);
+    _brandImageSettings =           new BrandImageSettings          (this);
+    _offlineMapsSettings =          new OfflineMapsSettings         (this);
+    _firmwareUpgradeSettings =      new FirmwareUpgradeSettings     (this);
+    _adsbVehicleManagerSettings =   new ADSBVehicleManagerSettings  (this);
 #if !defined(NO_ARDUPILOT_DIALECT)
     _apmMavlinkStreamRateSettings = new APMMavlinkStreamRateSettings(this);
 #endif
diff --git a/src/Settings/SettingsManager.h b/src/Settings/SettingsManager.h
index 6c3f867ed1..db66f2a0e7 100644
--- a/src/Settings/SettingsManager.h
+++ b/src/Settings/SettingsManager.h
@@ -26,6 +26,7 @@
 #include "OfflineMapsSettings.h"
 #include "APMMavlinkStreamRateSettings.h"
 #include "FirmwareUpgradeSettings.h"
+#include "ADSBVehicleManagerSettings.h"
 #if defined(QGC_AIRMAP_ENABLED)
 #include "AirMapSettings.h"
 #endif
@@ -53,6 +54,7 @@ public:
     Q_PROPERTY(QObject* brandImageSettings              READ brandImageSettings             CONSTANT)
     Q_PROPERTY(QObject* offlineMapsSettings             READ offlineMapsSettings            CONSTANT)
     Q_PROPERTY(QObject* firmwareUpgradeSettings         READ firmwareUpgradeSettings        CONSTANT)
+    Q_PROPERTY(QObject* adsbVehicleManagerSettings      READ adsbVehicleManagerSettings     CONSTANT)
 #if !defined(NO_ARDUPILOT_DIALECT)
     Q_PROPERTY(QObject* apmMavlinkStreamRateSettings    READ apmMavlinkStreamRateSettings   CONSTANT)
 #endif
@@ -73,6 +75,7 @@ public:
     BrandImageSettings*             brandImageSettings          (void) { return _brandImageSettings; }
     OfflineMapsSettings*            offlineMapsSettings         (void) { return _offlineMapsSettings; }
     FirmwareUpgradeSettings*        firmwareUpgradeSettings     (void) { return _firmwareUpgradeSettings; }
+    ADSBVehicleManagerSettings*     adsbVehicleManagerSettings  (void) { return _adsbVehicleManagerSettings; }
 #if !defined(NO_ARDUPILOT_DIALECT)
     APMMavlinkStreamRateSettings*   apmMavlinkStreamRateSettings(void) { return _apmMavlinkStreamRateSettings; }
 #endif
@@ -91,6 +94,7 @@ private:
     BrandImageSettings*             _brandImageSettings;
     OfflineMapsSettings*            _offlineMapsSettings;
     FirmwareUpgradeSettings*        _firmwareUpgradeSettings;
+    ADSBVehicleManagerSettings*     _adsbVehicleManagerSettings;
 #if !defined(NO_ARDUPILOT_DIALECT)
     APMMavlinkStreamRateSettings*   _apmMavlinkStreamRateSettings;
 #endif
diff --git a/src/Vehicle/ADSBVehicle.cc b/src/Vehicle/ADSBVehicle.cc
deleted file mode 100644
index 707e0f71cb..0000000000
--- a/src/Vehicle/ADSBVehicle.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/****************************************************************************
- *
- *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
- *
- * QGroundControl is licensed according to the terms in the file
- * COPYING.md in the root of the source code directory.
- *
- ****************************************************************************/
-
-#include "ADSBVehicle.h"
-
-#include <QDebug>
-#include <QtMath>
-
-ADSBVehicle::ADSBVehicle(mavlink_adsb_vehicle_t& adsbVehicle, QObject* parent)
-    : QObject       (parent)
-    , _icaoAddress  (adsbVehicle.ICAO_address)
-    , _callsign     (adsbVehicle.callsign)
-    , _altitude     (NAN)
-    , _heading      (NAN)
-    , _alert        (false)
-{
-    if (!(adsbVehicle.flags & ADSB_FLAGS_VALID_COORDS)) {
-        qWarning() << "At least coords must be valid";
-        return;
-    }
-    update(adsbVehicle);
-}
-
-ADSBVehicle::ADSBVehicle(const QGeoCoordinate& location, float heading, bool alert, QObject* parent)
-    : QObject(parent)
-    , _icaoAddress(0)
-    , _alert(alert)
-{
-    update(alert, location, heading);
-}
-
-void ADSBVehicle::update(bool alert, const QGeoCoordinate& location, float heading)
-{
-    _coordinate = location;
-    _altitude   = location.altitude();
-    _heading    = heading;
-    _alert      = alert;
-    emit coordinateChanged();
-    emit altitudeChanged();
-    emit headingChanged();
-    emit alertChanged();
-    _lastUpdateTimer.restart();
-}
-
-void ADSBVehicle::update(mavlink_adsb_vehicle_t& adsbVehicle)
-{
-    if (_icaoAddress != adsbVehicle.ICAO_address) {
-        qWarning() << "ICAO address mismatch expected:actual" << _icaoAddress << adsbVehicle.ICAO_address;
-        return;
-    }
-
-    if (!(adsbVehicle.flags & ADSB_FLAGS_VALID_COORDS)) {
-        return;
-    }
-
-    QString currCallsign(adsbVehicle.callsign);
-
-    if (currCallsign != _callsign) {
-        _callsign = currCallsign;
-        emit callsignChanged();
-    }
-
-    QGeoCoordinate newCoordinate(adsbVehicle.lat / 1e7, adsbVehicle.lon / 1e7);
-    if (newCoordinate != _coordinate) {
-        _coordinate = newCoordinate;
-        emit coordinateChanged();
-    }
-
-    double newAltitude = NAN;
-    if (adsbVehicle.flags & ADSB_FLAGS_VALID_ALTITUDE) {
-        newAltitude = (double)adsbVehicle.altitude / 1e3;
-    }
-    if (!(qIsNaN(newAltitude) && qIsNaN(_altitude)) && !qFuzzyCompare(newAltitude, _altitude)) {
-        _altitude = newAltitude;
-        emit altitudeChanged();
-    }
-
-    double newHeading = NAN;
-    if (adsbVehicle.flags & ADSB_FLAGS_VALID_HEADING) {
-        newHeading = (double)adsbVehicle.heading / 100.0;
-    }
-    if (!(qIsNaN(newHeading) && qIsNaN(_heading)) && !qFuzzyCompare(newHeading, _heading)) {
-        _heading = newHeading;
-        emit headingChanged();
-    }
-    _lastUpdateTimer.restart();
-}
-
-bool ADSBVehicle::expired()
-{
-    return _lastUpdateTimer.hasExpired(expirationTimeoutMs);
-}
diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc
index 55eee91eaa..3f5bbba0cb 100644
--- a/src/Vehicle/Vehicle.cc
+++ b/src/Vehicle/Vehicle.cc
@@ -36,7 +36,7 @@
 #include "QGCQGeoCoordinate.h"
 #include "QGCCorePlugin.h"
 #include "QGCOptions.h"
-#include "ADSBVehicle.h"
+#include "ADSBVehicleManager.h"
 #include "QGCCameraManager.h"
 #include "VideoReceiver.h"
 #include "VideoManager.h"
@@ -113,7 +113,6 @@ Vehicle::Vehicle(LinkInterface*             link,
     , _soloFirmware(false)
     , _toolbox(qgcApp()->toolbox())
     , _settingsManager(_toolbox->settingsManager())
-    , _csvLogTimer(this)
     , _joystickMode(JoystickModeRC)
     , _joystickEnabled(false)
     , _uas(nullptr)
@@ -297,10 +296,6 @@ Vehicle::Vehicle(LinkInterface*             link,
     _cameras = _firmwarePlugin->createCameraManager(this);
     emit dynamicCamerasChanged();
 
-    connect(&_adsbTimer, &QTimer::timeout, this, &Vehicle::_adsbTimerTimeout);
-    _adsbTimer.setSingleShot(false);
-    _adsbTimer.start(1000);
-
     // Start csv logger
     connect(&_csvLogTimer, &QTimer::timeout, this, &Vehicle::_writeCsvLine);
     _csvLogTimer.start(1000);
@@ -325,7 +320,6 @@ Vehicle::Vehicle(MAV_AUTOPILOT              firmwareType,
     , _soloFirmware(false)
     , _toolbox(qgcApp()->toolbox())
     , _settingsManager(_toolbox->settingsManager())
-    , _csvLogTimer(this)
     , _joystickMode(JoystickModeRC)
     , _joystickEnabled(false)
     , _uas(nullptr)
@@ -3770,20 +3764,32 @@ bool Vehicle::autoDisarm(void)
 
 void Vehicle::_handleADSBVehicle(const mavlink_message_t& message)
 {
-    mavlink_adsb_vehicle_t adsbVehicle;
+    mavlink_adsb_vehicle_t adsbVehicleMsg;
     static const int maxTimeSinceLastSeen = 15;
 
-    mavlink_msg_adsb_vehicle_decode(&message, &adsbVehicle);
-    if (adsbVehicle.flags | ADSB_FLAGS_VALID_COORDS) {
-        if (_adsbICAOMap.contains(adsbVehicle.ICAO_address)) {
-            if (adsbVehicle.tslc <= maxTimeSinceLastSeen) {
-                _adsbICAOMap[adsbVehicle.ICAO_address]->update(adsbVehicle);
-            }
-        } else if (adsbVehicle.tslc <= maxTimeSinceLastSeen) {
-            ADSBVehicle* vehicle = new ADSBVehicle(adsbVehicle, this);
-            _adsbICAOMap[adsbVehicle.ICAO_address] = vehicle;
-            _adsbVehicles.append(vehicle);
+    mavlink_msg_adsb_vehicle_decode(&message, &adsbVehicleMsg);
+    if (adsbVehicleMsg.flags | ADSB_FLAGS_VALID_COORDS && adsbVehicleMsg.tslc <= maxTimeSinceLastSeen) {
+        ADSBVehicle::VehicleInfo_t vehicleInfo;
+
+        vehicleInfo.availableFlags = 0;
+
+        vehicleInfo.location.setLatitude(adsbVehicleMsg.lat / 1e7);
+        vehicleInfo.location.setLatitude(adsbVehicleMsg.lon / 1e7);
+
+        vehicleInfo.callsign = adsbVehicleMsg.callsign;
+        vehicleInfo.availableFlags |= ADSBVehicle::CallsignAvailable;
+
+        if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_ALTITUDE) {
+            vehicleInfo.altitude = (double)adsbVehicleMsg.altitude / 1e3;
+            vehicleInfo.availableFlags |= ADSBVehicle::AltitudeAvailable;
+        }
+
+        if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_HEADING) {
+            vehicleInfo.heading = (double)adsbVehicleMsg.heading / 100.0;
+            vehicleInfo.availableFlags |= ADSBVehicle::HeadingAvailable;
         }
+
+        _toolbox->adsbVehicleManager()->adsbVehicleUpdate(vehicleInfo);
     }
 }
 
@@ -3895,11 +3901,11 @@ void Vehicle::_updateHighLatencyLink(bool sendCommand)
     }
 }
 
-void Vehicle::_trafficUpdate(bool alert, QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading)
+void Vehicle::_trafficUpdate(bool /*alert*/, QString /*traffic_id*/, QString /*vehicle_id*/, QGeoCoordinate /*location*/, float /*heading*/)
 {
-    Q_UNUSED(vehicle_id);
-    // qDebug() << "traffic update:" << traffic_id << vehicle_id << heading << location;
-    // TODO: filter based on minimum altitude?
+#if 0
+    // This is ifdef'ed out for now since this code doesn't mesh with the recent ADSB manager changes. Also airmap isn't in any
+    // released build. So not going to waste time trying to fix up unused code.
     if (_trafficVehicleMap.contains(traffic_id)) {
         _trafficVehicleMap[traffic_id]->update(alert, location, heading);
     } else {
@@ -3907,19 +3913,7 @@ void Vehicle::_trafficUpdate(bool alert, QString traffic_id, QString vehicle_id,
         _trafficVehicleMap[traffic_id] = vehicle;
         _adsbVehicles.append(vehicle);
     }
-
-}
-void Vehicle::_adsbTimerTimeout()
-{
-    // Remove all expired ADSB vehicle whether from AirMap or ADSB Mavlink
-    for (int i=_adsbVehicles.count()-1; i>=0; i--) {
-        ADSBVehicle* adsbVehicle = _adsbVehicles.value<ADSBVehicle*>(i);
-        if (adsbVehicle->expired()) {
-            qDebug() << "ADSB expired";
-            _adsbVehicles.removeAt(i);
-            adsbVehicle->deleteLater();
-        }
-    }
+#endif
 }
 
 void Vehicle::_mavlinkMessageStatus(int uasId, uint64_t totalSent, uint64_t totalReceived, uint64_t totalLoss, float lossPercent)
diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h
index 1407941c0e..4aa47f8b3d 100644
--- a/src/Vehicle/Vehicle.h
+++ b/src/Vehicle/Vehicle.h
@@ -34,7 +34,6 @@ class ParameterManager;
 class JoystickManager;
 class UASMessage;
 class SettingsManager;
-class ADSBVehicle;
 class QGCCameraManager;
 class Joystick;
 class VehicleObjectAvoidance;
@@ -621,7 +620,6 @@ public:
     Q_PROPERTY(int                  telemetryLNoise         READ telemetryLNoise                                        NOTIFY telemetryLNoiseChanged)
     Q_PROPERTY(int                  telemetryRNoise         READ telemetryRNoise                                        NOTIFY telemetryRNoiseChanged)
     Q_PROPERTY(QVariantList         toolBarIndicators       READ toolBarIndicators                                      NOTIFY toolBarIndicatorsChanged)
-    Q_PROPERTY(QmlObjectListModel*  adsbVehicles            READ adsbVehicles                                           CONSTANT)
     Q_PROPERTY(bool              initialPlanRequestComplete READ initialPlanRequestComplete                             NOTIFY initialPlanRequestCompleteChanged)
     Q_PROPERTY(QVariantList         staticCameraList        READ staticCameraList                                       CONSTANT)
     Q_PROPERTY(QGCCameraManager*    dynamicCameras          READ dynamicCameras                                         NOTIFY dynamicCamerasChanged)
@@ -880,7 +878,6 @@ public:
     void setPrearmError(const QString& prearmError);
 
     QmlObjectListModel* cameraTriggerPoints (void) { return &_cameraTriggerPoints; }
-    QmlObjectListModel* adsbVehicles        (void) { return &_adsbVehicles; }
 
     int  flowImageIndex() { return _flowImageIndex; }
 
@@ -1257,7 +1254,6 @@ private slots:
     void _mavlinkMessageStatus(int uasId, uint64_t totalSent, uint64_t totalReceived, uint64_t totalLoss, float lossPercent);
 
     void _trafficUpdate         (bool alert, QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading);
-    void _adsbTimerTimeout      ();
     void _orbitTelemetryTimeout (void);
     void _protocolVersionTimeOut(void);
     void _updateFlightTime      (void);
@@ -1468,10 +1464,7 @@ private:
     QTimer                          _flightTimeUpdater;
     TrajectoryPoints*               _trajectoryPoints;
     QmlObjectListModel              _cameraTriggerPoints;
-    QmlObjectListModel              _adsbVehicles;
-    QMap<uint32_t, ADSBVehicle*>    _adsbICAOMap;
-    QMap<QString, ADSBVehicle*>     _trafficVehicleMap;
-    QTimer                          _adsbTimer;
+    //QMap<QString, ADSBVehicle*>     _trafficVehicleMap;
 
     // Toolbox references
     FirmwarePluginManager*      _firmwarePluginManager;
diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml
index c161be0f7f..0b04a2949a 100644
--- a/src/ui/preferences/GeneralSettings.qml
+++ b/src/ui/preferences/GeneralSettings.qml
@@ -840,6 +840,59 @@ Rectangle {
 
                     Item { width: 1; height: _margins }
 
+                    QGCLabel {
+                        id:         adsbSectionLabel
+                        text:       qsTr("ADSB Server")
+                        visible:    QGroundControl.settingsManager.adsbVehicleManagerSettings.visible
+                    }
+                    Rectangle {
+                        Layout.preferredHeight: adsbGrid.height + (_margins * 2)
+                        Layout.preferredWidth:  adsbGrid.width + (_margins * 2)
+                        color:                  qgcPal.windowShade
+                        visible:                adsbSectionLabel.visible
+                        Layout.fillWidth:       true
+
+                        GridLayout {
+                            id:                         adsbGrid
+                            anchors.topMargin:          _margins
+                            anchors.top:                parent.top
+                            Layout.fillWidth:           true
+                            anchors.horizontalCenter:   parent.horizontalCenter
+                            columns:                    2
+
+                            property var  adsbSettings:    QGroundControl.settingsManager.adsbVehicleManagerSettings
+
+                            FactCheckBox {
+                                text:                   adsbGrid.adsbSettings.adsbServerConnectEnabled.shortDescription
+                                fact:                   adsbGrid.adsbSettings.adsbServerConnectEnabled
+                                visible:                adsbGrid.adsbSettings.adsbServerConnectEnabled.visible
+                                Layout.columnSpan:      2
+                            }
+
+                            QGCLabel {
+                                text:               adsbGrid.adsbSettings.adsbServerHostAddress.shortDescription
+                                visible:            adsbGrid.adsbSettings.adsbServerHostAddress.visible
+                            }
+                            FactTextField {
+                                fact:                   adsbGrid.adsbSettings.adsbServerHostAddress
+                                visible:                adsbGrid.adsbSettings.adsbServerHostAddress.visible
+                                Layout.preferredWidth:  _valueFieldWidth
+                            }
+
+                            QGCLabel {
+                                text:               adsbGrid.adsbSettings.adsbServerPort.shortDescription
+                                visible:            adsbGrid.adsbSettings.adsbServerPort.visible
+                            }
+                            FactTextField {
+                                fact:                   adsbGrid.adsbSettings.adsbServerPort
+                                visible:                adsbGrid.adsbSettings.adsbServerPort.visible
+                                Layout.preferredWidth:  _valueFieldWidth
+                            }
+                        }
+                    }
+
+                    Item { width: 1; height: _margins }
+
                     QGCLabel {
                         id:         videoSectionLabel
                         text:       qsTr("Video")
-- 
GitLab