Implements a new GUIDED_COURSE navigator mode that maintains a constant
ground-track bearing, altitude, and airspeed without manual stick input.
The mode is activated via MAVLink and accepts real-time in-flight updates:
- MAV_CMD_GUIDED_CHANGE_HEADING (HEADING_TYPE_COURSE_OVER_GROUND): set course
- MAV_CMD_DO_CHANGE_ALTITUDE: adjust target altitude
- MAV_CMD_DO_CHANGE_SPEED: adjust target airspeed
On activation the vehicle captures its current velocity vector as the
initial course bearing. A valid horizontal velocity estimate (GPS or
dead-reckoning) is required; course commands are rejected if unavailable.
* docs(tools): uORB parser: Comma separate multiple enums
* docs(tools): uORB parser: Backlink from enum to field
* docs(tools): uORB parser: index.md section for historic versions
* docs(tools): uORB parser: summary fragment to ease updates
For MAV_CMD_NAV_LOITER_TIME the parser used param4 both as the loiter
exit cross-track flag and as a multicopter yaw setpoint
(wrap_2pi(radians(param4))). Per the MAVLink spec param4 only selects the
loiter-circle exit behavior for forward-only vehicles and is not a yaw
field, so a ground station following the spec (e.g. param4=1 to request a
tangent exit) unintentionally forced a multicopter heading and could trip
the yaw-reached timeout.
Set yaw to NAN instead, so multicopters keep their current heading and no
heading is enforced; get_yaw_to_be_accepted() already treats a non-finite
yaw as reached. The exit cross-track flag is unchanged, and fixed-wing
behavior is unaffected (yaw is always considered reached there). The
download path already maps param4 to the exit cross-track flag, so it
stays consistent.
Fixes#27287
Signed-off-by: Andrii Anoshyn <anoshyn.andrii@gmail.com>
* docs(arkv6s): add flight controller documentation
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* docs(arkv6s): add product photo and note single-IMU variant
Drop in docs/assets/flight_controller/arkv6s/ark_v6s_front.jpg
so the page renders the board photo it already references, and
align the intro with the upstream description by spelling out
that the V6S is the single-IMU variant of the V6X.
* docs(arkv6s): add bootloader UART note, fix arkv6x RAM typo
Add the bootloader serial port note (TELEM1-only in bootloader mode)
to the ARKV6S docs, matching the ARKV6X docs. Also fix the ARKV6X
MCU spec listing "1MB Flash" instead of "1MB RAM".
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(optical_flow): report actual integration timespan in PAW3902/PAA3905
The chip accumulates delta_x/delta_y in its burst registers until the
Motion_Burst register is read, which clears the accumulator. The drivers
previously reported a hardcoded mode-dependent SAMPLE_INTERVAL_MODE_X as
the integration timespan regardless of how long ago the last burst read
happened.
This is wrong any time reads span more than one frame period - most
commonly when the Motion DRDY pin doesn't fire (chip detected no motion)
and the backup watchdog fires the read kBackupScheduleIntervalUs (20 ms)
later instead of every frame.
Downstream, VehicleOpticalFlow uses integration_timespan_us to size the
gyro integration window used for body-rate compensation, and ekf2's
_flow_gyro_bias estimator compares that gyro against the IMU at the
matched delayed time. A wrong window length misaligns the gyro
compensation with what the chip actually saw, and slowly biases
_flow_gyro_bias - contaminating motion-reported fusions on the
correctly-timed DRDY path too.
Replace the hardcoded value with the actual interval between consecutive
successful burst reads (clamped to a sane range). _timestamp_sample_last
is reset on driver Reset() and updated on every successful burst read
regardless of publish, since the chip clears its accumulator on every
read.
* refactor(optical_flow): use _ms literals for integration_timespan_us clamp
Replace raw 1000/200000 microsecond bounds with 1_ms/200_ms to match
the existing time_literals convention already used elsewhere in these
drivers. No behavior change.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Auto-selects DSHOT_MOTOR_PWM_BIT_WIDTH per timer clock at compile time
so ARK FPV emits DSHOT at the same bit period as the rest of the fleet.
The hardcoded WIDTH=20 truncates the prescaler on ARK FPV's 160 MHz APB1
(PLL1N=40, floor(160e6/600e3)=266 not divisible by 20), giving a 1.706us
bit period and 66.7%/33.3% duty cycles (vs DSHOT600 spec 75%/37.5%).
The new ladder tries WIDTH in {20, 19, 18} and picks the first that
divides cleanly. ARK FPV lands on W=19, matching the 1.75us bit period
of the 240 MHz boards. Duty cycles improve to 70%/35%. 240/96/108/84 MHz
boards keep W=20 (unchanged). 200 MHz boards (CubeOrange/Orange+,
h7extreme) fall through to W=20 (unchanged, fix needs W=22 with scaled
BIT_1/BIT_0 -- out of scope).
A #pragma message (not #warning, which -Werror=cpp would treat as an
error) flags boards where APB1 and APB2 timer clocks would emit DSHOT
at different rates -- currently only fmu-v4pro (APB1=90, APB2=180 MHz).
The whole block is guarded on STM32_APB2_TIM8_CLKIN so STM32F1 IO
co-processor boards (no TIM8, header still pulled in by io_timer.c)
keep W=20 silently.
Supersedes #27401.
* fix(ekf2): report HAGL variance for dist_bottom_var
dist_bottom_var was published as P(terrain, terrain) only. When the
range finder is the EKF height reference (EKF2_HGT_REF=2), the terrain
state is reset to 0 and not directly observed, so this variance does
not represent the actual uncertainty of dist_bottom (HAGL).
Use ComputeHaglInnovVar(P, 0) to report the proper HAGL variance,
which accounts for terrain state, vertical position state, and their
covariance.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(ekf2): clamp HAGL variance to non-negative
H*P*H^T should be >= 0 for a valid covariance, but float drift or
mildly non-PSD P can produce a tiny negative result. Clamp at the
accessor so consumers of lpos.dist_bottom_var never see a negative
variance.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* refactor(ina226): rewrite driver for robustness and clarity
Mirrors the INA238 rewrite (#27359):
- Non-blocking UNINITIALIZED -> RESET -> CONFIGURE -> MEASURE state
machine. Each transition is its own RunImpl tick with an explicit
ScheduleDelayed; init failures retry without losing the driver
instance.
- Tolerate ~2 s of consecutive collect() failures
(MAX_CONSECUTIVE_FAILURES = DISCONNECT_DEBOUNCE_US / SAMPLE_INTERVAL_US)
before a full reinit. Isolated I2C glitches just skip a cycle instead
of dropping battery_status.
- SAMPLE_INTERVAL_US = 100 ms, comfortably above the default ADC period
of ~75.3 ms (588 us * 2 channels * 64 avg). MEASURE compensates for
in-tick I2C time so each tick is exactly one interval apart.
- CONFIGURE waits SAMPLE_INTERVAL_US + 5 ms before the first MEASURE
read so the first averaged sample is ready.
- checkConfigurationRotating() reads one of {CONFIGURATION, CALIBRATION}
per cycle and compares it against the value we wrote, so an externally
reset device is detected within two cycles.
- '-t' arg validated against 1-3 at parse time; out-of-range values now
exit with an error.
- New perf counters ina226_bad_register and ina226_reinit for field
diagnosis; current driver state is surfaced in 'ina226 status'.
File layout: ina226_main.cpp folded into ina226.cpp. Constants/enums
namespaced under ina226. Params switched to DEFINE_PARAMETERS, and the
INA226_CONFIG raw-register override is removed in favor of a hardcoded
continuous-conversion config.
AuterionAutostarter::ina226_probe() updated to the new namespaced
ina226::Register and ina226::MANFID / ina226::DIEID constants.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(ina226): mask reserved bits when verifying CONFIGURATION register
The INA226 CONFIGURATION register has D15 (RST) self-clearing and
D14:D12 reserved, with D14 reading back as 1 regardless of what is
written. The verify step compared the raw read-back to the value
we wrote, so the check failed on every cycle and the driver
re-initialized itself every ~2 s. Mask the non-R/W bits before
comparing.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Address customer-reported dropouts traced to several issues in the
prior driver:
- A single transient I2C blip flipped _initialized=false and re-ran
probe()+Reset() inline on the next tick, losing most of a sample
period on every isolated glitch.
- start() scheduled the first collect() ~100 ms after the device reset
(INA238_SAMPLE_INTERVAL_US - 7 us), but the first averaged conversion
takes 540 us x 3 channels x 64 samples ~= 103.7 ms -- so the first
read sometimes hit post-reset zeros.
- The CONFIG register watchdog used set_bits/clear_bits that both
collapsed to 0 in HIGH ADC range, making it a no-op for the default
range. (ADCCONFIG and SHUNT_CAL were still checked.)
- setConnected(false) could fire up to three times per failed cycle
(collect, register-check branch, RunImpl else), collapsing the
intended ~2 s debounce.
- -t (battery index) was parsed with no range check; out-of-range
values silently clamped to 1 inside Battery.
Rewrite:
- Non-blocking UNINITIALIZED / RESET / CONFIGURE / MEASURE state
machine. Each step is its own RunImpl tick with an explicit
ScheduleDelayed; init failures retry without losing the driver
instance.
- Tolerate ~2 s of consecutive collect() failures
(MAX_CONSECUTIVE_FAILURES = DISCONNECT_DEBOUNCE_US /
SAMPLE_INTERVAL_US) before triggering a full reinit; isolated
glitches just skip a cycle.
- SAMPLE_INTERVAL_US = 105 ms, just above the 103.68 ms ADC conversion
period so we stop polling faster than the device produces samples.
MEASURE compensates for in-tick I2C time so each tick is exactly
SAMPLE_INTERVAL_US apart.
- CONFIGURE waits SAMPLE_INTERVAL_US + 5 ms before the first MEASURE
read, comfortably above the first averaged conversion.
- checkConfigurationRotating() reads one config register per cycle and
compares it against the value actually written, so an externally
reset device is detected within three cycles regardless of ADC range.
- -t arg validated against 1-3 at parse time; out-of-range values now
exit with an error.
- New perf counters ina238_bad_register and ina238_reinit. Driver
state is also surfaced in 'ina238 status'.
- File layout: ina238_registers.hpp folded into ina238.h,
ina238_main.cpp folded into ina238.cpp. Constants and enums namespaced
under ina238. Params switched to DEFINE_PARAMETERS.
- RESET state publishes documented invalid sentinels (current = -1,
temperature = NaN) instead of valid-looking zeros.
fix(ina238): pass literal i2c address to usage macro
PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS feeds its argument through the
module documentation parser's int(eval(...)) path, which only resolves
Python literals. The constexpr BASEADDR identifier crashed
make module_documentation with NameError. Match the convention in
sibling power_monitor drivers (ina220/ina226/ina228) and pass 0x45.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* docs(arkv6x): note bootloader UART7/TELEM1 mapping
The ARKV6X bootloader enables only UART7 (TELEM1), but the existing
serial port table reflects the runtime mapping where /dev/ttyS0 is
GPS1. Users flashing firmware over UART with px_uploader.py hit a
dead end when targeting GPS1 because no other UART responds in
bootloader mode.
* docs(arkv6x): fix uploader script name and add link
Script is named px4_uploader.py in the Tools directory; link to it
on GitHub so readers can find it.
* feat(simulation): add multi-video support into gstreamer plugin
Modified plugin `src/modules/simulation/gz_plugins/gstreamer`.
Previously when launched the plugin performed scanning of
all gazebo topics based on some regex, selected the first camera one
and launched gstreamer pipeline fetching images from the topic
and publishing into either udpsink or rtmp.
Now all the streaming functionality was moved into separate class:
`CameraStream`. It is responsible both for sub/unsub to gz topic
and gstreamer pipeline lifecycle.
Main class `GstCameraSystem` now stores a collection of
`CameraStream` instances which are created using gazebo's ECM
`eachNew` method (i.e. whenever a new camera appeared
in the world) and removed using `eachRemove` method
(whenever a camera disappears). The last thing worth noting is
that multi-vehicle streaming only works for udpsink, not RTMP,
since it requires careful consideration on how
the many rtmp paths should be specified.
On the other hand, udp is simple:
we use specified port as a base one and add instance number to it
to obtain exact port for each camera stream.
Moreover, `GstCameraSystem` keeps track on busy ports
and ensures that whenever restarted an instance
will stream its video to the same port as before.
* docs(simulation) add video streaming docs for Gazebo
The AirBrainH743 used Holybro's USB vendor ID (0x3162) by mistake.
This moves to 0x26ac like a few other boards do.
Signed-off-by: Julian Oes <julian@oes.ch>
The battery icon was hardcoded to BATT_3 regardless of actual charge
level. Map battery.remaining (0-1) to the seven available AT7456E
battery symbols (FULL through EMPTY).
Signed-off-by: SofianElmotiem <sofianelmotiem@gmail.com>
RC_CHAN_CNT is not "only meant for ground station use" — rc_update
reads it to compute _rc_calibrated, which gates
manual_control_input.valid. A value of 0 causes the RC manual
control input to be marked invalid and silently ignored by the
manual control selector, so flight mode switching and stick inputs
from RC stop working.
Tighten the short to mention calibration state and rewrite the long
to state the actual rule the firmware enforces.
Refs PX4#27439
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* mavlink: reassemble GPS_RTCM_DATA before GPS injection
* Apply minor comment requested changes
* Simplification: remove _completed_sequence asymetric protection
* Handle RTCM payload length which is an exact multiple of 180
* update docs
* lib gnss: new GpsRtcmMessageFragmenter to send RTCM via GPS_RTCM_DATA.hpp
* fix clang
* Remove RTCM fragmenter
* update docs
* Compatibility fallback for older QGroundControl builds that omit the final zero-length fragment
* mavlink receiver, remove while loop to avoid dead lock
* docs(update): Subedit
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
* docs(docs): format
---------
Co-authored-by: jonas <jonas.perolini@rigi.tech>
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
ModeManagement caches the most recent config_control_setpoints entry per
source_id (one per external mode) and reads it on every nav_state change
to decide which flight controllers to enable. The cache was read verbatim
regardless of when the entry was written, so a contract authored during
an earlier activation of the same mode -- e.g. a ground configuration that
disables all controllers, followed by re-entering the same mode in the
air -- briefly forced the new activation onto a controller configuration
intended for the previous one.
Stamp every cache write with the PX4-local receive time. On the first
updateControlMode() call for a new nav_state, refuse any cached entry
whose receive time predates the activation and publish the safe default
contract (every control loop enabled) instead, until a fresh entry arrives.
A single warning is emitted per activation when the fallback engages.
After sending reboot-to-bootloader, the PX4 USB CDC node briefly
disappears while the bootloader re-enumerates. Reopening the serial
port can land on a half-broken descriptor and the next tcdrain()
raises termios.error (5, 'Input/output error'). That bare OSError
escaped every retry layer and crashed the uploader, even though a
manual re-run would succeed once enumeration settled.
Convert OSError/SerialException from flush() and reset_buffers() into
the module's ConnectionError, matching how send()/recv() already
behave, and let the identify retry loops in _try_identify also catch
ConnectionError so a single transient I/O hiccup doesn't abort the
upload.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>