Skip to content

Mavlink compatibility patch#11180

Open
xznhj8129 wants to merge 25 commits intoiNavFlight:maintenance-10.xfrom
xznhj8129:mav_fix2
Open

Mavlink compatibility patch#11180
xznhj8129 wants to merge 25 commits intoiNavFlight:maintenance-10.xfrom
xznhj8129:mav_fix2

Conversation

@xznhj8129
Copy link
Contributor

WIP Extensive reworking of the MAVLink code to modernize, remove placeholder/boilerplate stuff and improve compatibility with GCS features like missions. No new Flight controller or MSP features added yet (to implement stuff like takeoff or setting altitude), that will be for a later stage.

Summary

  • Expand MAVLink support to include EXTENDED_SYS_STATE stream, protocol/autopilot version replies, available/current modes, and proper vehicle type detection. VTOL state is currently UNDEFINED
  • Rework mission handling to accept/send both MISSION_ITEM and MISSION_ITEM_INT with unified parsing, frame validation, and waypoint→MAV command mapping.
  • Improve stream-rate control (per-stream setters, GET/SET_MESSAGE_INTERVAL, REQUEST_DATA_STREAM), and split HUD/heartbeat sending for cleaner scheduling.
  • Add handling for HOME_POSITION (when GPS home is fixed) and SET_POSITION_TARGET_GLOBAL_INT as a GUIDED/GCS-nav waypoint update.
  • Update ArduPilot mode enumerations to current values; factor heartbeat mode flags based on actual flight mode rather than always marking manual/stabilize.
  • Timestamp GLOBAL_POSITION_INT with boot ms instead of raw usec to match MAVLink spec; include version header needed for autopilot/protocol version replies.

Details

  • Added new protocol definitions: EXTENDED_SYS_STATE stream ID/rate slot (for landed/flying state), ArduPilot version constants, frame-support mask helpers, and a vehicle-type mapper used by heartbeat and mode reports.
  • Stream control now uses mavlinkSetStreamRate/mavlinkStreamIntervalUs, tracks configured defaults, and drives EXT_SYS_STATE off the extra3 rate by default.
  • Telemetry scheduling separates HUD and heartbeat senders so we can use either seperately, emits EXTENDED_SYS_STATE explicitly, and wires the new stream into the periodic loop.
  • Modes: expanded plane/copter enums; available/current modes message support; heartbeat flags now set GUIDED/AUTO/STABILIZE based on FLM and respect manual-input gating. This is not perfect and needs real BOXID mapping.
  • Mission upload/download: request INT first; shared mavlinkHandleMissionItemCommon handles frame validation and waypoint creation; fillMavlinkMissionItemFromWaypoint builds outgoing items with frame selection via altitude mode; supports JUMP/SET_POI/SET_HEAD/RTH/LAND/LOITER/WAYPOINT. We only handle global and relative altitude frames, no NED or AMSL or terrain.
  • Commands: implement MAV_CMD handling for DO_REPOSITION, GET/SET_MESSAGE_INTERVAL, REQUEST_MESSAGE, REQUEST_PROTOCOL_VERSION, REQUEST_AUTOPILOT_CAPABILITIES, GET_HOME_POSITION; add ACK helper. Since COMMAND_LONG and COMMAND_INT are essentially the same thing but with just two params as ints vs floats, i united the two in a single function. INT-first request aligns with QGC; float-only GCS should still work.
  • Messages: support HOME_POSITION send; process REQUEST_DATA_STREAM and SET_POSITION_TARGET_GLOBAL_INT to update WP255 when allowed.
  • GLOBAL_POSITION_INT timestamp switched to ms (boot) per MAVLink field definition.

Testing

  • Testing with Speedybee F405WINGMINI in X-Plane HITL with MLRS mavlink receiver, routing BLE to Mavproxy
  • Using QGC and UASTOOL, it works, shows correct telemetry, can set GCS Nav, can upload and fly a simple mission
  • Mavlink receiver still works
  • Mostly testing with mavlink_type set to ARDUPILOT

Things in this PR that could need work

  • MSP to/from MAV mission translation isn't complete, it drops MSP-only fields (leg speed, LAND ground elevation, RTH land-flag, user-action bits which is pretty bad), so we need to think about how to handle this.
  • Parameter requests still aren't handled and don't return anything; questionnable if they ever should.
  • Upload/download missions via MISSION_ITEM and MISSION_ITEM_INT (QGC/MP/UASTOOL) including JUMP/ROI/yaw; verify sequence/ACKs and waypoint storage.
  • Verify stream-rate control: SET/GET_MESSAGE_INTERVAL, REQUEST_DATA_STREAM, and default reversion. Message interval rates could use some improvements and mapping to what streams we can actually send
  • Confirm heartbeat/custom_mode and available/current modes match expected ArduPilot mappings for multirotor vs fixed-wing profiles. This is an issue right now; as the current handling of AUTOPILOT_VERSION in Ardupilot mode blocks displaying of correct custom modes and shows Unknown (except GUIDED?) when set to a real AP version (ie 4.6.3), while setting it to 0 will make QGC spam request AUTOPILOT_VERSION. This may be related to (QGC issues regarding mode display)[https://github.com/Standard modes doesn't work for 'generic' autopilot mavlink/qgroundcontrol#13472]. UAStool does not display the correct fixed wing modes, so that's a hint. May try revisiting Standard Modes eventually, but i'm clearly still doing something wrong.
  • mavlinkSendAvailableModes sends all possible modes, not actual available modes; need to handle this like MSP_MODE_RANGES and return what modes are actually programmed
  • Question remains: If we pretend to be Ardupilot/PX4 (and i think we have to, because compatibility is easier than getting integrated into a bunch of different projects) we have to accept that some things will be broken and stay broken

@github-actions
Copy link

Branch Targeting Suggestion

You've targeted the master branch with this PR. Please consider if a version branch might be more appropriate:

  • maintenance-9.x - If your change is backward-compatible and won't create compatibility issues between INAV firmware and Configurator 9.x versions. This will allow your PR to be included in the next 9.x release.

  • maintenance-10.x - If your change introduces compatibility requirements between firmware and configurator that would break 9.x compatibility. This is for PRs which will be included in INAV 10.x

If master is the correct target for this change, no action is needed.


This is an automated suggestion to help route contributions to the appropriate branch.

@xznhj8129 xznhj8129 changed the base branch from master to maintenance-9.x January 16, 2026 16:19
@sensei-hacker sensei-hacker modified the milestone: 10.0 Feb 8, 2026
@xznhj8129
Copy link
Contributor Author

Update since last month

  • Implemented PROTOCOL_VERSION to use telemetryConfig()->mavlink.version * 100.
  • Guided altitude handling now respects MAV_FRAME via mavlinkFrameUsesAbsoluteAltitude for DO_REPOSITION, SET_POSITION_TARGET_GLOBAL_INT, legacy guided MISSION_ITEM, and MISSION_ITEM_INT guided.
  • Removed duplicate EXTENDED_SYS_STATE emission (battery/status path).
  • Heartbeat MAV type aligns with fixed-wing legacy state.
  • Mission upload while armed now rejected at MISSION_COUNT (with test).
  • Added guided MISSION_ITEM_INT current==2 handling (with test).
  • Updated available mode gating:
    • Plane CRUISE: BOXNAVCRUISE or (BOXNAVCOURSEHOLD + BOXNAVALTHOLD).
    • Copter GUIDED: BOXNAVPOSHOLD + BOXGCSNAV.
    • Copter BRAKE: BOXBRAKING.
  • Tests added/updated: protocol version, guided altitude, EXT_SYS_STATE emission, armed MISSION_COUNT reject, guided MISSION_ITEM_INT.
  • Fixed DO_JUMP waypoint p3 (must stay 0) and legacy guided MISSION_ITEM altitude mode (test coverage).
  • Fixed Mavlink mode display (QGC, Mavsdk, etc), mode fallback mapping so unknown/unmapped modes no longer emit invalid enum-end values.
  • Fixed failsafe mapping:
    • Copter failsafe fallback now maps to RTL instead of invalid.
    • Plane failsafe landing maps to AUTOLAND (was AUTO), fallback to RTL.
  • Added shared mode-selection helper so heartbeat/current/available mode use consistent vehicle-specific mapping.
  • AVAILABLE_MODES now filtered by configured BOX modes, instead of blindly advertising all modes.
  • Fixed HEARTBEAT vehicle type for fixed wing to report MAV_TYPE_FIXED_WING consistently.
  • Fixed protocol version response to use configured MAVLink version value instead of wrong constant wiring.
  • Fixed ATTITUDE roll/pitch/yaw rates to send radians/sec (conversion added).
  • Added stream-rate bounds guard in mavlinkSetStreamRate() for invalid stream index.
  • Guided waypoint writes now preserve altitude mode bit (NAV_WP_ALTMODE) based on MAVLink frame in:
    • MISSION_ITEM
    • MISSION_ITEM_INT guided path
    • DO_REPOSITION
    • SET_POSITION_TARGET_GLOBAL_INT
  • Fixed COMMAND_LONG dispatch bug: now passes MAV_FRAME_GLOBAL into common handler (was wrongly passing confirmation as frame argument).

Test infrastructure and coverage added:

  • Unit-test build wiring added for MAVLink unit tests in src/test/unit/CMakeLists.txt.
  • New src/test/unit/mavlink_unittest.cc with tests covering:
    • attitude units
    • COMMAND_LONG/COMMAND_INT reposition behavior
    • mission clear/count/request/upload flows
    • armed mission rejection and guided exceptions
    • SET_POSITION_TARGET_GLOBAL_INT
    • protocol version response
    • duplicate extended sys state regression
    • radio status and RC override
      Note: I don't trust unit tests too much, only flight tests.

Docs/enums changes:

@sensei-hacker sensei-hacker changed the base branch from maintenance-9.x to maintenance-10.x March 3, 2026 21:14
@sensei-hacker sensei-hacker added this to the 10.0 milestone Mar 3, 2026
# Conflicts:
#	docs/development/msp/inav_enums.json
#	src/main/telemetry/mavlink.c
@xznhj8129 xznhj8129 marked this pull request as ready for review March 3, 2026 22:35
@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Modernize MAVLink protocol implementation with improved GCS compatibility and mission handling

✨ Enhancement

Grey Divider

Walkthroughs

Description
  This description is generated by an AI tool. It may have inaccuracies
• Extensive modernization and reworking of MAVLink protocol implementation to improve GCS (Ground
  Control Station) compatibility
• Expanded MAVLink support including EXTENDED_SYS_STATE stream, protocol/autopilot version replies,
  available/current modes, and proper vehicle type detection
• Reworked mission handling to accept and send both MISSION_ITEM and MISSION_ITEM_INT with unified
  parsing, frame validation, and waypoint-to-MAV command mapping
• Improved stream-rate control with per-stream setters, GET/SET_MESSAGE_INTERVAL, and
  REQUEST_DATA_STREAM support
• Added HOME_POSITION handling and SET_POSITION_TARGET_GLOBAL_INT support for GUIDED/GCS-navigation
  waypoint updates
• Updated ArduPilot mode enumerations to current values with heartbeat mode flags based on actual
  flight mode
• Fixed GLOBAL_POSITION_INT timestamp to use boot milliseconds instead of raw microseconds per
  MAVLink specification
• Enhanced command handling for DO_REPOSITION, GET/SET_MESSAGE_INTERVAL, REQUEST_MESSAGE,
  REQUEST_PROTOCOL_VERSION, and REQUEST_AUTOPILOT_CAPABILITIES
• Unified COMMAND_LONG and COMMAND_INT handling into a single function for improved compatibility
• Tested with Speedybee F405WINGMINI in X-Plane HITL with MLRS mavlink receiver using QGC and
  UASTOOL
Diagram
flowchart LR
  A["MAVLink Protocol"] -->|"Enhanced Support"| B["EXTENDED_SYS_STATE<br/>Version Replies<br/>Mode Detection"]
  A -->|"Improved Missions"| C["MISSION_ITEM<br/>MISSION_ITEM_INT<br/>Unified Parsing"]
  A -->|"Stream Control"| D["Per-Stream Setters<br/>GET/SET_MESSAGE_INTERVAL<br/>REQUEST_DATA_STREAM"]
  A -->|"New Commands"| E["HOME_POSITION<br/>SET_POSITION_TARGET<br/>DO_REPOSITION"]
  B --> F["GCS Compatibility"]
  C --> F
  D --> F
  E --> F
Loading

Grey Divider

File Changes

Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Mar 3, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (4) 📎 Requirement gaps (0)

Grey Divider


Action required

1. DO_REPOSITION missing range checks 📘 Rule violation ⛨ Security
Description
MAV_CMD_DO_REPOSITION only checks NaN for lat/lon, but does not validate latitude/longitude
ranges or altitude finiteness/range before converting to scaled integers. This can lead to invalid
waypoints or overflow/undefined navigation behavior from malformed external MAVLink inputs.
Code

src/main/telemetry/mavlink.c[R1815-1851]

+static bool handleIncoming_COMMAND(uint8_t targetSystem, uint8_t ackTargetSystem, uint8_t ackTargetComponent, uint16_t command, uint8_t frame, float param1, float param2, float param3, float param4, float latitudeDeg, float longitudeDeg, float altitudeMeters)
+{
+    if (targetSystem != mavSystemId) {
+        return false;
+    }
+    UNUSED(param3);
+
+    switch (command) {
+        case MAV_CMD_DO_REPOSITION:
+            if (!mavlinkFrameIsSupported(frame,
+                MAV_FRAME_SUPPORTED_GLOBAL |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+                mavlinkSendCommandAck(command, MAV_RESULT_UNSUPPORTED, ackTargetSystem, ackTargetComponent);
+                return true;
+            }
+
+            if (isnan(latitudeDeg) || isnan(longitudeDeg)) {
+                mavlinkSendCommandAck(command, MAV_RESULT_FAILED, ackTargetSystem, ackTargetComponent);
+                return true;
+            }
          if (isGCSValid()) {
              navWaypoint_t wp;
              wp.action = NAV_WP_ACTION_WAYPOINT;
-                wp.lat = (int32_t)msg.x;
-                wp.lon = (int32_t)msg.y;
-                wp.alt = msg.z * 100.0f;
-                if (!isnan(msg.param4) && msg.param4 >= 0.0f && msg.param4 < 360.0f) {
-                    wp.p1 = (int16_t)msg.param4;
+                wp.lat = (int32_t)(latitudeDeg * 1e7f);
+                wp.lon = (int32_t)(longitudeDeg * 1e7f);
+                wp.alt = (int32_t)(altitudeMeters * 100.0f);
+                if (!isnan(param4) && param4 >= 0.0f && param4 < 360.0f) {
+                    wp.p1 = (int16_t)param4;
              } else {
                  wp.p1 = 0;
              }
-                wp.p2 = 0; // TODO: Alt modes 
-                wp.p3 = 0;
+                wp.p2 = 0;
+                wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
              wp.flag = 0;
Evidence
Compliance requires validating external numeric inputs for range/NaN before use. The new command
handler converts latitudeDeg, longitudeDeg, and altitudeMeters into scaled integers without
validating degree bounds or checking altitudeMeters for NaN/range.

src/main/telemetry/mavlink.c[1815-1851]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`MAV_CMD_DO_REPOSITION` accepts external MAVLink coordinates without validating latitude/longitude bounds or altitude finiteness/range before converting to scaled integers.
## Issue Context
This handler consumes untrusted MAVLink message payload values and writes them into a navigation waypoint (`setWaypoint(255, &amp;amp;amp;wp)`), so inputs must be checked for `NaN`/infinite values and for valid coordinate ranges to prevent invalid state.
## Fix Focus Areas
- src/main/telemetry/mavlink.c[1815-1851]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. DO_CHANGE_ALTITUDE accepts NaN 📘 Rule violation ⛯ Reliability
Description
MAV_CMD_DO_CHANGE_ALTITUDE converts param1 to cm using lrintf without validating that the
input is finite and within a safe range. A malformed MAVLink command can inject NaN/Inf or extreme
values, producing undefined or unsafe altitude targets.
Code

src/main/telemetry/mavlink.c[R1861-1868]

+        case MAV_CMD_DO_CHANGE_ALTITUDE:
+            {
+                const float altitudeMeters = param1;
              geoAltitudeDatumFlag_e datum;
+
              switch (frame) {
              case MAV_FRAME_GLOBAL:
              case MAV_FRAME_GLOBAL_INT:
Evidence
The checklist requires range/NaN validation for external numeric parameters before use. The new
altitude command path uses lrintf(altitudeMeters * 100.0f) without any isfinite/range checks on
altitudeMeters.

src/main/telemetry/mavlink.c[1861-1883]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`MAV_CMD_DO_CHANGE_ALTITUDE` uses `param1` directly without `NaN`/Inf/range validation, then converts via `lrintf`, which can yield undefined/unsafe results.
## Issue Context
This code processes external command payloads and sets an altitude target, so it must reject malformed inputs deterministically.
## Fix Focus Areas
- src/main/telemetry/mavlink.c[1861-1883]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Mission params not validated 📘 Rule violation ⛨ Security
Description
Mission item handling converts external MAVLink values (altitude, jump/loiter/yaw params) into
integral waypoint fields without checking NaN/finite status or bounds before lrintf/casts. This
can yield overflow/undefined conversions and corrupted mission state from malformed mission uploads.
Code

src/main/telemetry/mavlink.c[R1361-1512]

+static bool mavlinkHandleMissionItemCommon(bool useIntMessages, uint8_t frame, uint16_t command, uint8_t autocontinue, uint16_t seq, float param1, float param2, float param3, float param4, int32_t lat, int32_t lon, float altMeters)
+{
+    if (autocontinue == 0) {
+        mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
+        mavlinkSendMessage();
+        return true;
+    }
+
+    UNUSED(param3);
+
+    navWaypoint_t wp;
+    memset(&wp, 0, sizeof(wp));
+
+    switch (command) {
+        case MAV_CMD_NAV_WAYPOINT:
+            if (!mavlinkFrameIsSupported(frame,
+                MAV_FRAME_SUPPORTED_GLOBAL |
+                MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.action = NAV_WP_ACTION_WAYPOINT;
+            wp.lat = lat;
+            wp.lon = lon;
+            wp.alt = (int32_t)(altMeters * 100.0f);
+            wp.p1 = 0;
+            wp.p2 = 0;
+            wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
+            break;
+
+        case MAV_CMD_NAV_LOITER_TIME:
+            if (!mavlinkFrameIsSupported(frame,
+                MAV_FRAME_SUPPORTED_GLOBAL |
+                MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.action = NAV_WP_ACTION_HOLD_TIME;
+            wp.lat = lat;
+            wp.lon = lon;
+            wp.alt = (int32_t)(altMeters * 100.0f);
+            wp.p1 = (int16_t)lrintf(param1);
+            wp.p2 = 0;
+            wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
+            break;
+
+        case MAV_CMD_NAV_RETURN_TO_LAUNCH:
+            {
+                const bool coordinateFrame = mavlinkFrameIsSupported(frame,
+                    MAV_FRAME_SUPPORTED_GLOBAL |
+                    MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                    MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                    MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT);
+
+                if (!coordinateFrame && frame != MAV_FRAME_MISSION) {
+                    mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                    mavlinkSendMessage();
+                    return true;
+                }
+                wp.action = NAV_WP_ACTION_RTH;
+                wp.lat = 0;
+                wp.lon = 0;
+                wp.alt = coordinateFrame ? (int32_t)(altMeters * 100.0f) : 0;
+                wp.p1 = 0; // Land if non-zero
+                wp.p2 = 0;
+                wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
+                break;
+            }
+
+        case MAV_CMD_NAV_LAND:
+            if (!mavlinkFrameIsSupported(frame,
+                MAV_FRAME_SUPPORTED_GLOBAL |
+                MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.action = NAV_WP_ACTION_LAND;
+            wp.lat = lat;
+            wp.lon = lon;
+            wp.alt = (int32_t)(altMeters * 100.0f);
+            wp.p1 = 0; // Speed (cm/s)
+            wp.p2 = 0; // Elevation Adjustment (m): P2 defines the ground elevation (in metres) for the LAND WP. If the altitude mode is absolute, this is also absolute; for relative altitude, then it is the difference between the assumed home location and the LAND WP.
+            wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0; // Altitude Mode & Actions, P3 defines the altitude mode. 0 (default, legacy) = Relative to Home, 1 = Absolute (AMSL).
+            break;
+
+        case MAV_CMD_DO_JUMP:
+            if (frame != MAV_FRAME_MISSION) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            if (param1 < 0.0f) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.lat = 0;
+            wp.lon = 0;
+            wp.alt = 0;
+            wp.action = NAV_WP_ACTION_JUMP;
+            wp.p1 = (int16_t)lrintf(param1 + 1.0f);
+            wp.p2 = (int16_t)lrintf(param2);
+            wp.p3 = 0;
+            break;
+
+        case MAV_CMD_DO_SET_ROI:
+            if (param1 != MAV_ROI_LOCATION ||
+                !mavlinkFrameIsSupported(frame,
+                    MAV_FRAME_SUPPORTED_GLOBAL |
+                    MAV_FRAME_SUPPORTED_GLOBAL_INT |
+                    MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
+                    MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.action = NAV_WP_ACTION_SET_POI;
+            wp.lat = lat;
+            wp.lon = lon;
+            wp.alt = (int32_t)(altMeters * 100.0f);
+            wp.p1 = 0;
+            wp.p2 = 0;
+            wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
+            break;
+
+        case MAV_CMD_CONDITION_YAW:
+            if (frame != MAV_FRAME_MISSION) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            if (param4 != 0.0f) {
+                mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
+                mavlinkSendMessage();
+                return true;
+            }
+            wp.lat = 0;
+            wp.lon = 0;
+            wp.alt = 0;
+            wp.action = NAV_WP_ACTION_SET_HEAD;
+            wp.p1 = (int16_t)lrintf(param1);
+            wp.p2 = 0;
+            wp.p3 = 0;
Evidence
The checklist requires validating incoming numeric inputs for NaN and acceptable ranges before
applying them. The new shared mission handler directly casts altMeters and uses lrintf on
param1/param2 without checking for finiteness or ensuring the results fit into
int16_t/int32_t.

src/main/telemetry/mavlink.c[1361-1512]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Mission upload handlers accept external MAVLink mission item numeric fields and convert them (casts/`lrintf`) without validating finiteness or bounds, risking overflow/undefined conversions and invalid stored mission state.
## Issue Context
This is fed by untrusted GCS mission uploads (`MISSION_ITEM`/`MISSION_ITEM_INT`) and writes to internal waypoint storage.
## Fix Focus Areas
- src/main/telemetry/mavlink.c[1361-1512]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
4. REQUEST_DATA_STREAM ALL broken🐞 Bug ✓ Correctness
Description
REQUEST_DATA_STREAM uses req_stream_id as a direct index; MAV_DATA_STREAM_ALL (0) will only change
mavRates[0], but no telemetry sender ever triggers on stream 0. Ground stations that set all rates
via MAV_DATA_STREAM_ALL will see no effect on real streams (e.g., EXTENDED_STATUS=2, EXTRA*=10+).
Code

src/main/telemetry/mavlink.c[R2178-2187]

+    if (msg.start_stop == 0) {
+        mavlinkSetStreamRate(msg.req_stream_id, 0);
      return true;
  }
-    return false;
+
+    uint8_t rate = (uint8_t)msg.req_message_rate;
+    if (rate > TELEMETRY_MAVLINK_MAXRATE) {
+        rate = TELEMETRY_MAVLINK_MAXRATE;
+    }
+    mavlinkSetStreamRate(msg.req_stream_id, rate);
Evidence
MAVLink defines stream id 0 as MAV_DATA_STREAM_ALL (“Enable all data streams”). The handler sets
only mavRates[msg.req_stream_id] and never fans out updates when req_stream_id==0. INAV’s telemetry
loop only triggers on specific stream IDs (2,3,6,10,11,12,13), so updating index 0 does not change
what is sent.

src/main/telemetry/mavlink.c[2169-2189]
lib/main/MAVLink/common/common.h[982-994]
src/main/telemetry/mavlink.c[1305-1337]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`handleIncoming_REQUEST_DATA_STREAM()` treats `req_stream_id` as a single stream index. When a GCS uses `MAV_DATA_STREAM_ALL` (0) to configure all streams (as defined by MAVLink), INAV only updates `mavRates[0]`, which is not used by `processMAVLinkTelemetry()`.
### Issue Context
MAVLink defines `MAV_DATA_STREAM_ALL=0` as “Enable all data streams”. INAV’s scheduler triggers only specific stream IDs.
### Fix Focus Areas
- src/main/telemetry/mavlink.c[2169-2189]
- src/main/telemetry/mavlink.c[1305-1337]
### Expected change
- Add a branch for `msg.req_stream_id == MAV_DATA_STREAM_ALL` that iterates over the stream IDs INAV actually schedules and calls `mavlinkSetStreamRate()` for each.
- Preserve `start_stop==0` behavior (stop all), and clamp rate to `TELEMETRY_MAVLINK_MAXRATE`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. SET_POSITION_TARGET mask ignored🐞 Bug ✓ Correctness
Description
SET_POSITION_TARGET_GLOBAL_INT always writes WP255 using lat/lon/alt even when msg.type_mask
specifies that position/altitude should be ignored. This can overwrite the guided waypoint with
unintended coordinates when a sender transmits velocity-only/yaw-only setpoints.
Code

src/main/telemetry/mavlink.c[R2200-2218]

+    uint8_t frame = msg.coordinate_frame;
+    if (!mavlinkFrameIsSupported(frame,
+        MAV_FRAME_SUPPORTED_GLOBAL_INT |
+        MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
+        return true;
+    }
+
+    if (isGCSValid()) {
+        navWaypoint_t wp;
+        wp.action = NAV_WP_ACTION_WAYPOINT;
+        wp.lat = msg.lat_int;
+        wp.lon = msg.lon_int;
+        wp.alt = (int32_t)(msg.alt * 100.0f);
+        wp.p1 = 0;
+        wp.p2 = 0;
+        wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
+        wp.flag = 0;
+
+        setWaypoint(255, &wp);
Evidence
The MAVLink message explicitly includes a type_mask “to indicate which dimensions should be
ignored”, with specific X/Y/Z ignore bits. INAV decodes the message but never checks type_mask
before consuming lat_int/lon_int/alt and calling setWaypoint(255).

src/main/telemetry/mavlink.c[2191-2219]
lib/main/MAVLink/common/mavlink_msg_set_position_target_global_int.h[7-24]
lib/main/MAVLink/common/common.h[1854-1864]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`handleIncoming_SET_POSITION_TARGET_GLOBAL_INT()` ignores `msg.type_mask` and unconditionally consumes `lat_int/lon_int/alt` to update WP255.
### Issue Context
MAVLink defines `type_mask` as a bitmap of fields to ignore, with `POSITION_TARGET_TYPEMASK_X_IGNORE/Y_IGNORE/Z_IGNORE`.
### Fix Focus Areas
- src/main/telemetry/mavlink.c[2191-2222]
### Expected change
- Before using `lat_int/lon_int`, check that `!(msg.type_mask &amp;amp;amp; (POSITION_TARGET_TYPEMASK_X_IGNORE | POSITION_TARGET_TYPEMASK_Y_IGNORE))`.
- For altitude, either:
- If `Z_IGNORE` is set: do not update altitude (set `wp.alt = 0` so `setWaypoint(255)` keeps current altitude), or
- Skip waypoint update if altitude is required for your guided semantics.
- Consider also checking `msg.target_component` (if non-zero and not matching `mavComponentId`, ignore).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Unit test baudRates symbol 🐞 Bug ⛯ Reliability
Description
The new mavlink unit test defines baudRates with C++ linkage, but mavlink.c references an external C
symbol baudRates. This can cause a link failure (undefined reference to baudRates) when linking
C object code against the mangled C++ definition.
Code

src/test/unit/mavlink_unittest.cc[R94-97]

+const uint32_t baudRates[] = {
+    0, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 250000,
+    460800, 921600, 1000000, 1500000, 2000000, 2470000
+};
Evidence
mavlink.c calls openSerialPort with baudRates[baudRateIndex], relying on an external baudRates
symbol declared in io/serial.h. The unit test defines const uint32_t baudRates[] outside any
extern "C" block, so the symbol may be mangled by the C++ compiler and not satisfy the C
reference.

src/main/io/serial.h[89-90]
src/main/telemetry/mavlink.c[392-401]
src/test/unit/mavlink_unittest.cc[94-97]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`mavlink_unittest.cc` defines `baudRates` as a C++ global, but C code (`telemetry/mavlink.c`) expects an unmangled `baudRates` symbol.
### Issue Context
The unit test target links C objects (from `telemetry/mavlink.c`) with C++ objects (from the test). Without `extern &amp;amp;quot;C&amp;amp;quot;` on the definition, the linker may not match the symbol.
### Fix Focus Areas
- src/test/unit/mavlink_unittest.cc[94-97]
### Expected change
- Change the definition to have C linkage, e.g.:
- `extern &amp;amp;quot;C&amp;amp;quot; const uint32_t baudRates[] = { ... };`
- or wrap the definition in an `extern &amp;amp;quot;C&amp;amp;quot; { ... }` block.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

7. Enum source docs inconsistent 📘 Rule violation ✓ Correctness
Description
systemState_e is documented twice with conflicting _source references (fc_init.c vs
fc_init.h) in the generated enum reference markdown. This creates stale/inconsistent documentation
links after the source mapping change.
Code

docs/development/msp/inav_enums_ref.md[R5643-5649]

---
## <a id="enum-systemstate_e"></a>`systemState_e`
-> Source: ../../../src/main/fc/fc_init.h
+> Source: ../../../src/main/fc/fc_init.c
| Enumerator | Value | Condition |
|---|---:|---|
Evidence
The checklist requires documentation/links to stay synchronized with code after reorganizations. The
enum reference doc now contains duplicate systemState_e sections that disagree on the source file,
indicating docs are not synchronized/authoritative.

docs/development/msp/inav_enums_ref.md[5643-5661]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The enum reference markdown documents `systemState_e` twice with conflicting source locations, making the docs inconsistent.
## Issue Context
This PR changed enum source metadata, and the reference output now has diverging/stale links.
## Fix Focus Areas
- docs/development/msp/inav_enums_ref.md[5643-5661]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@xznhj8129
Copy link
Contributor Author

Don't merge yet, i need to fully test mavlink altitude setting

Comment on lines +1815 to 1851
static bool handleIncoming_COMMAND(uint8_t targetSystem, uint8_t ackTargetSystem, uint8_t ackTargetComponent, uint16_t command, uint8_t frame, float param1, float param2, float param3, float param4, float latitudeDeg, float longitudeDeg, float altitudeMeters)
{
if (targetSystem != mavSystemId) {
return false;
}
UNUSED(param3);

switch (command) {
case MAV_CMD_DO_REPOSITION:
if (!mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
mavlinkSendCommandAck(command, MAV_RESULT_UNSUPPORTED, ackTargetSystem, ackTargetComponent);
return true;
}

if (isnan(latitudeDeg) || isnan(longitudeDeg)) {
mavlinkSendCommandAck(command, MAV_RESULT_FAILED, ackTargetSystem, ackTargetComponent);
return true;
}

if (isGCSValid()) {
navWaypoint_t wp;
wp.action = NAV_WP_ACTION_WAYPOINT;
wp.lat = (int32_t)msg.x;
wp.lon = (int32_t)msg.y;
wp.alt = msg.z * 100.0f;
if (!isnan(msg.param4) && msg.param4 >= 0.0f && msg.param4 < 360.0f) {
wp.p1 = (int16_t)msg.param4;
wp.lat = (int32_t)(latitudeDeg * 1e7f);
wp.lon = (int32_t)(longitudeDeg * 1e7f);
wp.alt = (int32_t)(altitudeMeters * 100.0f);
if (!isnan(param4) && param4 >= 0.0f && param4 < 360.0f) {
wp.p1 = (int16_t)param4;
} else {
wp.p1 = 0;
}
wp.p2 = 0; // TODO: Alt modes
wp.p3 = 0;
wp.p2 = 0;
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
wp.flag = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

1. do_reposition missing range checks 📘 Rule violation ⛨ Security

MAV_CMD_DO_REPOSITION only checks NaN for lat/lon, but does not validate latitude/longitude
ranges or altitude finiteness/range before converting to scaled integers. This can lead to invalid
waypoints or overflow/undefined navigation behavior from malformed external MAVLink inputs.
Agent Prompt
## Issue description
`MAV_CMD_DO_REPOSITION` accepts external MAVLink coordinates without validating latitude/longitude bounds or altitude finiteness/range before converting to scaled integers.

## Issue Context
This handler consumes untrusted MAVLink message payload values and writes them into a navigation waypoint (`setWaypoint(255, &wp)`), so inputs must be checked for `NaN`/infinite values and for valid coordinate ranges to prevent invalid state.

## Fix Focus Areas
- src/main/telemetry/mavlink.c[1815-1851]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 1861 to 1868
case MAV_CMD_DO_CHANGE_ALTITUDE:
{
const float altitudeMeters = param1;
geoAltitudeDatumFlag_e datum;

switch (frame) {
case MAV_FRAME_GLOBAL:
case MAV_FRAME_GLOBAL_INT:
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

2. do_change_altitude accepts nan 📘 Rule violation ⛯ Reliability

MAV_CMD_DO_CHANGE_ALTITUDE converts param1 to cm using lrintf without validating that the
input is finite and within a safe range. A malformed MAVLink command can inject NaN/Inf or extreme
values, producing undefined or unsafe altitude targets.
Agent Prompt
## Issue description
`MAV_CMD_DO_CHANGE_ALTITUDE` uses `param1` directly without `NaN`/Inf/range validation, then converts via `lrintf`, which can yield undefined/unsafe results.

## Issue Context
This code processes external command payloads and sets an altitude target, so it must reject malformed inputs deterministically.

## Fix Focus Areas
- src/main/telemetry/mavlink.c[1861-1883]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1361 to +1512
static bool mavlinkHandleMissionItemCommon(bool useIntMessages, uint8_t frame, uint16_t command, uint8_t autocontinue, uint16_t seq, float param1, float param2, float param3, float param4, int32_t lat, int32_t lon, float altMeters)
{
if (autocontinue == 0) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}

UNUSED(param3);

navWaypoint_t wp;
memset(&wp, 0, sizeof(wp));

switch (command) {
case MAV_CMD_NAV_WAYPOINT:
if (!mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.action = NAV_WP_ACTION_WAYPOINT;
wp.lat = lat;
wp.lon = lon;
wp.alt = (int32_t)(altMeters * 100.0f);
wp.p1 = 0;
wp.p2 = 0;
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
break;

case MAV_CMD_NAV_LOITER_TIME:
if (!mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.action = NAV_WP_ACTION_HOLD_TIME;
wp.lat = lat;
wp.lon = lon;
wp.alt = (int32_t)(altMeters * 100.0f);
wp.p1 = (int16_t)lrintf(param1);
wp.p2 = 0;
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
break;

case MAV_CMD_NAV_RETURN_TO_LAUNCH:
{
const bool coordinateFrame = mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT);

if (!coordinateFrame && frame != MAV_FRAME_MISSION) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.action = NAV_WP_ACTION_RTH;
wp.lat = 0;
wp.lon = 0;
wp.alt = coordinateFrame ? (int32_t)(altMeters * 100.0f) : 0;
wp.p1 = 0; // Land if non-zero
wp.p2 = 0;
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
break;
}

case MAV_CMD_NAV_LAND:
if (!mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.action = NAV_WP_ACTION_LAND;
wp.lat = lat;
wp.lon = lon;
wp.alt = (int32_t)(altMeters * 100.0f);
wp.p1 = 0; // Speed (cm/s)
wp.p2 = 0; // Elevation Adjustment (m): P2 defines the ground elevation (in metres) for the LAND WP. If the altitude mode is absolute, this is also absolute; for relative altitude, then it is the difference between the assumed home location and the LAND WP.
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0; // Altitude Mode & Actions, P3 defines the altitude mode. 0 (default, legacy) = Relative to Home, 1 = Absolute (AMSL).
break;

case MAV_CMD_DO_JUMP:
if (frame != MAV_FRAME_MISSION) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
if (param1 < 0.0f) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.lat = 0;
wp.lon = 0;
wp.alt = 0;
wp.action = NAV_WP_ACTION_JUMP;
wp.p1 = (int16_t)lrintf(param1 + 1.0f);
wp.p2 = (int16_t)lrintf(param2);
wp.p3 = 0;
break;

case MAV_CMD_DO_SET_ROI:
if (param1 != MAV_ROI_LOCATION ||
!mavlinkFrameIsSupported(frame,
MAV_FRAME_SUPPORTED_GLOBAL |
MAV_FRAME_SUPPORTED_GLOBAL_INT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT |
MAV_FRAME_SUPPORTED_GLOBAL_RELATIVE_ALT_INT)) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.action = NAV_WP_ACTION_SET_POI;
wp.lat = lat;
wp.lon = lon;
wp.alt = (int32_t)(altMeters * 100.0f);
wp.p1 = 0;
wp.p2 = 0;
wp.p3 = mavlinkFrameUsesAbsoluteAltitude(frame) ? NAV_WP_ALTMODE : 0;
break;

case MAV_CMD_CONDITION_YAW:
if (frame != MAV_FRAME_MISSION) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED_FRAME, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
if (param4 != 0.0f) {
mavlink_msg_mission_ack_pack(mavSystemId, mavComponentId, &mavSendMsg, mavRecvMsg.sysid, mavRecvMsg.compid, MAV_MISSION_UNSUPPORTED, MAV_MISSION_TYPE_MISSION, 0);
mavlinkSendMessage();
return true;
}
wp.lat = 0;
wp.lon = 0;
wp.alt = 0;
wp.action = NAV_WP_ACTION_SET_HEAD;
wp.p1 = (int16_t)lrintf(param1);
wp.p2 = 0;
wp.p3 = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

3. Mission params not validated 📘 Rule violation ⛨ Security

Mission item handling converts external MAVLink values (altitude, jump/loiter/yaw params) into
integral waypoint fields without checking NaN/finite status or bounds before lrintf/casts. This
can yield overflow/undefined conversions and corrupted mission state from malformed mission uploads.
Agent Prompt
## Issue description
Mission upload handlers accept external MAVLink mission item numeric fields and convert them (casts/`lrintf`) without validating finiteness or bounds, risking overflow/undefined conversions and invalid stored mission state.

## Issue Context
This is fed by untrusted GCS mission uploads (`MISSION_ITEM`/`MISSION_ITEM_INT`) and writes to internal waypoint storage.

## Fix Focus Areas
- src/main/telemetry/mavlink.c[1361-1512]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +94 to +97
const uint32_t baudRates[] = {
0, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 250000,
460800, 921600, 1000000, 1500000, 2000000, 2470000
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

6. Unit test baudrates symbol 🐞 Bug ⛯ Reliability

The new mavlink unit test defines baudRates with C++ linkage, but mavlink.c references an external C
symbol baudRates. This can cause a link failure (undefined reference to baudRates) when linking
C object code against the mangled C++ definition.
Agent Prompt
### Issue description
`mavlink_unittest.cc` defines `baudRates` as a C++ global, but C code (`telemetry/mavlink.c`) expects an unmangled `baudRates` symbol.

### Issue Context
The unit test target links C objects (from `telemetry/mavlink.c`) with C++ objects (from the test). Without `extern "C"` on the definition, the linker may not match the symbol.

### Fix Focus Areas
- src/test/unit/mavlink_unittest.cc[94-97]

### Expected change
- Change the definition to have C linkage, e.g.:
  - `extern "C" const uint32_t baudRates[] = { ... };`
  - or wrap the definition in an `extern "C" { ... }` block.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

…reverted strict rc_override sysid check (caused fs), SET_POSITION_TARGET_LOCAL_NED for qgc change alt
@xznhj8129
Copy link
Contributor Author

xznhj8129 commented Mar 6, 2026

One must imagine Sisyphus happy

  • Fixed a major bug that wasn't detected in pure tcp SITL, MAVLink stream timing was tick-based, not real-time. When RADIO_STATUS arrived (txbuf=100), INAV’s flow-control path allowed sending every telemetry loop, so configured rates would go haywire and fire at high speed
  • removed the strict sysid check for RC overrides that i added, which caused failsafes when mavproxy was turned on. It may be a good idea to restrict it, but needs figuring out why that happens
  • Added SET_POSITION_TARGET_LOCAL_NED which is how QGC sends Alt change commands
  • Added mask support to SET_POSITION_TARGET_GLOBAL to differentiate XY vs Z vs XYZ setting commands
  • In navigation.c, i think i found the reason why POSHOLD would sometimes sink: setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos, posControl.actualState.yaw, NAV_POS_UPDATE_Z) was gated by if (!(prevFlags & NAV_CTL_ALT) || (prevFlags & NAV_AUTO_RTH) || (prevFlags & NAV_AUTO_WP) || terrainFollowingToggled) {, which means if none of those modes had ever been turned on, the FC would have an alt target of 0 on entering poshold. I'm not 100% sure on this, and taking it out of there might be wrong, but it fixed the behavior, so worth looking into more?

Testing:
FlyingRC H7WLite with MLRS receiver
QGC: modes, missions, goto, alt change all works ✅
UASTOOL: telemetry, goto, alt change works; modes to NOT work as it doesn't seem there is any way to change it from a Copter to a Fixed Wing
Mission Planner: not tested yet, Proton stopped working with it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants