diff --git a/qgcimages.qrc b/qgcimages.qrc
index 5c995eac1753..8a683dea8191 100644
--- a/qgcimages.qrc
+++ b/qgcimages.qrc
@@ -103,6 +103,8 @@
src/AutoPilotPlugins/PX4/Images/GeoFenceLight.svg
src/AnalyzeView/GeoTagIcon.svg
src/UI/toolbar/Images/Gps.svg
+ src/UI/toolbar/Images/GpsAuthentication.svg
+ src/UI/toolbar/Images/GpsInterference.svg
src/UI/toolbar/Images/Hamburger.svg
src/UI/toolbar/Images/HamburgerThin.svg
src/FlightMap/Images/Help.svg
diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc
index 73aef0074126..004851bb1832 100644
--- a/src/FirmwarePlugin/FirmwarePlugin.cc
+++ b/src/FirmwarePlugin/FirmwarePlugin.cc
@@ -204,6 +204,7 @@ const QVariantList &FirmwarePlugin::toolIndicators(const Vehicle*)
if (_toolIndicatorList.isEmpty()) {
_toolIndicatorList = QVariantList({
QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/VehicleGPSIndicator.qml")),
+ QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/GPSResilienceIndicator.qml")),
QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/TelemetryRSSIIndicator.qml")),
QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/RCRSSIIndicator.qml")),
QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Controls/BatteryIndicator.qml")),
diff --git a/src/QmlControls/GPSIndicatorPage.qml b/src/QmlControls/GPSIndicatorPage.qml
index 152d5a74a1ec..cf8a4786bfc2 100644
--- a/src/QmlControls/GPSIndicatorPage.qml
+++ b/src/QmlControls/GPSIndicatorPage.qml
@@ -63,6 +63,30 @@ ToolIndicatorPage {
updateSettingsDisplayId()
}
+ function errorText() {
+ if (!_activeVehicle) {
+ return qsTr("Disconnected");
+ }
+
+ switch (_activeVehicle.gps.systemErrors.value) {
+ case 1:
+ return qsTr("Incoming correction");
+ case 2:
+ return qsTr("Configuration");
+ case 4:
+ return qsTr("Software");
+ case 8:
+ return qsTr("Antenna");
+ case 16:
+ return qsTr("Event congestion");
+ case 32:
+ return qsTr("CPU overload");
+ case 64:
+ return qsTr("Output congestion");
+ default:
+ return qsTr("Multiple errors");
+ }
+ }
contentComponent: Component {
ColumnLayout {
@@ -96,6 +120,12 @@ ToolIndicatorPage {
label: qsTr("Course Over Ground")
labelText: activeVehicle ? activeVehicle.gps.courseOverGround.valueString : valueNA
}
+
+ LabelledLabel {
+ label: qsTr("GPS Error")
+ labelText: errorText()
+ visible: activeVehicle && activeVehicle.gps.systemErrors.value > 0
+ }
}
SettingsGroupLayout {
diff --git a/src/UI/toolbar/CMakeLists.txt b/src/UI/toolbar/CMakeLists.txt
index 634b8fbd722f..547d24baecca 100644
--- a/src/UI/toolbar/CMakeLists.txt
+++ b/src/UI/toolbar/CMakeLists.txt
@@ -10,6 +10,7 @@ qt_add_qml_module(ToolbarModule
EscIndicatorPage.qml
GCSControlIndicator.qml
GimbalIndicator.qml
+ GPSResilienceIndicator.qml
JoystickIndicator.qml
ModeIndicator.qml
MultiVehicleSelector.qml
diff --git a/src/UI/toolbar/GPSResilienceIndicator.qml b/src/UI/toolbar/GPSResilienceIndicator.qml
new file mode 100644
index 000000000000..2d11446eb5f8
--- /dev/null
+++ b/src/UI/toolbar/GPSResilienceIndicator.qml
@@ -0,0 +1,180 @@
+/****************************************************************************
+ *
+ * (c) 2009-2024 QGROUNDCONTROL PROJECT
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+import QtQuick
+import QtQuick.Layouts
+
+import QGroundControl
+import QGroundControl.Controls
+
+//-------------------------------------------------------------------------
+//-- GPS Resilience Indicator
+Item {
+ id: control
+ width: height
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: showIndicator
+
+ property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
+ property var _gpsAggregate: _activeVehicle ? _activeVehicle.gpsAggregate : null
+
+ property var qgcPal: QGroundControl.globalPalette
+
+ property bool showIndicator: _activeVehicle && _gpsAggregate && (
+ (_gpsAggregate.authenticationState.value > 0 && _gpsAggregate.authenticationState.value < 255) ||
+ (_gpsAggregate.spoofingState.value > 0 && _gpsAggregate.spoofingState.value < 255) ||
+ (_gpsAggregate.jammingState.value > 0 && _gpsAggregate.jammingState.value < 255)
+ )
+
+ // Authentication Icon (Outer/Bottom Layer)
+ QGCColoredImage {
+ id: authIcon
+ width: parent.height * 0.95
+ height: parent.height * 0.95
+ anchors.centerIn: parent
+ source: "/qmlimages/GpsAuthentication.svg"
+ fillMode: Image.PreserveAspectFit
+ sourceSize.height: height
+ color: _authColor()
+ visible: _gpsAggregate && _gpsAggregate.authenticationState.value > 0 && _gpsAggregate.authenticationState.value < 255
+ }
+
+ // Interference Icon (Inner/Top Layer)
+ QGCColoredImage {
+ id: interfIcon
+ width: parent.height * 0.55
+ height: parent.height * 0.55
+ anchors.centerIn: parent
+ source: "/qmlimages/GpsInterference.svg"
+ fillMode: Image.PreserveAspectFit
+ sourceSize.height: height
+ color: _interfColor()
+ visible: _gpsAggregate && (Math.max(_gpsAggregate.spoofingState.value, _gpsAggregate.jammingState.value) > 0) && (Math.max(_gpsAggregate.spoofingState.value, _gpsAggregate.jammingState.value) < 255)
+ }
+
+ function _authColor() {
+ if (!_gpsAggregate) return qgcPal.colorGrey;
+ switch (_gpsAggregate.authenticationState.value) {
+ case 1: return qgcPal.colorYellow; // Initializing
+ case 2: return qgcPal.colorRed; // Error
+ case 3: return qgcPal.colorGreen; // OK
+ default: return qgcPal.colorGrey; // Unknown or Disabled
+ }
+ }
+
+ function _interfColor() {
+ if (!_gpsAggregate) return qgcPal.colorGrey;
+ let maxState = Math.max(_gpsAggregate.spoofingState.value, _gpsAggregate.jammingState.value);
+ switch (maxState) {
+ case 1: return qgcPal.colorGreen; // Not spoofed/jammed
+ case 2: return qgcPal.colorOrange; // Mitigated
+ case 3: return qgcPal.colorRed; // Detected
+ default: return qgcPal.colorGrey; // Unknown
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: mainWindow.showIndicatorDrawer(resiliencePopup, control)
+ }
+
+ Component {
+ id: resiliencePopup
+ ToolIndicatorPage {
+ showExpand: expandedComponent ? true : false
+ contentComponent: resilienceContent
+ }
+ }
+
+ Component {
+ id: resilienceContent
+ ColumnLayout {
+ spacing: ScreenTools.defaultFontPixelHeight / 2
+
+ // Unified GPS Resilience Status
+ SettingsGroupLayout {
+ heading: qsTr("GPS Resilience Status")
+ showDividers: true
+
+ LabelledLabel {
+ label: qsTr("GPS Jamming")
+ labelText: _gpsAggregate ? (_gpsAggregate.jammingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _gpsAggregate && _gpsAggregate.jammingState.value > 0 && _gpsAggregate.jammingState.value < 255
+ }
+
+ LabelledLabel {
+ label: qsTr("GPS Spoofing")
+ labelText: _gpsAggregate ? (_gpsAggregate.spoofingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _gpsAggregate && _gpsAggregate.spoofingState.value > 0 && _gpsAggregate.spoofingState.value < 255
+ }
+
+ LabelledLabel {
+ label: qsTr("GPS Authentication")
+ labelText: _gpsAggregate ? (_gpsAggregate.authenticationState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _gpsAggregate && _gpsAggregate.authenticationState.value > 0 && _gpsAggregate.authenticationState.value < 255
+ }
+ }
+
+ // GPS 1 Details
+ SettingsGroupLayout {
+ heading: qsTr("GPS 1 Details")
+ showDividers: true
+ visible: _activeVehicle && _activeVehicle.gps && (
+ (_activeVehicle.gps.jammingState.value > 0 && _activeVehicle.gps.jammingState.value < 255) ||
+ (_activeVehicle.gps.spoofingState.value > 0 && _activeVehicle.gps.spoofingState.value < 255) ||
+ (_activeVehicle.gps.authenticationState.value > 0 && _activeVehicle.gps.authenticationState.value < 255)
+ )
+
+ LabelledLabel {
+ label: qsTr("Jamming")
+ labelText: (_activeVehicle && _activeVehicle.gps) ? (_activeVehicle.gps.jammingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps.jammingState.value > 0 && _activeVehicle.gps.jammingState.value < 255
+ }
+ LabelledLabel {
+ label: qsTr("Spoofing")
+ labelText: (_activeVehicle && _activeVehicle.gps) ? (_activeVehicle.gps.spoofingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps.spoofingState.value > 0 && _activeVehicle.gps.spoofingState.value < 255
+ }
+ LabelledLabel {
+ label: qsTr("Authentication")
+ labelText: (_activeVehicle && _activeVehicle.gps) ? (_activeVehicle.gps.authenticationState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps.authenticationState.value > 0 && _activeVehicle.gps.authenticationState.value < 255
+ }
+ }
+
+ // GPS 2 Details
+ SettingsGroupLayout {
+ heading: qsTr("GPS 2 Details")
+ showDividers: true
+ visible: _activeVehicle && _activeVehicle.gps2 && (
+ (_activeVehicle.gps2.jammingState.value > 0 && _activeVehicle.gps2.jammingState.value < 255) ||
+ (_activeVehicle.gps2.spoofingState.value > 0 && _activeVehicle.gps2.spoofingState.value < 255) ||
+ (_activeVehicle.gps2.authenticationState.value > 0 && _activeVehicle.gps2.authenticationState.value < 255)
+ )
+
+ LabelledLabel {
+ label: qsTr("Jamming")
+ labelText: (_activeVehicle && _activeVehicle.gps2) ? (_activeVehicle.gps2.jammingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps2.jammingState.value > 0 && _activeVehicle.gps2.jammingState.value < 255
+ }
+ LabelledLabel {
+ label: qsTr("Spoofing")
+ labelText: (_activeVehicle && _activeVehicle.gps2) ? (_activeVehicle.gps2.spoofingState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps2.spoofingState.value > 0 && _activeVehicle.gps2.spoofingState.value < 255
+ }
+ LabelledLabel {
+ label: qsTr("Authentication")
+ labelText: (_activeVehicle && _activeVehicle.gps2) ? (_activeVehicle.gps2.authenticationState.enumStringValue || qsTr("n/a")) : qsTr("n/a")
+ visible: _activeVehicle.gps2.authenticationState.value > 0 && _activeVehicle.gps2.authenticationState.value < 255
+ }
+ }
+ }
+ }
+}
diff --git a/src/UI/toolbar/Images/GpsAuthentication.svg b/src/UI/toolbar/Images/GpsAuthentication.svg
new file mode 100644
index 000000000000..8e7094e1bcb4
--- /dev/null
+++ b/src/UI/toolbar/Images/GpsAuthentication.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/UI/toolbar/Images/GpsInterference.svg b/src/UI/toolbar/Images/GpsInterference.svg
new file mode 100644
index 000000000000..f474e26bbdf9
--- /dev/null
+++ b/src/UI/toolbar/Images/GpsInterference.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/Vehicle/FactGroups/CMakeLists.txt b/src/Vehicle/FactGroups/CMakeLists.txt
index d69785d883a6..9952307fc8d5 100644
--- a/src/Vehicle/FactGroups/CMakeLists.txt
+++ b/src/Vehicle/FactGroups/CMakeLists.txt
@@ -27,6 +27,8 @@ target_sources(${CMAKE_PROJECT_NAME}
VehicleGPS2FactGroup.h
VehicleGPSFactGroup.cc
VehicleGPSFactGroup.h
+ VehicleGPSAggregateFactGroup.cc
+ VehicleGPSAggregateFactGroup.h
VehicleLocalPositionFactGroup.cc
VehicleLocalPositionFactGroup.h
VehicleLocalPositionSetpointFactGroup.cc
diff --git a/src/Vehicle/FactGroups/GPSFact.json b/src/Vehicle/FactGroups/GPSFact.json
index 989eb1b53688..746c3fa4e985 100644
--- a/src/Vehicle/FactGroups/GPSFact.json
+++ b/src/Vehicle/FactGroups/GPSFact.json
@@ -58,6 +58,55 @@
"name": "count",
"shortDesc": "Sat Count",
"type": "uint32"
+},
+{
+ "name": "systemErrors",
+ "shortDesc": "General System Errors",
+ "type": "uint32"
+},
+{
+ "name": "spoofingState",
+ "shortDesc": "Signal Spoofing State",
+ "type": "uint8",
+ "enumStrings": "Unknown,Not spoofed,Mitigated,Ongoing",
+ "enumValues": "0,1,2,3",
+ "decimalPlaces": 0
+},
+{
+ "name": "jammingState",
+ "shortDesc": "Signal Jamming State",
+ "type": "uint8",
+ "enumStrings": "Unknown,Not jammed,Mitigated,Ongoing",
+ "enumValues": "0,1,2,3",
+ "decimalPlaces": 0
+},
+{
+ "name": "authenticationState",
+ "shortDesc": "Signal Authentication State",
+ "type": "uint8",
+ "enumStrings": "Unknown,Initializing,Error,Ok,Disabled",
+ "enumValues": "0,1,2,3,4",
+ "decimalPlaces": 0
+},
+{
+ "name": "correctionsQuality",
+ "shortDesc": "Corrections Quality",
+ "type": "uint8"
+},
+{
+ "name": "systemQuality",
+ "shortDesc": "System Status Quality",
+ "type": "uint8"
+},
+{
+ "name": "gnssSignalQuality",
+ "shortDesc": "Gnss Signal Quality",
+ "type": "uint8"
+},
+{
+ "name": "postProcessingQuality",
+ "shortDesc": "Post Processing Quality",
+ "type": "uint8"
}
]
}
diff --git a/src/Vehicle/FactGroups/VehicleGPS2FactGroup.cc b/src/Vehicle/FactGroups/VehicleGPS2FactGroup.cc
index 75c58cdbf7a1..ce5004d638fa 100644
--- a/src/Vehicle/FactGroups/VehicleGPS2FactGroup.cc
+++ b/src/Vehicle/FactGroups/VehicleGPS2FactGroup.cc
@@ -10,6 +10,7 @@
#include "VehicleGPS2FactGroup.h"
#include "Vehicle.h"
#include "QGCGeo.h"
+#include "development/mavlink_msg_gnss_integrity.h"
#include
@@ -21,6 +22,9 @@ void VehicleGPS2FactGroup::handleMessage(Vehicle *vehicle, const mavlink_message
case MAVLINK_MSG_ID_GPS2_RAW:
_handleGps2Raw(message);
break;
+ case MAVLINK_MSG_ID_GNSS_INTEGRITY:
+ _handleGnssIntegrity(message);
+ break;
default:
break;
}
diff --git a/src/Vehicle/FactGroups/VehicleGPS2FactGroup.h b/src/Vehicle/FactGroups/VehicleGPS2FactGroup.h
index 05c1c7e4bf3f..dcfb3595690c 100644
--- a/src/Vehicle/FactGroups/VehicleGPS2FactGroup.h
+++ b/src/Vehicle/FactGroups/VehicleGPS2FactGroup.h
@@ -17,7 +17,10 @@ class VehicleGPS2FactGroup : public VehicleGPSFactGroup
public:
explicit VehicleGPS2FactGroup(QObject *parent = nullptr)
- : VehicleGPSFactGroup(parent) {}
+ : VehicleGPSFactGroup(parent)
+ {
+ _gnssIntegrityId = 1;
+ }
// Overrides from VehicleGPSFactGroup
void handleMessage(Vehicle *vehicle, const mavlink_message_t &message) final;
diff --git a/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.cc b/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.cc
new file mode 100644
index 000000000000..ec0c4a5192d3
--- /dev/null
+++ b/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.cc
@@ -0,0 +1,132 @@
+/****************************************************************************
+ *
+ * (c) 2009-2024 QGROUNDCONTROL PROJECT
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#include "VehicleGPSAggregateFactGroup.h"
+#include "VehicleGPSFactGroup.h"
+#include "QGCLoggingCategory.h"
+#include
+
+VehicleGPSAggregateFactGroup::VehicleGPSAggregateFactGroup(QObject *parent)
+ : FactGroup(1000, ":/json/Vehicle/GPSFact.json", parent)
+{
+ _addFact(&_spoofingStateFact);
+ _addFact(&_jammingStateFact);
+ _addFact(&_authenticationStateFact);
+ _addFact(&_isStaleFact);
+
+ _spoofingStateFact.setRawValue(255);
+ _jammingStateFact.setRawValue(255);
+ _authenticationStateFact.setRawValue(255);
+ _isStaleFact.setRawValue(true);
+
+ _staleTimer.setSingleShot(true);
+ _staleTimer.setInterval(GNSS_INTEGRITY_STALE_TIMEOUT_MS);
+ connect(&_staleTimer, &QTimer::timeout, this, &VehicleGPSAggregateFactGroup::_onStaleTimeout);
+}
+
+void VehicleGPSAggregateFactGroup::bindToGps(VehicleGPSFactGroup* gps1, VehicleGPSFactGroup* gps2)
+{
+ _clearConnections();
+ _gps1 = gps1;
+ _gps2 = gps2;
+
+ if (_gps1) {
+ _connections << connect(_gps1, &VehicleGPSFactGroup::gnssIntegrityReceived, this, &VehicleGPSAggregateFactGroup::_onIntegrityUpdated);
+ }
+ if (_gps2) {
+ _connections << connect(_gps2, &VehicleGPSFactGroup::gnssIntegrityReceived, this, &VehicleGPSAggregateFactGroup::_onIntegrityUpdated);
+ }
+ _updateAggregates();
+}
+
+void VehicleGPSAggregateFactGroup::_updateAggregates()
+{
+ updateFromGps(_gps1, _gps2);
+}
+
+void VehicleGPSAggregateFactGroup::_onIntegrityUpdated()
+{
+ _isStaleFact.setRawValue(false);
+ _staleTimer.start();
+ _updateAggregates();
+}
+
+void VehicleGPSAggregateFactGroup::_onStaleTimeout()
+{
+ _spoofingStateFact.setRawValue(255);
+ _jammingStateFact.setRawValue(255);
+ _authenticationStateFact.setRawValue(255);
+ _isStaleFact.setRawValue(true);
+}
+
+void VehicleGPSAggregateFactGroup::_clearConnections()
+{
+ for (const auto& c : _connections) {
+ QObject::disconnect(c);
+ }
+ _connections.clear();
+}
+
+int VehicleGPSAggregateFactGroup::_valueOrInvalid(Fact* fact)
+{
+ if (!fact) {
+ return -1;
+ }
+ const QVariant v = fact->rawValue();
+ if (!v.isValid()) {
+ return -1;
+ }
+ bool ok = false;
+ const int val = v.toInt(&ok);
+ if (!ok) {
+ return -1;
+ }
+ return (val == 255) ? -1 : val;
+}
+
+int VehicleGPSAggregateFactGroup::_mergeWorst(int a, int b)
+{
+ return qMax(a, b);
+}
+
+int VehicleGPSAggregateFactGroup::_mergeAuthentication(int a, int b)
+{
+ // Priority: Unknown < Disabled < Initializing < OK < Error
+ auto getWeight = [](int val) {
+ switch (val) {
+ case AUTH_INVALID: return -1;
+ case AUTH_UNKNOWN: return 0; // lowest priority)
+ case AUTH_DISABLED: return 1;
+ case AUTH_INITIALIZING: return 2;
+ case AUTH_OK: return 3;
+ case AUTH_ERROR: return 4; // highest priority
+ default: return -1;
+ }
+ };
+
+ return (getWeight(a) >= getWeight(b)) ? a : b;
+}
+
+void VehicleGPSAggregateFactGroup::updateFromGps(VehicleGPSFactGroup* gps1, VehicleGPSFactGroup* gps2)
+{
+ const int spoof1 = _valueOrInvalid(gps1 ? gps1->spoofingState() : nullptr);
+ const int spoof2 = _valueOrInvalid(gps2 ? gps2->spoofingState() : nullptr);
+ const int jam1 = _valueOrInvalid(gps1 ? gps1->jammingState() : nullptr);
+ const int jam2 = _valueOrInvalid(gps2 ? gps2->jammingState() : nullptr);
+ const int auth1 = _valueOrInvalid(gps1 ? gps1->authenticationState() : nullptr);
+ const int auth2 = _valueOrInvalid(gps2 ? gps2->authenticationState() : nullptr);
+
+ const int spoofMerged = _mergeWorst(spoof1, spoof2);
+ const int jamMerged = _mergeWorst(jam1, jam2);
+ const int authMerged = _mergeAuthentication(auth1, auth2);
+
+ _spoofingStateFact.setRawValue(spoofMerged == -1 ? 255 : spoofMerged);
+ _jammingStateFact.setRawValue(jamMerged == -1 ? 255 : jamMerged);
+ _authenticationStateFact.setRawValue(authMerged == -1 ? 255 : authMerged);
+}
diff --git a/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.h b/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.h
new file mode 100644
index 000000000000..bd8d364b552f
--- /dev/null
+++ b/src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ * (c) 2009-2024 QGROUNDCONTROL PROJECT
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+#pragma once
+
+#include "FactGroup.h"
+#include
+#include
+#include
+
+class VehicleGPSFactGroup;
+
+class VehicleGPSAggregateFactGroup : public FactGroup
+{
+ Q_OBJECT
+ Q_PROPERTY(Fact* spoofingState READ spoofingState CONSTANT)
+ Q_PROPERTY(Fact* jammingState READ jammingState CONSTANT)
+ Q_PROPERTY(Fact* authenticationState READ authenticationState CONSTANT)
+ Q_PROPERTY(Fact* isStale READ isStale CONSTANT)
+public:
+ enum AuthState {
+ AUTH_UNKNOWN = 0,
+ AUTH_INITIALIZING = 1,
+ AUTH_ERROR = 2,
+ AUTH_OK = 3,
+ AUTH_DISABLED = 4,
+ AUTH_INVALID = -1
+ };
+
+ explicit VehicleGPSAggregateFactGroup(QObject *parent = nullptr);
+
+ Fact* spoofingState() { return &_spoofingStateFact; }
+ Fact* jammingState() { return &_jammingStateFact; }
+ Fact* authenticationState() { return &_authenticationStateFact; }
+ Fact* isStale() { return &_isStaleFact; }
+
+ void updateFromGps(VehicleGPSFactGroup* gps1, VehicleGPSFactGroup* gps2);
+ void bindToGps(VehicleGPSFactGroup* gps1, VehicleGPSFactGroup* gps2);
+
+private slots:
+ void _updateAggregates();
+ void _onIntegrityUpdated();
+ void _onStaleTimeout();
+
+private:
+ static constexpr int GNSS_INTEGRITY_STALE_TIMEOUT_MS = 5000;
+
+ static int _mergeWorst(int a, int b);
+ static int _mergeAuthentication(int a, int b);
+ static int _valueOrInvalid(Fact* fact);
+ void _clearConnections();
+
+ VehicleGPSFactGroup* _gps1 = nullptr;
+ VehicleGPSFactGroup* _gps2 = nullptr;
+ QTimer _staleTimer;
+ QVector _connections;
+
+ Fact _spoofingStateFact = Fact(0, QStringLiteral("spoofingState"), FactMetaData::valueTypeUint8);
+ Fact _jammingStateFact = Fact(0, QStringLiteral("jammingState"), FactMetaData::valueTypeUint8);
+ Fact _authenticationStateFact = Fact(0, QStringLiteral("authenticationState"), FactMetaData::valueTypeUint8);
+ Fact _isStaleFact = Fact(0, QStringLiteral("isStale"), FactMetaData::valueTypeBool);
+};
\ No newline at end of file
diff --git a/src/Vehicle/FactGroups/VehicleGPSFactGroup.cc b/src/Vehicle/FactGroups/VehicleGPSFactGroup.cc
index cebadd664218..f7a734e15dd2 100644
--- a/src/Vehicle/FactGroups/VehicleGPSFactGroup.cc
+++ b/src/Vehicle/FactGroups/VehicleGPSFactGroup.cc
@@ -10,6 +10,8 @@
#include "VehicleGPSFactGroup.h"
#include "Vehicle.h"
#include "QGCGeo.h"
+#include "QGCLoggingCategory.h"
+#include "development/mavlink_msg_gnss_integrity.h"
#include
@@ -25,6 +27,14 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject *parent)
_addFact(&_yawFact);
_addFact(&_lockFact);
_addFact(&_countFact);
+ _addFact(&_systemErrorsFact);
+ _addFact(&_spoofingStateFact);
+ _addFact(&_jammingStateFact);
+ _addFact(&_authenticationStateFact);
+ _addFact(&_correctionsQualityFact);
+ _addFact(&_systemQualityFact);
+ _addFact(&_gnssSignalQualityFact);
+ _addFact(&_postProcessingQualityFact);
_latFact.setRawValue(std::numeric_limits::quiet_NaN());
_lonFact.setRawValue(std::numeric_limits::quiet_NaN());
@@ -33,6 +43,13 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject *parent)
_vdopFact.setRawValue(std::numeric_limits::quiet_NaN());
_courseOverGroundFact.setRawValue(std::numeric_limits::quiet_NaN());
_yawFact.setRawValue(std::numeric_limits::quiet_NaN());
+ _spoofingStateFact.setRawValue(255);
+ _jammingStateFact.setRawValue(255);
+ _authenticationStateFact.setRawValue(255);
+ _correctionsQualityFact.setRawValue(255);
+ _systemQualityFact.setRawValue(255);
+ _gnssSignalQualityFact.setRawValue(255);
+ _postProcessingQualityFact.setRawValue(255);
}
void VehicleGPSFactGroup::handleMessage(Vehicle *vehicle, const mavlink_message_t &message)
@@ -49,6 +66,9 @@ void VehicleGPSFactGroup::handleMessage(Vehicle *vehicle, const mavlink_message_
case MAVLINK_MSG_ID_HIGH_LATENCY2:
_handleHighLatency2(message);
break;
+ case MAVLINK_MSG_ID_GNSS_INTEGRITY:
+ _handleGnssIntegrity(message);
+ break;
default:
break;
}
@@ -99,3 +119,24 @@ void VehicleGPSFactGroup::_handleHighLatency2(const mavlink_message_t &message)
_setTelemetryAvailable(true);
}
+
+void VehicleGPSFactGroup::_handleGnssIntegrity(const mavlink_message_t& message)
+{
+ mavlink_gnss_integrity_t gnssIntegrity;
+ mavlink_msg_gnss_integrity_decode(&message, &gnssIntegrity);
+
+ if (gnssIntegrity.id != _gnssIntegrityId) {
+ return;
+ }
+
+ systemErrors()->setRawValue (gnssIntegrity.system_errors);
+ spoofingState()->setRawValue (gnssIntegrity.spoofing_state);
+ jammingState()->setRawValue (gnssIntegrity.jamming_state);
+ authenticationState()->setRawValue (gnssIntegrity.authentication_state);
+ correctionsQuality()->setRawValue (gnssIntegrity.corrections_quality);
+ systemQuality()->setRawValue (gnssIntegrity.system_status_summary);
+ gnssSignalQuality()->setRawValue (gnssIntegrity.gnss_signal_quality);
+ postProcessingQuality()->setRawValue(gnssIntegrity.post_processing_quality);
+
+ emit gnssIntegrityReceived();
+}
diff --git a/src/Vehicle/FactGroups/VehicleGPSFactGroup.h b/src/Vehicle/FactGroups/VehicleGPSFactGroup.h
index 75a6aac85b69..b7d0c891d463 100644
--- a/src/Vehicle/FactGroups/VehicleGPSFactGroup.h
+++ b/src/Vehicle/FactGroups/VehicleGPSFactGroup.h
@@ -14,15 +14,23 @@
class VehicleGPSFactGroup : public FactGroup
{
Q_OBJECT
- Q_PROPERTY(Fact *lat READ lat CONSTANT)
- Q_PROPERTY(Fact *lon READ lon CONSTANT)
- Q_PROPERTY(Fact *mgrs READ mgrs CONSTANT)
- Q_PROPERTY(Fact *hdop READ hdop CONSTANT)
- Q_PROPERTY(Fact *vdop READ vdop CONSTANT)
- Q_PROPERTY(Fact *courseOverGround READ courseOverGround CONSTANT)
- Q_PROPERTY(Fact *yaw READ yaw CONSTANT)
- Q_PROPERTY(Fact *count READ count CONSTANT)
- Q_PROPERTY(Fact *lock READ lock CONSTANT)
+ Q_PROPERTY(Fact *lat READ lat CONSTANT)
+ Q_PROPERTY(Fact *lon READ lon CONSTANT)
+ Q_PROPERTY(Fact *mgrs READ mgrs CONSTANT)
+ Q_PROPERTY(Fact *hdop READ hdop CONSTANT)
+ Q_PROPERTY(Fact *vdop READ vdop CONSTANT)
+ Q_PROPERTY(Fact *courseOverGround READ courseOverGround CONSTANT)
+ Q_PROPERTY(Fact *yaw READ yaw CONSTANT)
+ Q_PROPERTY(Fact *count READ count CONSTANT)
+ Q_PROPERTY(Fact *lock READ lock CONSTANT)
+ Q_PROPERTY(Fact* systemErrors READ systemErrors CONSTANT)
+ Q_PROPERTY(Fact* spoofingState READ spoofingState CONSTANT)
+ Q_PROPERTY(Fact* jammingState READ jammingState CONSTANT)
+ Q_PROPERTY(Fact* authenticationState READ authenticationState CONSTANT)
+ Q_PROPERTY(Fact* correctionsQuality READ correctionsQuality CONSTANT)
+ Q_PROPERTY(Fact* systemQuality READ systemQuality CONSTANT)
+ Q_PROPERTY(Fact* gnssSignalQuality READ gnssSignalQuality CONSTANT)
+ Q_PROPERTY(Fact* postProcessingQuality READ postProcessingQuality CONSTANT)
public:
explicit VehicleGPSFactGroup(QObject *parent = nullptr);
@@ -36,14 +44,26 @@ class VehicleGPSFactGroup : public FactGroup
Fact *yaw() { return &_yawFact; }
Fact *count() { return &_countFact; }
Fact *lock() { return &_lockFact; }
+ Fact *systemErrors() { return &_systemErrorsFact; }
+ Fact *spoofingState() { return &_spoofingStateFact; }
+ Fact *jammingState() { return &_jammingStateFact; }
+ Fact *authenticationState() { return &_authenticationStateFact; }
+ Fact *correctionsQuality() { return &_correctionsQualityFact; }
+ Fact *systemQuality() { return &_systemQualityFact; }
+ Fact *gnssSignalQuality() { return &_gnssSignalQualityFact; }
+ Fact *postProcessingQuality() { return &_postProcessingQualityFact; }
// Overrides from FactGroup
void handleMessage(Vehicle *vehicle, const mavlink_message_t &message) override;
+signals:
+ void gnssIntegrityReceived();
+
protected:
void _handleGpsRawInt(const mavlink_message_t &message);
void _handleHighLatency(const mavlink_message_t &message);
void _handleHighLatency2(const mavlink_message_t &message);
+ void _handleGnssIntegrity(const mavlink_message_t& message);
Fact _latFact = Fact(0, QStringLiteral("lat"), FactMetaData::valueTypeDouble);
Fact _lonFact = Fact(0, QStringLiteral("lon"), FactMetaData::valueTypeDouble);
@@ -54,4 +74,14 @@ class VehicleGPSFactGroup : public FactGroup
Fact _yawFact = Fact(0, QStringLiteral("yaw"), FactMetaData::valueTypeDouble);
Fact _countFact = Fact(0, QStringLiteral("count"), FactMetaData::valueTypeInt32);
Fact _lockFact = Fact(0, QStringLiteral("lock"), FactMetaData::valueTypeInt32);
+ Fact _systemErrorsFact = Fact(0, QStringLiteral("systemErrors"), FactMetaData::valueTypeUint32);
+ Fact _spoofingStateFact = Fact(0, QStringLiteral("spoofingState"), FactMetaData::valueTypeUint8);
+ Fact _jammingStateFact = Fact(0, QStringLiteral("jammingState"), FactMetaData::valueTypeUint8);
+ Fact _authenticationStateFact = Fact(0, QStringLiteral("authenticationState"), FactMetaData::valueTypeUint8);
+ Fact _correctionsQualityFact = Fact(0, QStringLiteral("correctionsQuality"), FactMetaData::valueTypeUint8);
+ Fact _systemQualityFact = Fact(0, QStringLiteral("systemQuality"), FactMetaData::valueTypeUint8);
+ Fact _gnssSignalQualityFact = Fact(0, QStringLiteral("gnssSignalQuality"), FactMetaData::valueTypeUint8);
+ Fact _postProcessingQualityFact = Fact(0, QStringLiteral("postProcessingQuality"), FactMetaData::valueTypeUint8);
+
+ uint8_t _gnssIntegrityId {};
};
diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc
index 56d2ee3fc90c..8690655da7cd 100644
--- a/src/Vehicle/Vehicle.cc
+++ b/src/Vehicle/Vehicle.cc
@@ -101,6 +101,7 @@ Vehicle::Vehicle(LinkInterface* link,
, _vehicleFactGroup (this)
, _gpsFactGroup (this)
, _gps2FactGroup (this)
+ , _gpsAggregateFactGroup (this)
, _windFactGroup (this)
, _vibrationFactGroup (this)
, _temperatureFactGroup (this)
@@ -205,6 +206,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType,
, _vehicleFactGroup (this)
, _gpsFactGroup (this)
, _gps2FactGroup (this)
+ , _gpsAggregateFactGroup (this)
, _windFactGroup (this)
, _vibrationFactGroup (this)
, _clockFactGroup (this)
@@ -314,6 +316,8 @@ void Vehicle::_commonInit(LinkInterface* link)
// Flight modes can differ based on advanced mode
connect(QGCCorePlugin::instance(), &QGCCorePlugin::showAdvancedUIChanged, this, &Vehicle::flightModesChanged);
+ _gpsAggregateFactGroup.bindToGps(&_gpsFactGroup, &_gps2FactGroup);
+
_createImageProtocolManager();
_createStatusTextHandler();
_createMAVLinkLogManager();
@@ -321,6 +325,7 @@ void Vehicle::_commonInit(LinkInterface* link)
// _addFactGroup(_vehicleFactGroup, _vehicleFactGroupName);
_addFactGroup(&_gpsFactGroup, _gpsFactGroupName);
_addFactGroup(&_gps2FactGroup, _gps2FactGroupName);
+ _addFactGroup(&_gpsAggregateFactGroup, _gpsAggregateFactGroupName);
_addFactGroup(&_windFactGroup, _windFactGroupName);
_addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName);
_addFactGroup(&_temperatureFactGroup, _temperatureFactGroupName);
diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h
index d1c56ea091bb..86361854f1b5 100644
--- a/src/Vehicle/Vehicle.h
+++ b/src/Vehicle/Vehicle.h
@@ -36,6 +36,7 @@
#include "VehicleGeneratorFactGroup.h"
#include "VehicleGPS2FactGroup.h"
#include "VehicleGPSFactGroup.h"
+#include "VehicleGPSAggregateFactGroup.h"
#include "VehicleHygrometerFactGroup.h"
#include "VehicleLocalPositionFactGroup.h"
#include "VehicleLocalPositionSetpointFactGroup.h"
@@ -254,6 +255,7 @@ class Vehicle : public VehicleFactGroup
Q_PROPERTY(FactGroup* vehicle READ vehicleFactGroup CONSTANT)
Q_PROPERTY(FactGroup* gps READ gpsFactGroup CONSTANT)
Q_PROPERTY(FactGroup* gps2 READ gps2FactGroup CONSTANT)
+ Q_PROPERTY(FactGroup* gpsAggregate READ gpsAggregateFactGroup CONSTANT)
Q_PROPERTY(FactGroup* wind READ windFactGroup CONSTANT)
Q_PROPERTY(FactGroup* vibration READ vibrationFactGroup CONSTANT)
Q_PROPERTY(FactGroup* temperature READ temperatureFactGroup CONSTANT)
@@ -585,6 +587,7 @@ class Vehicle : public VehicleFactGroup
FactGroup* vehicleFactGroup () { return _vehicleFactGroup; }
FactGroup* gpsFactGroup () { return &_gpsFactGroup; }
FactGroup* gps2FactGroup () { return &_gps2FactGroup; }
+ FactGroup* gpsAggregateFactGroup () { return &_gpsAggregateFactGroup; }
FactGroup* windFactGroup () { return &_windFactGroup; }
FactGroup* vibrationFactGroup () { return &_vibrationFactGroup; }
FactGroup* temperatureFactGroup () { return &_temperatureFactGroup; }
@@ -1220,6 +1223,7 @@ private slots:
const QString _vehicleFactGroupName = QStringLiteral("vehicle");
const QString _gpsFactGroupName = QStringLiteral("gps");
const QString _gps2FactGroupName = QStringLiteral("gps2");
+ const QString _gpsAggregateFactGroupName = QStringLiteral("gpsAggregate");
const QString _windFactGroupName = QStringLiteral("wind");
const QString _vibrationFactGroupName = QStringLiteral("vibration");
const QString _temperatureFactGroupName = QStringLiteral("temperature");
@@ -1238,6 +1242,7 @@ private slots:
VehicleFactGroup* _vehicleFactGroup;
VehicleGPSFactGroup _gpsFactGroup;
VehicleGPS2FactGroup _gps2FactGroup;
+ VehicleGPSAggregateFactGroup _gpsAggregateFactGroup;
VehicleWindFactGroup _windFactGroup;
VehicleVibrationFactGroup _vibrationFactGroup;
VehicleTemperatureFactGroup _temperatureFactGroup;