Commit Graph

50509 Commits

Author SHA1 Message Date
Jacob Dahl
5ea7337a31 feat(crsf): add local altitude/vertical-speed telemetry and fix GPS frame (#27948)
* fix(crsf): correct GPS telemetry ground speed, course, and altitude offset

Ground speed was sent from the NED down-velocity component with the km/h conversion inverted, course over ground wraps negative values through the uint16 field, and the rc_input frame applied a +1 m offset where the protocol expects +1000 m.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(crsf): send local altitude and vertical speed telemetry

Add the CRSF barometric altitude frame (0x09), fed from vehicle_local_position, to both the crsf_rc driver and the legacy rc_input telemetry. Shows up as Alt and VSpd sensors on EdgeTX/OpenTX radios, complementing the MSL altitude already sent in the GPS frame.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(crsf): clarify local-altitude telemetry semantics and units

Note that the Alt/VSpd sensors reuse the CRSF baro-altitude frame to carry PX4's fused local altitude (above the estimator origin), not a raw barometer reading, and document the EdgeTX version requirements. Add terse unit comments on the GPS-frame fields.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-19 10:16:23 -06:00
Jacob Dahl
d11b996d89 fix(uavcan): publish non-valid range readings with quality 0 (#27963)
The rangefinder bridge published TOO_CLOSE, TOO_FAR and UNDEFINED
readings with signal_quality -1 (unknown) and the raw wire range, so
consumers treated them as usable measurements. DroneCAN rangefinder
nodes commonly report an out-of-range sentinel value in the range field
for these reading types (e.g. max_distance + 1 for TOO_FAR); flight
logs show the EKF fusing a node's 101 m TOO_FAR sentinel as terrain
distance at 45-113 m AGL.

Per the distance_sensor uORB convention (0 = invalid signal), mark all
non-VALID_RANGE readings as quality 0 so downstream consumers reject
them.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-19 10:14:50 -06:00
PX4BuildBot
6120aa53df docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-07-18 16:04:11 +00:00
msl-dev
70bfe34b13 fix(mavlink): use published Open Drone ID Basic ID (#27936) 2026-07-18 09:52:40 -06:00
Bartok
e32e595fb1 fix(gps): correct overridden spelling in GPS config param long (#27956)
User-facing parameter long description for operators.

Co-authored-by: Daniel Pike <daniel@team-of-light.com>
2026-07-18 09:49:01 -06:00
Bartok
edca1f16e3 fix(ekf2): correct Minimum spelling in range validity param short (#27955)
* fix(ekf2): correct Minimum spelling in range validity param short

User-facing EKF2 range finder parameter description for QGC metadata.

* ci: retrigger SITL after flaky standard_vtol failure

---------

Co-authored-by: Daniel Pike <daniel@team-of-light.com>
2026-07-18 09:48:29 -06:00
Bartok
e0c4b15652 fix(mc_pos_control): correct initial spelling in MPC_TILTMAX_LND short (#27951)
User-facing parameter description shown in QGC/MAVLink metadata.

Co-authored-by: Daniel Pike <daniel@team-of-light.com>
2026-07-18 09:48:06 -06:00
Bartok
67b6679918 docs(zenoh): correct overridden spelling in publisher options note (#27958)
Co-authored-by: Daniel Pike <daniel@team-of-light.com>
2026-07-18 16:33:23 +01:00
Jacob Dahl
de8158101c chore(claude): use Summary/Problem/Solution format in pr skill (#27949)
Give PRs generated via the skill a fixed, skimmable structure instead of an ad-hoc leading paragraph. Keeps the existing no-attribution, no-test-plan, and terse rules.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-17 18:29:36 -06:00
PX4BuildBot
616c25b77d docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-07-17 15:57:37 +00:00
Julian Oes
0f78cd65a0 test(mavsdk): retry arming to tolerate transient is_armable flicker
wait_until_ready() polls MAVSDK's is_armable, but a known hysteresis gap
(and estimator heading-reference flicker at startup) means the flag can
read armable while PX4 then denies the arm command with 'no heading
reference'. This made arm() fail intermittently. Retry the arm command
until the autopilot accepts it, so the transient is absorbed while real
failures still time out.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
ac14e4a93e test(mavsdk): raise RTL disarm timeout to 150s to match siblings
The four older VTOL RTL tests waited only 120s for disarm while running
the same climb-to-550m-and-land missions as the other RTL tests, which
already use 150s. 'RTL direct Mission Land' occasionally tipped just
over 120s of sim time (observed 123.76s), failing wait_until_disarmed.
Align all of them to 150s for margin.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
734d511114 ci(sitl_tests): tolerate gcov negative counts in coverage capture
Switching the SITL tests to the SIH simulator exercises sih.cpp, whose
gcov data contains a spurious negative counter (line 510) that newer
lcov treats as a fatal error, failing the coverage step. Pass
--ignore-errors negative so lcov zeroes the bad count and continues, as
the tool itself suggests.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
9976825d80 test(mavsdk): loosen takeoff altitude reach tolerance to 0.1m
The 0.01m (1cm) tolerance was not reachable within the 15s sim-time
window for the standard_vtol, causing the takeoff-and-hold test to time
out. Use 0.1m, which still verifies altitude hold but is physically
achievable, and stays below the 0.15m hold-monitoring tolerance.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
8e9ab68f11 test(mavsdk): retry offboard start to beat MAVSDK setpoint watchdog
MAVSDK's OffboardImpl::process_heartbeat() resets the setpoint state to
NotActive when a heartbeat without offboard mode arrives more than 3 s
after _last_started. _last_started is only ever written by start(), so
before the first start() it is epoch zero and the guard is always true:
any heartbeat that slips in between set_*() and start() makes start()
fail with NoSetpointSet.

At speed factor 30 PX4's 1 Hz (sim time) heartbeat arrives every ~33 ms
of wall time, which made 'Offboard attitude control' fail roughly one
run in three. Re-send the setpoint and retry start() in a small loop at
both offboard start sites. Failed 1/3 before, 16/16 after.

This is arguably a MAVSDK bug (_last_started should be initialized when
setpoint streaming begins); a retry keeps the tests robust either way.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
27a21488b2 fix(lockstep_scheduler): fix signal-loss race and ABBA deadlock
Two related thread-safety fixes for the cond_timedwait + set_absolute_time
dance, both surfaced under TSan with multi-instance Mavlink and a fast
sim clock advance:

1. Signal loss between releasing _timed_waits_mutex and entering
   pthread_cond_wait. set_absolute_time could have already broadcast
   to a waiter that hadn't actually started waiting yet, and the
   broadcast would be missed -> wait blocks forever.

   Fix: cond_timedwait uses pthread_cond_timedwait with a short
   wall-clock timeout (10 ms) and re-checks the timeout flag. Lost
   signals turn into a maximum of one loop iteration of latency.

2. ABBA between (passed_lock -> _timed_waits_mutex) used by
   cond_timedwait to mark `done`, and (passed_lock under
   _timed_waits_mutex) used by set_absolute_time to broadcast. TSan
   flagged the inversion immediately.

   Fix: split set_absolute_time into three phases. Phase 1 marks
   timed_outs and stages waiters onto a per-call signal_next list,
   under _timed_waits_mutex only. Phase 2 broadcasts to each waiter,
   outside _timed_waits_mutex but under a new _signaling_mutex held
   for the duration. Phase 3 clears _setting_time under the
   _timed_waits_mutex again.

   The waiter's "dance" (when it sees _setting_time still true on
   exit) acquires _signaling_mutex first, then _timed_waits_mutex,
   guaranteeing it cannot return — and let its stack-local
   passed_lock/passed_cond go out of scope — until set_absolute_time
   has finished signaling.

The TimedWait::timeout flag also becomes std::atomic<bool> since it
is now read by cond_timedwait without holding _timed_waits_mutex.
2026-07-17 10:51:42 -05:00
Julian Oes
28b7c7e970 fix(rcS): use awk instead of bc for scaled timeout math
bc isn't installed in the px4-dev CI container we now use. awk is
required by POSIX so it's always present. Same float math, no extra
dependency.
2026-07-17 10:51:42 -05:00
Julian Oes
ac8f3df583 refactor(mavsdk_tests): use sim time directly for execute_mission timeout
poll_condition_with_timeout polls the autopilot's monotonic time, which is
already simulation time. The previous code divided a sim-time budget by the
speed factor to compute a wall-clock timeout, but this is unnecessary and
adds a dependency on the speed-factor query.
2026-07-17 10:51:42 -05:00
Julian Oes
92c6818fc5 fix(rcS): scale COM_DISARM_PRFLT by sim speed factor
Symmetric with the existing COM_DL_LOSS_T / COM_RC_LOSS_T /
COM_OF_LOSS_T / COM_OBC_LOSS_T scalings just above. At high simulation
speed the default 10s preflight disarm is reached much sooner in
real wall time than tests expect, so scale it the same way.
2026-07-17 10:51:42 -05:00
Ramon Roche
9a446cce0c fix(mavsdk_tests): use simulation time for sleeps in basics test
Replace std::this_thread::sleep_for() with tester.sleep_for() to use
simulation-aware sleep that accounts for speed factor under lockstep.
2026-07-17 10:51:42 -05:00
Ramon Roche
d5cfc0fd97 fix(mavsdk_tests): use simulation time for timeouts
Replace 5 functions that used std::future::wait_for() (wall-clock time)
with poll_condition_with_timeout() (simulation time). This prevents
timeouts from expiring prematurely when running at high speed factors
under lockstep.

Affected functions:
- wait_until_altitude()
- wait_for_landed_state()
- wait_for_flight_mode()
- start_and_wait_for_mission_sequence()
- wait_until_speed_lower_than()

Also remove debug printf statements from start_checking_altitude().
2026-07-17 10:51:42 -05:00
Julian Oes
991f8d40af test(mavsdk): clear unused params in land-start VTOL plan
The DO_LAND_START item carried loiter params (heading required, radius,
and a stray 1 in param3) hand-copied from the adjacent LOITER_TO_ALT
item, and LOITER_TO_ALT itself had param3 set, which the spec defines
as empty. PX4's mission item param validation now rejects such items.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
fb4e62fd92 fix(mavlink): accept spec-defined params in mission item validation
The param validation table rejected mission items that QGC-generated
plans legitimately contain:

- NAV_VTOL_LAND param3 is the approach altitude per the MAVLink spec.
  QGC writes it in every VTOL landing pattern; PX4's mission logic
  ignores it, but that is no reason to reject the upload.
- DO_LAND_START param5-7 are the optional lat/lon/alt marker position
  per the spec, which QGC also writes.

Both made any QGC VTOL landing plan fail to upload with
MAV_MISSION_INVALID_PARAMx, surfaced by the SIH SITL VTOL tests.

Signed-off-by: Julian Oes <julian@oes.ch>
2026-07-17 10:51:42 -05:00
Julian Oes
cc635a55c8 fix(build): move mavsdk_tests target out of gazebo-classic cmake file
The ExternalProject_Add(mavsdk_tests ...) lived at the bottom of
sitl_targets_gazebo-classic.cmake, outside the if(gazebo_FOUND) gate
and with a comment noting it doesn't depend on sitl_gazebo. Historical
artifact: mavsdk_tests is a black-box MAVLink test driver that talks
to a running PX4 over UDP, and works against any simulator backend
(sitl_gazebo-classic, gz, sih, jmavsim, ...).

With CI moving from classic Gazebo to SIH, the misplacement becomes
actively misleading. Pull the block into a new top-level
cmake/mavsdk_tests.cmake and include it from CMakeLists.txt under the
existing posix-only block, so the dependency graph reflects what is
actually true.

Behavior is unchanged: target is EXCLUDE_FROM_ALL, only built on
demand via `make mavsdk_tests` or `make tests_integration`.
2026-07-17 10:51:42 -05:00
Julian Oes
18dc9a2665 fix(mavsdk_tests): bump MAVSDK version 2026-07-17 10:51:42 -05:00
Julian Oes
4ddbcebcd5 feat(simulator_sih): implement lockstep by waiting for outputs
Without this, we see test failures in CI as soon as CPU load is higher.
2026-07-17 10:51:42 -05:00
Julian Oes
61ce628c39 build(cmake): add missing include 2026-07-17 10:51:42 -05:00
Julian Oes
ec982625b6 test(ros): add new model_prefix 2026-07-17 10:51:42 -05:00
Julian Oes
93c42252ff feature(CI): use SIH sim 2026-07-17 10:51:42 -05:00
Julian Oes
eaaf6f676a test(mavsdk): leave tests backwards compatible but enable SIH
This reverts some of the changes to keep runners of classic Gazebo in
there but leave it optional, so we can still use it if required.
2026-07-17 10:51:42 -05:00
Julian Oes
69d17b14aa feat(simulation): implement required failure hooks 2026-07-17 10:51:42 -05:00
Matthias Grob
cf8a65c5dd test(mavsdk): switch tests to SIH simulator 2026-07-17 10:51:42 -05:00
Ramon Roche
1499238f1c revert(fw_mode_manager): drop GUIDED_COURSE guard on course setpoints
Reverts the hotfix from #27487. The navigator now guarantees the course
field is NaN unless Course mode explicitly commands it, so
PX4_ISFINITE(course) is again a sufficient trigger for course guidance.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-07-17 17:50:25 +02:00
Ramon Roche
9f916e9811 fix(navigator): keep course setpoint NaN outside Course mode
The reposition triplet was zero-initialized at boot and memset after
consumption, and mission_item_to_position_setpoint() never wrote the
course field, so a finite course (0.0, or stale after leaving Course
mode) could leak into published position setpoints. Reset the
reposition triplet with reset_position_setpoint() and clear course when
converting mission items, so the field is only finite while Course mode
commands it.

Replaces the consumer-side clear from #27537 with a fix at the source.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-07-17 17:50:25 +02:00
Bartok
c3aba30d5b fix(sih): correct particles spelling in drag coefficient descriptions (#27938)
SIH_KDV/SIH_KDW longDesc said 'particules'. Param metadata only.

AI-assisted; human-reviewed.
2026-07-17 09:47:43 -06:00
Bartok
595074650a fix(ekf2): correct initialize spelling in magnetometer fusion param (#27937)
GCS long description for EKF2_MAG_TYPE used 'initalize'. Operator-facing metadata only.

AI-assisted; human-reviewed.
2026-07-17 09:45:52 -06:00
Bartok
6708c8a2dd fix(muorb): correct initialize spelling in protobuf channel logs (#27940)
PX4_INFO strings said 'initalize method'. Log text only.

AI-assisted; human-reviewed.
2026-07-17 09:45:15 -06:00
Mathieu Bresciani
d36e1f6aa4 fix(preflt-mag): warning msg without "Fail" when configured as warning (#27942) 2026-07-17 14:16:16 +02:00
PX4BuildBot
87fdc29e34 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-07-17 02:49:36 +00:00
Jacob Dahl
0524a374d9 fix(drivers/uavcannode): make CANNODE_NODE_ID the sole node ID policy (#27644)
* fix(drivers/uavcannode): make CANNODE_NODE_ID the sole node ID policy

The startup bootloader_app_shared_write() never reached the bootloader:
it only accepts App-signed data and invalidates the shared region on
every boot. The only consumer was uavcannode itself on a restart
without a reset (nsh stop/start), where the persisted bus_speed=0 made
the CAN driver init fail and left the node off the bus. The bootloader
learns our node ID exclusively through the firmware update handoff in
cb_beginfirmware_update, which already carries the static ID.

CANNODE_NODE_ID now overrides the bootloader handoff only when nonzero,
so the default (0) reuses the ID the bootloader already allocated
instead of running allocation a second time. The handoff is ignored
unless it carries a valid bitrate, healing regions poisoned by firmware
that wrote bus_speed=0. Max is 125 because 126/127 are reserved for
debug tools by DroneCAN convention, matching UAVCAN_NODE_ID.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(drivers/uavcannode): harden bootloader handoff validation

Invalidate the shared region whenever it was read valid, not only when
the handoff is accepted, so stale data cannot linger on boards whose
bootloader never invalidates it. Require a plausible bitrate (the
CANNODE_BITRATE minimum) rather than nonzero: the canbootloader alt-app
update path can hand off bus_speed = CAN_UNDEFINED (999). Reword the
invalid-param error, which claimed dynamic allocation while keeping the
bootloader handoff.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-16 20:43:22 -06:00
Jacob Dahl
f2e4d9d35c docs(rtk): note SPARTN on the GPS inject path (#27934)
CONFIG_GPS_SPARTN landed in #27919; call out the opt-in framing path and that keys stay on the receiver.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-16 18:12:26 -06:00
msl-dev
eddc929db3 fix(mavlink): handle unknown battery time remaining (#27929) 2026-07-16 14:41:33 -06:00
Bartok
69c709a431 fix: correct user-visible log/doc typos across modules (#27932)
Consolidates several small spelling fixes in user-visible strings:
- cyphal: 'succesfull' -> 'successful' (Set portID log)
- muorb apps: 'succesfully' -> 'successfully' (protobuf test logs)
- pga460: 'occured' -> 'occurred' (thermal shutdown log)
- camera_trigger: 'recieved' -> 'received' (module help + docs)

Logging/docs-only, no behavior change.
2026-07-16 14:39:27 -06:00
PX4BuildBot
7f42ae3db0 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-07-16 20:31:13 +00:00
Alex Klimaj
90913df7de feat(gps): inject SPARTN corrections alongside RTCM (#27919)
* feat(gps): inject SPARTN corrections alongside RTCM

Add a SPARTN transport-layer framer and feed gps_inject_data through both
RTCM3 and SPARTN parsers so PointPerfect-style SPARTN streams can be
reassembled and written to the receiver the same way RTCM already is.

Gated by CONFIG_GPS_SPARTN (default on). Disabled on px4_fmu-v6x where
flash is already at the limit; enabled on ark_can-rtk-gps.

Depends on PX4-GPSDrivers for automatic u-blox SPARTN input enable.

Signed-off-by: alexklimaj <alex@arkelectron.com>

* fix(gnss): avoid undefined shift in SPARTN CRC-32

Use uint64_t for the CRC working register so n==32 does not perform
1u << 32 (clang-analyzer BitwiseShift).

Signed-off-by: alexklimaj <alex@arkelectron.com>

* make format

* feat(gps): enable SPARTN support in board configurations

* feat(gps): enhance SPARTN support with additional frame tracking and status reporting

* fix(gps): frame RTCM3 and SPARTN from a single buffer

Feeding every inject chunk to an independent framer per protocol let each
one resync inside the other's payloads. That is not symmetric: RTCM3 is
covered by CRC-24Q, but SPARTN's header carries no usable integrity check
(TF006 is 4 bits over a non-byte-aligned field) and TF005 permits an 8-bit
message CRC, so a stray 0x73 in an RTCM3 payload is framed as SPARTN at
roughly 1 in 1024.

RTCM3-only is what every board actually runs, and over 50 MB of it the two
framers produced 211 bogus SPARTN frames (210 declaring CRC-8), each
re-injecting up to 1 kB of the stream back into the receiver. The reverse
direction produced none.

Frame both protocols from one buffer instead: whichever preamble comes
first is framed, and a valid frame consumes its own payload, so bytes
inside one protocol's frame never start the other's. The same 50 MB now
yields zero. Frames are also injected in arrival order rather than all
RTCM3 then all SPARTN, and one buffer replaces two (2248 B/instance,
down from ~4350 B).

Also reject TF002 message types 5-119, which SPARTN reserves, as the one
header field with a checkable range.

CONFIG_GPS_SPARTN was default y, so it built into every target with a GPS
including px4_fmu-v6x, which the flash report showed gaining the framer
despite the intent to keep it off. Default it to n and enable it explicitly
where it is wanted; the ark GPS boards already opt in, and SITL opts in so
the framing tests keep running in CI.

Rtcm3Parser and SpartnParser are replaced by CorrectionFramer; their tests
carry over to it. RtcmStress fed "garbage" drawn from 0x01-0xD2 to avoid a
preamble, which includes 0x73, and RtcmBustedSender ended its stream on a
candidate the framer was still waiting to complete; both now avoid every
preamble and flush respectively.

* feat(gps): enable SPARTN on ARK flight controllers

Covers receivers attached over UART rather than CAN. All four targets
link with margin; fmu-v6xrt has no px4board on this branch yet.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: alexklimaj <alex@arkelectron.com>
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-07-16 14:24:07 -06:00
PX4BuildBot
1bd36e8974 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-07-16 19:31:15 +00:00
Jacob Dahl
5d8874cd2b fix(gps): split RTCM corrections and moving-baseline uORB topics (#27097)
* fix(gps): split RTCM corrections and moving-baseline uORB topics

The single gps_inject_data topic served two unrelated purposes:
external fixed-base RTCM corrections (from MAVLink GPS_RTCM_DATA or
UAVCAN RTCMStream) and moving-base-to-rover RTCM 4072. In a
dual-GPS-with-moving-base plus fixed-base setup, the two streams
collided on the same queue and the FMU UAVCAN bridge mirrored
fixed-base RTCM onto the MovingBaselineData CAN message, breaking
rover heading or RTK fix (see PX4/PX4-Autopilot#27088).

Split by role:
- rtcm_corrections    (renamed from gps_inject_data): external RTCM
                       flowing into the vehicle; producers are
                       MAVLink, UAVCAN RTCMStream, and GPS drivers in
                       dump mode.
- rtcm_moving_baseline (new): moving-base GPS output intended for a
                       rover; single producer per vehicle
                       (MAX_INSTANCES = 1).

The GPS driver routes its own RTCM output to the right topic via
GPSHelper::isMovingBase(), and gates the two inbound streams per role
using new GPSHelper virtuals (PX4-GPSDrivers#212):
shouldInjectRTCMCorrections() is true for any configured receiver, so a
UART2 moving-base rover still accepts fixed-base corrections over its
main link; shouldInjectMovingBaseline() is true only for a UART1/CAN
heading rover, since a UART2 rover gets the baseline directly in
hardware and a moving base produces rather than consumes it. That
submodule PR also renames the ambiguous UBXMode fields to name their
UART explicitly (RoverWithMovingBase -> RoverWithMovingBaseUART2,
MovingBase -> MovingBaseUART2).

Septentrio's publish_rtcm_corrections() always publishes to
rtcm_moving_baseline (only the Secondary moving base calls it).
Rover-side consumers (gps, septentrio, uavcan bridge) drain both topics
independently; each topic gets its own stale-link switchover timer so
corrections failover is not suppressed by moving-baseline traffic, or
vice versa.

FMU UAVCAN bridge: two independent drain loops, one per topic. No
more dual-publish of a single uORB message onto both RTCMStream and
MovingBaselineData CAN streams.

CANnode MovingBaselineDataPub subscribes to rtcm_moving_baseline. The
bus_type == UAVCAN check is kept, now purely as a loop guard so a node
with both CANNODE_PUB_MBD and CANNODE_SUB_MBD does not rebroadcast a
peer's moving-baseline data back onto the bus. CANnode RTCMStream
subscriber maps each CAN source node ID to its own rtcm_corrections
instance (one PublicationMulti per source, capped at MAX_INSTANCES) so
multiple CAN RTCM sources (e.g. dual rovers outputting MSM7 for logging
plus a fixed-base feed) land on independent uORB instances instead of
interleaving on one, which would otherwise defeat the consumer's
per-instance stale-link selection.

Depends on PX4-GPSDrivers#212 (submodule bump included).

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(gps): use separate RTCM parsers for corrections and moving baseline

On a rover injecting both fixed-base corrections and moving-baseline RTCM, feeding both streams through a single parser allowed a fragmented frame from one source to be corrupted by bytes interleaved from the other. Reassemble each stream in its own Rtcm3Parser so frames are recovered independently.

Also collapse the two near-identical topics into a single RtcmData.msg published under both rtcm_corrections and rtcm_moving_baseline (the SensorGps pattern), track corrections and moving-baseline injection on separate perf counters so the reported corrections rate is no longer inflated by moving-baseline traffic, and zero-initialize the CAN DeviceId unions before populating their fields.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(septentrio): avoid bugprone sizeof division on RTCM byte buffer

moving_baseline.data is a uint8_t array, so sizeof(data)/sizeof(data[0]) divides by 1; clang-tidy's bugprone-sizeof-expression flags this as a suspicious sizeof(T)/sizeof(T). Use sizeof(data) directly - the capacity value is unchanged.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* refactor(gps): use dedicated per-stream RTCM drain functions

rtcm_moving_baseline has a single publisher (instance 0), so its
consumers are now a plain uORB::Subscription instead of a 4-instance
SubscriptionMultiArray, and the per-stream selected-instance and
stale-link timer members it no longer needs are removed.

With each RTCM stream now a fixed type with a single caller, the
templated drain helpers (drain_rtcm_subscriptions, the overloaded
drain_rtcm_to_can) bought nothing, so replace them with dedicated
functions: drainRtcmCorrections()/drainMovingBaseline() in the GPS
driver and the UAVCAN bridge, drain_rtcm_corrections()/
drain_moving_baseline() in Septentrio. The UAVCAN bridge calls
PublishRTCMStream/PublishMovingBaselineData directly instead of through
Forward lambdas.

Rename SeptentrioDriver::publish_rtcm_corrections() to
publish_moving_baseline(): it is only reached from the Secondary
moving-base decode path and only ever emits moving-baseline RTCM.

Corrections-path behavior (instance selection, generation-gap warning,
burst cap, self-injection filter) is unchanged.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* refactor(gps): rename RTCM inject gate to receiverReady and simplify chunk helper

Rename GPSHelper::shouldInjectRTCMCorrections() to receiverReady(). The
virtual gates injection of both RTCM corrections and moving-baseline, and
for UBX it simply reports whether the receiver is configured, so the name
now describes what it actually gates rather than implying it only concerns
corrections. Bumps the GPS-drivers submodule to the matching rename.

Drop the vestigial message-type template parameter from publish_rtcm_chunks:
both topics share rtcm_data_s, so only the publication type needs templating.

* fix(septentrio): log dropped RTCM uORB generations

Match the gps driver and warn when the RTCM corrections or moving-baseline
subscription skips a uORB generation, so dropped injection data is visible.

* docs(docs): Docs only update to the RtcmData msg

* chore(gps): pin GPSDrivers to merged main

Contains #212 (RTCM/moving-baseline gating virtuals), #213 (X20 CFG-ODO
NAK tolerance), and #215 (SPARTN input enable, best-effort VALSET).

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(septentrio): reassemble RTCM frames per stream before injecting

Both drains wrote raw uORB chunks to the receiver, so a fragmented frame
on one stream could get the other stream's bytes spliced in mid-frame and
corrupt both. Reassemble each stream in its own parser and only write
complete frames, mirroring the gps driver. Injection stats now count
frames instead of uORB chunks.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* refactor(mavlink)!: remove GPS_RTCM_DATA output stream

GPS_RTCM_DATA is a GCS-to-vehicle correction transport; echoing
rtcm_corrections back out over MAVLink had no consumer and the echo was
lossy anyway (uint8 len and 180-byte payload truncate 300-byte uORB
chunks). Receiving GPS_RTCM_DATA is unchanged.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
2026-07-16 13:23:26 -06:00
Julian Oes
a609464200 fix(simulation): don't publish gimbal when not available (#27908)
We should not publish gimbal things if we don't actually have a gimbal
model in Gz. Otherwise this confuses any other gimbal setups that one
might try.
2026-07-16 12:11:13 -06:00
Andrew Brahim
10c8115e5b fix(barometer): update i2c speed for bmp581 to 400khz (#27776)
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2026-07-16 11:35:50 -06:00
Bartok
1ec054b3da docs(rtk_gps): fix received spelling in RTCM fragment note (#27923)
AI-assisted; human-reviewed.
2026-07-16 11:12:16 -06:00