Skip to content

RFC - Improve GNSS integrity information reporting - #30

Open
bgptiste wants to merge 11 commits into
mavlink:masterfrom
bgptiste:rfc-gnss-integrity
Open

RFC - Improve GNSS integrity information reporting #30
bgptiste wants to merge 11 commits into
mavlink:masterfrom
bgptiste:rfc-gnss-integrity

Conversation

@bgptiste

@bgptiste bgptiste commented Jul 3, 2026

Copy link
Copy Markdown

The current GNSS_INTEGRITY message provides GNSS resilience information at the receiver level, such as jamming, spoofing, and signal authentication status. However, it does not expose information at the individual RF-band level.

This RFC proposes introducing a new GNSS_BANDS message to complement GNSS_INTEGRITY by reporting jamming information for each RF front-end / frequency band. Providing per-band visibility would enable operators to better understand interference events, support post-flight analysis, and facilitate decision-making.

The proposal also aims to keep these two messages as vendor-agnostic as possible, allowing different GNSS receiver manufacturers to populate the same fields using equivalent information while avoiding vendor-specific abstractions. The primary GNSS receiver outputs considered in this work are those from Septentrio (Mosaic-G5 and X5) and u-blox (X20 and F9) receivers, whose formats are supported by PX4 and ArduPilot. NovAtel OEM7 receiver outputs were also studied as secondary references to support and validate this vendor-agnostic approach.

Full details are provided in the dedicated RFC.

Thanks to @dakejahl for the valuable discussions that helped shape this proposal.
I look forward to your feedback and any suggestions for improvement.

@hamishwillee

Copy link
Copy Markdown
Collaborator

@peterbarker @auturgy @tridge @julianoes Your feedback needed please. Thanks @bgptiste

@hamishwillee

Copy link
Copy Markdown
Collaborator

Thanks @bgptiste - we didn't get to this in the MAV Call, but will add to next week's agenda.

@bgptiste

bgptiste commented Jul 16, 2026

Copy link
Copy Markdown
Author

Thanks a lot @hamishwillee.

I have just updated the RFC to address the intended message rates and overhead, and I also mentioned the perspective of DroneCAN standardization.

Another point that will be very interesting to discuss is the decision to use a single multi-band GNSS_BANDS message, where each field is represented as an array indexed by the associated frequency band, rather than multiple single-band GNSS_BANDS messages. This is covered in particular in the new section on message overhead that I added.

Comment thread text/gnss-integrity-report.md Outdated
Comment thread text/gnss-integrity-report.md
Comment thread text/gnss-integrity-report.md
Comment thread text/gnss-integrity-report.md
Comment thread text/gnss-integrity-report.md Outdated

The proposed structure stores the information for all reported bands in a single `GNSS_BANDS` message. The `band_count` field indicates the number of frequency bands contained in the arrays below. Each field is therefore defined as a static array of size `GNSS_MAX_BANDS` (not yet defined), with the same index referring to the same reported band across all arrays. This avoids the overhead of multiple small messages while keeping the message structure simple for flight controller implementations. The choice of this structure is further discussed and justified in the [Intended message overhead](#intended-message-overhead) section.

Minimal `GNSS_BANDS` message:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Move to top of section

@hamishwillee hamishwillee left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bgptiste I have only scanned this, but it looks like a really good basis for the discussion. Thank you.

bgptiste and others added 2 commits July 17, 2026 10:27
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…th terminology descriptions) + document structure (after reviews)
@bgptiste

bgptiste commented Jul 17, 2026

Copy link
Copy Markdown
Author

Thank you for the feedback @hamishwillee. The suggestions are really helpful.

I have added the proposed message at the top of each section, as recommended. However, I decided not to include the changes made in a <details> block for now. This would require adding a summary beforehand, and I felt it would introduce some repetition of information, especially since this type of list is also used to justify the changes. Instead, I chose to highlight the modifications in bold within the list, making it easier to distinguish them from the associated rationale.

Regarding RAIM, a second look at the receiver documentation revealed that raim_hfom and raim_vfom fields (originally described as horizontal and vertical figures of merit) actually correspond to horizontal and vertical protection levels (HPL and VPL). The fields have been renamed accordingly. As recommended, I have also added a note explaining these terminologies in the list of changes.

@DronecodeBot

Copy link
Copy Markdown

This pull request has been mentioned on Dronecode Forum | Open Source Drone Development. There might be relevant details there:

https://discuss.px4.io/t/px4-dev-call-jul-22-2026-team-sync-and-community-q-a/49236/4

@dakejahl

Copy link
Copy Markdown

I captured my thoughts for the full picture of GNSS rework across mavlink/dronecan here
https://github.com/dakejahl/px4_ideas/blob/main/gnss/data_reporting_over_mavlink_and_dronecan.md

@hamishwillee

hamishwillee commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

@peterbarker @tridge Can you please review this? Would appreciate you also commenting on Jake's broader proposal linked in ^^^.

That is complementary to this RFC - it puts the RFC in the context of a message set like this:

GNSS - Base stream (5-10 Hz)  used by GCS: fix, position, velocity, accuracies, yaw
  │
  ├─ GNSS_INTEGRITY:  receiver-level health, jamming/spoofing/auth summary, RAIM, antenna, uptime
  ├─ GNSS_BANDS: per-RF-band interference: frequency, jammed, mitigated
  └─ GNSS_SATS: per-satellite: constellation, svid, signal, C/N0, elevation, azimuth, flags

Messages are linked by common id. Suggestion is GNSS is what is always needed and is streamed by default. The others are on-demand from GCS, and streamed at notionally 1Hz or less.

It all seems very well thought out by someone who manufactures, uses and integrates these things in both MAVLink and CAN. Given how flawed our current messages are, it seems timely to discuss - right?

@tridge

tridge commented Aug 19, 2026

Copy link
Copy Markdown
Member

@bgptiste I'm a skeptic on this, it presupposes a GCS operator can react to a hostile GNSS environment to avoid the issue. By the time a GCS operator can react to this sort of information the EKF state is already corrupted. It also assumes there is a mechanism for the operator to reconfigure the receiver to avoid the issue - the only way we have to do that now is GNSS protocol tunneling (eg. tunnel UBX to the GCS). That is quite doable (I've done it in flight) but if you have that then you wouldn't be using a generic MAVLink GNSS integrity message, you would be using that tunneled data to display the vendors own information which is likely to be a lot more useful (eg. the ubx spectrogram display)

@dakejahl

Copy link
Copy Markdown

it presupposes a GCS operator can react to a hostile GNSS environment to avoid the issue

They can land, switch to a manual mode, or reschedule the mission.

It also assumes there is a mechanism for the operator to reconfigure the receiver to avoid the issue

I'd argue that reconfiguration / mitigation is a separate issue. And you're right that it's more complex and likely cannot be done generically.

We've had a lot of users ask for this. Making GNSS receiver health a first class feature at the GCS can prevent confusion and improve mission reliability. It is better to know about GNSS health issues while on the ground than finding out through EKF divergence after takeoff. It will especially help new users doing airframe bringup, since they often make the mistake of positioning USB or other RF emitting devices too close to their receivers.

@dakejahl

Copy link
Copy Markdown

Checked the u-blox side of this against the F9 HPG 1.51 and X20 HPG 2.10 interface descriptions while wiring UBX-SEC-SIG into PX4 (PX4/PX4-GPSDrivers#231). Strong +1 on the direction; a few things that would change consumer code or are misread from the ICDs:

  1. GNSS_JAMMING_STATE renumbering inverts severity. Today 2 = MITIGATED, 3 = DETECTED; the RFC makes 2 = DETECTED, 3 = MITIGATED, while the spoofing enum in the same RFC keeps 3 as the worst case (AFFECTED). QGC's resilience indicator colours by max(spoofing, jamming), and PX4's sensor_gps constants and drivers assume 3 is worst. Suggest keeping the numeric values and only renaming (1 SPECTRUM_CLEAN, 2 MITIGATED, 3 DETECTED), so the enum stays max-able and nothing downstream silently flips.

  2. Entries are center frequencies, not RF front ends. SEC-SIG.jamStateCentFreq is one entry per center frequency with an in-use signal: an X20 with all constellations on reports up to eight (1575.42, 1561.098, 1602, 1227.6, 1246, 1268.52, 1278.75, 1176.45 MHz), three of them in the same L1 front end. Septentrio's RFBand entries are interference detections with a frequency and bandwidth. So "sent once per RF front-end / frequency band" will mislead implementers into a 1:1 band mapping, and GNSS_MAX_BANDS needs to be 8 rather than 3. Reporting the frequency and letting the GCS derive L1/L2/L5 is the right call; the description just needs to say so. Also worth stating that unused entries are zero-filled so MAVLink 2 truncation can drop them — an 0xFF invalid marker in an unused entry defeats that.

  3. u-blox mitigation is available. MON-RF.cwSuppression is "CW interference suppression level" per RF block, i.e. the notch in effect, and the block covering a frequency is known (rfBlockGnssBand on HPG 2.10, blockId before). That populates band_mitigation_state = CANCELLED rather than leaving it unknown.

  4. raim_stateUBX-TIM-TP.raim is T-RAIM on the time pulse, not position integrity. u-blox has no position RAIM state or protection level; better to leave the RAIM fields invalid than report "RAIM OK" about the PPS.

  5. Authentication result has no home. With OK redefined as "operating normally" and failures routed to spoofing_state, u-blox's actual outputs (NAV-PVT.nmaFixStatus/authTime, NAV-SIG.authStatus per signal) cannot be expressed: the ICD says explicitly that "Not Verified" does not imply spoofing. Suggest 3 = AUTHENTICATING (running, current solution not verified) and a new 5 = AUTHENTICATED (current solution verified), so OSNMA answers the question it exists for.

  6. corrections_age from NAV-PVT.lastCorrectionAge is a 4-bit bin (0–1 s … ≥120 s); the field description should say which edge a range-reporting receiver sends, or the centisecond unit implies precision the receiver lacks.

  7. Per-signal spoofing/authentication isn't speculative on u-blox: NAV-SIG has authStatus per signal and the X20 2.10 ICD refers to a per-signal spfState.

  8. system_errors on u-blox can also draw on MON-SYS (errorCount, warnCount), not only the antenna status.

@ThomasRigi

Copy link
Copy Markdown

My general feedback is that I like the direction it takes, but I'm unsure how much in-depth debugging info we need to send via mavlink. The average user (at least the average user of our drones) won't know the difference between "L1 is jammed" and "L1 is jammed by a 200 kHz carrier at -70 dBm", to quote from Jake's broader proposal (our users probably won't know what L1 is). But knowing that when a receiver is jammed is important information to them: they need to start thinking about fallbacks and alternatives.

In any case, @dakejahl has made it clear that they have an actual use case for sending this much debugging info, so it definitely makes sense to enable that with a dedicated message. And then for each individual case you can choose if you want to send only the "high-level" info, or also all the in-depth details per band. Might also depend on available bandwidth etc of your setup.

In that sense, I think the proposed split into GNSS_INTEGRITY and GNSS_BANDS works very well. (Add in @dakejahl 's proposition of GNSS_SATS and possibly even forwarding of the spectrum if bandwidth allows for full-in-depth live debugging.)

  • use only GNSS_INTEGRITY if you want a general sense of how robust your GNSS signal is.
  • add the dedicated message(s) on top if you want more debugging capabilities.
    -> I'm in favour of adding every field to GNSS_BANDS that someone considers useful for live debugging.

@hamishwillee

Copy link
Copy Markdown
Collaborator

@tridge Jake's response to your concern seems reasonable. The design makes the data of concern optional, so it doesn't have to clog the network if unwanted - just like any other debugging or domain/specific messages.

@bgptiste Can you update the PR in line with Jakes comments in #30 (comment)

The normal next-step in the MAVLink process is to create PRs for development.xml and start prototyping. The prototyping should test the assumptions we're making here - such as usefulness of specific fields.
I think we're at this point unless there is further concrete feedback?

I'd also like to see progression on the other messages you talk about above @dakejahl - is there any that you would like me to try push forward?

@bgptiste

Copy link
Copy Markdown
Author

@hamishwillee Yes, I will update the RFC accordingly.

Next, as requested in the broader proposal, I will provide an evaluation of the receiver field mapping for the future GNSS_SATS.

@bgptiste

bgptiste commented Sep 6, 2026

Copy link
Copy Markdown
Author

Thanks for the feedback @dakejahl. I have updated the RFC accordingly to address these points.

Before moving forward with the prototyping PR in development.xml, there are four remaining questions I'd like to clarify:

  1. GNSS_SPOOFING_STATE enum alignment: I updated GNSS_JAMMING_STATE to maintain 3 as the worst-case severity:
    1 = SPECTRUM_CLEAN, 2 = MITIGATED, 3 = DETECTED.
    To avoid conflicting definitions where 2 means MITIGATED for jamming but DETECTED for spoofing, should we align GNSS_SPOOFING_STATE to follow the exact same structure?

    1 = SPECTRUM_CLEAN
    2 = MITIGATED (even if spoofing mitigation is not directly reported by current receivers)
    3 = DETECTED
    4 = AFFECTED (if we keep the state indicating potential output impact)
    

    For reference, the vendor flags we are mapping are:

    • Septentrio RFStatus.Flags:
      Bit 0: SIG_AUTH_ALERT: Set when the receiver determined that the
          GNSS signals at its RF connector may not be authentic and that
          its output (position or raw measurements) may therefore be mis-
          leading. The receiver may be connected to a GNSS simulator, or
          be subject to a spoofing attack.
          This bit is based on a set of built-in tests to check the authenticity
          of the GNSS signals. In addition to those tests, Navigation Mes-
          sage Authentication (NMA) is performed. If NMA verification fails,
          bit NAV_MSG_AUTH_ALERT is set instead.
          Note that bit SIG_AUTH_ALERT may be set even if no interference
          is detected (i.e. with no associated RFBand sub-blocks).
      Bit 1: NAV_MSG_AUTH_ALERT: Set when a non-authentic navigation
          message is detected by NMA checks (e.g. Galileo OSNMA or Fu-
          gro AtomiChron NMA).
      
    • u-blox UBX-SEC-SIG.spfState:
      Spoofing state
          • 0: Unknown or deactivated: Monitor is not
          enabled, monitor is uninitialized, or the antenna is
          disconnected.
          • 1: No spoofing indicated: All spoofing detectors
          are active and report normal behavior.
          • 2: Spoofing suspected: Spoofing detectors report
          suspicion of a spoofing attack.
          • 3: Spoofing detected: Spoofing detectors confirm
          the presence of a spoofing attack.
      

    Would you recommend aligning 2 = MITIGATED across both enums, or keeping spoofing without a MITIGATED state and directly using 2 = DETECTED, 3 = AFFECTED (if AFFECTED is retained)?

  2. MAVLink 2 truncation for GNSS_BANDS: To ensure MAVLink 2 payload truncation works efficiently on empty/unused array entries in GNSS_BANDS, should we set 0 as the invalid value for interference_bandwidth and interference_power instead of UINT16_MAX / INT8_MIN? Even though 0 can technically be a valid physical value (e.g. 0 dBm or 0 kHz for pulsed interference).

  3. Per-band spoofing and authentication granularity: After reviewing the u-blox X20 (2.10 / 2.11) ICDs, spfState in UBX-SEC-SIG appears to be exclusively a global receiver state rather than a field inside the jamStateCentFreq sub-blocks (which only contain centFreq and jammed). Similarly, I didn't find a spfState inside UBX-NAV-SIG.

    For authentication, UBX-NAV-SIG indeed provides per-signal authStatus for each tracked/searched signal, but this comes from a different message than UBX-SEC-SIG.
    Given this, should we consider adding a band_authentication_state field instead of per-band spoofing? On Septentrio's side, authentication granularity is reported per satellite via masks (GALAuthStatus.GalAuthenticMask / GpsAuthenticMask) rather than per RF band.

  4. GNSS_SEPT_QUALITY optional message: Is it worth adding the optional GNSS_SEPT_QUALITY message to preserve Septentrio’s legacy 0-10 diagnostic indicators? This would avoid losing helpful metrics for Septentrio users while keeping the main GNSS_INTEGRITY message completely vendor-agnostic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants