Skip to content

feat(gps): add support for UBX_MSG_MON_SPAN - #28173

Merged
ThomasRigi merged 5 commits into
mainfrom
pr-ubx-mon-span
Sep 10, 2026
Merged

ThomasRigi merged 5 commits into
mainfrom
pr-ubx-mon-span

Conversation

@ThomasRigi

Copy link
Copy Markdown
Member

Solved Problem

Replaces #20474

UBX_MSG_MON_SPAN has a lot of valuable information for debugging degraded GNSS reception.

Solution

Following the example of @dagar in #20474, but extending it for multi-block logging so that the data is

Changelog Entry

For release notes:

New parameter: GPS_UBX_SPECTRUM

Test coverage

tbc

Context

Not advisable to run on low baudrate. The messages are big.

@github-actions github-actions Bot added kind:feature Request or change that adds new functionality. scope:drivers Device drivers and hardware interfaces. scope:sensors Sensor pipeline, calibration, voting, or sensor validation. scope:uorb uORB messages, generated interfaces, or message translation. scope:logging ULog, logger, replay, events, or diagnostics. labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

🔎 FLASH Analysis

px4_fmu-v5x [Total VM Diff: 1312 byte (0.07 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +1.29Ki  +0.1% +1.29Ki    .text
    +9.5%    +304  +9.5%    +304    GPSDriverUBX::payloadRxDone()
    +0.1%    +196  +0.1%    +196    [section .text]
    +0.9%    +188  +0.9%    +188    uORB::compressed_fields
     +39%    +168   +39%    +168    GPSDriverUBX::restartSurveyIn()
    +3.0%    +108  +3.0%    +108    px4::logger::LoggedTopics::add_default_topics()
    +3.0%     +88  +3.0%     +88    GPSDriverNMEA::handleMessage()
    [NEW]     +84  [NEW]     +84    GPS::publishRF()
    +2.1%     +56  +2.1%     +56    GPSDriverAshtech::handleMessage()
    +6.8%     +48  +6.8%     +48    GPS::GPS()
    [NEW]     +32  [NEW]     +32    CSWTCH.245
    +9.0%     +24  +9.0%     +24    GPS::~GPS()
     +11%     +20   +11%     +20    GPSDriverUBX::GPSDriverUBX()
    +7.3%     +16  +7.3%     +16    GPS::callback()
    +4.6%     +16  +4.6%     +16    GPSDriverUBX::payloadRxAddMonVer()
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block0
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block1
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block2
    +0.9%     +12  +0.9%     +12    uorb_topics_list
    -4.7%     -20  -4.7%     -20    param_reset_specific
    [DEL]     -32  [DEL]     -32    CSWTCH.237
   -100.0%     -36 -100.0%     -36    [36 Others]
  +0.0% +1.04Ki  [ = ]       0    .debug_abbrev
  +0.0%     +40  [ = ]       0    .debug_aranges
  +0.0%     +84  [ = ]       0    .debug_frame
  +0.1% +31.2Ki  [ = ]       0    .debug_info
  +0.1% +2.42Ki  [ = ]       0    .debug_line
     +40%      +2  [ = ]       0    [Unmapped]
    +0.1% +2.42Ki  [ = ]       0    [section .debug_line]
  +0.0% +1.54Ki  [ = ]       0    .debug_loclists
  +0.1%    +322  [ = ]       0    .debug_rnglists
  +0.1% +1.88Ki  [ = ]       0    .debug_str
   -50.0%      -1  [ = ]       0    [Unmapped]
    +0.1% +1.88Ki  [ = ]       0    [section .debug_str]
  +0.0%    +180  [ = ]       0    .strtab
    [DEL]     -11  [ = ]       0    CSWTCH.237
    [NEW]     +11  [ = ]       0    CSWTCH.245
    [NEW]     +39  [ = ]       0    GPS::_is_rf_block_main_advertised
    [NEW]     +38  [ = ]       0    GPS::publishRF()
    +0.1%     +19  [ = ]       0    [section .strtab]
     +52%     +16  [ = ]       0    __nxsched_process_timer_veneer
   -33.3%     -16  [ = ]       0    __nxsem_restore_baseprio_veneer
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block0
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block1
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block2
  +0.0%    +208  [ = ]       0    .symtab
    [DEL]     -32  [ = ]       0    CSWTCH.237
    [NEW]     +48  [ = ]       0    CSWTCH.245
    +100%     +16  [ = ]       0    ConstLayer::containedAsBitset()
     +50%     +16  [ = ]       0    ConstLayer::contains()
   -33.3%     -16  [ = ]       0    ConstLayer::store()
   -50.0%     -16  [ = ]       0    FieldSensorBiasEstimator::updateEstimate()
    [NEW]     +32  [ = ]       0    GPS::_is_rf_block_main_advertised
   -16.7%     -16  [ = ]       0    GPS::callback()
    [NEW]     +48  [ = ]       0    GPS::publishRF()
     +50%     +16  [ = ]       0    GPSDriverUBX::activateRTCMOutput()
     +14%     +16  [ = ]       0    GPSDriverUBX::payloadRxDone()
   -33.3%     -16  [ = ]       0    RTCM_BASE_MSGOUT_I2C
   -92.9%     +48  [ = ]       0    [12 Others]
    -0.3%     -32  [ = ]       0    [section .symtab]
     +33%     +16  [ = ]       0    ___ZN3Ekf4fuseERKN6matrix6VectorIfLj24EEEf_veneer
     +50%     +16  [ = ]       0    ____aeabi_l2f_veneer
   -25.0%     -16  [ = ]       0    __file_get2_veneer
     +67%     +32  [ = ]       0    __nxsched_process_timer_veneer
   -40.0%     -32  [ = ]       0    __nxsem_restore_baseprio_veneer
    [NEW]     +48  [ = ]       0    __orb_sensor_gnss_rf_block0
    [NEW]     +32  [ = ]       0    __orb_sensor_gnss_rf_block1
   +31% +2.72Ki  [ = ]       0    [Unmapped]
  -0.1%      -8  -0.1%      -8    .ramfunc
     +20%      +4   +20%      +4    get_orb_meta()
     +14%      +1   +14%      +1    ____aeabi_l2f_veneer
   -12.5%      -1 -12.5%      -1    __poll_notify_veneer
    -1.9%      -4  -1.9%      -4    param_get
   -25.0%      -4 -25.0%      -4    param_get_index
    -8.3%      -4  -8.3%      -4    param_get_system_default_value
  +0.1% +42.9Ki  +0.1% +1.28Ki    TOTAL

px4_fmu-v6x [Total VM Diff: 1416 byte (0.07 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +1.38Ki  +0.1% +1.38Ki    .text
    +9.5%    +304  +9.5%    +304    GPSDriverUBX::payloadRxDone()
    +0.9%    +188  +0.9%    +188    uORB::compressed_fields
    +0.1%    +184  +0.1%    +184    [section .text]
     +39%    +168   +39%    +168    GPSDriverUBX::restartSurveyIn()
    +3.0%    +108  +3.0%    +108    px4::logger::LoggedTopics::add_default_topics()
    +3.0%     +88  +3.0%     +88    GPSDriverNMEA::handleMessage()
    [NEW]     +84  [NEW]     +84    GPS::publishRF()
    +2.1%     +56  +2.1%     +56    GPSDriverAshtech::handleMessage()
    +6.8%     +48  +6.8%     +48    GPS::GPS()
    [NEW]     +32  [NEW]     +32    CSWTCH.245
   -99.8%     +28 -99.8%     +28    [15 Others]
    +9.0%     +24  +9.0%     +24    GPS::~GPS()
    +0.0%     +24  +0.0%     +24    g_cromfs_image
     +11%     +20   +11%     +20    GPSDriverUBX::GPSDriverUBX()
    +7.3%     +16  +7.3%     +16    GPS::callback()
    +4.6%     +16  +4.6%     +16    GPSDriverUBX::payloadRxAddMonVer()
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block0
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block1
    [NEW]     +16  [NEW]     +16    __orb_sensor_gnss_rf_block2
    +0.9%     +12  +0.9%     +12    uorb_topics_list
    [DEL]     -32  [DEL]     -32    CSWTCH.237
  +0.1% +1.04Ki  [ = ]       0    .debug_abbrev
  +0.0%     +40  [ = ]       0    .debug_aranges
  +0.0%    +104  [ = ]       0    .debug_frame
  +0.1% +30.9Ki  [ = ]       0    .debug_info
  +0.1% +2.51Ki  [ = ]       0    .debug_line
   -66.7%      -4  [ = ]       0    [Unmapped]
    +0.1% +2.51Ki  [ = ]       0    [section .debug_line]
  +0.0% +1.51Ki  [ = ]       0    .debug_loclists
  +0.1%    +337  [ = ]       0    .debug_rnglists
  +0.1% +1.87Ki  [ = ]       0    .debug_str
  +0.0%    +180  [ = ]       0    .strtab
    [DEL]     -11  [ = ]       0    CSWTCH.237
    [NEW]     +11  [ = ]       0    CSWTCH.245
    [NEW]     +39  [ = ]       0    GPS::_is_rf_block_main_advertised
    [NEW]     +38  [ = ]       0    GPS::publishRF()
    +0.1%     +19  [ = ]       0    [section .strtab]
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block0
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block1
    [NEW]     +28  [ = ]       0    __orb_sensor_gnss_rf_block2
  +0.0%    +208  [ = ]       0    .symtab
    [DEL]     -32  [ = ]       0    CSWTCH.237
    [NEW]     +48  [ = ]       0    CSWTCH.245
   -50.0%     -16  [ = ]       0    FieldSensorBiasEstimator::updateEstimate()
    [NEW]     +32  [ = ]       0    GPS::_is_rf_block_main_advertised
   -16.7%     -16  [ = ]       0    GPS::callback()
    [NEW]     +48  [ = ]       0    GPS::publishRF()
     +50%     +16  [ = ]       0    GPSDriverUBX::activateRTCMOutput()
     +14%     +16  [ = ]       0    GPSDriverUBX::payloadRxDone()
   -33.3%     -16  [ = ]       0    RTCM_BASE_MSGOUT_I2C
    [NEW]     +48  [ = ]       0    __orb_sensor_gnss_rf_block0
    [NEW]     +32  [ = ]       0    __orb_sensor_gnss_rf_block1
    [NEW]     +32  [ = ]       0    __orb_sensor_gnss_rf_block2
     +25%     +16  [ = ]       0    matrix::Matrix<>::operator*()
     +20%     +16  [ = ]       0    on_topic_update()
   -50.0%     -16  [ = ]       0    topic_id_from_orb()
   +62% +2.62Ki  [ = ]       0    [Unmapped]
  +0.1% +42.7Ki  +0.1% +1.38Ki    TOTAL

Updated: 2026-09-10T16:05:50

Comment thread msg/SensorGnssSpectrum.msg Outdated
Comment thread msg/SensorGnssSpectrum.msg Outdated
Comment thread msg/SensorGnssSpectrum.msg Outdated
Comment thread src/modules/logger/logged_topics.cpp Outdated
Comment thread src/drivers/gps/params.yaml Outdated
Comment thread msg/SensorGnssRf.msg Outdated
Comment thread msg/SensorGnssRf.msg Outdated
Comment thread msg/SensorGnssRf.msg Outdated
Comment thread msg/SensorGnssSpectrum.msg Outdated
Comment thread msg/SensorGnssSpectrum.msg Outdated
Comment thread msg/SensorGnssRf.msg Outdated
@dakejahl
dakejahl requested review from JonasPerolini and removed request for dagar August 13, 2026 16:45
@dakejahl

Copy link
Copy Markdown
Contributor

Note

Claude review on behalf of @dakejahl

Continuing #28173 (comment) at top level, since it touches SensorGnssSpectrum.msg and the driver as well as SensorGnssRf.msg.

To be clear about what I'm asking for: I'm not proposing we rename the topics. sensor_gnss_rf_block0/1/2 plus PublicationMulti for main/secondary receiver is fine — keep it, and keep block_id as the routing index. My point is only about the payload.

Someone opening sensor_gnss_rf_block1 in a log needs to know whether that AGC/jamming number is L1, L2 or L5. An L5 jammer and an L2 jammer are different problems, and the index→band mapping is receiver-dependent, so the index alone isn't interpretable. Those are two separate concerns: how we address the topics vs. what the payload means. You've already added rf_band, so I think we largely agree — what's left is that it's UNKNOWN on exactly the receiver everyone flies.

On "we can't create an enum in general": the line you quoted from X20 2.02 — "The band which the RF block represents is subject to product configuration" — is the description of blockId, and rfBlockGnssBand sits directly underneath it in the same table. That sentence is u-blox explaining why they added the band field, not saying the band is unknowable. (It's already in 2.03, by the way, not new in 2.10.)

And F9 HPG 1.51 does document the mapping — it's just inside the blockId description instead of a separate field:

blockId — RF block ID (0 = L1 band, 1 = L2 or L5 band depending on product configuration)

"L2 or L5 depending on product configuration" is ZED-F9P (L1/L2) vs ZED-F9P-15B (L1/L5), and the driver already distinguishes those two: Board::u_blox9_F9P_L1L2 vs Board::u_blox9_F9P_L1L5. It has to, in order to pick SIGNAL_GPS_L2C_ENA vs SIGNAL_GPS_L5_ENA in configureDevice(). So the information is already sitting in _board.

Concretely

MON-SPAN — derive from center. There's no blockId and no band field, and the payload is byte-identical between 1.51 and 2.10, but every block carries its own center frequency. This case needs no board table and no firmware gating — it works on every receiver and every firmware. SensorGnssSpectrum.msg should carry the same rf_band field rather than telling every consumer (log review, QGC, a future MAVLink/DroneCAN bridge) to re-derive it.

MON-RF — rfBlockGnssBand on X20 2.03+, else _board, else UNKNOWN.

Band from center frequency (ITU RNSS allocations):

center band
1164–1215 MHz L5 / E5a 1176.45, E5b 1207.14, B2a
1215–1260 MHz L2 — GPS L2C 1227.6, GLONASS L2 ~1246
1260–1300 MHz L6 / E6 1278.75, B3 1268.52
1559–1610 MHz L1 — GPS 1575.42, B1I 1561.098, GLONASS L1 ~1602

Two smaller things that fall out of this

  1. gnss_rf.rf_band = ...block[i].rfBlockGnssBand is read unconditionally, but on 1.51 that byte is reserved2[0]. §3.3.2 UBX reserved elements: "The contents of these elements should be ignored in output messages." Nothing guarantees it reads zero on an F9. Gating the read fixes this and the F9 mapping in the same branch.

  2. RF_BAND_* currently mirrors u-blox's values 1:1, so it inherits their gap — u-blox has no L6/E6 value (0–4 = unknown/L1/L2/L3/L5) even though, as you pointed out, the X20 supports it. Worth adding RF_BAND_L6 so the SPAN-derived path can report it.

Unrelated, but while we're in here

X20 2.x adds recInf.msgSource at byte 2 of both MON-RF and MON-SPAN (0 = single antenna, 1 = RF_IN_1, 2 = RF_IN_2); the driver currently treats bytes 2–3 as reserved0. On a dual-antenna heading module a single receiver reports for both antennas, which doesn't map onto "instance = main vs. secondary GNSS". Fine to leave for a follow-up, but we shouldn't silently drop it.

@ThomasRigi

ThomasRigi commented Aug 20, 2026 •

Copy link
Copy Markdown
Member Author

Ok I (finally) understand how you intended it. I misunderstood that you wanted to replace the block_id field with the rf_band field. In that case I'm ok to add rf_band also to SensorGnssSpectrum.msg. Even though personally I have a preference for not logging "duplicate" data: in our branch we had even opted for removing one of the three fields from the relation "number of points = spectrum_span / resolution" -> with two you can easily reconstruct the third. But I see the point of simplicity and analogicity (is this a word? :P) to SensorGnssRf.msg.

I do agree that for SensorGnssRf it's advantageous to know the band when opening the log. I see that using using _board therefore makes sense to use to fill rf_band for it.

edit:
What do we want the enum to look like ? For now it's directly based on ubx X20 2.02 :
image
In your post above, you also mention L6 frequency, but L3 isn't in it. And there are also different bands/names such as E6 and G2 according to different constellations.

Do we want to create the enum independently of X20 2.02 and just cast that when parsing it to the PX4 enum ? I think it's the cleanest in the end.

According to https://gnss.store/blogs/gnss-antennas/l1-l2-l5-l3-and-simply-l-frequency-bands?srsltid=AfmBOoqo_-hiFOi4MKw49QtAaPJXyDCTK-aIOhonVq8boVKkvTLgRIpc:

According to the ITU-R (formerly CCIR) recommendations with numbers 1901-1906, there are five frequency bands allocated for GNSS, of which three are used:

1,559-1,610 MHz, referred to as L1, E1, B1
1,215-1,300 MHz, referred to as L2, E6, B3, L6
1,164-1,215 MHz, referred to as L5, E5, B2, L3

Should we just follow that ?

  • for X20 MON-RF, that would mean casting both L5 and L3 into the same band though ? But if they're roughly the same band anyway...

@dakejahl

Copy link
Copy Markdown
Contributor

Maybe simpler to skip the enum and just use center_frequency. SensorGnssSpectrum already has it (measured, from the payload), so nothing to add there. For SensorGnssRf the driver fills a nominal band frequency (X20 rfBlockGnssBand → 1575.42/1227.6/1202.025/1176.45 MHz, else _board for F9P L1L2 vs L1L5, 0 = unknown) — same lookup as before, just not baked into the message. Consumers bucket by the ITU allocation, so L3-vs-L5 and L2-vs-E6/L6 naming stops mattering, and L6 on X20 works without a PX4 enum change. Worth a comment that it's nominal, not measured — and if GPS_UBX_SPECTRUM is on, the driver could use the MON-SPAN center for the same block instead.

@ThomasRigi

Copy link
Copy Markdown
Member Author

I pushed the changes for center_frequency. I only have a platform with two F9P-L1L2 receivers at hand to test:
image
log_4_2026-8-25-14-49-22.zip

Comment thread src/modules/logger/logged_topics.cpp Outdated
Comment thread src/drivers/gps/gps.cpp Outdated
Comment thread src/drivers/gps/gps.cpp Outdated
@dakejahl

dakejahl commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Claude review on behalf of @dakejahl

RAM: the MON-SPAN buffer is unconditional. ubx_payload_rx_mon_span_t is 4 + 3×272 = 820 bytes and is a ubx_buf_t union member, so sizeof(ubx_buf_t) goes 92 → 820 — +728 bytes of heap per GPS driver object, whether or not GPS_UBX_SPECTRUM is set. TASK_STACK_SIZE 2040 → 2750 (gps.cpp:153) adds another +710 for the sensor_gnss_spectrum_s scratch in the parse loop, which the prologue reserves regardless of _spectrum_analyzer; that one also isn't guarded by CONFIG_GPS_UBX, so ashtech/NMEA-only boards pay it too. ~2.1 kB single GPS, ~3.6 kB dual, for users who never enable the feature.

A config GPS_UBX_SPECTRUM Kconfig (default n, same shape as GPS_SPARTN in src/drivers/gps/Kconfig:46) compiles out the union member, the parse case, the publishers and the logger entries in one move. If you'd rather keep it runtime-selectable, heap-allocate both the span payload buffer and the sensor_gnss_spectrum_s scratch only when _spectrum_analyzer is set.

MON-SPAN is configured at navigation rate, not 1 Hz. cfgValset<uint8_t>(UBX_CFG_KEY_MSGOUT_UBX_MON_SPAN_UART1, 1, ...) emits one message per epoch. With the default rate_meas that is 10 Hz on X20/M10 and 5 Hz on F9P:

  • X20, 3 blocks: 828 B/frame × 10 Hz = 8.3 kB/s = 72% of a 115200 link
  • F9P, 2 blocks: 556 B × 5 Hz = 2.8 kB/s = 24%

The comment above rate_meas already notes 25 Hz at 115200 causes dropouts; this will starve NAV-PVT and RTCM. The logger samples at 0.2 Hz, so nothing is lost by setting the divisor to _output_rate (or 1000 / rate_meas) for ~1 Hz output.

Logger: the spectrum topics should be requested conditionally, not made optional. add_optional_topic_multi can't work here — it is evaluated once at logger task start and never revisited, which is what you saw. But non-optional isn't free either: Logger::write_formats() (logger.cpp:1104) has no valid() check, so the sensor_gnss_rf and sensor_gnss_spectrum FORMAT records land in every ULog on every vehicle even when nothing publishes, and the 12 slots come out of MAX_TOPICS_NUM = 255 — the default profile already requests 154 mandatory + up to 127 optional, so the cap is reachable and add_topic drops whatever comes last.

Gate the six spectrum entries on the param instead; logged_topics.cpp:247 already does this for SYS_HITL:

int32_t gps_ubx_spectrum = 0;
param_get(param_find("GPS_UBX_SPECTRUM"), &gps_ubx_spectrum);

if (gps_ubx_spectrum > 0) {
	add_topic_multi("sensor_gnss_spectrum_block0", 5000, 2);
	// ...
}

The RF entries can stay unconditional since MON-RF is always enabled. (write_all_add_logged_msg does check valid(), so no dataset shows up for a topic that never advertises — only the format record leaks.)

Asymmetric receivers drop the secondary's high blocks. If the main reports fewer RF blocks than the secondary (main F9P = 2, secondary X20 = 3), _is_rf_block2_main_advertised is never set and the secondary's block 2 is silently discarded for the life of the boot — the same shape as the existing satellite_info limitation, now multiplied by six flags. Advertising all kMaxBlocks publishers deterministically at driver init, gated on one flag for "main has advertised", would remove the per-block coupling.

MON-SPAN is only enabled on UART1. Every other message uses cfgValsetPort, which covers I2C/UART1/UART2/USB/SPI. As written the feature silently does nothing on an I2C- or SPI-attached receiver, while still costing UART1 bandwidth on a port nobody reads.

Collapse the six publish functions. publishRFBlock{0,1,2} and publishSpectrumBlock{0,1,2} differ only in which publisher and flag they touch. Arrays indexed by block_id (_sensor_gnss_rf_pub[3], _is_rf_main_advertised[3]) remove four functions and roughly 300 bytes of flash, and make the bounds check explicit rather than an if/else chain that silently drops out-of-range ids.

Bin centre formula is off by half a bin. SensorGnssSpectrum.msg:6 gives f(i) = center_frequency + spectrum_span * (i - 127) / 256. The 256 bins tile [center - span/2, center + span/2], so bin i is centred at center + span * (i - 127.5) / 256; as written f(127) claims to be exactly the centre.

@ThomasRigi

ThomasRigi commented Sep 2, 2026 •

Copy link
Copy Markdown
Member Author

On RAM / Kconfig: I haven't found time yet to implement it. Hopefully I can do it tomorrow 🤞

On the configured frequency: I'm pretty sure that when we checked on the oscilloscope that we only got one message (per block) per second. To check, I put logging at unlimited frequency:
image
and in the log I indeed only have one point per second:
image
So I don't really understand what I'd have to do differently (let alone how I would validate the change).

Logger: I gated it behind the param, will also gate it behind Kconfig on top.

Asymmetric receivers drop the secondary's high blocks.: In that case put the receiver with more blocks as the main ;) I don't see an easy and clean way of handling it, and I can't test it because I'm lacking the hardware. So I'd prefer to tackle it in a follow-up PR, if you think it's worth tackling. It's not a new limitation either, as it already existed on sat_info.

MON-SPAN is only enabled on UART1: I personally prefer not to activate it on all ports if they're not used, and it's also what @dagar put in PX4/PX4-GPSDrivers#115. But if there are actually people using the GPS on other ports that would also want to have the spectrum logged, we can change the code to activate it on all.

Collapse the six publish functions : Done. Indeed cleaner now 👍

Bin centre formula is off by half a bin: u-blox says to calculate it the way we do, so I would definitely keep it as is:
image

@ThomasRigi

Copy link
Copy Markdown
Member Author

@dakejahl I implemented the Kconfig to the best of my knowledge (which means no prior knowledge 😬)
I've seen your comment on it being a pain in the submodule, but I just did the same #if defined(CONFIG_GPS_UBX_SPAN) everywhere and it seems to work... Are there any nuances I'm missing ? Also let me know if there are individual board configs where I need to set something.

One point I didn't know in particular: Is there a way to gate the parameter GPS_UBX_SPECTRUM behind this Kconfig too?

Quick tests:
Defaulting it to n indeed makes the topic not known:
image
and the flash size on build is a little smaller (98.69% compared to 98.73% with it active)

When with it defaulting to !BOARD_CONSTRAINED_FLASH, I still get readings on a Pixhawk 6C:
image

@ThomasRigi
ThomasRigi force-pushed the pr-ubx-mon-span branch 2 times, most recently from b091823 to bf95ce9 Compare September 8, 2026 17:35
@ThomasRigi

Copy link
Copy Markdown
Member Author

I rebased, updated the submodule and squashed my commits for a cleaner history. Testing on my setup with Pixhawk 6C and two F9P receivers went well. Attaching the corresponding logs. I tested once with the GPS_UBX_SPAN Kconfig as set in this PR and once forcing it to n: mon-rf-span-testing.zip

In particular, I checked that the center frequency of mon-rf (roughly) matches the one reported in mon-span. And that the sensor_gnss_spectrum_block* topics are unknown without GPS_UBX_SPAN.

The other fields' values seem reasonable to me, but they are harder to have a "groundtruth".

Happy if we can get this merged soon!

dakejahl
dakejahl previously approved these changes Sep 8, 2026
Comment thread src/drivers/gps/Kconfig Outdated
Comment thread msg/SensorGnssSpectrum.msg
Comment thread msg/SensorGnssRf.msg
ThomasRigi and others added 3 commits September 9, 2026 12:07
…roved GNSS debugging capabilities.

Introduction of two new uorb messages to log the content of UBX_MSG_MON_SPAN and UBX_MON_RF on a per-band level per receiver.
Gated behind Kconfig GPS_UBX_SPAN and new parameter GPS_UBX_SPECTRUM.
Co-authored-by: Jacob Dahl <37091262+dakejahl@users.noreply.github.com>
@ThomasRigi

Copy link
Copy Markdown
Member Author

I merged the submodule (PX4/PX4-GPSDrivers#222) and updated this PR to point to newest main. Also rebased this MR onto main. Good for me to merge @dakejahl. Thanks for all the inputs!

Comment thread src/drivers/gps/gps.cpp Outdated
Comment thread src/drivers/gps/params.yaml Outdated
Comment thread src/drivers/gps/gps.cpp
Comment thread src/drivers/gps/Kconfig
@github-actions github-actions Bot added scope:build-system CMake, Kconfig, board config, or build tooling. scope:boards Board-specific changes or hardware definitions. labels Sep 10, 2026

@JonasPerolini JonasPerolini left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm, thanks!

@ThomasRigi
ThomasRigi merged commit 37e0cb3 into main Sep 10, 2026
74 checks passed
@ThomasRigi
ThomasRigi deleted the pr-ubx-mon-span branch September 10, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:feature Request or change that adds new functionality. scope:boards Board-specific changes or hardware definitions. scope:build-system CMake, Kconfig, board config, or build tooling. scope:drivers Device drivers and hardware interfaces. scope:logging ULog, logger, replay, events, or diagnostics. scope:sensors Sensor pipeline, calibration, voting, or sensor validation. scope:uorb uORB messages, generated interfaces, or message translation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants