.pr_agent_auto_best_practices - iNavFlight/inav GitHub Wiki
Pattern 1: Ensure payload and buffer sizes are validated before reading or writing to avoid out-of-bounds access in protocol handlers and I/O code.
Example code before:
switch (cmd) {
case CMD_X:
uint8_t id = sbufReadU8(src);
uint16_t val = sbufReadU16(src);
handler[id] = val;
break;
}
Example code after:
switch (cmd) {
case CMD_X:
if (dataSize < 3) return MSP_RESULT_ERROR;
uint8_t id;
if (!sbufReadU8Safe(&id, src)) return MSP_RESULT_ERROR;
uint16_t val = sbufReadU16(src);
if (id >= HANDLER_MAX) return MSP_RESULT_ERROR;
handler[id] = val;
break;
}
Relevant past accepted suggestions:
Suggestion 1:
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 2:
Avoid using a hardcoded layout index
Instead of hardcoding the OSD layout index to 0, consider using the currently active layout or extending the MSP command to allow specifying the layout index.
-osdLayoutsConfigMutable()->item_pos[0][item] = sbufReadU16(src) | (1 << 13);
+// Suggestion: use the current layout instead of a hardcoded one.
+// Note: `currentLayout` is a global variable defined in `osd.c` and might need to be made accessible here.
+extern uint8_t currentLayout;
+osdLayoutsConfigMutable()->item_pos[currentLayout][item] = sbufReadU16(src) | (1 << 13);Suggestion 3:
Handle yaw from MAVLink command
Handle the yaw parameter (param4) from the MAV_CMD_DO_REPOSITION command instead of ignoring it, by assigning it to wp.p1 to control the vehicle's heading.
src/main/telemetry/mavlink.c [1150-1153]
-wp.p1 = wp.p2 = wp.p3 = 0;
+if (!isnan(msg.param4) && msg.param4 >= 0.0f && msg.param4 < 360.0f) {
+ wp.p1 = msg.param4;
+} else {
+ wp.p1 = -1; // Use -1 to indicate no heading change
+}
+wp.p2 = wp.p3 = 0;
wp.flag = 0;
setWaypoint(255, ℘);Suggestion 4:
Support additional MAVLink coordinate frames
Add support for MAV_FRAME_GLOBAL_RELATIVE_ALT and MAV_FRAME_GLOBAL_TERRAIN_ALT coordinate frames to improve compatibility with various Ground Control Stations.
src/main/telemetry/mavlink.c [1131-1142]
-if (msg.frame != MAV_FRAME_GLOBAL) {
+if (msg.frame != MAV_FRAME_GLOBAL && msg.frame != MAV_FRAME_GLOBAL_RELATIVE_ALT && msg.frame != MAV_FRAME_GLOBAL_TERRAIN_ALT) {
mavlink_msg_command_ack_pack(mavSystemId, mavComponentId, &mavSendMsg,
msg.command,
MAV_RESULT_UNSUPPORTED,
0, // progress
0, // result_param2
mavRecvMsg.sysid,
mavRecvMsg.compid);
mavlinkSendMessage();
return true;
}Pattern 2: Normalize angles and maintain consistent units through calculations, converting once at boundaries to avoid wrap and precision errors.
Example code before:
int deg = CENTIDEGREES_TO_DEGREES(angleCd + 18000);
while (deg < 0) deg += 360;
while (deg >= 360) deg -= 360;
rel = -deg;
if (rel <= -180) rel = 180 + (rel + 180);
Example code after:
int32_t angCd = angleCd + 18000;
while (angCd < 0) angCd += 36000;
while (angCd >= 36000) angCd -= 36000;
int32_t relCd = angCd - yawDeci * 10;
while (relCd <= -18000) relCd += 36000;
while (relCd > 18000) relCd -= 36000;
int relDeg = CENTIDEGREES_TO_DEGREES(relCd);
Relevant past accepted suggestions:
Suggestion 1:
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 2:
Normalize angles in one unit
Keep all math in centidegrees until final conversion and normalize once; avoid redundant casts and flips that can introduce off-by-one errors.
src/main/programming/logic_condition.c [787-816]
-case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg
+case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg, signed [-180,180], 0 = headwind, left negative
#ifdef USE_WIND_ESTIMATOR
{
if (isEstimatedWindSpeedValid()) {
- uint16_t windAngle;
- getEstimatedHorizontalWindSpeed(&windAngle);
- gvSet(3, DECIDEGREES_TO_DEGREES(attitude.values.yaw));
- int32_t relativeWindHeading = windAngle + 18000 - DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
-
- relativeWindHeading = (CENTIDEGREES_TO_DEGREES((int)relativeWindHeading));
- while (relativeWindHeading < 0) {
- relativeWindHeading += 360;
- }
- while (relativeWindHeading >= 360) {
- relativeWindHeading -= 360;
- }
-
- relativeWindHeading = -relativeWindHeading;
- if (relativeWindHeading <= -180) {
- relativeWindHeading = 180 + (relativeWindHeading + 180);
- }
-
- return relativeWindHeading;
- } else
+ uint16_t windAngleCd;
+ getEstimatedHorizontalWindSpeed(&windAngleCd); // [0,36000)
+ // Convert wind vector angle (towards) to heading-from
+ int32_t windHeadingCd = (int32_t)windAngleCd + 18000; // centidegrees
+ // Normalize to [0,36000)
+ while (windHeadingCd < 0) windHeadingCd += 36000;
+ while (windHeadingCd >= 36000) windHeadingCd -= 36000;
+ // Aircraft yaw is in decidegrees; convert to centidegrees
+ int32_t yawCd = DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
+ // Relative offset: wind heading relative to aircraft nose (right positive)
+ int32_t relCd = windHeadingCd - yawCd; // centidegrees
+ // Normalize to (-18000, 18000]
+ while (relCd <= -18000) relCd += 36000;
+ while (relCd > 18000) relCd -= 36000;
+ // Convert to degrees with sign convention: left negative
+ int32_t relDeg = CENTIDEGREES_TO_DEGREES(relCd);
+ return relDeg;
+ } else {
return 0;
+ }
}
#else
- return 0;
+ return 0;
#endif
- break;
+ break;Suggestion 3:
Fix incorrect manual yaw rate display
Correct the OSD element for Manual Yaw Rate (OSD_MANUAL_YAW_RATE) to use the manual yaw rate value and adjustment function instead of the stabilized ones.
case OSD_MANUAL_YAW_RATE:
- osdDisplayAdjustableDecimalValue(elemPosX, elemPosY, "MYR", 0, currentControlProfile->stabilized.rates[FD_YAW], 3, 0, ADJUSTMENT_YAW_RATE);
+ osdDisplayAdjustableDecimalValue(elemPosX, elemPosY, "MYR", 0, currentControlProfile->manual.rates[FD_YAW], 3, 0, ADJUSTMENT_MANUAL_YAW_RATE);
return true;Suggestion 4:
Avoid premature type casting to float
In osdGet3DSpeed, declare vert_speed and hor_speed as float to prevent loss of precision from premature casting before the Pythagorean calculation.
src/main/io/osd_common.c [202-207]
int16_t osdGet3DSpeed(void)
{
- int16_t vert_speed = getEstimatedActualVelocity(Z);
- int16_t hor_speed = gpsSol.groundSpeed;
+ float vert_speed = getEstimatedActualVelocity(Z);
+ float hor_speed = gpsSol.groundSpeed;
return (int16_t)calc_length_pythagorean_2D(hor_speed, vert_speed);
}Pattern 3: Guard against uninitialized or invalid state by providing defaults and safe fallbacks in control logic and APIs.
Example code before:
switch (item) {
case ITEM_A: idx = 0; break;
case ITEM_B: idx = 1; break;
}
use(buffer[idx]);
Example code after:
switch (item) {
case ITEM_A: idx = 0; break;
case ITEM_B: idx = 1; break;
default: return; // unknown item
}
// defaults
static uint8_t sysId = 1;
use(buffer[idx]);
Relevant past accepted suggestions:
Suggestion 1:
Prevent use of uninitialized variable
In osdEraseCustomItem, add a default case to the switch statement that returns from the function. This prevents unintended side effects when an item that is not a custom element is passed.
switch(item){
case 147:
customElementIndex = 0;
break;
case 148:
customElementIndex = 1;
break;
case 149:
customElementIndex = 2;
break;
case 154:
customElementIndex = 3;
break;
case 155:
customElementIndex = 4;
break;
case 156:
customElementIndex = 5;
break;
case 157:
customElementIndex = 6;
break;
case 158:
customElementIndex = 7;
break;
+ default:
+ return;
}
uint8_t len = customElementLength(customElementIndex);Suggestion 2:
Initialize static variable at declaration
Initialize the static variable mavSystemId to a default value of 1 at declaration. This prevents it from being zero-initialized to an invalid system ID, making the code more robust.
src/main/telemetry/mavlink.c [186-187]
// Set mavSystemId from telemetryConfig()->mavlink.sysid
-static uint8_t mavSystemId;
+static uint8_t mavSystemId = 1;Suggestion 3:
Handle yaw from MAVLink command
Handle the yaw parameter (param4) from the MAV_CMD_DO_REPOSITION command instead of ignoring it, by assigning it to wp.p1 to control the vehicle's heading.
src/main/telemetry/mavlink.c [1150-1153]
-wp.p1 = wp.p2 = wp.p3 = 0;
+if (!isnan(msg.param4) && msg.param4 >= 0.0f && msg.param4 < 360.0f) {
+ wp.p1 = msg.param4;
+} else {
+ wp.p1 = -1; // Use -1 to indicate no heading change
+}
+wp.p2 = wp.p3 = 0;
wp.flag = 0;
setWaypoint(255, ℘);Suggestion 4:
Prevent division by zero during normalization
Add a check to ensure vCoGlocal has a non-zero magnitude before normalization to prevent a potential division-by-zero error.
src/main/flight/imu.c [472-482]
-if (vectorNormSquared(&vHeadingEF) > 0.01f) {
+if (vectorNormSquared(&vHeadingEF) > 0.01f && vectorNormSquared(&vCoGlocal) > 0.01f) {
// Normalize to unit vector
vectorNormalize(&vHeadingEF, &vHeadingEF);
vectorNormalize(&vCoGlocal, &vCoGlocal);
// error is cross product between reference heading and estimated heading (calculated in EF)
vectorCrossProduct(&vCoGErr, &vCoGlocal, &vHeadingEF);
// Rotate error back into body frame
quaternionRotateVector(&vCoGErr, &vCoGErr, &orientation);
}Pattern 4: Align feature flags, enums, and configuration macros with actual hardware and code paths to avoid mismatches and dead code.
Example code before:
#define SENSORS_SET (SENSOR_ACC|SENSOR_MAG|SENSOR_BARO)
#define MAX_PWM_OUTPUT_PORTS 8
Example code after:
#define SENSORS_SET (SENSOR_ACC|SENSOR_BARO) // no mag on this target
#if defined(TARGET_9_SERVO)
#define MAX_PWM_OUTPUT_PORTS 9
#else
#define MAX_PWM_OUTPUT_PORTS 8
#endif
Relevant past accepted suggestions:
Suggestion 1:
Fix typo in preprocessor directive
Fix the typo NEXUS_9SERVOS to NEXUSX_9SERVOS in the preprocessor directive to ensure correct I2C configuration for that target variant.
src/main/target/NEXUSX/target.h [50-57]
-#if defined(NEXUSX) || defined(NEXUS_9SERVOS)
+#if defined(NEXUSX) || defined(NEXUSX_9SERVOS)
#define USE_I2C_DEVICE_2 // clashes with UART3
#define I2C2_SCL PB10
#define I2C2_SDA PB11
#define DEFAULT_I2C BUS_I2C2
#else
#define DEFAULT_I2C BUS_I2C3
#endifSuggestion 2:
Correct maximum PWM output ports
Conditionally define MAX_PWM_OUTPUT_PORTS to 9 for the NEXUSX_9SERVOS and NEXUSX_NOI2C variants to match their 9 defined servo outputs.
src/main/target/NEXUSX/target.h [161]
+#if defined(NEXUSX_9SERVOS) || defined(NEXUSX_NOI2C)
+#define MAX_PWM_OUTPUT_PORTS 9
+#else
#define MAX_PWM_OUTPUT_PORTS 8
+#endifSuggestion 3:
Remove unsupported magnetometer from sensors
Remove the unsupported SENSOR_MAG from the SENSORS_SET macro, as the target hardware does not include a magnetometer.
src/main/target/NEXUSX/target.h [129]
-#define SENSORS_SET (SENSOR_ACC|SENSOR_MAG|SENSOR_BARO)
+#define SENSORS_SET (SENSOR_ACC|SENSOR_BARO)Suggestion 4:
Update max enum values
Update CURRENT_SENSOR_MAX and VOLTAGE_SENSOR_MAX in their respective enums to CURRENT_SENSOR_SMARTPORT to correctly reflect the new maximum value.
src/main/sensors/battery_config_structs.h [28-45]
typedef enum {
CURRENT_SENSOR_NONE = 0,
CURRENT_SENSOR_ADC,
CURRENT_SENSOR_VIRTUAL,
CURRENT_SENSOR_FAKE,
CURRENT_SENSOR_ESC,
CURRENT_SENSOR_SMARTPORT,
- CURRENT_SENSOR_MAX = CURRENT_SENSOR_FAKE
+ CURRENT_SENSOR_MAX = CURRENT_SENSOR_SMARTPORT
} currentSensor_e;
typedef enum {
VOLTAGE_SENSOR_NONE = 0,
VOLTAGE_SENSOR_ADC,
VOLTAGE_SENSOR_ESC,
VOLTAGE_SENSOR_FAKE,
VOLTAGE_SENSOR_SMARTPORT,
- VOLTAGE_SENSOR_MAX = VOLTAGE_SENSOR_FAKE
+ VOLTAGE_SENSOR_MAX = VOLTAGE_SENSOR_SMARTPORT
} voltageSensor_e;Suggestion 5:
Pattern 5: Exclude generated or environment-specific build artifacts from version control and scripts; operate safely on files that actually exist.
Example code before:
rm -rf */
for f in *.hex; do mv "$f" "new_$f"; done
git add CMakeFiles/Makefile.cmake
Example code after:
# Only remove empty dirs safely
find . -mindepth 1 -maxdepth 1 -type d -empty -print -delete
# Rename if files exist
shopt -s nullglob
for f in *.hex; do mv "$f" "new_$f"; done
# .gitignore
CMakeFiles/
**/DependInfo.cmake
**/Makefile.cmake
Relevant past accepted suggestions:
Suggestion 1:
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.
-# 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 2:
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 3:
Suggestion 4:
Suggestion 5:
[Auto-generated best practices - 2025-12-10]