.pr_agent_auto_best_practices - iNavFlight/inav GitHub Wiki

Pattern 1: Check hardware polling-loop completion explicitly before reconfiguring DMA or continuing an I/O operation; if the peripheral remains busy after the timeout, abort and return an error. Disable request sources before changing an active DMA stream and restore them only after successful reconfiguration.

Example code before:

disableDma(stream);
while (dmaEnabled(stream) && timeout--) {
}
configureDma(stream);

Example code after:

disableDmaRequest(timer);
disableDma(stream);
while (dmaEnabled(stream) && timeout--) {
}
if (dmaEnabled(stream)) {
    return STATUS_TIMEOUT;
}
configureDma(stream);
enableDmaRequest(timer);
Relevant past accepted suggestions:
Suggestion 1:
  • [possible issue] In `SD_StartBlockTransfert`, check if the DMA disable loop timed out. If it did, set an error and return to avoid reconfiguring an active DMA stream, which could cause unpredictable behavior.
    Suggestion 2:
  • [possible issue] In `SD_HighSpeed`, if the `swTimeout` is reached while waiting for SDIO status, return an error like `SD_TIMEOUT` instead of just breaking the loop to prevent processing incomplete data. [possible issue, importance: 7]
    New proposed code:
    Suggestion 3:
  • [possible issue] In `impl_timerPWMSetDMACircular`, add a check after the DMA disable wait loop to handle the timeout case. If the timeout is reached, abort the DMA reconfiguration to prevent potential instability.
    Suggestion 4:
  • [possible issue] In `impl_timerPWMSetDMACircular` for `stdperiph`, add a polling loop to wait for the DMA stream's enable bit to clear after disabling it, before changing its configuration. This prevents a race condition.
    Suggestion 5:
  • [possible issue] Fix the incorrect DMA timeout check by re-evaluating the stream status after the loop instead of checking if `timeout` is zero.
    Suggestion 6:
  • [learned best practice] Match the HAL implementation by disabling the timer’s DMA request source before disabling/reconfiguring the DMA stream, then re-enable it afterward to prevent mid-transition DMA triggers.
    Suggestion 7:
  • [general] Add a timeout or retry counter to the polling loops in `STORAGE_Read` to prevent the system from hanging if the SD card becomes unresponsive.

  • Pattern 2: Validate externally supplied lengths, parsed values, and lookup indices before reading buffers or indexing arrays. Reject malformed input deterministically rather than allowing partial parsing, out-of-bounds access, or invalid API parameters.

    Example code before:

    const int index = findPort(id);
    ports[index].enabled = true;
    const uint16_t value = readU16(input);
    

    Example code after:

    if (dataSize < 2) {
        return STATUS_INVALID_INPUT;
    }
    const int index = findPort(id);
    if (index < 0 || index >= PORT_COUNT) {
        return STATUS_INVALID_INPUT;
    }
    const uint16_t value = readU16(input);
    ports[index].enabled = true;
    
    Relevant past accepted suggestions:
    Suggestion 1: [reliability] `parseInt` result not validated
    `parseInt` result not validated The PR comment step uses `parseInt(process.env.PR_NUMBER)` without checking for `NaN` or enforcing base-10 parsing, which can lead to non-deterministic behavior if the env var is missing/malformed. This violates the requirement to validate external inputs and handle invalid values deterministically.

    Issue description

    The workflow parses PR_NUMBER from an environment variable using parseInt(...) and then uses it without checking for NaN (and without specifying radix 10). If the env var is missing or malformed, this can produce non-deterministic behavior (e.g., NaN in URLs / API params) instead of a clear, deterministic failure.

    Issue Context

    This job runs with elevated permissions (pull-requests: write) and should validate external inputs (including env vars derived from artifacts/outputs) before use.

    Fix Focus Areas

    • .github/workflows/pr-test-builds.yml[107-110] բավ

    Suggestion 2:

    Add payload size validation check

    Add a payload size check in the MSP_OSD_CUSTOM_POSITION handler to ensure the incoming data is at least 3 bytes before reading from the buffer.

    src/main/fc/fc_msp.c [2718-2731]

     case MSP_OSD_CUSTOM_POSITION: {
    +    if (dataSize < 3) {
    +        return MSP_RESULT_ERROR;
    +    }
         uint8_t item;
         sbufReadU8Safe(&item, src);
         if (item < OSD_ITEM_COUNT){ // item == addr
             osdEraseCustomItem(item);
             osdLayoutsConfigMutable()->item_pos[0][item] = sbufReadU16(src) | (1 << 13);
             osdDrawCustomItem(item);
         }
         else{
             return MSP_RESULT_ERROR;
         }
     
         break;
     }

    Suggestion 3:
  • [learned best practice] Validate `"$#"` before reading `$1` and print a short usage message on error so failures are deterministic and user-friendly.
    Suggestion 4:
  • [possible issue] Add a check to ensure the index returned by `findSerialPortIndexByIdentifier` is valid (>= 0) before using it to access the `portConfigs` array to prevent potential crashes.
    Suggestion 5:
  • [learned best practice] Before writing variable-length frames, validate the computed payload/frame length against `CRSF_FRAME_SIZE_MAX`/`CRSF_PAYLOAD_SIZE_MAX` (and remaining `sbuf` capacity) and clamp counts or abort if it won’t fit.
    Suggestion 6:
  • [general] Add a check to the firmware renaming script to ensure `.hex` files exist before attempting to loop through and rename them, preventing potential errors.

  • Pattern 3: Propagate initialization, transmission, encoding, and asynchronous API failures instead of continuing or finalizing output unconditionally. Retain queued data for retry when a backend reports “not sent,” and commit state changes only after successful initialization.

    Example code before:

    initializeBackend(&config);
    transmit(packet);
    queuePop(&queue);
    

    Example code after:

    if (!initializeBackend(&config)) {
        return STATUS_INIT_FAILED;
    }
    const int result = transmit(packet);
    if (result > 0) {
        queuePop(&queue);
    } else if (result < 0) {
        return STATUS_IO_ERROR;
    }
    
    Relevant past accepted suggestions:
    Suggestion 1: [reliability] PWM beeper init unchecked
    PWM beeper init unchecked In the runtime-assigned beeper path, beeperInit() sets pwmMode=true and returns without verifying that beeperPwmInit() succeeded; if PWM allocation fails, pwmWriteBeeper() becomes a no-op and the compile-time beeper fallback is skipped, disabling beeps silently. This creates a brittle failure mode when timer channel allocation fails (e.g., no TCH available).

    Issue description

    The runtime OUTPUT_MODE_BEEPER path unconditionally commits to PWM mode (beeperConfigMutable()->pwmMode = true) and returns after calling beeperPwmInit(), but beeperPwmInit() can fail and leave the beeper backend uninitialized. In that case pwmWriteBeeper() no-ops and there is no fallback.

    Issue Context

    • beeperPwmInit() can return early when timerGetTCH() fails.
    • pwmWriteBeeper() returns immediately when beeperPwm == NULL.

    Fix Focus Areas

    • src/main/drivers/sound_beeper.c[81-91]

    Implementation direction

    • Make beeperPwmInit() report success (e.g., return bool), or add a small getter like bool beeperPwmIsInitialized(void).
    • In the runtime assignment path:
      • only set pwmMode=true and return if initialization succeeded;
      • otherwise continue into the existing compile-time beeper initialization path (or log an error and keep beeper in GPIO mode if possible).

    Suggestion 2: [correctness] `canardSTM32ComputeTimings` unchecked
    `canardSTM32ComputeTimings` unchecked `canardSTM32ComputeTimings()` returns `bool` but its result is ignored and `out_timings` is used unconditionally to configure the peripheral. If timing computation fails, CAN may be initialized with invalid/uninitialized timing values without any error propagation.

    Issue description

    CAN timing computation failure is ignored, potentially configuring hardware with invalid values.

    Issue Context

    The timing helper explicitly returns false for invalid/unsatisfied configurations; initialization should not proceed on failure.

    Fix Focus Areas

    • src/main/drivers/dronecan/libcanard/canard_stm32h7xx_driver.c[162-168]

    Suggestion 3: [correctness] `canardSTM32CAN1_Init()` return ignored
    `canardSTM32CAN1_Init()` return ignored `canardSTM32CAN1_Init()` returns a status code but `dronecanInit()` ignores it and continues initialization. This can leave DroneCAN partially initialized and failing silently at runtime.

    Issue description

    DroneCAN initialization ignores CAN peripheral init failures.

    Issue Context

    Proceeding after failed CAN init can cause confusing runtime behavior and make debugging difficult.

    Fix Focus Areas

    • src/main/drivers/dronecan/dronecan.c[404-440]

    Suggestion 4: [correctness] TX queue popped on failure
    TX queue popped on failure `processCanardTxQueue()` always pops the libcanard TX queue even when `canardSTM32Transmit()` returns 0 (not sent, e.g. TX FIFO full). This will silently drop DroneCAN frames under load.

    Issue description

    processCanardTxQueue() drops frames by popping the TX queue even when the hardware transmit reports “not sent yet” (return 0).

    Issue Context

    On STM32H7 the transmit function returns 0 when HAL_FDCAN_AddMessageToTxFifoQ fails (e.g. TX FIFO full), which should be retried.

    Fix Focus Areas

    • src/main/drivers/dronecan/dronecan.c[358-375]
    • src/main/drivers/dronecan/libcanard/canard_stm32h7xx_driver.c[83-128]

    Suggestion 5: [correctness] F7 transmit always succeeds
    F7 transmit always succeeds On STM32F7, `canardSTM32Transmit()` returns 1 even when `HAL_CAN_Transmit()` fails, masking errors and causing upper layers to believe the frame was sent.

    Issue description

    STM32F7 canardSTM32Transmit() reports success even on HAL transmit failure, hiding errors and causing silent packet loss.

    Issue Context

    Callers use the return value to decide whether to keep or drop frames.

    Fix Focus Areas

    • src/main/drivers/dronecan/libcanard/canard_stm32f7xx_driver.c[127-170]
    • src/main/drivers/dronecan/dronecan.c[358-375]

    Suggestion 6:
  • [possible issue] In `processCrsf`, check the return value of `crsfRpm` and only call `crsfFinalize` if data was successfully written to the buffer.
    Suggestion 7:
  • [possible issue] In `processCrsf`, make the call to `crsfFinalize` conditional on `crsfTemperature` actually writing data to avoid sending empty temperature frames.
    Suggestion 8:
  • [learned best practice] Await the API call and wrap in try/catch to fail the step on error and aid troubleshooting.

  • Pattern 4: Keep generated build outputs and environment-specific artifacts out of source control; generate them in the build directory or update tracked reference files only through an explicit regeneration command. Add generated build directories to ignore rules.

    Example code before:

    add_custom_command(
        TARGET firmware POST_BUILD
        COMMAND generate_schema ${CMAKE_SOURCE_DIR}/schema.db
    )
    

    Example code after:

    add_custom_command(
        TARGET firmware POST_BUILD
        COMMAND generate_schema ${CMAKE_BINARY_DIR}/schema.db
    )
    # Provide a separate explicit update-schema target for tracked references.
    
    Relevant past accepted suggestions:
    Suggestion 1: [correctness] `dsdlc_generated` code committed
    `dsdlc_generated` code committed This PR adds `dsdlc_generated` DroneCAN DSDL outputs directly to the repo, which are generated artifacts and can make builds non-reproducible and the repo noisy. These files should be generated into the build directory (or updated via an explicit opt-in step) and excluded from normal source tracking.

    Issue description

    The PR commits DSDL-generated DroneCAN sources/headers under dsdlc_generated, which are generated artifacts.

    Issue Context

    Generated artifacts should be produced as part of the build (or via an explicit opt-in update command) and not be committed as normal source to keep the repository clean and reproducible.

    Fix Focus Areas

    • src/main/drivers/dronecan/dsdlc_generated/src/uavcan.equipment.air_data.Sideslip.c[1-8]
    • cmake/main.cmake[2-12]

    Suggestion 2:

    Remove generated build file from repository

    Remove the generated CMake build file from the repository. It contains user-specific absolute paths that will cause build failures for other developers and should be added to .gitignore.

    src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/DependInfo.cmake [1-474]

    -# Consider dependencies only in project.
    -set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF)
    +# This file should be removed from the repository.
     
    -# The set of languages for which implicit dependencies are needed:
    -set(CMAKE_DEPENDS_LANGUAGES
    -  "ASM"
    -  )
    -# The set of files for implicit dependencies of each language:
    -set(CMAKE_DEPENDS_CHECK_ASM
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S" "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/__/__/__/__/lib/main/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S.obj"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/main/startup/startup_stm32f722xx.s" "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/__/__/startup/startup_stm32f722xx.s.obj"
    -  )
    -set(CMAKE_ASM_COMPILER_ID "GNU")
    -
    -# Preprocessor definitions for this target.
    -set(CMAKE_TARGET_DEFINITIONS_ASM
    -...
    -
    -# The include file search paths:
    -set(CMAKE_ASM_TARGET_INCLUDE_PATH
    -  "main/target/AXISFLYINGF7PRO"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/STM32F7/Drivers/STM32F7xx_HAL_Driver/Inc"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/STM32F7/Drivers/CMSIS/Device/ST/STM32F7xx/Include"
    -...
    -

    Suggestion 3:

    Remove generated file from version control

    Remove the generated Makefile.cmake file from version control. This file is environment-specific and should be ignored by adding the CMakeFiles directory to .gitignore.

    src/CMakeFiles/Makefile.cmake [1-9]

    -# CMAKE generated file: DO NOT EDIT!
    -# Generated by "Unix Makefiles" Generator, CMake Version 4.1
    +# This file should be removed from the pull request and repository.
     
    -# The generator used is:
    -set(CMAKE_DEPENDS_GENERATOR "Unix Makefiles")
    -
    -# The top level Makefile was generated from the following files:
    -set(CMAKE_MAKEFILE_DEPENDS
    -  "CMakeCache.txt"
    -...
    -

    Suggestion 4:
  • [learned best practice] Avoid mutating `cmake/pg_struct_sizes.db` in a POST_BUILD step; instead write an updated copy to a build output file (or gate updates behind an explicit `--update-db` flag) and print instructions to apply it.

  • Pattern 5: When changing documented protocol values, configuration limits, or feature behavior, update every corresponding generated reference and related documentation page so they agree with the implementation. Describe exact semantics, including sentinels, units, direction conventions, and command-specific replacements.

    Example code before:

    // Implementation
    enum Mode { MODE_A = 0, MODE_B = 2 };
    
    // Reference documentation
    MODE_A = 0
    MODE_B = 1
    

    Example code after:

    // Implementation and regenerated reference
    enum Mode { MODE_A = 0, MODE_B = 2 };
    
    MODE_A = 0
    MODE_B = 2
    Unknown value: -1
    
    Relevant past accepted suggestions:
    Suggestion 1: [maintainability] Stale enum protocol docs
    Stale enum protocol docs The PR changes wire values of `resolutionType_e` (e.g., `HD_6022` becomes 3 and `HD_5320` becomes 4, adding `HD_3016=2`), but the committed MSP enum reference docs still list the old ordinals. This leaves the repository’s protocol documentation inconsistent with the firmware, risking mis-implementation by tooling/third parties relying on these references.

    Issue description

    resolutionType_e is a wire-protocol enum (sent as a raw byte via MSP_DP_OPTIONS). This PR updates its explicit values, but the committed enum reference artifacts under docs/development/msp/ still document the old ordinals, causing protocol documentation drift.

    Issue Context

    The repo contains auto-generated enum reference files (inav_enums.json, inav_enums_ref.md) produced by docs/development/msp/gen_docs.sh / gen_enum_md.py. These should reflect the updated resolutionType_e members and values (including the newly reintroduced HD_3016=2).

    Fix Focus Areas

    • src/main/io/displayport_msp_osd.c[64-73]
    • docs/development/msp/inav_enums.json[3300-3306]
    • docs/development/msp/inav_enums_ref.md[4824-4834]

    What to change

    • Regenerate/update docs/development/msp/inav_enums.json and docs/development/msp/inav_enums_ref.md so resolutionType_e matches the new wire values:
    • SD_3016 = 0
    • HD_5018 = 1
    • HD_3016 = 2
    • HD_6022 = 3
    • HD_5320 = 4
    • Prefer running the documented generator (docs/development/msp/gen_docs.sh) and committing the updated artifacts, rather than hand-editing.

    Suggestion 2: [correctness] MAVLink not transmit-only
    MAVLink not transmit-only docs/Telemetry.md states MAVLink in INAV is “transmit-only”, but the current MAVLink telemetry implementation reads and processes incoming MAVLink messages (missions/params/RC override/etc). This misleads users about MAVLink capabilities and is in-scope because the PR edited this sentence but kept the incorrect claim.

    Issue description

    docs/Telemetry.md says MAVLink is transmit-only, but the implementation processes inbound MAVLink messages. This is incorrect documentation and can lead users to wrong configuration/expectations.

    Issue Context

    The telemetry MAVLink code contains an explicit receive loop and dispatches received message IDs.

    Fix Focus Areas

    • docs/Telemetry.md[205-210]
    • src/main/telemetry/mavlink.c[1388-1433]
    • src/main/telemetry/mavlink.c[1444-1456]

    Suggestion 3: [maintainability] SoftSerial baud docs conflict
    SoftSerial baud docs conflict This PR removes the “softserial is limit to 19200” note from the iBus telemetry section, but other docs still state SoftSerial is limited to 19200 baud. This leaves contradictory guidance across the documentation set and will confuse users configuring SoftSerial-based telemetry/logging.

    Issue description

    After this PR, docs/Telemetry.md no longer mentions a 19200 SoftSerial limit for iBus, but docs/Serial.md and docs/Blackbox.md still claim SoftSerial is limited to 19200 baud. This is conflicting documentation.

    Issue Context

    The PR’s stated goal is to fix misleading SoftSerial-limit docs; this should be applied consistently across the documentation set.

    Fix Focus Areas

    • docs/Telemetry.md[231-237]
    • docs/Serial.md[46-48]
    • docs/Blackbox.md[68-75]

    Suggestion 4: [maintainability] Softserial limit docs conflict
    Softserial limit docs conflict docs/Telemetry.md removes the “softserial limited to 19200” guidance, but other docs still claim SoftSerial is capped at 19200, creating contradictory configuration guidance. The SoftSerial implementation appears to accept an arbitrary baud (no explicit 19200 clamp), so the remaining “19200 limit” statements are likely misleading or at least need qualification as a practical recommendation rather than a hard limit.

    Issue description

    docs/Telemetry.md no longer states SoftSerial is limited to 19200 baud, but docs/Serial.md and docs/Blackbox.md still state (as a hard limit) that SoftSerial is limited to 19200. This creates contradictory documentation after this PR.

    Issue Context

    The SoftSerial code path takes a baud parameter and configures timers from it without a clear 19200 cap, so documentation that presents 19200 as a strict limit is inconsistent with the current implementation.

    Fix Focus Areas

    • docs/Telemetry.md[205-236]
    • docs/Serial.md[36-49]
    • docs/Blackbox.md[68-76]
    • src/main/drivers/serial_softserial.c[170-189]
    • src/main/drivers/serial_softserial.c[204-345]

    Suggested change

    Update the other docs to match the new reality (e.g., remove the “limited to 19200” hard-limit phrasing) or rephrase consistently as a practical/recommended limit if that’s the intended guidance (e.g., “SoftSerial may be unreliable at high baud rates; prefer hardware UART for >19200 / high bandwidth”).


    Suggestion 5: [correctness] MAVLink not transmit-only
    MAVLink not transmit-only docs/Telemetry.md states MAVLink in INAV is “transmit-only”, but the current MAVLink telemetry implementation reads and processes incoming MAVLink messages (missions/params/RC override/etc). This misleads users about MAVLink capabilities and is in-scope because the PR edited this sentence but kept the incorrect claim.

    Issue description

    docs/Telemetry.md says MAVLink is transmit-only, but the implementation processes inbound MAVLink messages. This is incorrect documentation and can lead users to wrong configuration/expectations.

    Issue Context

    The telemetry MAVLink code contains an explicit receive loop and dispatches received message IDs.

    Fix Focus Areas

    • docs/Telemetry.md[205-210]
    • src/main/telemetry/mavlink.c[1388-1433]
    • src/main/telemetry/mavlink.c[1444-1456]

    Suggestion 6: [maintainability] Softserial limit docs conflict
    Softserial limit docs conflict docs/Telemetry.md removes the “softserial limited to 19200” guidance, but other docs still claim SoftSerial is capped at 19200, creating contradictory configuration guidance. The SoftSerial implementation appears to accept an arbitrary baud (no explicit 19200 clamp), so the remaining “19200 limit” statements are likely misleading or at least need qualification as a practical recommendation rather than a hard limit.

    Issue description

    docs/Telemetry.md no longer states SoftSerial is limited to 19200 baud, but docs/Serial.md and docs/Blackbox.md still state (as a hard limit) that SoftSerial is limited to 19200. This creates contradictory documentation after this PR.

    Issue Context

    The SoftSerial code path takes a baud parameter and configures timers from it without a clear 19200 cap, so documentation that presents 19200 as a strict limit is inconsistent with the current implementation.

    Fix Focus Areas

    • docs/Telemetry.md[205-236]
    • docs/Serial.md[36-49]
    • docs/Blackbox.md[68-76]
    • src/main/drivers/serial_softserial.c[170-189]
    • src/main/drivers/serial_softserial.c[204-345]

    Suggested change

    Update the other docs to match the new reality (e.g., remove the “limited to 19200” hard-limit phrasing) or rephrase consistently as a practical/recommended limit if that’s the intended guidance (e.g., “SoftSerial may be unreliable at high baud rates; prefer hardware UART for >19200 / high bandwidth”).


    Suggestion 7: [maintainability] SoftSerial baud docs conflict
    SoftSerial baud docs conflict This PR removes the “softserial is limit to 19200” note from the iBus telemetry section, but other docs still state SoftSerial is limited to 19200 baud. This leaves contradictory guidance across the documentation set and will confuse users configuring SoftSerial-based telemetry/logging.

    Issue description

    After this PR, docs/Telemetry.md no longer mentions a 19200 SoftSerial limit for iBus, but docs/Serial.md and docs/Blackbox.md still claim SoftSerial is limited to 19200 baud. This is conflicting documentation.

    Issue Context

    The PR’s stated goal is to fix misleading SoftSerial-limit docs; this should be applied consistently across the documentation set.

    Fix Focus Areas

    • docs/Telemetry.md[231-237]
    • docs/Serial.md[46-48]
    • docs/Blackbox.md[68-75]

    Suggestion 8: [maintainability] Softserial limit docs conflict
    Softserial limit docs conflict docs/Telemetry.md removes the “softserial limited to 19200” guidance, but other docs still claim SoftSerial is capped at 19200, creating contradictory configuration guidance. The SoftSerial implementation appears to accept an arbitrary baud (no explicit 19200 clamp), so the remaining “19200 limit” statements are likely misleading or at least need qualification as a practical recommendation rather than a hard limit.

    Issue description

    docs/Telemetry.md no longer states SoftSerial is limited to 19200 baud, but docs/Serial.md and docs/Blackbox.md still state (as a hard limit) that SoftSerial is limited to 19200. This creates contradictory documentation after this PR.

    Issue Context

    The SoftSerial code path takes a baud parameter and configures timers from it without a clear 19200 cap, so documentation that presents 19200 as a strict limit is inconsistent with the current implementation.

    Fix Focus Areas

    • docs/Telemetry.md[205-236]
    • docs/Serial.md[36-49]
    • docs/Blackbox.md[68-76]
    • src/main/drivers/serial_softserial.c[170-189]
    • src/main/drivers/serial_softserial.c[204-345]

    Suggested change

    Update the other docs to match the new reality (e.g., remove the “limited to 19200” hard-limit phrasing) or rephrase consistently as a practical/recommended limit if that’s the intended guidance (e.g., “SoftSerial may be unreliable at high baud rates; prefer hardware UART for >19200 / high bandwidth”).


    Suggestion 9: [correctness] LIMIT rate can rebound
    LIMIT rate can rebound The docs say `fw_autotune_rate_adjustment=LIMIT` "only ever lowers" rates, but the implementation can also increase a previously-lowered rate again (while still capping it at the initial rate). Users may therefore still observe `roll_rate`/`pitch_rate`/`yaw_rate` increases during a LIMIT autotune flight, contradicting the doc.

    Issue description

    docs/Autotune - fixedwing.md currently states that fw_autotune_rate_adjustment=LIMIT "only ever lowers" the rates. In the code, rate adjustment can step up or down by 10 dps based on measured capability; in LIMIT mode it is merely capped at the starting rate (and can rise back toward that cap).

    Issue Context

    This is documentation-only, but it can still mislead users who choose LIMIT expecting a strictly one-way decrease.

    Fix Focus Areas

    • docs/Autotune - fixedwing.md[27-27]

    Suggested wording example:

    • Replace "LIMIT only ever lowers them" with something like "LIMIT will not increase rates above the starting values (but may raise a previously-lowered rate back up to that cap)."

    Suggestion 10:

    Harmonize operand semantics and docs

    Mirror the sign and invalid-return conventions with the new relative wind field in docs and keep operand meanings consistent; update docs and ensure both operands follow the same invalid sentinel and frame definitions.

    src/main/programming/logic_condition.c [763-785]

    -case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg
    +case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg, 0..359, -1 invalid
     #ifdef USE_WIND_ESTIMATOR
         {
             if (isEstimatedWindSpeedValid()) {
    -            uint16_t windAngle;
    -            getEstimatedHorizontalWindSpeed(&windAngle);
    -            int32_t windHeading = windAngle + 18000; // Correct heading to display correctly.
    -    
    -            windHeading = (CENTIDEGREES_TO_DEGREES((int)windHeading));
    -            while (windHeading < 0) {
    -                windHeading += 360;
    -            }
    -            while (windHeading >= 360) {
    -                windHeading -= 360;
    -            }
    -            return windHeading;
    -        } else
    +            uint16_t windAngleCd;
    +            getEstimatedHorizontalWindSpeed(&windAngleCd);
    +            // Convert wind vector angle (towards) to heading-from (meteorological) in degrees [0,360)
    +            int32_t headingDeg = CENTIDEGREES_TO_DEGREES((int)(windAngleCd + 18000));
    +            while (headingDeg < 0) headingDeg += 360;
    +            while (headingDeg >= 360) headingDeg -= 360;
    +            return headingDeg;
    +        } else {
                 return -1;
    +        }
         }
     #else
    -        return -1;
    +    return -1;
     #endif
    -        break;
    +    break;

    Suggestion 11:
  • [learned best practice] Make the replacement guidance exact per command (get vs set) to avoid ambiguity and divergence with docs/clients; reference only the corresponding replacement for each define.
    Suggestion 12:
  • [general] Clarify the troubleshooting guide for "Surface mode not working" by distinguishing between `nav_max_terrain_follow_alt` (max operational altitude) and `inav_max_surface_altitude` (sensor's reliable range).
    Suggestion 13:
  • [learned best practice] Remove brittle line-number references and add commands to programmatically verify/update versions to prevent drift.

  • [Auto-generated best practices - 2026-09-03]

  • ⚠️ **GitHub.com Fallback** ⚠️