Skip to content

Conversation

@Tory9
Copy link

@Tory9 Tory9 commented Sep 4, 2025

What does this PR do?

This PR implements support for collecting GNSS resilience data from Septentrio receivers and relaying it to the Ground Control Station (GCS).


Why is this change needed?

GNSS resilience information—such as spoofing, jamming, and authentication states—is crucial for operational safety and informed decision-making. Providing this data to the GCS enhances an operator's situational awareness, allowing for better flight planning and in-flight control.


How is it implemented?

The Septentrio driver has been modified to:

  • Auto-configure the receiver to output the SBF blocks containing resilience information (if auto-config is enabled).
  • Parse these new SBF message types.
  • Map the extracted data to the MAVLink GNSS_INTEGRITY message.
  • Add the GNSS_INTEGRITY message to the STREAM_EXTENDED_STATUS to ensure it is sent to the GCS.

Related Work & Context


How has this been tested?

The feature was validated on a CubeOrange+ using a replayed SBF log file that was recorded in an environment with active jamming and spoofing.

Copy link
Contributor

@peterbarker peterbarker left a comment

Choose a reason for hiding this comment

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

Nice to see some effort being put in to report this stuff.

Comment on lines 1491 to 1502
uint8_t id = instance;
uint32_t system_error = get_system_errors(instance);
uint8_t authentication = get_authentication_state(instance);
uint8_t jamming = get_jamming_state(instance);
uint8_t spoofing = get_spoofing_state(instance);
uint8_t raim_state = UINT8_MAX; //not implemented yet
uint16_t raim_hfom = UINT16_MAX; //not implemented yet
uint16_t raim_vfom = UINT16_MAX; //not implemented yet
uint8_t corrections_quality = UINT8_MAX; //not implemented yet
uint8_t systems_status_summary = UINT8_MAX; //not implemented yet
uint8_t gnss_signal_quality = UINT8_MAX; //not implemented yet
uint8_t post_processing_quality = UINT8_MAX; //not implemented yet
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't bother making these locals, just throw them straight into the packet; add comments to indicate which field is being passed where things are unclear.

uint8_t systems_status_summary = UINT8_MAX; //not implemented yet
uint8_t gnss_signal_quality = UINT8_MAX; //not implemented yet
uint8_t post_processing_quality = UINT8_MAX; //not implemented yet
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "gps %u with auth %u and spooof %u", id, authentication, spoofing);
Copy link
Contributor

Choose a reason for hiding this comment

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

Diagnostics to be removed :-)


/// GPS error bits. These are kept aligned with MAVLink by
/// static_assert in AP_GPS.cpp
enum GPS_Errors {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make these enum-class and contained within the AP_GPS class? Would allow for dropping the extra namespacing.

Comment on lines 174 to 175
enum GPS_Authentication {
AUTHENTICATION_UNKNOWN = 0, ///< The GPS receiver does not provide GPS signal authentication info
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
enum GPS_Authentication {
AUTHENTICATION_UNKNOWN = 0, ///< The GPS receiver does not provide GPS signal authentication info
enum Authentication {
UNKNOWN = 0, ///< The GPS receiver does not provide GPS signal authentication info

... and move to within AP_GPS:: namespace. This reduces a lot of duplication.

The enumeration you're modelling this on is one of three enuemations we use for status type in ArduPilot - but it's also the one I believe we're trying to remove!

return state[instance].gps_yaw_configured;
}

// general errors in the GNSS system
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we really need these accessors, but if you do want to keep them please make them private.

Comment on lines 544 to 546
uint32_t get_system_errors() const {
return get_system_errors(primary_instance);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is used?

We have a no-dead-code policy which we mostly enforce....

Similarly for other "primary_instance" methods

Comment on lines 595 to 597
if (RxError & INVALIDCONFIG) {
state.system_errors |= AP_GPS::GPS_Errors::CONFIGURATION;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if a static const data structure here which is iterated over might make this a bit nicer / simpler to extend later / more compact byte-wise.

}
state.jamming_state = AP_GPS::GPS_Jamming::JAMMING_OK;
for (int i = 0; i < temp.N; i++) {
RFStatusRFBandSubBlock* rf_band_data = (RFStatusRFBandSubBlock*)(&temp.RFBand + i * temp.SBLength);
Copy link
Contributor

Choose a reason for hiding this comment

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

This construct makes me unhappy.

At the very least you can (a) create a pointer you can index into and (b) use a reference for each of the entries in the array for pulling things out.

But I'd really also like to see a belt-and-bracers check that we don't step outside the packet here. When pointer games go wrong vehicles get lost. Right here if temp.N is corrupt (which can happen if the packets gets through the checksumming while invalid, or if the GPS device itself puts a bad value in) we could try to access memory we don't have access to and fall out of the sky. We had a similar problem with massive packet loss in this driver that we fixed with #29399

static const ap_message STREAM_EXTENDED_STATUS_msgs[] = {
MSG_SYS_STATUS,
MSG_POWER_STATUS,
MSG_GNSS_INTEGRITY,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know whether we will want to stream this message by default. Telemetry bandwidth is actually one of those things that doesn't seem to be growing for a lot of users!

Users can get the autopilot to start streaming the message in many ways if we don't decide to stream this by default.

@Tory9
Copy link
Author

Tory9 commented Sep 9, 2025

@peterbarker , thank you for a detailed review. I have considered your suggestions and applied some changes. I have a question about streaming this message by default. Since I'm new to ArduPilot, could you mb hint at the best way to introduce this to the community?

@Tory9 Tory9 requested a review from peterbarker September 11, 2025 07:39

#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
// general errors in the GNSS system
uint32_t get_system_errors(uint8_t instance) const {
Copy link
Member

Choose a reason for hiding this comment

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

Not a blocker but one suggestion that was raised on the dev call that that it might be worth combining all of these get functions into one function as they are all called in once in the same currently.

@MattKear
Copy link
Member

A general comment that was made on the dev call was that it would be good if an accessor variable can be added. If the GPS is sending the relevant information then the variable is set true. That way any get functions could return early if the accessor if false.

#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
if (temp.Flags==0) {
state.spoofing_state = static_cast<uint8_t>(AP_GPS::Spoofing::OK);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: the brackets and else should be on the same line

@rmackay9
Copy link
Contributor

Could you double check that each commit only affects a single subsystem? Also each commit should be prefixed with the subsystem affected. E.g. "AP_GPS: add xxxx"

Finally could you rebase on master to get rid of the "merge" commit?

@peterbarker
Copy link
Contributor

https://ardupilot.org/dev/docs/contributing.html

@peterbarker
Copy link
Contributor

https://ardupilot.org/dev/docs/mavlink-requesting-data.html

@rmackay9 rmackay9 added the WikiNeeded needs wiki update label Sep 24, 2025
@Georacer
Copy link
Contributor

Georacer commented Sep 24, 2025

Discussion on today's Dev Call:

Peter: The feature should be define-fenced for 1M flash boards.
Randy: Please split the commits into one for each subsystem.
P: What is the size-cost to the output binary when the feature is left undefined? It would be nice to have this as small as possible, ideally zero.
R: It’s very zero for small boards, it’s a lot for larger boards.
P: A couple dozen bytes extra when the feature is turned off is okay. Anything larger is a problem.
R: We also need a build option for the build server, in the build_options.py file.
Victoria: I’ve added the message on the EXTRA_3 stream, but I don’t see it in QGC.
P: Adding the message ID on the stream ID list is one way to have the message sent.
Another is to have the GCS request for this message on-demand.
QGC probably doesn’t have compiled-in
You can add a SEND_STATUSTEXT macro before the place where you try to send the message and see if this code path is being traversed.

R: If the feature is off by default, we should be streaming the message in this case.
P: Agreed.
Matt: We can have it off by default, we don’t know how much usage this feature will get.
P: It also needs a change in our Wiki, to document this feature.

@Tory9 Tory9 force-pushed the pr-resilience3 branch 3 times, most recently from 2fdd96a to 6dd1438 Compare October 8, 2025 13:05
@Tory9
Copy link
Author

Tory9 commented Oct 8, 2025

Hello @peterbarker,

I was thinking about the issue of memory being occupied by this feature even if it's not used, which is a problem for boards with smaller flash memory. However, on the other hand, requiring a custom build—even with the great web tool ArduPilot has—might discourage some users from using the feature.

Thus, I've implemented the following solution:

  • The feature's code is excluded at compile time for boards with less than 1MB of memory.
  • For boards with more memory, the feature code is included, but if the connected receiver is not a Septentrio receiver, then the message is not streamed.
  • If users with smaller flash on their controller want to include the feature, they can still do so with a custom build.
    Please let me know if this approach is okay and if I correctly made the custom build implementation

Copy link
Contributor

@peterbarker peterbarker left a comment

Choose a reason for hiding this comment

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

Looking pretty good now!


#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
void AP_GPS::send_mavlink_gnss_integrity(mavlink_channel_t chan, uint8_t instance) {
if(state[instance].has_gnss_integrity) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if(state[instance].has_gnss_integrity) {
if (!state[instance].has_gnss_integrity) {
return;
}

... just return early if there's nothing to do

void AP_GPS::send_mavlink_gnss_integrity(mavlink_channel_t chan, uint8_t instance) {
if(state[instance].has_gnss_integrity) {

mavlink_msg_gnss_integrity_send(chan,
Copy link
Contributor

Choose a reason for hiding this comment

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

indenting problem here...

bool have_gps_yaw_configured(uint8_t instance) const {
return state[instance].gps_yaw_configured;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

stray whitespace change

#ifndef AP_GPS_GPS2_RTK_SENDING_ENABLED
#define AP_GPS_GPS2_RTK_SENDING_ENABLED HAL_GCS_ENABLED && AP_GPS_ENABLED && GPS_MAX_RECEIVERS > 1 && (AP_GPS_SBF_ENABLED || AP_GPS_ERB_ENABLED)
#endif
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Stray whitespace change

constexpr const char* AP_GPS_SBF::_initialisation_blob[];
constexpr const char* AP_GPS_SBF::sbas_on_blob[];

const AP_GPS_SBF::SBF_Error_Map AP_GPS_SBF::sbf_error_map[] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const AP_GPS_SBF::SBF_Error_Map AP_GPS_SBF::sbf_error_map[] = {
static const AP_GPS_SBF::SBF_Error_Map AP_GPS_SBF::sbf_error_map[] {

Copy link
Author

Choose a reason for hiding this comment

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

I have the static keyword on the declaration in the .h file. If I add it to the definition in the .cpp file too, the compiler throws an error, so i didnt apply this change

(unsigned int)(RxError & RX_ERROR_MASK), (unsigned int)(temp.RxError & RX_ERROR_MASK));
}
RxError = temp.RxError;
if ((ExtError & EXT_ERROR_MASK) != (temp.ExtError & EXT_ERROR_MASK)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

So since these changes are outside the new AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED, I guess these must be changes to existing behaviour?

These changes are not pulled out as a separate commit, and you haven't mentioned them anywhere, I think?

If the extent of the changes comes to "Print external error changes as they come in" then we can probably let that slide.

One thing here, 'though, is that our send-rate here is limited only to the rate at which we get messages from the external unit. That risks flooding telemetry links and making vehicles choke up on other telemetry... pre-existing in this driver, so also not a big deal.

Copy link
Author

Choose a reason for hiding this comment

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

You're right. This change tho kinda related to adding the GNSS integrity message. Previously, we only monitored and reported internal errors (RxError). However, with the addition of the GNSS_INTEGRITY message, it became meaningful to also retrieve external errors (ExtError) from the ReceiverStatus SBF message. To keep the implementation consistent with the existing code, I added the same GCS reporting mechanism for external errors that was already in place for internal errors. As GCS_SEND_TEXT calls are purely informational and not used for decision-making we can drop it as well, however then we will also need to drop existing error reporting for internal errors for consistency. These are also only sent on change of error, which normally shouldn't happen with high frequency as well.

case RFStatus:
{
const msg4092 &temp = sbf_msg.data.msg4092u;
check_new_itow(temp.TOW, sbf_msg.length);
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, change unrelated to new functionality. I think this is probably benign, but it might just be neater to not know about the message when we're not doing integrity stuff?

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for the suggestion. I've moved the guard inside the case block, so if integrity is disabled we just skip processing entirely. Placing the entire case RFStatus inside the guard, that's also an option, but it seems less consistent to me. In AP_GPS_SBF.h we define/intro the msg4092 for RFStatus, and this definition/intro is not guarded by the integrity flag. So keeping the case label present seems more consistent with that approach. However, if you think encapsulating the entire case in the guard would be cleaner for this driver, I'm happy to make that change. Please let me know what you think.

const uint8_t *packet_end = (const uint8_t *)&temp + (sbf_msg.length - 8);

for (uint8_t i = 0; i < temp.N; i++) {
if (p + temp.SBLength > packet_end) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also check that (p + temp.SBLength) -(&temp.RFBand) is < sizeof(sbf_msg).

We do not want to implicitly trust this packet to not be corrupt, even if it does pass a checksum. If temp.SBLength or tmp.N are wild then we run the risk of falling out of the sky. Which is bad.

... also, you can probably make this a lot neater by casting (const uint8_t *)&temp.RFBand to const RFStatusRFBandSubBlock &rf_band_data and incrementing that to move between the N entries, rather than adding temp.SBLength to a uint8_t*. You can use pointer subtraction on p and the &temp.RFBand similarly to my suggestion above to work out whether you're going to over-read the buffer.

Copy link
Author

Choose a reason for hiding this comment

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

I've pushed up a new version that adds the safety checks you recommended. It now validates the N and SBLength fields against the total packet size before processing the sub-blocks. Regarding your suggestion to make it neater by incrementing a RFStatusRFBandSubBlock* pointer: I looked into it, but it felt a bit risky. The SBF spec mentions that SBLength can include padding, which means it might not always equal sizeof(RFStatusRFBandSubBlock). If that happened, incrementing the typed pointer would get out of sync with the actual data stream.

{
const msg4245 &temp = sbf_msg.data.msg4245u;
check_new_itow(temp.TOW, sbf_msg.length);
#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to the above - should we know about GALAuthStatus at all if we're not doing integrity stuff?

#endif // AP_MAVLINK_SET_GPS_GLOBAL_ORIGIN_MESSAGE_ENABLED

#ifndef AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
#define AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED AP_GPS_SBF_ENABLED && HAL_GCS_ENABLED && (HAL_PROGRAM_SIZE_LIMIT_KB > 1024)
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll probably change this down-the-line - we can actually use this new information you're supplying in the EKF to change how ArduPilot responds to bad GPS data. But this is fine for now.

@peterbarker
Copy link
Contributor

BTW, how does this PR relate to #27891 ?

@Tory9
Copy link
Author

Tory9 commented Oct 24, 2025

@peterbarker This is an old PR opened by a former Septentrio intern. I based this PR on it but had to create a fresh one. You can safely close that one. Sorry, I should’ve mentioned it in the description of PR

@peterbarker
Copy link
Contributor

@peterbarker This is an old PR opened by a former Septentrio intern. I based this PR on it but had to create a fresh one. You can safely close that one. Sorry, I should’ve mentioned it in the description of PR

Thanks.

Note that if you based your work on someone else's unmerged work it's often a good idea to add a Co-authored-by: to the commits containing their work.

Copy link
Contributor

@peterbarker peterbarker left a comment

Choose a reason for hiding this comment

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

This pretty much looks good to me now.

Those sanity checks are really something so I'm going to mark this for DevCall so someone else an stare at them for a while to see if they're also comfortable with them. We have to be really careful with this sort of stuff - if we stuff up then things fall out of the sky.

Too spammy - and these numbers mean nothing to our users:

Image

I get "system error software" when using this PR:

STABILIZE> 2025-11-09 11:40:18.84: GNSS_INTEGRITY (id=441) (seq=129) (src=1/1) (in-link=None) (signed=No)
    id: 0
    system_errors: 0x4 (4)
      ! GPS_SYSTEM_ERROR_INCOMING_CORRECTIONS
      ! GPS_SYSTEM_ERROR_CONFIGURATION
        GPS_SYSTEM_ERROR_SOFTWARE
      ! GPS_SYSTEM_ERROR_ANTENNA
      ! GPS_SYSTEM_ERROR_EVENT_CONGESTION
      ! GPS_SYSTEM_ERROR_CPU_OVERLOAD
      ! GPS_SYSTEM_ERROR_OUTPUT_CONGESTION
    authentication_state: 4 (GPS_AUTHENTICATION_STATE_DISABLED)
    jamming_state: 1 (GPS_JAMMING_STATE_OK)
    spoofing_state: 1 (GPS_SPOOFING_STATE_OK)
    raim_state: 255 ([UNKNOWN])
    raim_hfom: 65535cm
    raim_vfom: 65535cm
    corrections_quality: 255
    system_status_summary: 255
    gnss_signal_quality: 255
    post_processing_quality: 255

... man, we really should rename GPS_JAMMING_STATE_OK to GPS_JAMMING_STATE_NOT_JAMMED - assuming that's what that means. Similarly for spoofing.


#endif


Copy link
Contributor

Choose a reason for hiding this comment

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

stray whitespace change

#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
case MSG_GNSS_INTEGRITY:
CHECK_PAYLOAD_SIZE(GNSS_INTEGRITY);
AP::gps().send_mavlink_gnss_integrity(chan, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for now, but if someone had two Septentrio devices attached they might want data from both...

enum {
SOFTWARE = (1 << 3), // set upon detection of a software warning or error. This bit is reset by the command lif, error
WATCHDOG = (1 << 4), // set when the watch-dog expired at least once since the last power-on.
ANTENNA = (1 << 5), // set when antenna overcurrent condition is detected
Copy link
Contributor

Choose a reason for hiding this comment

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

alignment

#ifndef AP_GPS_GPS2_RTK_SENDING_ENABLED
#define AP_GPS_GPS2_RTK_SENDING_ENABLED HAL_GCS_ENABLED && AP_GPS_ENABLED && GPS_MAX_RECEIVERS > 1 && (AP_GPS_SBF_ENABLED || AP_GPS_ERB_ENABLED)
#endif
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

stray whitespace change


#if AP_MAVLINK_MSG_GNSS_INTEGRITY_ENABLED
void AP_GPS::send_mavlink_gnss_integrity(mavlink_channel_t chan, uint8_t instance) {
if(!state[instance].has_gnss_integrity) {
Copy link
Contributor

Choose a reason for hiding this comment

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

missing a bounds check for instance here. Since we only send for instance0 I'm not that fussed.

@Tory9
Copy link
Author

Tory9 commented Nov 9, 2025

Hey! The GPS_SYSTEM_ERROR_SOFTWARE error is coming from the ReceiverStatus message's RxError field - specifically the SOFTWARE bit, which is set upon detection of a software warning or error. To reset this bit and see the exact issue, the "lif, error" command should be given to the receiver. This bit can trigger for big issues affecting the operation but also for less critical things like logging-related stuff. The receiver doesn't usually give detailed error info unless you ask it with "lif, error". If you've got logged SBF data from when this happened, I can still replay it and try to figure out what the issue was. The ExtError field is showing SISERROR (Signal-In-Space Error) - which means at least one satellite is broadcasting sketchy position data. Quite possibly, the SISERROR is what's triggering the SOFTWARE error bit internally. I will test it with a few different receivers to see if I get the same issue and if that would be useful, prepare some testing reports.

I agree that the GCS reporting is spammy based on what you sent. But honestly, I think there's something wrong with the receiver config/operation in that image, so I wouldn't expect it to toggle between no error and SISERROR all the time. Under normal operation, it shouldn't trigger often. That said, it's also not super important to send this to GCS, and I agree the numbers don't really tell users much. I can either rework it to send more informative messages or just remove it entirely. Let me know what you prefer.

@Tory9
Copy link
Author

Tory9 commented Nov 9, 2025

Regarding renaming GPS_JAMMING_STATE_OK to GPS_JAMMING_STATE_NOT_JAMMED, I agree this would probably make more sense. I'm not sure how exactly the naming was chosen because the MAVLink message was developed a while back by another student. Since the message is still in development.xml, I can open a PR to change the naming. However, I'll also need to coordinate changes with the PX4 implementation, which was already merged into master. If you think the current naming is not representative enough, I can change it across MAVLink/ ArduPilot/ PX4/ QGC.

@peterbarker
Copy link
Contributor

Regarding renaming GPS_JAMMING_STATE_OK to GPS_JAMMING_STATE_NOT_JAMMED, I agree this would probably make more sense. I'm not sure how exactly the naming was chosen because the MAVLink message was developed a while back by another student. Since the message is still in development.xml, I can open a PR to change the naming. However, I'll also need to coordinate changes with the PX4 implementation, which was already merged into master. If you think the current naming is not representative enough, I can change it across MAVLink/ ArduPilot/ PX4/ QGC.

I'll ping in @hamishwillee on this.

While yes, it is in development.xml, I don't think I can really justify pushing for it if it's already in PX4 master.... I just probably should have picked it up on the original PR :-)

@peterbarker
Copy link
Contributor

peterbarker commented Nov 9, 2025

Hey! The GPS_SYSTEM_ERROR_SOFTWARE error is coming from the ReceiverStatus message's RxError field - specifically the SOFTWARE bit, which is set upon detection of a software warning or error. To reset this bit and see the exact issue, the "lif, error" command should be given to the receiver. This bit can trigger for big issues affecting the operation but also for less critical things like logging-related stuff. The receiver doesn't usually give detailed error info unless you ask it with "lif, error". If you've got logged SBF data from when this happened, I can still replay it and try to figure out what the issue was. The ExtError field is showing SISERROR (Signal-In-Space Error) - which means at least one satellite is broadcasting sketchy position data. Quite possibly, the SISERROR is what's triggering the SOFTWARE error bit internally. I will test it with a few different receivers to see if I get the same issue and if that would be useful, prepare some testing reports.

I agree that the GCS reporting is spammy based on what you sent. But honestly, I think there's something wrong with the receiver config/operation in that image, so I wouldn't expect it to toggle between no error and SISERROR all the time. Under normal operation, it shouldn't trigger often. That said, it's also not super important to send this to GCS, and I agree the numbers don't really tell users much. I can either rework it to send more informative messages or just remove it entirely. Let me know what you prefer.

gps1_003.log

We should at least work out how to log this stuff if we aren't going to send it to the user. That's just a Log_Write call to something like a GSDB message.

@Tory9
Copy link
Author

Tory9 commented Nov 17, 2025

Hi, thank you for the review. I have applied the suggestions. I want to address two things:

  1. Logging of internal errors:
    I tested the feature with Mosaic X5 and Mosaic H receivers and also experienced the GCS messages about internal errors. However, in my case it was never a software issue but rather CPU overload on the receiver. The errors weren't persistent and only occurred when I heavily loaded the receiver—for example, opening the web interface while simultaneously logging messages and streaming on two ports with two streams each. Under these conditions, the error triggering is completely normal. I would even say it's useful to have these messages as an indication of misuse or failure.

I didn't experience the message flooding issue you reported, so I examined your log. I translated the bin to asc using bin2asc.exe, and the tool detected multiple corruptions reported as:
CRC Error: SBF_ERR_CRC
Decoder Error: SBF_ERR_DROPPED_106_BYTES
This could be related to the software error/warning bit being set. Otherwise, the data I could retrieve looked normal. The exact error can be viewed in the web GUI or RxControl by typing lif, error in the expert console.

I believe this is related to receiver operation rather than our code. If you think this message shouldn't be sent to the GCS, I can log it internally instead. However, normally if this message floods the GCS, it would indicate something is wrong with the receiver, as this is not expected behavior. Please let me know what you think.

  1. Instance handling:
    I initially thought implementing this for a single receiver would be sufficient, but your intuition about this line: AP::gps().send_mavlink_gnss_integrity(chan, 0) being off was spot on 😅. I broke the GPS1 port cable on my CubeOrange+ and could only find a replacement for GPS2. When I started testing with GPS2, it didn't work. It took me some time to realize there was an instance mismatch—I was passing instance 0 to send_mavlink_gnss_integrity() but the GPS was connected to instance 1 (GPS2 port).

I've now changed it to use the primary instance instead of hardcoding instance 0, and it works. However, I'm now thinking it might make more sense to send data from all GPS instances (or merge them) to the GCS. Please let me know what you think would be the best approach.

@hamishwillee
Copy link
Contributor

Regarding renaming GPS_JAMMING_STATE_OK to GPS_JAMMING_STATE_NOT_JAMMED, I agree this would probably make more sense....
While yes, it is in development.xml, I don't think I can really justify pushing for it if it's already in PX4 master.... I just probably should have picked it up on the original PR :-)

@peterbarker @Tory9 , I'll take a PR with this change and see if I can get it agreement to change it.
We should then move this into common.xml.

The change is only to an enum value name, which is run-time safe. This particular value is not directly referenced in PX4. However it might require a compile-time change to QGC.

@Tory9 Are you happy to create such a PR?

@Tory9
Copy link
Author

Tory9 commented Nov 19, 2025

@hamishwillee Sounds good. Do you think it would make sense for me to first join one of the MAVLink Coordination Calls to discuss a clearer naming (and check if any other fields should be aligned), and then open the PR?

@hamishwillee
Copy link
Contributor

hamishwillee commented Nov 19, 2025

@hamishwillee Sounds good. Do you think it would make sense for me to first join one of the MAVLink Coordination Calls to discuss a clearer naming (and check if any other fields should be aligned), and then open the PR?

No, I think you should PR just this change with your suggestion for the enum value name and a reason.

If you think other changes are warranted then please suggest them in the discussion, but don't make them.
This is deployed, so we can't change field names or units. We can add new fields and "in theory" modify enum value names, but not their order.

[Actually, we "can" break any users in any way because this is in development, but we would prefer not to]

Note that of course you are more than welcome to join the mav call now or any time, but it isn't necessary for this case.

@Tory9
Copy link
Author

Tory9 commented Nov 26, 2025

@peterbarker @hamishwillee Hi, I’ve opened a PR with the suggested naming changes. I’m wondering whether that MAVLink PR will block this one from moving forward, or we still can finalize it?

@Tory9
Copy link
Author

Tory9 commented Dec 11, 2025

@peterbarker Hi! In the latest commits I updated the data transmission to send from all GPS instances that support this message. I also attempted to align the naming with the new MAVLink definitions, but since the pipeline hasn’t been updated yet, it caused a large number of test failures, so I reverted that change. Please let me know if anything else needs improvement

@peterbarker
Copy link
Contributor

@peterbarker Hi! In the latest commits I updated the data transmission to send from all GPS instances that support this message. I also attempted to align the naming with the new MAVLink definitions, but since the pipeline hasn’t been updated yet, it caused a large number of test failures, so I reverted that change. Please let me know if anything else needs improvement

Renamed enumeation values should now be available.

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

Labels

WikiNeeded needs wiki update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants