Commit Graph
51275 Commits
Author SHA1 Message Date
Jacob Dahl 923e340c3d refactor(ekf2): index GNSS check failures by the estimator_status constants (#28812)
The fail status union and the check mask enum each restated a check layout by hand next to the msg constants, joined by a per-check map, so appending a check to one copy silently misassigned it in the others. GnssChecks now keeps one fail word indexed by the msg constants, and one table maps each check to its EKF2_GPS_CHECK bit, checked at compile time. Saved parameters, logs and events keep their bit layouts.

Assisted-by: Claude:claude-opus-5-5
2026-09-29 11:36:22 -06:00
PX4BuildBot 8f106ed256 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-29 17:23:59 +00:00
Jacob Dahl 247b0b3423 fix(commander): report GNSS position loss from vehicle_gnss (#28912)
#28873 merged after the GPS to GNSS rename was tested, so its position-loss report still read sensor_gps_s from vehicle_gps_position and main doesn't build.

Assisted-by: Claude:claude-opus-5-5
2026-09-29 11:17:43 -06:00
Andrea Bernasconi 80e494bb79 refactor(rate_control): replace gain compression template with setters (#28910)
Templating GainCompression3d on its parameter IDs made the compiler emit
every method twice on boards that build both the fixed-wing and the
multicopter rate controller, as each instantiation is a separate class.
Inlining does not remove the duplicate because update() is too large to
be inlined.

Make GainCompression3d a plain class configured through setters and let
each rate controller own its FW_GC_* or MC_GC_* parameters. This saves
1040 bytes of flash on px4_fmu-v6x_default with no change in behaviour.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Andrea Bernasconi <andrea.bernasconi@auterion.com>
2026-09-29 19:09:12 +02:00
Jacob Dahl e40501eba0 refactor(msg): rename GPS to GNSS in messages, topics and sensor parameters (#24399)
* refactor(msg): rename SensorGps to SensorGnss and add VehicleGnss

GPS is one constellation; the receivers and the rest of PX4 handle GNSS. Field names lose their units (latitude, speed_accuracy, course, ...), which move into the metadata comments. The sensors module's output becomes VehicleGnss on vehicle_gnss: the selected receiver's sample next to what the sensors module adds to it (corrected sample time, antenna position, selected instance), so a consumer can tell the two apart and later selection and check results have a place to go.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/gps): publish sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/septentrio): publish sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/pps_capture): read sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(lib/gnss): select receivers on sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(sensors): publish VehicleGnss on vehicle_gnss and rename SENS_GPS* to SENS_GNSS*

The sensors module's output nests the selected receiver's sample, with the corrected sample time, antenna position and selected instance next to it. SENS_GPS_MASK/TAU/PRIME and SENS_GPSn_ID/OFFX/OFFY/OFFZ/DELAY take the SENS_GNSS name the heading parameters already use; saved parameters migrate on import, and the EKF2_GPS_POS/EKF2_GPS_DELAY translations now land on the new names directly.

Assisted-by: Claude:claude-opus-5-5

* refactor(logger): log sensor_gnss and vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(ekf2): fuse vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(replay): replay vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(local_position_estimator): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(attitude_estimator_q): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(lib/terrain_estimation): take sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(lib/wind_estimator): read vehicle_gnss in the replay script

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(vision_target_estimator): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(navigator): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(commander): read sensor_gnss and vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(mavlink): stream sensor_gnss and vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(uxrce_dds_client): bridge vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(zenoh): bridge vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(failure_injection): inject GNSS failures on sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/ins): publish sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/uavcan): publish sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/uavcannode): read sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/cyphal): bridge sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/telemetry): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/rc): read vehicle_gnss for telemetry

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/osd): read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(drivers/transponder): read sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(boards/modalai): publish sensor_gnss from the voxl2 drivers

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(examples): publish sensor_gnss and read vehicle_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(simulation): publish sensor_gnss

Follows the SensorGps to SensorGnss and vehicle_gps_position to VehicleGnss rename.

Assisted-by: Claude:claude-opus-5-5

* refactor(gnss): name GNSS variables gnss in the sensors module and receiver drivers

GPS is one constellation. Identifiers that hold or handle GNSS samples, or bind to the renamed parameters, follow the message and parameter rename; classes, files, modules and names tied to parameters that keep GPS stay.

Assisted-by: Claude:claude-opus-5-5

* refactor(gnss): name GNSS variables gnss in the estimators and navigator

GPS is one constellation. Identifiers that hold or handle GNSS samples, or bind to the renamed parameters, follow the message and parameter rename; classes, files, modules and names tied to parameters that keep GPS stay.

Assisted-by: Claude:claude-opus-5-5

* refactor(gnss): name GNSS variables gnss in commander, mavlink and failure injection

GPS is one constellation. Identifiers that hold or handle GNSS samples, or bind to the renamed parameters, follow the message and parameter rename; classes, files, modules and names tied to parameters that keep GPS stay.

Assisted-by: Claude:claude-opus-5-5

* refactor(gnss): name GNSS variables gnss in the remaining drivers and simulation

GPS is one constellation. Identifiers that hold or handle GNSS samples, or bind to the renamed parameters, follow the message and parameter rename; classes, files, modules and names tied to parameters that keep GPS stay.

Assisted-by: Claude:claude-opus-5-5

* docs(gnss): reference sensor_gnss, vehicle_gnss and SENS_GNSS* parameters

The upgrade guide covers the topic, field, ROS 2, parameter and Cyphal register renames.

Assisted-by: Claude:claude-opus-5-5

* fix(boards): name EKF2::UpdateGnssSample in the fmu-v6xrt ITCM lists

The rename left the old UpdateGpsSample symbol in the lists, so the ITCM check fails and the function drops out of ITCM on the boards it doesn't check.

Assisted-by: Claude:claude-opus-5-5

* chore(drivers/gps): bump PX4-GPSDrivers to the merged sensor_gnss rename

The submodule pointed at the PX4-GPSDrivers#243 branch commit; #243 is merged.

Assisted-by: Claude:claude-opus-5-5

* docs(msg): restore WGS84 in SensorGnss and point VehicleGlobalPosition at vehicle_gnss

The rename dropped the only mention of the WGS84 datum and ellipsoid, and left VehicleGlobalPosition referring to the removed vehicle_gps_position topic.

Assisted-by: Claude:claude-opus-5-5

* refactor(msg): drop selected_instance from VehicleGnss

receiver.device_id already names the selected receiver, and 0 while blended. The sensor_gnss instance depends on the order drivers advertise at boot, so it doesn't identify a receiver across boots or logs, and nothing reads it. It would also be meaningless on the per-receiver VehicleGnss instances proposed in #28894.

Assisted-by: Claude:claude-opus-5-5

* refactor(gnss): fuse vehicle_gnss.timestamp_sample directly and document the receiver's corrected time

vehicle_gnss.timestamp is now the sensors module's publish time, so EKF2's check of timestamp_sample against it was always true; the sensors module always fills timestamp_sample. The message docs described receiver.timestamp_sample as the driver's uncorrected value, but the sensors module writes the corrected time into it as well.

Assisted-by: Claude:claude-opus-5-5

* docs(msg): point VehicleGnss users at the top-level timestamp_sample

uXRCE-DDS shifts only top-level timestamp and timestamp_sample into agent time, so receiver.timestamp_sample reaches ROS 2 on the PX4 boot clock even though it holds the same value on uORB.

Assisted-by: Claude:claude-opus-5-5

* docs(releases): tell ROS 2 users which VehicleGnss timestamps to read

Only top-level timestamps are converted to ROS time, so a port that reads receiver.timestamp gets the PX4 boot clock, and vehicle_gnss.timestamp is now the sensors module's publish time rather than the receiver's.

Assisted-by: Claude:claude-opus-5-5
2026-09-29 10:36:49 -06:00
Gürkan Yılmaz 5afe025b73 docs(flight_task): fix typo in FlightTaskAuto (#28911) 2026-09-29 16:52:20 +01:00
Davide Iafrate ea495a418e docs(simulation): fix name of baylands world sdf file (#28909) 2026-09-29 14:31:08 +01:00
Matthias Grob bf52da8598 fix(GotoControl): same EKF reset solution but save some flash 2026-09-29 13:46:24 +02:00
Matthias Grob 049ea8c079 fix(GotoControl): handle EKF resets correctly 2026-09-29 13:46:24 +02:00
Matthias Grob 6b7a4cede2 fix(MulticopterPositionControl): correct units for goto setpoint yaw smoothing parameters 2026-09-29 13:46:24 +02:00
PX4BuildBot e81e29d933 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-29 10:30:13 +00:00
Andrea Bernasconi 0d104226d3 feat(mc_rate_control): add rate loop gain compression for MC (#28719)
* feat(mc_rate_control): add rate loop gain compression

Extend the gain compression already available on fixed-wing to the
multicopter rate controller, so that an oscillation (limit cycle) on the
torque setpoint dynamically reduces the loop gain instead of requiring a
manual retune. The gain recovers to 1.0 once the oscillation stops.

GainCompression3d is templated on the enable and minimum gain parameter
IDs so both airframe types share one implementation while keeping their
own MC_GC_* / FW_GC_* parameters. Behaviour is opt-in: MC_GC_EN defaults
to disabled.

The dt clamp in the spectral damper is widened from 1 ms to 0.125 ms
because multicopter rate loops run well above the 1 kHz that the previous
floor assumed, which would otherwise mis-scale the filter coefficients.
Compression is reset while landed so that ground contact vibration does
not compress the gains before takeoff.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Andrea Bernasconi <andrea.bernasconi@auterion.com>

* fix(rate_control): fully restart gain compression on reset

reset() only restored the scalar compression gains, leaving the spectral
damper history and the cached gain vector untouched. A reset therefore did
not really restart at gain 1: the next cycle still multiplied the torque
setpoint by the previously compressed vector, and the stale HPF/LPF state
could immediately recreate compression. Clear the detector history and the
cached gains so that disarming, a non-rotary-wing transition or ground
contact genuinely restart from an uncompressed loop.

Seed the high-pass filter from the first sample after a reset instead of
zeroing the input history. Since the multicopter controller resets on every
cycle while landed, a zeroed history would make the first airborne sample
look like a step edge and inject a spurious spike into the spectral damper
right at takeoff.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Andrea Bernasconi <andrea.bernasconi@auterion.com>

* docs(mc_rate_control): document multicopter gain compression

Add a feature page for gain compression on multicopters, mirroring the
fixed-wing one but covering the multicopter specifics: it is opt-in
(MC_GC_EN defaults to disabled), and compression is reset while disarmed
or landed so ground contact cannot compress the gains before takeoff.

The fixed-wing block diagram is linked rather than duplicated, since it
contains an airspeed scaling stage that does not exist in the multicopter
rate loop.

Also note in the multicopter PID tuning guide that compression should be
off during manual tuning, as it damps out the very oscillation the tuning
process looks for.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Andrea Bernasconi <andrea.bernasconi@auterion.com>

---------

Signed-off-by: Andrea Bernasconi <andrea.bernasconi@auterion.com>
2026-09-29 12:22:31 +02:00
bresch 19ed79efa1 fix(mc_att_control): gate the yaw rate command during the arming gesture
This prevents the yawspeed filter from accumulating a large value during
stick gesture arming
2026-09-29 10:36:45 +02:00
bresch d6817b8fdf fix(stick_yaw): clear the yawspeed filter on reset
This prevents a stale yawspeed setpoint from being released when the module
is activated again with the stick centred
2026-09-29 10:36:45 +02:00
bresch 3a2baa472f test(mc_att_control): assert no heading windup at both ends of MC_REF_FF
MC_REF_FF does not scale the path a wound-up reference takes to the output,
so the fixture's default gain of 1 was not covering the flown configuration
2026-09-29 10:36:45 +02:00
bresch d9fa6201f4 fix(mc_att_control): stop the reference heading winding up on a yaw rate command
This keeps the reference heading on the setpoint heading instead of
integrating the commanded yaw rate, which grew the yaw rate setpoint far
past the command whenever the vehicle could not follow it
2026-09-29 10:36:45 +02:00
SaibernardandJacob Dahl 27d3a62df1 feat(commander): name the GNSS reason when position is lost in flight (#28873)
* feat(commander): name the GNSS reason when position is lost in flight

When GNSS goes bad in flight and takes the local position estimate with
it, the operator got "GNSS data fusion stopped" about two seconds after
the position went, and nothing said why. EKF2 computes which receiver
check failed every cycle and publishes it in
estimator_status.gps_check_fail_flags, and outside of spoofing and
jamming commander only reports those bits while disarmed. A receiver
that stops sending altogether sets no bit at all.

Report it the moment the local position estimate becomes invalid in
flight, when GNSS position was fused within the last ten seconds. A
failing check gives one event carrying every failing check as a
bitfield, a receiver whose last sample is over a second old gives one
event saying so, and boards with room for it also get a statustext. A
vehicle that never fused GNSS gets nothing, since neither says anything
about why its estimate went. What the estimator decides is untouched.

In flight EKF2 raises only the fix, horizontal, vertical and speed
accuracy, spoofing and jamming checks, so any bit set at that moment is a
real cause. Once the position is invalid the existing fusion stopped
event already drops to Info, so the log now reads reason, then
consequence.

Closes #24355

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>

* test(commander): cover the GNSS reason reported when position is lost

Eight cases on EstimatorChecks driven directly, with the failsafe flags
kept across cycles as commander keeps them: the failing check is named
when position is lost in flight, with every bit in the event, it is
reported once and not again while the position stays lost, spoofing is
named ahead of an accuracy check, a receiver that stopped sending is
named as such, and nothing is reported when no check fails with the
receiver alive, when GNSS was not in use, when the vehicle has no GNSS
configured, or while disarmed.

Without the change four of them fail.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>

* fix(commander): remember the failing GNSS checks and name a silent receiver first

The flags in estimator_status describe only the newest sample, while
EKF2 keeps rejecting samples for at least a second after one failed and
keeps fusing for up to seven, so the check that kept GNSS out can have
passed again by the time the position goes and nothing was reported.
Every check that failed is now remembered until none has failed for the
same ten seconds that bound the recently fused window.

EKF2 runs the checks only on new samples, so a receiver that stopped
keeps the flags of its last sample. The silence is tested first and is
the reason reported.

The statustext could never be sent. Commander runs the checks without a
mavlink log publisher first, that pass consumes the position validity
edge, and the legacy pass that has the publisher sees the flag already
set. The event reaches the ground station and the log on its own, so the
statustext, its text selection and the test hook for it are gone.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>

* test(commander): cover a check that cleared before position was lost

Two more cases: the check fails on one sample and passes on the next,
and the position goes a cycle later, which still names that check; and a
receiver that stops after a failing sample is reported as silent rather
than as that check. The statustext assertions are gone with the
statustext.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>

* fix(commander): expire each failed GNSS check on its own timer

The shared latch cleared only after ten seconds without any failure, so a check that kept flapping held every earlier failure in the reason, e.g. a one-off spoofing flag or a drift check from the ground before takeoff. Each check now drops out ten seconds after it last failed.

The fail time is the estimator_status timestamp, when EKF2 reported the check, which also lets the test age a report.

Assisted-by: Claude:claude-opus-5-5

---------

Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>
Co-authored-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-28 20:41:31 -06:00
PX4BuildBot 16922e6694 docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-29 02:01:38 +00:00
Julian Oes c1f998dda7 fix(battery): assume full cell voltage at 4.2v (#28889)
It never really made sense to me to have the max cell voltage at this
arbitrary value, just to - presumably - make the estimation better.

I'm tempted to set this to what is "right" and then see what the effects
are.
2026-09-28 19:54:22 -06:00
Jacob Dahl c39dfca8cc feat(gnss): inject corrections from the serial port's work queue (#28890)
* fix(gps): keep fixed-base corrections away from a moving-base rover

Since #27097 every u-blox receiver gets the autopilot's fixed-base corrections, on the claim that a UART2 moving-base rover still wants them over its main link. An RTK engine works against one reference station: u-blox's moving-base application note (UBX-19009093, figure 2) sends static-base corrections to the moving base only, so the rover inherits an absolute fix through the baseline, and the ZED-F9P integration manual (UBX-18010802, 3.1.5.4) requires the reference station ID of the observations to match the station message, which a rover fed two bases cannot rely on. ArduPilot's AP_GPS::inject_data skips moving-baseline rovers, and before #27097 this driver skipped the UART2 rover too. A moving-base rover on either UART now takes only its baseline.

* fix(gps): skip the autopilot's corrections for a UART2 static-base rover

GPS_UBX_MODE 5 feeds the rover a static base's RTCM on UART2, yet the driver still injected the autopilot's fixed-base corrections on UART1. Unless both links carry the same base, the rover then sees two reference stations and can settle on either. It now skips injection like the moving-base rovers.

* feat(gnss): add a correction injector that runs on the serial port's work queue

The gps and septentrio drivers each select a corrections source, reassemble frames and write them from their reader thread, which blocks on the UART between epochs, so a correction waits for the read to return: up to one output interval in the septentrio driver. The injector does this once for every driver, woken by the correction topics on the port's work queue. It writes a frame only when the TX buffer has room for all of it, since a cut frame fails its CRC at the receiver; it feeds a receiver one stream, fixed-base corrections or its moving base's baseline, since an RTK engine works against one reference station; and it drops frame formats the receiver does not accept. It opens its own descriptor on the work queue because on NuttX a descriptor belongs to the task that opened it.

* refactor(gps): inject corrections through the gnss correction injector

Corrections were written from the driver's reader thread between reads, so each waited for a read to time out, and the driver carried its own copy of the source selection, framing and TX-space checks. The injector writes them from the port's work queue as they are published. It runs only while receiverReady() holds, as the injection did before, and is stopped before the driver reconfigures the receiver.

Receivers other than u-blox now get RTCM3 only: SPARTN and AssistNow MGA frames are u-blox formats. A UART1 moving-base rover reports its baseline's injection rate in rtcm_injection_rate. The full communication dump (GPS_DUMP_COMM 1) no longer includes injected corrections, which are written outside the driver thread.

* refactor(septentrio): inject corrections through the gnss correction injector

Corrections were written only after receive() returned with a complete SBF block, so each could wait up to one output interval, and they were written in every driver state, baud-rate detection and configuration included, where they share the line with commands. The injector writes them from the port's work queue as they are published, only while the receiver is streaming, and is stopped around a scheduled reset so no correction lands between forcing command input and the reset command.

A moving-base rover now takes only its moving base's stream, not fixed-base corrections as well, since an RTK engine works against one reference station; the fixed-base corrections still reach the moving base. Only RTCM3 is written: SPARTN and AssistNow MGA are u-blox formats. Injected corrections no longer count toward the controller -> receiver data rate or appear in the SEP_DUMP_COMM dump.

* refactor(gnss): give the correction injector the baudrate, not the driver's port

The injector held a reference to the driver's serial port only to read its baudrate, from the work queue while the driver thread owns the port. The baudrate is fixed while the injector runs, so the driver now passes it in the start() config.
2026-09-28 17:46:52 -06:00
PX4BuildBot db8bc69c4a docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-28 23:46:19 +00:00
Jacob Dahl eb8f2f9b42 refactor(ekf2): separate GNSS heading from position into independent topic (#27102)
* feat(sensors/vehicle_gps_position): publish GNSS heading on its own topic

Dual-antenna heading was only reachable through the blended
vehicle_gps_position, so it carried the position timestamp, followed the
position blending weights and was published at the position rate.
vehicle_gnss_heading carries one selected source at a time, preferring
sensor_gnss_relative (callback driven) and falling back to
sensor_gps.heading for receivers that do not publish it. Per-receiver
delay and the driver heading offset are resolved by device_id; a sample
timestamp within 10 ms of the publish timestamp is treated as unset so
the configured delay applies.

* refactor(ekf2): fuse GNSS heading from its own sample buffer

Heading rode on gnssSample and was only fused when a position sample was
popped: faster heading was dropped by the observation rate limit, its
timestamp was the position timestamp and a position outage blocked the
start. gnssYawSample has its own TimestampedRingBuffer and pop. Non-finite
yaw is rejected at the interface and a NaN heading_offset is treated as
zero instead of reaching the fusion Jacobian. The test simulator feeds
heading as an independent sensor.

* feat(sensors/vehicle_gps_position): per-receiver baseline rotation SENS_GPSn_ROT

The antenna baseline orientation lived in three places depending on the
transport: the serial driver (GPS_YAW_OFFSET), the Septentrio receiver
(SEP_YAW_OFFS, SEP_PITCH_OFFS) and EKF2 for anything arriving with a NaN
offset, in practice CAN receivers (EKF2_GPS_YAW_OFF). A second receiver
with a different baseline had no parameter at all. Drivers now publish
the raw baseline heading and vehicle_gps_position rotates it into the
body frame from the receiver's SENS_GPSn_* slot, the same Rotation enum
the other sensors use, before blending and before publishing
vehicle_gnss_heading. Bumps PX4-GPSDrivers so sensor_gnss_relative
carries a real UTC time and no parse-time sample timestamp, which lets
the relative-position heading be delay-compensated and PPS-aligned.

* refactor(drivers/gps): drop the UBX heading_offset setting

Bumps PX4-GPSDrivers to PX4/PX4-GPSDrivers#241, which removes the
driver-side heading offset now that SENS_GPSn_ROT is the only place the
baseline is rotated.

Assisted-by: Claude:claude-opus-5-5

* refactor(sensors/vehicle_gps_position): remove sensor_gps.heading_offset

With the baseline rotation in SENS_GPSn_ROT, a finite offset only meant
"the driver already rotated this heading, skip SENS_GPSn_ROT". The only
remaining writer was older DroneCAN node firmware through the Fix2 ECEF
hack, which made a CAN node's heading switch between two conventions
depending on whether a RelPosHeading arrived between Fix2 messages: with
a non-zero SENS_GPSn_ROT yaw the heading jumped by that angle. Every
receiver's heading is now the measured baseline and SENS_GPSn_ROT is the
only rotation. MicroStrain external heading aiding, which recovered the
baseline heading from vehicle_gps_position, reads vehicle_gnss_heading.

Assisted-by: Claude:claude-opus-5-5

* fix(mavlink): report the body-frame GNSS heading in GPS_RAW_INT and GPS2_RAW

sensor_gps now carries the measured baseline heading, so for a baseline
not along the body x axis the yaw field was off by the mounting angle.
The body-frame heading is taken from vehicle_gnss_heading when the
streamed receiver is the heading source.

Assisted-by: Claude:claude-opus-5-5

* fix(replay): replay vehicle_gnss_heading to EKF2

ReplayEkf2 publishes only the topics it lists from inside the lockstep
barrier, so EKF2 replay never received the heading.

Assisted-by: Claude:claude-opus-5-5

* feat(parameters): migrate GNSS heading offsets to SENS_GPSn_YAW

GPS_YAW_OFFSET, SEP_YAW_OFFS and EKF2_GPS_YAW_OFF were removed without a
migration, so an upgraded vehicle silently lost its antenna mounting
offset and its GNSS yaw was off by that angle.

Assisted-by: Claude:claude-opus-5-5

* fix(sensors/vehicle_gps_position): build GNSS heading only where it is consumed

The heading selection, baseline rotation and SENS_GPSn_ROT parameters
overflowed diatone_mamba-f405-mk2 flash by 657 bytes. Boards built
without EKF2 GNSS yaw fusion or MicroStrain have no consumer for
vehicle_gnss_heading, so SENSORS_VEHICLE_GNSS_HEADING defaults on only
with one of them. The parameter migration also drops fmodf, since the
old parameters never exceeded one wrap.

Assisted-by: Claude:claude-opus-5-5

* feat(sensors/vehicle_gps_position): carry the heading receiver's jamming and spoofing state

So that EKF2 can gate GNSS yaw on the receiver providing the heading, which is not necessarily the position receiver.

Assisted-by: Claude:claude-fable-5-1

* fix(ekf2): gate GNSS yaw on the heading, not the position checks

Yaw fusion could only start once the position checks passed, and stopped with them. On an ARK G5 outdoors the heading was good from 36 s after power-up and was first fused at 104 s, held back by EPV while Galileo HAS converged; a 0.45 s PDOP failure while the vehicle was handled on the ground stopped yaw for 10 s with its innovations under 5.4 deg.

The heading is its own observation, often from its own receiver: yaw now starts on fresh data, tilt alignment and the heading receiver's spoofing and jamming state under the EKF2_GPS_CHECK bits, and keeps running when position and velocity fusion stop. The hold-off after a yaw fusion failure was a side effect of the position checks resetting; it is now a yaw timer that withholds the heading from a reset for the GNSS health time.

Assisted-by: Claude:claude-fable-5-1

* fix(sensors/vehicle_gps_position): carry an unknown GNSS sample time as 0

A GNSS sample time within 10 ms of the publish time was treated as unset and replaced by SENS_GPSn_DELAY. An ARK G5 over DroneCAN measures 8.3 to 21.5 ms from epoch to receipt, so 0.4% of its headings were re-stamped 110 ms early; at 20 Hz EKF2 dropped them as out of order, at a lower rate they would have been fused 100 ms late. A stamp meaning unset that crossed a busy bus in more than 10 ms would have been trusted instead.

Unknown is now 0 end to end: the serial driver leaves timestamp_sample at 0, the CAN node sends RelPosHeading's timestamp as UNKNOWN for it, and the autopilot's bridge publishes 0 when the node timestamp can't be used. Any other sample time before the publish time is trusted.

Assisted-by: Claude:claude-opus-5-5

* feat(sensors/vehicle_gps_position): derive the GNSS heading baseline from antenna positions

SENS_GPSn_ROT and its Euler angles carried one number: roll never moves a baseline along x, pitch only flagged a vertical one, and the heading model uses the baseline yaw alone. Nothing checked the heading against the installation either, so a receiver that flagged float solutions as valid reset yaw 87 deg wrong at first fix on an ARK G5 moving-base pair.

As in ArduPilot, the baseline now comes from the antenna positions: SENS_GPSn_HDG selects the other receiver's antenna (moving base, from both slots' SENS_GPSn_OFF) or a custom SENS_GPSn_BLX/Y/Z (single dual-antenna receiver), and a heading is used only when the receiver reports a baseline within 20% of it in length and in its vertical component over the sample's attitude. Those float solutions reported 10.9 m and 1.04 m for a 0.347 m baseline. A source is published once it has passed for 1 s: the headings right after the base antenna was re-plugged passed for 0.9 s while up to 25 deg wrong. vehicle_gnss_heading logs the reported baseline length.

A yaw offset can't be turned into a baseline, so GPS_YAW_OFFSET, SEP_YAW_OFFS and EKF2_GPS_YAW_OFF are not migrated; GNSS yaw stays off, with a warning, until SENS_GPSn_HDG is set.

Assisted-by: Claude:claude-opus-5-5

* fix(ekf2): stop GNSS yaw on a spoofed or jammed heading receiver

The heading receiver's spoofing and jamming state only kept yaw fusion from starting; a receiver that reported spoofing in flight kept feeding yaw, where position fusion stops. Its samples are now not fused, the fusion stops once none has been for the reset timeout, and a reset to its heading waits for the GNSS health time, as for position.

Assisted-by: Claude:claude-opus-5-5

* docs(dronecan): configure the ARK G5 heading baseline on the autopilot

The page still configured the heading offset with the module's SEP_OFFS_YAW, and required GPS blending for the heading to be published, which no longer carries it.

Assisted-by: Claude:claude-opus-5-5

* fix(sensors/vehicle_gps_position): drop a mismatched GNSS heading without restarting the settle

Outdoors on an ARK G5 moving-base pair 0.35 m apart, the reported down component of the baseline scattered by up to 0.16 m while the airframe was turned, and 9 cm at rest, against the 7 cm the vertical check allows. The rejected headings were as good as the accepted ones, but every rejection restarted the 1 s settle, so during 720 deg turns a heading was published for 4 to 62% of the time.

A sample that fails the baseline checks is now only dropped; the settle restarts when the receiver reports no heading. Replayed on that log, 85 to 99% of the turns get a heading.

Assisted-by: Claude:claude-opus-5-5

* fix(sensors/vehicle_gps_position): drop the GNSS heading baseline's vertical check

The check compared the reported down component of the baseline with the configured baseline rotated by the vehicle attitude, within 20% of the baseline length. On an ARK G5 moving-base pair 0.35 m apart the down component scattered by up to 9 cm at rest and 0.16 m while the airframe was turned, so it rejected good headings: across seven outdoor logs it rejected 184 samples under 5 deg of error and 6 over 10 deg, all of which the EKF innovation gate would handle, and nothing the length check missed on a receiver that withholds float headings.

The length check and the near-vertical check on the reported baseline stay. The sensors module no longer reads the vehicle attitude to check the heading.

* feat(sensors/vehicle_gps_position): SENS_GNSSn_HDG with the auxiliary antenna position

The heading parameters are new, so they take the GNSS name the rest of the SENS_GPS family moves to rather than being renamed again shortly. The setup names what the receiver is: the rover of a moving base pair, or a receiver with two antennas. For the latter the second antenna is given by its position, like the main one's SENS_GPSn_OFF, so every setup derives the baseline from antenna positions.

Assisted-by: Claude:claude-opus-5-5

* feat(drivers/septentrio): publish the heading baseline on sensor_gnss_relative

The heading only reached PX4 through sensor_gps, without the baseline the receiver measured, so the sensors module could not check it against the configured one and a heading from float ambiguities, confident on a baseline metres off, was used. AuxAntPositions (dual antenna) or BaseVectorGeod (moving base rover) now go out with the heading of the same epoch on sensor_gnss_relative at EndOfAtt, the heading valid only with fixed ambiguities. Ported from ARK's driver, where it was tested outdoors on mosaic-G5 P3H, P6 and P8.

A receiver that rejects the extra blocks falls back to the previous output without a heading. The heading accuracy is the square root of the reported variance; it was the deg^2 variance scaled as if it were degrees.

Assisted-by: Claude:claude-opus-5-5

* refactor(sensors/vehicle_gps_position): take GNSS heading only from sensor_gnss_relative

The sensor_gps fallback carried no reported baseline, so its headings skipped the length check that rejects a wrong ambiguity fix. Every receiver that matters now reports on sensor_gnss_relative: u-blox, Septentrio, the Unicore UM982 (PX4/PX4-GPSDrivers bump) and DroneCAN RelPosHeading. The heading fields of sensor_gps go, with the paths that filled them: NMEA HDT, Trimble MB-Two, Femtomes, SBG and MicroStrain, the heading in DroneCAN Fix2 on both the node and the autopilot, and the RelPosHeading copy into sensor_gps.

Assisted-by: Claude:claude-opus-5-5

* docs(gps_compass): configure GNSS heading with SENS_GNSSn_HDG

The heading parameters were renamed and the heading now only comes from receivers that report their baseline, so the Trimble MB-Two and Femtomes pages no longer describe a heading setup. The antenna spacing is the recommended 30 cm rather than the 5 cm the code rejects below.

Assisted-by: Claude:claude-opus-5-5

* chore(drivers/gps): point PX4-GPSDrivers at the merged PX4/PX4-GPSDrivers#242

The submodule tracked the PR's branch commit; the squash-merged commit has the same tree.

Assisted-by: Claude:claude-opus-5-5
2026-09-28 17:37:58 -06:00
PX4BuildBot 095b1af76d docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-28 22:52:00 +00:00
Jacob DahlandHamish Willee 7e913fa766 docs(params): shorten verbose descriptions of commonly used parameters (#28850)
* docs(params): shorten verbose descriptions of commonly used parameters

Descriptions ship xz-compressed in ROMFS on boards without
CONSTRAINED_FLASH, and these carried restatements of the short
description, option lists already in the enum/bitmask labels, and
boilerplate. Every fact is kept; the text is 54% shorter.

Assisted-by: Claude:claude-opus-5-5

* Apply batched suggestions from code review

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

---------

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
2026-09-28 16:45:22 -06:00
Ramon Roche ec2687abc3 ci(usb-ids): check board USB IDs on PRs that change them
The workflow triggers on pull requests that touch a board defconfig or
the USB ID tooling, and checks only the defconfigs the PR adds or
modifies. The file list comes from diffing the checked-out PR merge
commit against its base parent, so no API call or token is needed. A PR that changes the tooling itself is checked against every
board, so a checker change is proven on the real tree and not only by
its unit tests. A test keeps the tooling list and the workflow paths
filter in sync.

A manual run checks every board. There is no push or scheduled run: the
registry repo checks its own changes against PX4 main, and board
changes reach main only through PRs.

The mypy and flake8 step for the checker and runner lives in
python_checks.yml with the other Python tooling checks.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 14:44:48 -07:00
Ramon Roche 89549434e8 ci(usb-ids): add a checker for board USB IDs against the Dronecode registry
Boards that ship the Dronecode USB vendor ID 0x3643 must use a product
ID allocated in the Dronecode/usb-ids registry, and nothing in the tree
enforces that. A board reusing another board's PID, or picking an
unallocated one, becomes ambiguous to host tools that identify boards
by VID/PID.

The contract is the registry mapping: a defconfig under
boards/<vendor>/<board>/ using VID 0x3643 must use a registered PID
whose px4_board is exactly <vendor>/<board>. The USB vendor string is
deliberately not checked, since the registry does not govern it. Boards
on other vendor IDs are ignored.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 14:44:48 -07:00
Holybro-Zebulon 3fe7e7af35 feat(boards): connect Kakute H7 Wing VTX power control (#28793)
Reuse RC_MAP_PAY_SW through the board payload power interface and keep sensor reset from overriding VTX power.
2026-09-28 12:49:59 -07:00
Ramon Roche 1fd3a84780 fix(lockstep_scheduler): stop swallowing wakeups in cond_timedwait
cond_timedwait() re-waited on a 10 ms wall-clock timeout until either a
signal or its virtual timeout arrived. A signal from the caller's own
signaler (px4_sem_post, or the unit test's broadcast) that lands while
the waiter is between that wall-clock timeout and re-acquiring the mutex
finds no waiter and is lost, so the waiter sleeps until its virtual
timeout. In lockstep_scheduler_test nothing else advances time and the
test hangs; in SITL a posted semaphore can report ETIMEDOUT late.

The loop is not needed: the caller holds the mutex until
pthread_cond_wait() releases it and set_absolute_time() takes that
mutex before broadcasting, so a timeout broadcast cannot be missed. The
original signal loss came from the MAX_WAKEUPS cap that the signal_next
list already removed. Go back to a single wait and keep the three-phase
signaling.

Refs #28862

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 12:25:27 -07:00
Ramon Roche 2ce73120bd test(ekf2): init the hrt in EKF2SelectorTest
The test starts the hrt worker without hrt_init(), so the callout lock
stays a default non-recursive mutex and the worker deadlocks on its own
nested lock in hrt_tim_isr() -> hrt_call_invoke() the first time a tick
fires. The selector's 10 ms backup reschedule normally keeps pushing the
tick out, so it only fires when Run() pauses under load or in the slower
Coverage build, where it hangs the test until the ctest timeout.
VehicleAirDataTest and VehicleMagnetometerTest already do this.

Refs #28862

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 12:25:27 -07:00
Ramon Roche 14cf29bb31 fix(build): check submodules once per configure, never reset changes
Every px4_add_git_submodule call ran check_submodules.sh twice, at
configure and build time, and with CI=true force-updated each submodule
every time, a 2017 workaround for an interactive prompt that is gone.
VS Code and CLion force-updated too, resetting submodule changes under
development.

The check now runs once per configure. A missing submodule is fetched;
one at another commit is kept as it is, with a warning locally and an
error in CI.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:50:38 -07:00
Ramon Roche b8d4f0b637 fix(bench): pin the autopilot component instead of the first heartbeat
Some boards heartbeat from more than one component on the same sysid. An
FMUv6X-RT on v1.17 sends HEARTBEAT from compid 1 (PX4) and from compid 236
with autopilot=MAV_AUTOPILOT_INVALID. connect() accepted the first
heartbeat from anyone and left target_component at pymavlink's default of
0, so param and mission requests went out as broadcast, the other
component could answer, and param_stress mixed its PARAM_VALUE stream
(index, count, values) into the autopilot's download.

connect() now waits for a heartbeat whose autopilot is not
MAV_AUTOPILOT_INVALID and pins target_system/target_component to its
source. pymavlink only latches target_system once and never sets the
component, so the pin holds for the life of the connection. The
wait_heartbeat() helper shares that filter and, once pinned, only counts
the pinned component; wait_reconnect() goes through connect() and gets
the same behavior after a reboot.

PARAM_VALUE replies in px4bench.params, param_stress and link_forwarding
are filtered to the pinned component, and flight_mission uses the pinned
component for MAVFTP and the armed/disarmed heartbeat checks instead of a
hardcoded 1.

Diagnosed by @farhangnaderi in #27852.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:52 -07:00
Ramon Roche f0f4612928 fix(bench): explain used-param semantics in the param_exists failure
The 1.17 run in #27840 read 'SDLOG_UTC_OFFSET not found in downloaded
set' as the parameter not existing on that release. It does exist there;
the download itself was the anomaly (19 params total, where a booted
board reports hundreds, since the autopilot lists used params only).
Name the downloaded count and that semantics in the failure so the next
tiny param set is diagnosed as a degraded session, not a missing param.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:52 -07:00
Ramon Roche 5dc6742bfa fix(bench): require pymavlink >= 2.4.42 for the mavftp module
log_transfer crashed with a raw ImportError on setups whose pymavlink
predates the mavftp module (#27840). mavftp first shipped in pymavlink
2.4.42, so raise the floor in pyproject.toml, Tools/setup/requirements.txt
and the README, and guard the import in px4bench.ftp so an old install
reports a clear upgrade hint instead of a traceback. Both mavftp
consumers now import it through the guard.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:52 -07:00
Ramon Roche 249d95dc1e fix(bench): re-send nsh wake writes and centralize the shell open timeout
Running the suite from main against a release/1.17 FMUv6X-RT board
(#27840) failed boot_health and flight_mission at 'shell open' while
reboot_loop passed the same check 10/10 in the same run. The failing
opens were the sessions immediately following a closed one, so the
single wake write was being consumed before the firmware nsh pipes were
reading and no amount of waiting could recover it. Re-send the wake
write every second until the deadline, strip stale BENCHOPEN wake lines
from subsequent command output, and replace the 5s hardcoded at every
call site with one px4bench.SHELL_OPEN_TIMEOUT (10s) so the budget is
tunable in one place.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:52 -07:00
Ramon Roche 5062756be4 docs(offboard): correct fixed-wing velocity and BODY_NED support
Velocity setpoints are not ignored on fixed-wing. When combined with a
position setpoint, FixedWingModeManager selects FW_POSCTRL_MODE_AUTO_PATH
and follows a path through the position tangent to the horizontal
velocity direction, with the acceleration component normal to the
velocity used as path curvature.

Velocity-only setpoints are not supported: offboard without a position
setpoint clears flag_control_position_enabled, so set_control_mode_current
falls through to FW_POSCTRL_MODE_OTHER and no new lateral/longitudinal
setpoints are published. Since MAV_FRAME_BODY_NED always discards the
position, it cannot be used for fixed-wing.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:03 -07:00
Ramon Roche 6152abde11 docs(offboard): clarify BODY_NED applies to velocity and acceleration setpoints only
BODY_NED position setpoints have never been supported: the receiver
rotates velocity and acceleration into the local frame and sets
position to NAN, and for fixed-wing (position setpoints only) the
frame cannot be used at all. The docs listed input combinations and
coordinate frames as independent lists, implying every combination
works in every frame, which misled users into filing #27617.

Also update stale code links still pointing at FlightTaskOffboard.cpp,
removed in the v1.12 offboard rework (#16739).

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:47:03 -07:00
Ramon Roche 35c5b21867 fix(ekf2): repair the ULog to replay csv converter
The replay csv generator drifted away from both the current message
definitions and the C++ loader that consumes its output.

The range finder reused the 'quality' column name of the optical flow, so
pandas merged the two columns and the distance was appended behind the
quality. SensorSimulator::setSingleReplaySample() reads the distance first,
so any log containing both sensors produced swapped range samples. Give the
range finder its own column names.

getGpsData() still read the removed alt/lon/lat fields of
vehicle_gps_position, so every GPS sample was silently dropped. Read
altitude_msl_m/latitude_deg/longitude_deg instead, emit them in the
altitude, latitude, longitude order the loader expects, and scale the
degrees by 1e7 to compensate for the 1e-7 rescale the loader still applies.

getVioData() read the removed x/y/z and vx/vy/vz fields, use position[] and
velocity[].

The bare except clauses hid all of this behind "X data not detected", so
print the exception too. Missing topics are still skipped, not fatal.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 11:22:17 -07:00
Ramon Roche 9a17deda06 ci(clang-tidy): prepare the clang build dirs in sequence
`make -j clang-ci` configured px4_sitl_default-clang and
px4_sitl_default-clang-test at the same time, and both configures fetch
the same submodules. About 1 in 200 Static Analysis runs failed with
`index.lock: File exists` or a half-cloned libevents.

The workflow now runs the configure-only test dir first, which fetches
the submodules, then builds the clang dir, which finds them present.
clang-ci had no other user, so it is removed.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 10:55:25 -07:00
Ramon Roche e0dc50d7cc build(uavcan)!: stop embedding CAN node firmware in flight controller builds
CONFIG_BOARD_UAVCAN_PERIPHERALS made ROMFS clone the PX4 repository and
run a second full build of a CAN node on every build of the flight
controller, to carry that node's firmware in /etc/uavcan/fw/. Its only
user, px4_fmu-v5_uavcanv0periph (CUAV CAN GPS v1), disabled about 30
modules to make room, and was downloaded 40 times in five years, never
through QGroundControl. Releases now publish every CAN node's firmware
standalone as <target>.uavcan.bin, which the DroneCAN firmware server
installs from the SD card on any flight controller.

Remove the option, the ROMFS ExternalProject and the board variant, and
point the DroneCAN docs and the release notes at the SD card path. The
server still serves /etc/uavcan/fw/, which is now empty.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 09:45:09 -07:00
Ramon Roche 89c68cd37f ci(build-all): build two targets per job concurrently
Each build job compiled its group one target at a time, and the serial
parts of every build (configure, the NuttX make, linking) left the 4-vCPU
runners about half idle. Two concurrent builds keep them around 80% busy
and cut total build-step time about 24% and runner cost about 17%.
Larger runners and more slots were measured and cost more than they save.

build_all_runner.py replaces build_all_runner.sh. NuttX compiles inside
its source tree, so each extra slot is a git worktree under .build_slots/.
All submodules are fetched once before building, each slot clones them
locally from the checkout, and builds run with GIT_SUBMODULES_ARE_EVIL=1
so CMake's per-configure submodule check does not sync and update them
concurrently on the shared .git/config. Targets that write the same
build/ directory (<board>_deb with <board>_default, the metadata targets
with px4_sitl_default) always share a slot, and the runner refuses a plan
that would split them. Every target is built even after a failure. Build
output streams with a [seconds|slot target] prefix, each target prints its
memory usage or, on failure, the excerpt from the first error, and a Build
Logs step prints each target's full log as one collapsed group. Slot build
directories are moved into build/, so packaging is unchanged.

The matrix now carries targets as a list plus a slot count from
build_all_config.yml; the comma-joined string existed only because
workflow expressions could not pass an array to a shell command, which
join() now does. The scan job runs new tests for the generated matrix and
the slot assignment.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 09:45:09 -07:00
Ramon Roche 9a90020437 fix(romfs): serialize gencromfs across concurrent builds
gencromfs writes its intermediate data to fixed file names in /tmp
(/tmp/gencromfs-000000, ...) because its mkstemp path is compiled out, so
two NuttX builds generating a ROMFS at the same moment overwrite each
other's temporary files and emit a corrupted nsh_romfsimg.c ("/*" within
comment, invalid suffix "x00"). Run gencromfs under flock where it is
available; it takes about a second, so the lock costs nothing.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-28 09:45:09 -07:00
PX4BuildBot 898d6056cf docs: auto-sync metadata [skip ci]
Co-Authored-By: PX4 BuildBot <bot@px4.io>
2026-09-28 16:14:41 +00:00
XiaoYang Chen 46a77d8ad1 fix oob bug in tests file2 module (#28586) 2026-09-28 10:09:20 -06:00
XiaoYang Chen c865dc9fde fix null pointer write in sd_stress (#28795) 2026-09-28 10:08:56 -06:00
Jacob Dahl b3e343c153 ci(codecov): disable the PR comment
The comment reports a patch percentage and a project total that is mostly
SITL coverage carried forward from main, but not which lines are uncovered,
so it adds noise to every PR without telling the author what to test. The
informational patch and project statuses stay and link to the line-level
report.

Assisted-by: Claude:claude-opus-5-5
2026-09-27 22:30:38 -07:00
Ramon Roche 8d446794ca docs(skills): say PRs may be squashed at maintainer discretion (#28871)
The pr and commit skills claimed every PR is squash-merged, which
contradicts CONTRIBUTING.md: merge strategy is case by case, with both
squash and rebase merges enabled. The wrong claim led to fix-up commits
being kept on branches on the assumption they would be squashed away.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-27 16:27:20 -06:00
PX4 Build BotandCrowdin Bot 4ce66eb466 docs(i18n): PX4 guide translations (Crowdin) - uk (#28866)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
Checks / Unit Test Coverage (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v2 (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-09-27 13:34:48 +10:00
PX4 Build BotandCrowdin Bot 56d5e8661a docs(i18n): PX4 guide translations (Crowdin) - zh-CN (#28867)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-09-27 13:34:19 +10:00
Ramon Roche 939b420488 ci(codecov): upload unit test coverage (#28869)
* ci(codecov): upload unit test coverage and limit SITL coverage to main

Coverage on main came from a single standard_vtol SITL flight, unit tests
were never uploaded, and the Coverage build never hit ccache because
ccache 4.9.1 (Ubuntu 24.04) rejects -fprofile-update=atomic. The flag only
keeps hit counts exact under thread races; line coverage is unaffected and
lcov already ignores the resulting negative counts.

Unit tests now build with coverage and upload as the unittests flag on
every PR and push to main. standard_vtol builds as Coverage on main only
and uploads as sitl-vtol, carried forward on PRs. codecov.yml sets the
default branch, informational project/patch statuses and ignores.
Per-build-type ccache keys stop the Coverage and RelWithDebInfo jobs from
overwriting each other's cache.

Fixes #28862

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>

* ci(codecov): run unit test coverage as its own job

The Coverage build shifts EKF replay float output, so running it in the
Unit Tests job made the change indication step auto-commit new baselines
to the PR branch. Unit Tests goes back to the plain build and a separate
job produces the unittests upload. disable_search keeps the Codecov
uploader to the lcov report instead of running gcov over the build tree.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>

---------

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-26 19:11:53 -07:00
Ramon Roche bf9b1610ca fix(ros2): pin BuildKit for larger dev container SBOMs
Publishing px4-dev-ros2-gazebo fails because its SPDX attestation exceeds
BuildKit 0.32's 40 MiB limit. Pin the builder to v0.33.0 (80 MiB), matching
the fix already applied to build_deb_package.yml in 61907962c9.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-09-26 18:51:08 -07:00