Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/pr-branch-suggestion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: PR Branch Suggestion

on:
pull_request_target:
types: [opened]
branches:
- master

jobs:
suggest-branch:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Suggest maintenance branch
uses: actions/github-script@v7
with:
script: |
const comment = `### 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.*`;

try {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
} catch (err) {
core.setFailed(`Failed to post suggestion comment: ${err}`);
}
3 changes: 3 additions & 0 deletions docs/Programming Framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ for complete documentation on using JavaScript to program your flight controller
| 54 | Mag calibration | Trigger a magnetometer calibration. |
| 55 | Set Gimbal Sensitivity | Scales `Operand A` from [`-16` : `15`]
| 56 | Override Minimum Ground Speed | When active, sets the minimum ground speed to the value specified in `Operand A` [m/s]. Minimum allowed value is set in `nav_min_ground_speed`. Maximum value is `150` |
| 57 | Trigonometry: ACos | Computes ACOS of (`Operand A` / `Operand B`) using the fast approximation. If `Operand B` is `0`, `1000` is used. Input is clamped to [-1, 1] and the result is returned in degrees. |
| 58 | Trigonometry: ASin | Computes ASIN of (`Operand A` / `Operand B`) using the fast approximation. If `Operand B` is `0`, `1000` is used. Input is clamped to [-1, 1] and the result is returned in degrees. |
| 59 | Trigonometry: ATan2 | Computes ATAN2 using `Operand A` as Y and `Operand B` as X with the fast approximation. Returns the angle in degrees. |

### Operands

Expand Down
4 changes: 2 additions & 2 deletions docs/javascript_programming/OPERATIONS_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ All INAV logic condition operations supported by the firmware are now fully impl
✅ **Arithmetic**: ADD, SUB, MUL, DIV, MODULUS (via +, -, *, /, %)
✅ **Comparisons**: EQUAL, GREATER_THAN, LOWER_THAN, APPROX_EQUAL (via ===, >, <, approxEqual())
✅ **Logical**: AND, OR, NOT, XOR, NAND, NOR (via &&, ||, !, xor(), nand(), nor())
✅ **Math**: MIN, MAX, SIN, COS, TAN, ABS (via Math.min/max/sin/cos/tan/abs)
✅ **Math**: MIN, MAX, SIN, COS, TAN, ACOS, ASIN, ATAN2, ABS (via Math.min/max/sin/cos/tan/acos/asin/atan2/abs)
✅ **Scaling**: MAP_INPUT, MAP_OUTPUT (via mapInput(), mapOutput())
✅ **Flow control**: STICKY, EDGE, DELAY, TIMER, DELTA (via on.* and helper functions)
✅ **Variables**: GVAR_SET, GVAR_INC, GVAR_DEC (via assignments, ++, --)
Expand Down Expand Up @@ -98,7 +98,7 @@ if (rc[0].low && rc[1].mid && rc[2].high) {

- RC channels: `rc[0]` through `rc[17]` (18 channels)
- RC channel properties: `.value` (1000-2000us), `.low` (<1333us), `.mid` (1333-1666us), `.high` (>1666us)
- All trig functions (sin/cos/tan) take degrees, not radians
- Trig functions: sin/cos/tan take degrees; acos/asin use ratios (-1..1) and atan2 returns degrees from y/x inputs
- MODULUS operator bug fixed: `%` now correctly generates MODULUS operation
- MAP_INPUT normalizes to [0:1000], MAP_OUTPUT scales from [0:1000]

Expand Down
2 changes: 1 addition & 1 deletion docs/javascript_programming/api_definitions_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Waypoint mission data:

Math functions:
- **Basic**: min, max, abs
- **Trig**: sin, cos, tan (degrees)
- **Trig**: sin, cos, tan (degrees), acos/asin (ratio inputs), atan2 (y, x -> degrees)
- **Mapping**: mapInput, mapOutput
- **Arithmetic**: add, sub, mul, div, mod (operators)

Expand Down
2 changes: 1 addition & 1 deletion docs/javascript_programming/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Comprehensive guide to all INAV logic condition operations:
- ✅ Arithmetic: `+`, `-`, `*`, `/`, `%`
- ✅ Comparisons: `===`, `>`, `<`, `approxEqual()`
- ✅ Logical: `&&`, `||`, `!`, `xor()`, `nand()`, `nor()`
- ✅ Math: `Math.min()`, `Math.max()`, `Math.sin()`, `Math.cos()`, `Math.tan()`, `Math.abs()`
- ✅ Math: `Math.min()`, `Math.max()`, `Math.sin()`, `Math.cos()`, `Math.tan()`, `Math.acos()`, `Math.asin()`, `Math.atan2()`, `Math.abs()`
- ✅ Scaling: `mapInput()`, `mapOutput()`
- ✅ Flow control: `edge()`, `sticky()`, `delay()`, `timer()`, `whenChanged()`
- ✅ Variables: `gvar[0-7]`, `let`, `var`
Expand Down
14 changes: 14 additions & 0 deletions src/main/programming/logic_condition.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,20 @@ static int logicConditionCompute(
return tan_approx(DEGREES_TO_RADIANS(operandA)) * temporaryValue;
break;

case LOGIC_CONDITION_ACOS:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(acos_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f)));
break;

case LOGIC_CONDITION_ASIN:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(asin_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f)));
break;
Comment on lines +412 to +415
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Correct the LOGIC_CONDITION_ASIN implementation, which currently computes ACOS instead of ASIN, by applying the acos_to_asin_approx macro to the result. [possible issue, importance: 9]

Suggested change
case LOGIC_CONDITION_ASIN:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(asin_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f)));
break;
case LOGIC_CONDITION_ASIN:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(acos_to_asin_approx(acos_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f))));
break;

Comment on lines +407 to +415
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Use a float denominator (with a safe default) and compute the clamped ratio once to avoid repeated casts and make the boundary behavior explicit and consistent. [Learned best practice, importance: 5]

Suggested change
case LOGIC_CONDITION_ACOS:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(acos_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f)));
break;
case LOGIC_CONDITION_ASIN:
temporaryValue = (operandB == 0) ? 1000 : operandB;
return RADIANS_TO_DEGREES(asin_approx(constrainf((float)operandA / (float)temporaryValue, -1.0f, 1.0f)));
break;
case LOGIC_CONDITION_ACOS: {
const float denom = (operandB == 0) ? 1000.0f : (float)operandB;
const float ratio = constrainf((float)operandA / denom, -1.0f, 1.0f);
return RADIANS_TO_DEGREES(acos_approx(ratio));
} break;
case LOGIC_CONDITION_ASIN: {
const float denom = (operandB == 0) ? 1000.0f : (float)operandB;
const float ratio = constrainf((float)operandA / denom, -1.0f, 1.0f);
return RADIANS_TO_DEGREES(asin_approx(ratio));
} break;


case LOGIC_CONDITION_ATAN2:
return RADIANS_TO_DEGREES(atan2_approx((float)operandA, (float)operandB));
break;
Comment on lines +417 to +419
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Normalize the returned degrees to a defined range (e.g., (-180, 180]) and add an explicit fallback for the (0,0) input so consumers don't depend on undefined/implementation-specific angle conventions. [Learned best practice, importance: 6]

Suggested change
case LOGIC_CONDITION_ATAN2:
return RADIANS_TO_DEGREES(atan2_approx((float)operandA, (float)operandB));
break;
case LOGIC_CONDITION_ATAN2: {
if (operandA == 0 && operandB == 0) {
return 0;
}
float deg = RADIANS_TO_DEGREES(atan2_approx((float)operandA, (float)operandB));
while (deg <= -180.0f) deg += 360.0f;
while (deg > 180.0f) deg -= 360.0f;
return (int32_t)deg;
} break;


case LOGIC_CONDITION_MIN:
return (operandA < operandB) ? operandA : operandB;
break;
Expand Down
3 changes: 3 additions & 0 deletions src/main/programming/logic_condition.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ typedef enum {
LOGIC_CONDITION_RESET_MAG_CALIBRATION = 54,
LOGIC_CONDITION_SET_GIMBAL_SENSITIVITY = 55,
LOGIC_CONDITION_OVERRIDE_MIN_GROUND_SPEED = 56,
LOGIC_CONDITION_ACOS = 57,
LOGIC_CONDITION_ASIN = 58,
LOGIC_CONDITION_ATAN2 = 59,
LOGIC_CONDITION_LAST
} logicOperation_e;

Expand Down