Skip to content

feat(gps): Comprehensive GPS/RTK/NTRIP subsystem improvements#14112

Open
HTRamsey wants to merge 1 commit intomavlink:masterfrom
HTRamsey:feature/gps-updates
Open

feat(gps): Comprehensive GPS/RTK/NTRIP subsystem improvements#14112
HTRamsey wants to merge 1 commit intomavlink:masterfrom
HTRamsey:feature/gps-updates

Conversation

@HTRamsey
Copy link
Copy Markdown
Collaborator

Summary

Restructures and enhances the GPS subsystem with improved architecture, testability, and UI integration.

Architecture

  • Reorganized src/GPS/ into NTRIP/ and RTK/ subdirectories with clean separation of concerns
  • NTRIPTransport abstract base class with factory injection pattern for testability
  • RTCMRouter for multi-device RTCM distribution (vehicles via MAVLink + base stations via serial)
  • GPSTransport abstraction decoupling GPS provider from serial implementation
  • GPSEventModel for structured, queryable event logging

NTRIP

  • Exponential-backoff reconnect policy (NTRIPReconnectPolicy)
  • Connection statistics tracking (NTRIPConnectionStats)
  • GGA provider with configurable position source (NTRIPGgaProvider)
  • UDP RTCM forwarding (NTRIPUdpForwarder)
  • SSL/TLS error propagation (NTRIPError::SslError)
  • Transport config validation (NTRIPTransportConfig::isValid())
  • Fixed reconnect ordering bug where stopNTRIP() canceled pending reconnect timer
  • Fixed stopNTRIP() not canceling reconnect when called while idle with pending timer

RTK

  • RTCMMavlink MAVLink fragmentation with bandwidth tracking
  • RTKSatelliteModel for per-satellite signal strength display
  • GPSRTKFactGroup with survey-in progress, baseline, and heading facts

Vehicle Integration

  • New "RTK Corrections Status" section cross-referencing correction source (NTRIP/RTK base) with vehicle fix type
  • RTCM bandwidth and bytes-sent visible in both corrections status and NTRIP sections
  • 30-second warning when corrections flow but vehicle hasn't achieved RTK fix
  • Toolbar corrections dot: green when vehicle has RTK fix, orange when sending but no fix yet

UI

  • Unified GPS settings page (merged NTRIP settings into GPS settings)
  • Signal strength bar chart for RTK base satellites
  • Survey-in progress bar with accuracy convergence
  • Scrollable event log (QGCListView)
  • GPS 1/2 resilience sections via Repeater

Cleanup

  • Removed dead _MSC_VER < 1900 compatibility code
  • Changed _WIN32 to Q_OS_WIN for Qt convention consistency
  • Release-safe state guards (qCCritical replacing Q_ASSERT_X)

Tests

  • 213 tests across 22 suites, all passing
  • End-to-end tests with local TCP NTRIP caster (NTRIPEndToEndTest)
  • Full RTCM pipeline E2E: caster → transport → parser → router → MAVLink fragmentation
  • Mock infrastructure: MockNTRIPTransport, MockGPSRtk
  • Integration tests: NTRIPIntegrationTest, RTKIntegrationTest, SettingsLifecycleTest
  • RTCMTestHelper for building valid RTCM3 frames with correct CRC

Test plan

  • All 213 GPS unit/integration/E2E tests pass
  • Clean build with no warnings (-Werror)
  • Manual test: NTRIP connect/disconnect/reconnect cycle
  • Manual test: RTK base station survey-in and RTCM streaming
  • Manual test: Verify "RTK Corrections Status" section shows correct state
  • Manual test: Verify toolbar dot color changes with RTK fix status
  • Manual test: Settings page layout and functionality

@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 2 times, most recently from bd106e6 to ace132e Compare March 12, 2026 15:36
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 12, 2026

Build Results

Platform Status

Platform Status Details
Linux Failed View
Windows Passed View
MacOS Passed View
Android Passed View

Some builds failed.

Pre-commit

Check Status Details
pre-commit Failed (non-blocking) View

Pre-commit hooks: 4 passed, 36 failed, 7 skipped.

Artifact Sizes

Artifact Size
QGroundControl 248.06 MB
QGroundControl-installer-AMD64 134.92 MB
QGroundControl-installer-AMD64-ARM64 77.55 MB
QGroundControl-installer-ARM64 106.25 MB
QGroundControl-linux 339.16 MB
QGroundControl-mac 189.29 MB
QGroundControl-windows 189.31 MB
No baseline available for comparison

Updated: 2026-04-17 01:30:49 UTC • Triggered by: Android

@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 17 times, most recently from 10839e3 to 3c34872 Compare March 12, 2026 20:48
@HTRamsey HTRamsey marked this pull request as ready for review March 12, 2026 21:47
Copilot AI review requested due to automatic review settings March 12, 2026 21:47
Copy link
Copy Markdown
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 substantially refactors and expands QGroundControl’s GPS subsystem, splitting responsibilities into RTK and NTRIP components, adding new abstractions for transports and routing, and integrating new status/controls into the UI and QML globals.

Changes:

  • Introduces new RTK/NTRIP architecture components (RTCM routing, MAVLink RTCM fragmentation + stats, reconnect policy, connection stats, UDP forwarder) and adds extensive unit/integration/E2E test coverage.
  • Extends vehicle GPS FactGroups and metadata (altitudes, accuracies, speeds, and GNSS integrity enums) and updates toolbar indicator behavior/badges.
  • Adds/updates QML settings pages and supporting reusable QML controls, plus new RTK base station map overlay.

Reviewed changes

Copilot reviewed 149 out of 152 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/QmlControls/QGroundControlQmlGlobal.h Exposes new GPS-related objects to QML (gpsManager, gpsRtk fact group, models).
src/QmlControls/QGroundControlQmlGlobal.cc Wires QML globals to GPSManager singletons and models in ctor.
src/GPS/GPSManager.h/.cc Implements multi-device RTK management, RTCM router/mavlink, and event log plumbing.
src/GPS/NTRIP/NTRIPReconnectPolicy.cc Adds exponential-backoff reconnect logic for NTRIP.
test/GPS/NTRIP/NTRIPHttpTransportTest.cc Updates whitelist tests and adds transport config validation tests.
src/GPS/RTK/RTKBaseStationMapItem.qml Adds map overlay for RTK base station marker/line.
src/UI/toolbar/GPSIndicator.qml Enhances GPS indicator coloring, badges, and click handling.
Comments suppressed due to low confidence (1)

test/GPS/NTRIP/NTRIPHttpTransportTest.cc:78

  • This assertion is logically equivalent to QVERIFY(t._rtcmParser.isWhitelisted(9999)); but the current !x == false form is hard to read and easy to misinterpret. Simplifying it will make the intent (empty whitelist accepts all IDs) clearer.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +118 to +124
QGCLabel {
anchors.centerIn: parent
text: "!"
color: "white"
font.bold: true
font.pixelSize: parent.height * 0.7
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The interference badge label uses a hardcoded "white" text color. To keep theming consistent (light/dark palettes, custom themes), prefer using QGCPalette colors instead of literal color strings.

Copilot uses AI. Check for mistakes.
Comment thread src/QmlControls/QGroundControlQmlGlobal.h Outdated
Comment thread src/QmlControls/QGroundControlQmlGlobal.cc Outdated
Comment thread src/GPS/GPSManager.cc
if (_deviceMap.isEmpty()) {
return nullptr;
}
return _deviceMap.cbegin().value();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

_primaryDevice() selects the primary RTK device via QHash::cbegin(), but QHash iteration order is not deterministic. With multiple RTK devices connected, the chosen primary (and thus the fact group / satellite model / position source exposed to UI) can change unpredictably. Consider explicitly tracking a primary device (first-connected, user-selected, etc.) or using an ordered container.

Suggested change
return _deviceMap.cbegin().value();
// Select primary device deterministically by choosing the device with the
// lexicographically smallest key, instead of relying on QHash iteration order.
auto bestIt = _deviceMap.cbegin();
for (auto it = _deviceMap.cbegin(); it != _deviceMap.cend(); ++it) {
if (it.key() < bestIt.key()) {
bestIt = it;
}
}
return bestIt.value();

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +37
int NTRIPReconnectPolicy::nextBackoffMs() const
{
return qMin(kMinReconnectMs * (1 << qMin(_attempts, 4)), kMaxReconnectMs);
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

nextBackoffMs() caps the exponent with qMin(_attempts, 4), so the backoff tops out at 16s (1s * 2^4) and will never reach kMaxReconnectMs (30s). If the intended max is 30s, allow attempts to grow until the computed backoff exceeds kMaxReconnectMs (then clamp), or adjust the cap accordingly.

Copilot uses AI. Check for mistakes.
Comment thread src/GPS/RTK/RTKBaseStationMapItem.qml Outdated
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 2 times, most recently from be8e7f1 to 8492da8 Compare March 24, 2026 01:22
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch from 8490048 to 6b29db1 Compare March 25, 2026 04:36
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 7 times, most recently from c2693db to cfa8369 Compare March 28, 2026 04:41
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 2 times, most recently from 949fd32 to 1e08787 Compare April 8, 2026 13:51
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 2 times, most recently from 41c6bf3 to 95d7bbe Compare April 16, 2026 09:58
@HTRamsey HTRamsey requested a review from Copilot April 16, 2026 11:31
Copy link
Copy Markdown
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

Copilot reviewed 225 out of 232 changed files in this pull request and generated 3 comments.

Comment thread src/GPS/SatelliteModel.h
Comment on lines +75 to +83
};

// Rebuild _constellationSummary from used-satellite counts.
// counts maps full constellation name → used satellite count.
void _buildSummary(const QMap<QString, int> &counts);

// Map QGeoSatelliteInfo::SatelliteSystem to the canonical constellation name
// used throughout this class ("GPS", "GLONASS", "Galileo", "BeiDou", "QZSS", …).
static QString _qtSystemName(QGeoSatelliteInfo::SatelliteSystem system);
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

SatelliteModel.h uses QMap in the _buildSummary signature, but the header doesn't include <QtCore/QMap>. This can break compilation in any translation unit that includes SatelliteModel.h without already including QMap. Add the missing include in the header to keep it self-contained.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +66
qint64 NmeaPipeDevice::writeData(const char *data, qint64 len)
{
_buffer.append(data, len);
emit readyRead();
return len;
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

NmeaPipeDevice::writeData appends directly to _buffer without enforcing kMaxBufferSize, so callers using QIODevice::write can grow the buffer unbounded (feedData has overflow protection but writeData bypasses it). Apply the same overflow handling in writeData (or make the device read-only and route all writes through feedData).

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +34
void NTRIPConnectionStats::stop()
{
_rateTimer.stop();
if (_rateTracker.bytesPerSec() != 0.0) {
_rateTracker.reset();
emit dataRateChanged();
}
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

NTRIPConnectionStats::stop() may call _rateTracker.reset(), which also resets totalBytes (bytesReceived), but the code only emits dataRateChanged(). This leaves the bytesReceived Q_PROPERTY potentially out of sync with QML bindings. If stop() resets the tracker, also emit bytesReceivedChanged (and consider whether stop() should reset totals at all vs. only clearing the rate).

Copilot uses AI. Check for mistakes.
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 6 times, most recently from 93d439e to cbf9949 Compare April 17, 2026 17:54
@HTRamsey HTRamsey closed this Apr 17, 2026
@HTRamsey HTRamsey reopened this Apr 18, 2026
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch 4 times, most recently from 1cfa68f to 683bbe0 Compare April 18, 2026 05:17
@HTRamsey HTRamsey force-pushed the feature/gps-updates branch from 683bbe0 to dc8932b Compare April 18, 2026 05:33
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.

2 participants