Skip to content

Conversation

@Tory9
Copy link
Contributor

@Tory9 Tory9 commented Dec 25, 2025

Description

Adds GPS resilience monitoring to display jamming, spoofing, and authentication status from MAVLink GNSS_INTEGRITY messages.

Based on: PR #13009

Key Features:

  • Dual-GPS aggregation: merges integrity data from GPS1 and GPS2 using worst-case logic to always display the most critical state
  • Layered toolbar indicator: shield icon representing authentication status with overlaid interference waves for jamming/spoofing alerts
  • Per-GPS drill-down view: status panel showing individual GPS receiver resilience metrics
  • GPS error field enhancement: extends existing GPS status QML with error reporting
Screenshot 2025-12-23 171843

Type of Change

New feature (non-breaking change that adds functionality)

Related Work

MAVLink Message Definition (development. xml): mavlink/mavlink#2110
Message value naming updated: mavlink/mavlink#2385
PX4 implementation using GNSS_INTEGRITY: PX4/PX4-Autopilot#25012 (merged)
ArduPilot implementation using GNSS_INTEGRITY: ArduPilot/ardupilot#31040 (in review)

Hardware Tested:

Flight Controller: CubeOrange+
Flight Stacks: ArduPilot & PX4 implementations
GNSS Receiver: Septentrio Mosaic X5
Platform: Linux

Copilot AI review requested due to automatic review settings December 25, 2025 14:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds GPS resilience monitoring to QGroundControl, enabling the display of jamming, spoofing, and authentication status from MAVLink GNSS_INTEGRITY messages. The implementation includes a dual-GPS aggregation system that merges integrity data from GPS1 and GPS2 using worst-case logic, a layered toolbar indicator with shield and interference wave icons, and a per-GPS drill-down panel for detailed status information.

Key Changes

  • Created VehicleGPSAggregateFactGroup to aggregate integrity data from dual GPS receivers using worst-case merging logic
  • Extended VehicleGPSFactGroup and VehicleGPS2FactGroup to handle GNSS_INTEGRITY MAVLink messages with new facts for jamming, spoofing, authentication, and quality metrics
  • Added GPSResilienceIndicator.qml toolbar component with layered icons and color-coded status display

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/Vehicle/Vehicle.h/cc Integrated VehicleGPSAggregateFactGroup into Vehicle fact group hierarchy
src/Vehicle/FactGroups/VehicleGPSFactGroup.h/cc Added GNSS_INTEGRITY message handling and 8 new integrity-related facts
src/Vehicle/FactGroups/VehicleGPSAggregateFactGroup.h/cc New class implementing dual-GPS aggregation with worst-case merging and stale data detection
src/Vehicle/FactGroups/VehicleGPS2FactGroup.h/cc Configured GPS2 to handle GNSS_INTEGRITY messages with ID=1
src/Vehicle/FactGroups/GPSFact.json Added metadata for 8 new GPS integrity facts with enum definitions
src/UI/toolbar/GPSResilienceIndicator.qml New toolbar indicator with layered shield/interference icons and drill-down panel
src/QmlControls/GPSIndicatorPage.qml Added GPS error field with system error descriptions
src/UI/toolbar/Images/*.svg Added authentication shield and interference wave SVG icons
src/Vehicle/FactGroups/CMakeLists.txt Added VehicleGPSAggregateFactGroup source files
src/UI/toolbar/CMakeLists.txt Added GPSResilienceIndicator.qml to toolbar module
qgcimages.qrc Registered new GPS authentication and interference SVG resources
src/FirmwarePlugin/FirmwarePlugin.cc Added GPSResilienceIndicator to default toolbar indicators list

_isStaleFact.setRawValue(true);

_staleTimer.setSingleShot(true);
_staleTimer.setInterval(5000);
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stale timer interval is hardcoded to 5000 milliseconds. Consider defining this as a named constant (e.g., static constexpr int GNSS_INTEGRITY_STALE_TIMEOUT_MS = 5000;) to improve code readability and make it easier to adjust if needed.

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +88
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");
}
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The systemErrors field appears to be a bitmask (values are powers of 2: 1, 2, 4, 8, 16, 32, 64), but the switch statement handles only exact single-bit values. When multiple errors occur simultaneously (e.g., value = 3 for bits 0 and 1 set), it will fall through to "Multiple errors" without identifying which specific errors are present. Consider implementing proper bit-flag checking to handle combinations of errors, or at least document this behavior if it's intentional.

Suggested change
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");
}
var errors = _activeVehicle.gps.systemErrors.value;
var messages = [];
if (errors & 1) {
messages.push(qsTr("Incoming correction"));
}
if (errors & 2) {
messages.push(qsTr("Configuration"));
}
if (errors & 4) {
messages.push(qsTr("Software"));
}
if (errors & 8) {
messages.push(qsTr("Antenna"));
}
if (errors & 16) {
messages.push(qsTr("Event congestion"));
}
if (errors & 32) {
messages.push(qsTr("CPU overload"));
}
if (errors & 64) {
messages.push(qsTr("Output congestion"));
}
if (messages.length === 1) {
return messages[0];
} else if (messages.length > 1) {
return messages.join(", ");
} else {
return qsTr("Multiple errors");
}

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does need to be addressed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally grouped multiple errors into a single "Multiple errors" message to keep the UI clean, as there isn't much space to list several error strings like "Incoming correction, Configuration" without cluttering the display. Since these are exceptional situations, and having multiple at once is even rarer, a simple summary seemed like the best trade-off, especially since investigation would be required anyway. However, if you think seeing every individual error is critical, I can add the bit-flag checking

@Tory9 Tory9 force-pushed the pr-resilience-improved branch from f722357 to 20b869e Compare December 25, 2025 15:53
@HTRamsey
Copy link
Collaborator

you can ignore the pre-commit for now. Is this ready?

@Tory9
Copy link
Contributor Author

Tory9 commented Dec 25, 2025

Hi @HTRamsey. Yes, I think it is ready for review.
A few design decisions are worth discussing in case the community sees a better or more suitable approach, such as the addition of VehicleGPSAggregateFactGroup, authentication state merge priority, and info displaying in the GUI

@HTRamsey
Copy link
Collaborator

HTRamsey commented Dec 25, 2025

Gotcha, in general it look good to me. We'll wait for @DonLakeFlyer to check out the QML and your attached picture

Copy link
Collaborator

@dakejahl dakejahl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to aggregate the data? I would probably try and use the FactGroupListModel so that you can show the data from both GPS if available rather than aggregating and only showing the worst.

Comment on lines +71 to +88
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");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does need to be addressed

Comment on lines 135 to 141
case -1: return -1;
case 0: return 0;
case 4: return 1;
case 1: return 2;
case 3: return 3;
case 2: return 4;
default: return -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be defined somewhere, not magic numbers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch, addressed this in commit 507556e

, _isStaleFact (0, "isStale", FactMetaData::valueTypeBool, this)
{
_spoofingStateFact.setEnumInfo(
QStringList({ "Unknown", "Not spoofed", "Mitigated", "Ongoing" }),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already done during the metadata parsing, why do it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I added it initially I wasn’t loading the parent metadata, but it makes more sense to rely on it. I dropped the redundant setEnumInfo in commit 34a7f52

#include "Vehicle.h"
#include "QGCGeo.h"
#include "QGCLoggingCategory.h"
#include "development/mavlink_msg_gnss_integrity.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So QGC is indeed building and shipping development messages? I thought recently we decided not to do that anymore due to the standard modes fiasco? Should we put the mavlink development message features behind a preprocessor guard for debug builds only?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was like that, and then we needed development for some particular reason that I can't recall right now

: VehicleGPSFactGroup(parent) {}
: VehicleGPSFactGroup(parent)
{
_setGnssIntegrityContext(1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this supposed to do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted each GPS fact group to filter GNSS_INTEGRITY msgs only for its corresponding receiver (gps1/gps2). That’s why I added gnssIntegrityId, which is checked in _handleGnssIntegrity before setting values. Earlier I used a helper _setGnssIntegrityContext(), but it was simpler to set the ID directly in the constructor. This is in commit b9c531e

@Tory9
Copy link
Contributor Author

Tory9 commented Dec 29, 2025

Do we really want to aggregate the data? I would probably try and use the FactGroupListModel so that you can show the data from both GPS if available rather than aggregating and only showing the worst.

This aggregation strategy was actually recommended in earlier discussions (see PR #13009). The idea is to use the toolbar icon for a "worst-case" summary—if either GPS has an issue, the pilot sees it immediately without having to mentally parse two separate statuses. Since we still show the full breakdown for GPS 1 and GPS 2 in the expanded details view, we get the immediate warning on the main screen with the specific details just a click away

@Tory9 Tory9 requested a review from dakejahl January 5, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants