Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
779b21e
Refuse assigning SBUS output to a second serial port
Sep 10, 2026
cd44927
Hide RX downlink power OSD element when the link does not report it
Sep 10, 2026
5c4db1d
servos: keep speed limit carry over when switching back
Sep 10, 2026
fe5ea81
Clear stale receiver power when telemetry becomes unavailable
Raffi1202 Sep 11, 2026
bec7f37
docs: note that SBUS servo output takes only one port
Sep 13, 2026
bfcd366
Geozone: mark the nearest-safehome runtime zone as enabled
b14ckyy Sep 22, 2026
e4aeeb4
OSD: use atan2f for the telemetry tracker elevation
b14ckyy Sep 22, 2026
2674c9e
CLI/pitot: print floats as fixed point instead of %f
b14ckyy Sep 22, 2026
95911d5
Replace newlib strstr/strnstr with a small own search
b14ckyy Sep 22, 2026
214e199
Use INAV's trig approximations in the wind estimator and OSD tracker
b14ckyy Sep 22, 2026
81b3563
Drop qsort: sort battery profiles and geozones in place
b14ckyy Sep 22, 2026
88f3503
Navigation: order the FSM descriptor fields to avoid padding
b14ckyy Sep 22, 2026
dfcd7c7
Return true after blanking the RX downlink power element
Raffi1202 Sep 24, 2026
a46881e
gps: ask u-blox receivers for satellite details twice a second
MrScothh Sep 24, 2026
f0fbc98
gps: give the GPS port a 512 byte receive buffer
MrScothh Sep 24, 2026
3a0ced5
gps: shorter comments, the buffer's limit, and u-blox only
MrScothh Sep 25, 2026
5b310b4
Trim comments to the reasons the code doesn't show
MrScothh Sep 27, 2026
f6c16c8
gps: stub serialSetRxBuffer in the heartbeat test
MrScothh Sep 28, 2026
e24b2cb
serial: name the SBUS duplicate check as a getter, trim comment and docs
Raffi1202 Oct 2, 2026
0da3929
docs: state when the RX downlink power element is hidden
Raffi1202 Oct 2, 2026
b25e907
servos: shorten the carry over comment to one line
Raffi1202 Oct 4, 2026
ba39efa
F4 uart: mask received data to 8 bits, align with F7/H7
jlpoltrack Oct 5, 2026
fd12836
Merge maintenance-10.x into feature/uart-rx-buffer
MrScothh Oct 6, 2026
75f3455
pwm_mapping: a timer output on UART1's pins is skipped when UART1 has…
MrScothh Oct 8, 2026
f8af477
fix:修正fcSetArmState的解锁结果判断
00lllooolll00 Oct 12, 2026
3b3c9bc
ist8310: reset the chip at init and use single measurements
MrScothh Oct 14, 2026
58059b2
ist8310: drop samples outside the measuring range
MrScothh Oct 14, 2026
69be400
fw pid: an I-term that opposes a big stick input fades while the lock…
MrScothh Oct 25, 2026
793aa6e
fw pid: the I-term fade only above the lock's own stick threshold, an…
MrScothh Oct 25, 2026
1973ce0
Merge pull request #11912 from Raffi1202/fix/sbus-output-second-uart
sensei-hacker Oct 28, 2026
7c2ac35
Merge pull request #11917 from Raffi1202/fix/vtol-servo-speed-carryover
sensei-hacker Oct 28, 2026
78654d6
Merge pull request #12181 from MrScothh/fix/fw-iterm-lock-decay
sensei-hacker Nov 2, 2026
b006087
Merge pull request #12012 from b14ckyy/flash-opt
sensei-hacker Nov 2, 2026
d0c611e
Merge pull request #12131 from MrScothh/fix/pwm-uart1-conflict
sensei-hacker Nov 9, 2026
68bb04e
filter cut off fix
breadoven Nov 9, 2026
c1a5a8f
Update pitotmeter.c
breadoven Nov 9, 2026
ae6ace4
Merge pull request #12216 from breadoven/abo_pitot_filter_fix
breadoven Nov 10, 2026
92958dc
Merge pull request #12152 from MrScothh/fix/ist8310-single-measurement
sensei-hacker Nov 11, 2026
861564b
Fix typo in flash chip management section
sensei-hacker Nov 11, 2026
fca135c
Merge pull request #12229 from iNavFlight/sensei-hacker-patch-9
sensei-hacker Nov 11, 2026
2671b7c
Merge pull request #12146 from 00lllooolll00/fix/fc-arm-state-bool
sensei-hacker Nov 15, 2026
c9893bb
Merge pull request #12001 from MrScothh/feature/uart-rx-buffer
sensei-hacker Nov 15, 2026
318cdda
Merge pull request #11914 from Raffi1202/fix/crsf-downlink-tx-power
sensei-hacker Nov 15, 2026
42b609e
Merge pull request #12109 from jlpoltrack/sbus-f4-highframe-fix
sensei-hacker Nov 15, 2026
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
2 changes: 1 addition & 1 deletion docs/Cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Note that the usual caveat that one should not share a timer with both a motor a

## Flash chip management

For targets that have a flash data chip, typically used for blackbox logs, the following additional comamnds are provided.
For targets that have a flash data chip, typically used for blackbox logs, the following additional commands are provided.

| Command | Effect |
| ------- | ------ |
Expand Down
2 changes: 1 addition & 1 deletion docs/OSD.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Here are the OSD Elements provided by INAV.
| 157 | OSD_CUSTOM_ELEMENT_7 | 8.0.0 | |
| 158 | OSD_CUSTOM_ELEMENT_8 | 8.0.0 | |
| 159 | OSD_LQ_DOWNLINK | 8.0.0 | |
| 160 | OSD_RX_POWER_DOWNLINK | 8.0.0 | |
| 160 | OSD_RX_POWER_DOWNLINK | 8.0.0 | The element is hidden while the receiver transmit power is 0 mW, which is also the value when the RC link does not report it. MSP receivers that send `MSP2_COMMON_SET_MSP_RC_INFO` and mLRS as MAVLink receiver report it (mLRS at 0 dBm or less reads as 0 mW). INAV does not read it from CRSF links such as ExpressLRS. |
| 161 | OSD_RX_BAND | 8.0.0 | |
| 162 | OSD_RX_MODE | 8.0.0 | |
| 163 | OSD_COURSE_TO_FENCE | 8.0.0 | |
Expand Down
1 change: 1 addition & 0 deletions docs/Serial.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ e.g. after configuring a port for GPS enable the GPS feature.
* All telemetry systems except MSP will ignore any attempts to override the baudrate.
* MSP/CLI can be shared with EITHER Blackbox OR telemetry (LTM or MAVlink, not RX telemetry). In shared mode blackbox or telemetry will be output only when armed.
* Smartport telemetry cannot be shared with MSP.
* SBUS servo output (`SERVO_SERIAL`) can only be used on one port. The `serial` CLI command refuses to assign it to a second port. If a saved configuration already has it on several ports, only the first one in the `serial` list outputs SBUS.
* No other serial port sharing combinations are valid.
* You can use as many different telemetry systems as you like at the same time.
* You can only use each telemetry system once. e.g. FrSky telemetry cannot be used on two port, but LTN Telemetry and FrSky on two different ports is fine.
Expand Down
26 changes: 26 additions & 0 deletions src/main/common/string_light.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,29 @@ int sl_strncasecmp(const char * s1, const char * s2, int n)

return d;
}

const char * sl_strnstr(const char * haystack, const char * needle, int n)
{
if (*needle == '\0') {
return haystack;
}
for (; n > 0 && *haystack != '\0'; haystack++, n--) {
const char * h = haystack;
const char * nd = needle;
int left = n;
while (left > 0 && *h != '\0' && *h == *nd) {
h++;
nd++;
left--;
}
if (*nd == '\0') {
return haystack;
}
}
return NULL;
}

const char * sl_strstr(const char * haystack, const char * needle)
{
return sl_strnstr(haystack, needle, INT_MAX);
}
3 changes: 3 additions & 0 deletions src/main/common/string_light.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ void sl_toupperptr(char * c);

int sl_strcasecmp(const char * s1, const char * s2);
int sl_strncasecmp(const char * s1, const char * s2, int n);
// newlib's strstr/strnstr link the two-way search machinery (~2.8 KB); plain scans are enough here
const char * sl_strstr(const char * haystack, const char * needle);
const char * sl_strnstr(const char * haystack, const char * needle, int n);
53 changes: 25 additions & 28 deletions src/main/drivers/compass/compass_ist8310.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@
* This bit will be set to zero after POR routine
*/

#define IST8310_REG_DATA 0x03
#define IST8310_REG_STAT1 0x02
#define IST8310_DRDY_MASK 0x01
#define IST8310_REG_WHOAMI 0x00
Expand All @@ -96,64 +95,62 @@
#define IST8310_REG_PDCNTL 0x42

// Parameter
// ODR = Output Data Rate, we use single measure mode for getting more data.
#define IST8310_ODR_SINGLE 0x01
#define IST8310_ODR_10_HZ 0x03
#define IST8310_ODR_20_HZ 0x05
#define IST8310_ODR_50_HZ 0x07
#define IST8310_ODR_100_HZ 0x06

// Device ID (ist8310 -> 0x10)
#define IST8310_CHIP_ID 0x10
#define IST8310_AVG_16 0x24
#define IST8310_PULSE_DURATION_NORMAL 0xC0
#define IST8310_RANGE_XY 16000 // mG, 1600 uT
#define IST8310_RANGE_Z 25000 // mG, 2500 uT

#define IST8310_CNTRL2_RESET 0x01
#define IST8310_CNTRL2_DRPOL 0x04
#define IST8310_CNTRL2_DRENA 0x08

static bool ist8310Init(magDev_t * mag)
{
busWrite(mag->busDev, IST8310_REG_CNTRL1, IST8310_ODR_50_HZ);
delay(5);
// A reboot without a power cycle leaves the chip as it was, and it may never measure again
busWrite(mag->busDev, IST8310_REG_CNTRL2, IST8310_CNTRL2_RESET);
delay(50); // power-on reset routine: 50 ms max

busWrite(mag->busDev, IST8310_REG_AVERAGE, IST8310_AVG_16);
delay(5);

busWrite(mag->busDev, IST8310_REG_PDCNTL, IST8310_PULSE_DURATION_NORMAL);
delay(5);

// Single measurement is the only mode the datasheet defines besides stand-by
busWrite(mag->busDev, IST8310_REG_CNTRL1, IST8310_ODR_SINGLE);

return true;
}

static bool ist8310Read(magDev_t * mag)
{
uint8_t status = 0;

if (!busRead(mag->busDev, IST8310_REG_STAT1, &status)) {
// STAT1 and the data in one read: in single measurement mode the data only changes after a trigger
uint8_t buf[7];
if (!busReadBuf(mag->busDev, IST8310_REG_STAT1, buf, 7)) {
return false;
}

// The IST8310 updates its data registers once per output-data period. Only read
// them once DRDY is set; a read racing an update would mix old and new samples
// across axes, corrupting the mag vector and calibration. When no fresh sample
// is ready, return success so the caller keeps the previous values.
if (!(status & IST8310_DRDY_MASK)) {
return true;
}

uint8_t buf[6];
uint8_t LSB2FSV = 3; // 3mG - 14 bit

bool ack = busReadBuf(mag->busDev, IST8310_REG_DATA, buf, 6);
if (!ack) {
return false;
const int x = (int16_t)(buf[2] << 8 | buf[1]) * LSB2FSV;
const int y = (int16_t)(buf[4] << 8 | buf[3]) * LSB2FSV;
const int z = (int16_t)(buf[6] << 8 | buf[5]) * LSB2FSV;

// Past the measuring range a sample can only be corrupt, and one spoils a compass calibration
const bool inRange = ABS(x) <= IST8310_RANGE_XY && ABS(y) <= IST8310_RANGE_XY && ABS(z) <= IST8310_RANGE_Z;

if ((buf[0] & IST8310_DRDY_MASK) && inRange) {
// Invert Y axis to convert from left to right coordinate system
mag->magADCRaw[X] = x;
mag->magADCRaw[Y] = -y;
mag->magADCRaw[Z] = z;
}

// Invert Y axis to co convert from left to right coordinate system
mag->magADCRaw[X] = (int16_t)(buf[1] << 8 | buf[0]) * LSB2FSV;
mag->magADCRaw[Y] = -(int16_t)(buf[3] << 8 | buf[2]) * LSB2FSV;
mag->magADCRaw[Z] = (int16_t)(buf[5] << 8 | buf[4]) * LSB2FSV;
// Also restarts a measurement that never completed: the task is far slower than its 6 ms
busWrite(mag->busDev, IST8310_REG_CNTRL1, IST8310_ODR_SINGLE);

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/drivers/pitotmeter/pitotmeter_dlvr_l10d.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static void dlvr_calculate(pitotDev_t * pitot, float *pressure, float *temperatu

// result must fit inside the max pressure range
if ((dP_inchH2O > RANGE_INCH_H2O) || (dP_inchH2O < -RANGE_INCH_H2O)) {
LOG_DEBUG( PITOT,"DLVR: Out of range. pressure = %f", (double)(dP_inchH2O) );
LOG_DEBUG( PITOT,"DLVR: Out of range. pressure = %d/1000 inH2O", (int)(dP_inchH2O * 1000.0f) );
return;
}

Expand Down
9 changes: 9 additions & 0 deletions src/main/drivers/pwm_mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ static bool checkPwmTimerConflicts(const timerHardware_t *timHw)
{
serialPortPins_t uartPins;

#if defined(USE_UART1)
// MSP is UART1's default next to the VCP and has always given these pins up to the outputs
const serialPortConfig_t *uart1Config = serialFindPortConfiguration(SERIAL_PORT_USART1);
uartGetPortPins(UARTDEV_1, &uartPins);
if (uart1Config && (uart1Config->functionMask & ~FUNCTION_MSP) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
return true;
}
#endif

#if defined(USE_UART2)
uartGetPortPins(UARTDEV_2, &uartPins);
if (doesConfigurationUsePort(SERIAL_PORT_USART2) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
Expand Down
20 changes: 20 additions & 0 deletions src/main/drivers/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

#include "platform.h"

#include "build/atomic.h"

#include "drivers/nvic.h"

#include "serial.h"

void serialPrint(serialPort_t *instance, const char *str)
Expand Down Expand Up @@ -133,3 +137,19 @@ bool serialIsIdle(serialPort_t *instance)
else
return false;
}

// Swaps in a larger receive buffer; what the old one held is dropped
void serialSetRxBuffer(serialPort_t *instance, volatile uint8_t *buffer, uint32_t size)
{
if (size <= instance->rxBufferSize) {
return;
}

// The receive interrupt must never see the new buffer with the old size or indices
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
instance->rxBuffer = buffer;
instance->rxBufferSize = size;
instance->rxBufferHead = 0;
instance->rxBufferTail = 0;
}
}
1 change: 1 addition & 0 deletions src/main/drivers/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ void serialPrint(serialPort_t *instance, const char *str);
uint32_t serialGetBaudRate(serialPort_t *instance);
bool serialIsConnected(const serialPort_t *instance);
bool serialIsIdle(serialPort_t *instance);
void serialSetRxBuffer(serialPort_t *instance, volatile uint8_t *buffer, uint32_t size);

// A shim that adapts the bufWriter API to the serialWriteBuf() API.
void serialWriteBufShim(void *instance, const uint8_t *data, int count);
Expand Down
7 changes: 3 additions & 4 deletions src/main/drivers/serial_uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
#define UART_AF(uart, af) CONCAT3(GPIO_AF, af, _ ## uart)

// Since serial ports can be used for any function these buffer sizes should be equal
// The two largest things that need to be sent are: 1, MSP responses, 2, UBLOX SVINFO packet.
// The largest thing that needs to be sent is an MSP response.

// Size must be a power of two due to various optimizations which use 'and' instead of 'mod'
// Various serial routines return the buffer occupied size as uint8_t which would need to be extended in order to
// increase size further.
// Size must be a power of two due to various optimizations which use 'and' instead of 'mod'.
// The GPS port gets a larger receive buffer of its own, see gps.c.
#define UART1_RX_BUFFER_SIZE 256
#define UART1_TX_BUFFER_SIZE 256
#define UART2_RX_BUFFER_SIZE 256
Expand Down
2 changes: 1 addition & 1 deletion src/main/drivers/serial_uart_stm32f4xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ void uartIrqHandler(uartPort_t *s)
{
if (USART_GetITStatus(s->USARTx, USART_IT_RXNE) == SET) {
if (s->port.rxCallback) {
s->port.rxCallback(s->USARTx->DR, s->port.rxCallbackData);
s->port.rxCallback(s->USARTx->DR & 0xFF, s->port.rxCallbackData); // mask out parity bit
} else {
s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->DR;
s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
Expand Down
18 changes: 12 additions & 6 deletions src/main/fc/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,12 @@ static void cliSerial(char *cmdline)
return;
}

const uint32_t duplicatedFunctions = serialGetDuplicatedSinglePortFunctions(&portConfig);
if (duplicatedFunctions) {
cliPrintErrorLinef("Function %d is already assigned to another port", (int)duplicatedFunctions);
return;
}

memcpy(currentConfig, &portConfig, sizeof(portConfig));
}

Expand Down Expand Up @@ -1009,9 +1015,9 @@ static void cliSerialPassthrough(char *cmdline)
baud = fastA2I(tok);
break;
case 2:
if (strstr(tok, "rx") || strstr(tok, "RX"))
if (sl_strstr(tok, "rx") || sl_strstr(tok, "RX"))
mode |= MODE_RX;
if (strstr(tok, "tx") || strstr(tok, "TX"))
if (sl_strstr(tok, "tx") || sl_strstr(tok, "TX"))
mode |= MODE_TX;
break;
case 3:
Expand Down Expand Up @@ -4024,7 +4030,7 @@ static void cliSet(char *cmdline)
cliPrintVar(val, len); // when len is 1 (when * is passed as argument), it will print min/max values as well, for gui
cliPrintLinefeed();
}
} else if ((eqptr = strstr(cmdline, "=")) != NULL) {
} else if ((eqptr = strchr(cmdline, '=')) != NULL) {
// has equals

char *lastNonSpaceCharacter = eqptr;
Expand Down Expand Up @@ -4377,9 +4383,9 @@ static void cliStatus(char *cmdline)
}
cliPrintLinefeed();
cliPrintLinef(" SATS: %i", gpsSol.numSat);
cliPrintLinef(" HDOP: %f", (double)(gpsSol.hdop / (float)HDOP_SCALE));
cliPrintLinef(" EPH : %f m", (double)(gpsSol.eph / 100.0f));
cliPrintLinef(" EPV : %f m", (double)(gpsSol.epv / 100.0f));
cliPrintLinef(" HDOP: %d.%02d", gpsSol.hdop / HDOP_SCALE, gpsSol.hdop % HDOP_SCALE);
cliPrintLinef(" EPH : %d.%02d m", gpsSol.eph / 100, gpsSol.eph % 100);
cliPrintLinef(" EPV : %d.%02d m", gpsSol.epv / 100, gpsSol.epv % 100);
//cliPrintLinef(" GNSS Capabilities: %d", gpsUbloxCapLastUpdate());
cliPrintLinef(" GNSS Capabilities:");
cliPrintLine(" GNSS Provider active/default");
Expand Down
2 changes: 1 addition & 1 deletion src/main/fc/fc_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ bool fcSetArmState(bool arm)
disarm(DISARM_SWITCH);
}

return ARMING_FLAG(ARMED) == arm;
return (ARMING_FLAG(ARMED) != 0) == arm;
}

#define TELEMETRY_FUNCTION_MASK (FUNCTION_TELEMETRY_HOTT | FUNCTION_TELEMETRY_SMARTPORT | FUNCTION_TELEMETRY_LTM | FUNCTION_TELEMETRY_MAVLINK | FUNCTION_TELEMETRY_IBUS)
Expand Down
2 changes: 1 addition & 1 deletion src/main/fc/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void settingGetName(const setting_t *val, char *buf)
bool settingNameContains(const setting_t *val, char *buf, const char *cmdline)
{
settingGetName(val, buf);
return strstr(buf, cmdline) != NULL;
return sl_strstr(buf, cmdline) != NULL;
}

bool settingNameIsExactMatch(const setting_t *val, char *buf, const char *cmdline, uint8_t var_name_length)
Expand Down
23 changes: 21 additions & 2 deletions src/main/flight/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ static EXTENDED_FASTRAM int16_t headingHoldTarget;
static EXTENDED_FASTRAM pt1Filter_t headingHoldRateFilter;
static EXTENDED_FASTRAM pt1Filter_t fixedWingTpaFilter;

// Time constant of an I-term that opposes a big stick input while the lock holds it (see iTermLockApply)
#define FW_ITERM_LOCK_FADE_S 0.5f
// Rate target, as a fraction of the maximum rate, from which the lock counts a stick input as big
#define FW_ITERM_LOCK_BIG_STICK 0.2f

// Thrust PID Attenuation factor. 0.0f means fully attenuated, 1.0f no attenuation is applied
STATIC_FASTRAM bool pidGainsUpdateRequired= true;
FASTRAM int16_t axisPID[FLIGHT_DYNAMICS_INDEX_COUNT];
Expand Down Expand Up @@ -1063,7 +1068,7 @@ static void applyAutoTransitionTargetItermLock(autoTransitionTargetPidState_t *p
const float dampingFactor = attenuation(rateTarget, maxRate * targetPidProfile->fwItermLockRateLimit / 100.0f);
const bool errorThresholdReached = fabsf(rateError) > maxRate * targetPidProfile->fwItermLockEngageThreshold / 100.0f;

if (fabsf(rateTarget) > maxRate * 0.2f) {
if (fabsf(rateTarget) > maxRate * FW_ITERM_LOCK_BIG_STICK) {
pidState->attenuation.targetOverThresholdTimeMs = pidLoopNowMs;
}

Expand Down Expand Up @@ -1116,6 +1121,11 @@ static int16_t applyAutoTransitionTargetFixedWingRateController(autoTransitionTa
pidState->errorGyroIf += rateError * pidState->kI * dT * pidState->attenuation.aI;
}

if (axis != FD_YAW && pidState->errorGyroIf * rateTarget < 0.0f
&& fabsf(rateTarget) > controlProfile->stabilized.rates[axis] * 10.0f * FW_ITERM_LOCK_BIG_STICK) {
pidState->errorGyroIf *= 1.0f - MIN((1.0f - pidState->attenuation.aI) * dT / FW_ITERM_LOCK_FADE_S, 1.0f);
}

applyAutoTransitionTargetItermLimiting(pidState, itermLimitActive);

const uint16_t limit = 500;
Expand Down Expand Up @@ -1669,7 +1679,7 @@ static void iTermLockApply(pidState_t *pidState, const float rateTarget, const f
const bool errorThresholdReached = fabsf(rateError) > maxRate * pidProfile()->fwItermLockEngageThreshold / 100.0f;

//When abs of rate target is above 20% of max rate, we start tracking time
if (fabsf(rateTarget) > maxRate * 0.2f) {
if (fabsf(rateTarget) > maxRate * FW_ITERM_LOCK_BIG_STICK) {
pidState->attenuation.targetOverThresholdTimeMs = pidLoopNowMs;
}

Expand Down Expand Up @@ -1709,6 +1719,15 @@ static void NOINLINE pidApplyFixedWingRateController(pidState_t *pidState, float
pidState->errorGyroIf += rateError * pidState->kI * dT * pidState->attenuation.aI;
}

// The lock holds the I-term at whatever it had when the stick went out. Built in a motion the pilot did not ask
// for, it can stand against the stick and eat the feed-forward that is left: then it fades while the stick is out.
// Only for the inputs the lock counts as big, so a small correction keeps the trim, and not on yaw, whose rate
// target in a banked turn is the turn's and its I-term the rudder trim
if (pidState->axis != FD_YAW && pidState->errorGyroIf * rateTarget < 0.0f
&& fabsf(rateTarget) > currentControlProfile->stabilized.rates[pidState->axis] * 10.0f * FW_ITERM_LOCK_BIG_STICK) {
pidState->errorGyroIf *= 1.0f - MIN((1.0f - pidState->attenuation.aI) * dT / FW_ITERM_LOCK_FADE_S, 1.0f);
}

applyItermLimiting(pidState);

const uint16_t limit = pidState->pidSumLimit;
Expand Down
4 changes: 2 additions & 2 deletions src/main/flight/servos.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,10 @@ void loadCustomServoMixer(void)
// target-profile response.
if (carryOverServoSpeedLimitsOnNextLoad) {
for (int i = 0; i < servoRuleCount; i++) {
if(currentServoMixer[i].inputSource == INPUT_MIXER_SWITCH_HELPER || movefilterCount >= MAX_SERVO_RULES_SWITCH_CARRY) {
//will not carry over INPUT_MIXER_SWITCH_HELPER rules
if (movefilterCount >= MAX_SERVO_RULES_SWITCH_CARRY) {
break;
}
// carry helper rules too, they still hold the decaying output of an earlier switch
if(currentServoMixer[i].speed != 0 && fabsf(servoSpeedLimitFilter[i].state) > 0.01f) {
servoMixerSwitchHelper[movefilterCount].targetChannel = currentServoMixer[i].targetChannel;
servoMixerSwitchHelper[movefilterCount].speed = currentServoMixer[i].speed;
Expand Down
6 changes: 3 additions & 3 deletions src/main/flight/wind_estimator.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ void updateWindEstimator(timeMs_t currentTimeMs)
memcpy(lastFuselageDirection, fuselageDirection, sizeof(lastFuselageDirection));
memcpy(lastGroundVelocity, groundVelocity, sizeof(lastGroundVelocity));

float theta = atan2f(groundVelocityDiff[Y], groundVelocityDiff[X]) - atan2f(fuselageDirectionDiff[Y], fuselageDirectionDiff[X]); // equation 9
float sintheta = sinf(theta);
float costheta = cosf(theta);
float theta = atan2_approx(groundVelocityDiff[Y], groundVelocityDiff[X]) - atan2_approx(fuselageDirectionDiff[Y], fuselageDirectionDiff[X]); // equation 9
float sintheta = sin_approx(theta);
float costheta = cos_approx(theta);

float wind[XYZ_AXIS_COUNT];
wind[X] = (groundVelocitySum[X] - V * (costheta * fuselageDirectionSum[X] - sintheta * fuselageDirectionSum[Y])) * 0.5f; // equation 10
Expand Down
Loading
Loading