Skip to content

Updates to wind in the IPF#11109

Merged
MrD-RC merged 5 commits into
masterfrom
MrD_Updates-to-wind-in-the-IPF
Nov 11, 2025
Merged

Updates to wind in the IPF#11109
MrD-RC merged 5 commits into
masterfrom
MrD_Updates-to-wind-in-the-IPF

Conversation

@MrD-RC

@MrD-RC MrD-RC commented Nov 9, 2025

Copy link
Copy Markdown
Member

User description

  • Correct the wind heading value. It was 180 degrees off and another 22 degrees clockwise.
  • Added Relative wind offset which gives the relative difference in degrees between the wind heading and aircraft heading. 0 degrees is flying directly in to the headwind. -180 to 1 degree(s) signifies a counter-clockwise offset.
image For this test I had a 13.5knots (approx. 25km/h) wind speed at 550ft AMSL with a 225° heading (south westerly). The altitude reads 0 due to many reboots of the FC during the flight. After each reboot, I loitered until the wind speed was estimated around 25km/h. I realise that the wind direction arrow from my IPF is 180 degrees out.




Requires Configurator iNavFlight/inav-configurator#2426


PR Type

Enhancement, Bug fix


Description

  • Corrected wind heading calculation by adding 18000 centidegrees offset

  • Removed incorrect 22-degree clockwise adjustment from wind heading

  • Added new Relative Wind Offset operand for aircraft-to-wind heading comparison

  • Updated documentation with new operand definition and behavior


Diagram Walkthrough

flowchart LR
  A["Wind Heading Calculation"] -->|"Add 18000 centidegrees"| B["Corrected Wind Heading"]
  B -->|"Remove 22° adjustment"| C["Accurate Wind Direction"]
  D["Aircraft Heading"] -->|"Compare with Wind"| E["Relative Wind Offset"]
  C -->|"0° = headwind"| E
  E -->|"Negative = left offset"| F["Navigation Logic"]
Loading

File Walkthrough

Relevant files
Bug fix, enhancement
logic_condition.c
Wind heading fix and relative wind offset implementation 

src/main/programming/logic_condition.c

  • Fixed wind heading calculation by adding 18000 centidegrees offset
    instead of using incorrect 22-degree adjustment
  • Renamed variable from windAngle to windHeading for clarity
  • Implemented new LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET
    case with relative wind offset calculation
  • Relative wind offset compares aircraft heading to wind heading with
    proper angle normalization
+41/-8   
Enhancement
logic_condition.h
Add relative wind offset operand enum                                       

src/main/programming/logic_condition.h

  • Added new enum value
    LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET with operand ID 49
  • Maintains sequential operand numbering for logic condition framework
+1/-0     
Documentation
Programming Framework.md
Document relative wind offset operand                                       

docs/Programming Framework.md

  • Added documentation for operand 49 (Relative Wind Offset) with
    detailed description
  • Clarified behavior: 0° indicates flying directly into headwind,
    negative values indicate left offset
  • Documented fallback behavior when wind estimator is unavailable
+1/-0     

- Correct the wind heading value. It was 180 degrees off and another 22 degrees clockwise.
- Added `Relative wind offset` which gives the relative difference in degrees between the wind heading and aircraft heading. 0 degrees is flying directly in to the headwind. -180 to 1 degree(s) signifies a counter-clockwise offset.
@qodo-code-review

qodo-code-review Bot commented Nov 9, 2025

Copy link
Copy Markdown
Contributor

PR Compliance Guide 🔍

(Compliance updated until commit 0b7d9e4)

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No auditing: The newly added wind-related operands modify runtime behavior but introduce no logging of
critical actions or outcomes, and it is unclear whether such actions require auditability
in this context.

Referred Code
        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg 0 - 360; -1 if not valid
#ifdef USE_WIND_ESTIMATOR
        {
            if (isEstimatedWindSpeedValid()) {
                uint16_t windAngle;
                getEstimatedHorizontalWindSpeed(&windAngle);
                int32_t windHeading = (int32_t)windAngle + 18000; // Correct heading to display correctly.

                while (windHeading < 0) windHeading += 36000;
                while (windHeading >= 36000) windHeading -= 36000;

                return (int32_t)CENTIDEGREES_TO_DEGREES(windHeading);
            } else
                return -1;
        }
#else
        return -1;
#endif
        break;

        case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg -180 to 180; 0 if not valid


 ... (clipped 22 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit 4db578e
Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logs: New logic computing wind heading and relative wind offset performs critical flight-related
calculations without any added audit logging, but this subsystem may not be expected to
log at this layer.

Referred Code
        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg
#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
                return -1;
        }
#else
            return -1;


 ... (clipped 33 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Invalid wind case: When wind estimation is invalid, LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET
returns 0 which is indistinguishable from a valid “headwind” reading, potentially masking
an error; prior pattern returns -1 for invalid values.

Referred Code
                return relativeWindHeading;
            } else
                return 0;
        }
#else
            return 0;
#endif

Learn more about managing compliance generic rules or creating your own custom rules

@qodo-code-review

qodo-code-review Bot commented Nov 9, 2025

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Remove leftover debugging code
Suggestion Impact:The commit removed the gvSet(3, DECIDEGREES_TO_DEGREES(attitude.values.yaw)); call from the RELATIVE_WIND_OFFSET case, eliminating the side effect as suggested.

code diff:

-                gvSet(3, DECIDEGREES_TO_DEGREES(attitude.values.yaw));
-                int32_t relativeWindHeading = windAngle + 18000 - DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
+                int32_t relativeWindHeading = (int32_t)windAngle + 18000 - DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
         

Remove the call to gvSet from logicConditionGetFlightOperandValue to eliminate
unintended side effects, as it appears to be leftover debugging code.

src/main/programming/logic_condition.c [793]

-gvSet(3, DECIDEGREES_TO_DEGREES(attitude.values.yaw));
 
+

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that a 'getter' function should not have side effects, and the gvSet call is likely leftover debugging code that could cause unpredictable behavior.

Low
Learned
best practice
Harmonize operand semantics and docs
Suggestion Impact:The commit updated LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION to document the valid range and invalid sentinel, corrected angle normalization using centidegrees before converting to degrees, and adjusted returns accordingly. It also harmonized relative wind offset normalization and return semantics.

code diff:

-        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg
+        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg 0 - 360; -1 if not valid
 #ifdef USE_WIND_ESTIMATOR
         {
             if (isEstimatedWindSpeedValid()) {
                 uint16_t windAngle;
                 getEstimatedHorizontalWindSpeed(&windAngle);
-                int32_t windHeading = windAngle + 18000; // Correct heading to display correctly.
+                int32_t windHeading = (int32_t)windHeading + 18000; // Correct heading to display correctly.
         
-                windHeading = (CENTIDEGREES_TO_DEGREES((int)windHeading));
-                while (windHeading < 0) {
-                    windHeading += 360;
-                }
-                while (windHeading >= 360) {
-                    windHeading -= 360;
-                }
-                return windHeading;
+                while (windHeading < 0) windHeading += 36000;
+                while (windHeading >= 36000) windHeading -= 36000;
+                
+                return (int32_t)CENTIDEGREES_TO_DEGREES(windHeading);
             } else
                 return -1;
         }
 #else
-            return -1;
-#endif
-            break;
-
-        case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg
+        return -1;
+#endif
+        break;
+
+        case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg -180 to 180; 0 if not valid
 #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);
+                int32_t relativeWindHeading = (int32_t)windAngle + 18000 - DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
         
-                relativeWindHeading = (CENTIDEGREES_TO_DEGREES((int)relativeWindHeading));
-                while (relativeWindHeading < 0) {
-                    relativeWindHeading += 360;
-                }
-                while (relativeWindHeading >= 360) {
-                    relativeWindHeading -= 360;
-                }
+                while (relativeWindHeading < 0) relativeWindHeading += 36000;
+                while (relativeWindHeading >= 36000) relativeWindHeading -= 36000;
                 
                 relativeWindHeading = -relativeWindHeading;
-                if (relativeWindHeading <= -180) {
-                    relativeWindHeading = 180 + (relativeWindHeading + 180);
-                }
-
-                return relativeWindHeading;
+                if (relativeWindHeading <= -18000)
+                    relativeWindHeading = 18000 + (relativeWindHeading + 18000);
+
+                return (int32_t)CENTIDEGREES_TO_DEGREES(relativeWindHeading);
             } else
                 return 0;
         }
 #else
-            return 0;
-#endif
-            break;        
+        return 0;
+#endif
+        break;        

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;

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Align mode/enum priorities and documented fields across modules and docs to keep a single source of truth.

Low
Normalize angles in one unit
Suggestion Impact:The commit changed both wind direction and relative wind offset calculations to normalize in centidegrees (0–36000 / ±18000) and convert to degrees only at return, removing intermediate degree conversions and loops. It also fixed types/casts accordingly.

code diff:

-        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg
+        case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg 0 - 360; -1 if not valid
 #ifdef USE_WIND_ESTIMATOR
         {
             if (isEstimatedWindSpeedValid()) {
                 uint16_t windAngle;
                 getEstimatedHorizontalWindSpeed(&windAngle);
-                int32_t windHeading = windAngle + 18000; // Correct heading to display correctly.
+                int32_t windHeading = (int32_t)windHeading + 18000; // Correct heading to display correctly.
         
-                windHeading = (CENTIDEGREES_TO_DEGREES((int)windHeading));
-                while (windHeading < 0) {
-                    windHeading += 360;
-                }
-                while (windHeading >= 360) {
-                    windHeading -= 360;
-                }
-                return windHeading;
+                while (windHeading < 0) windHeading += 36000;
+                while (windHeading >= 36000) windHeading -= 36000;
+                
+                return (int32_t)CENTIDEGREES_TO_DEGREES(windHeading);
             } else
                 return -1;
         }
 #else
-            return -1;
-#endif
-            break;
-
-        case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg
+        return -1;
+#endif
+        break;
+
+        case LOGIC_CONDITION_OPERAND_FLIGHT_RELATIVE_WIND_OFFSET: // deg -180 to 180; 0 if not valid
 #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);
+                int32_t relativeWindHeading = (int32_t)windAngle + 18000 - DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw);
         
-                relativeWindHeading = (CENTIDEGREES_TO_DEGREES((int)relativeWindHeading));
-                while (relativeWindHeading < 0) {
-                    relativeWindHeading += 360;
-                }
-                while (relativeWindHeading >= 360) {
-                    relativeWindHeading -= 360;
-                }
+                while (relativeWindHeading < 0) relativeWindHeading += 36000;
+                while (relativeWindHeading >= 36000) relativeWindHeading -= 36000;
                 
                 relativeWindHeading = -relativeWindHeading;
-                if (relativeWindHeading <= -180) {
-                    relativeWindHeading = 180 + (relativeWindHeading + 180);
-                }
-
-                return relativeWindHeading;
+                if (relativeWindHeading <= -18000)
+                    relativeWindHeading = 18000 + (relativeWindHeading + 18000);
+
+                return (int32_t)CENTIDEGREES_TO_DEGREES(relativeWindHeading);
             } else
                 return 0;
         }
 #else
-            return 0;
-#endif
-            break;        
+        return 0;
+#endif
+        break;        

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 processed]

Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Use correct operators and types; avoid unnecessary casts and ensure angle normalization matches units without mixing decidegrees/centidegrees.

Low
  • Update

- Removed debug
- Streamlined code
Fixed stupid error that my compiler didn't catch!
@MrD-RC

MrD-RC commented Nov 9, 2025

Copy link
Copy Markdown
Member Author

Seems like there's an issue with the SITL generation. Have any changes been made recently to SITL?

@stronnag

stronnag commented Nov 9, 2025

Copy link
Copy Markdown
Collaborator

Seems like there's an issue with the SITL generation. Have any changes been made recently to SITL?

As the SITL builds for Linux and MacOS, this seems to be an issue with GH's Windows/Cygwin environment.

@MrD-RC
MrD-RC merged commit 2a0a882 into master Nov 11, 2025
22 checks passed
@MrD-RC
MrD-RC deleted the MrD_Updates-to-wind-in-the-IPF branch November 11, 2025 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants