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
65 changes: 65 additions & 0 deletions src/main/common/maths.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,71 @@ float acos_approx(float x)
}
#endif

/**
* Fast power approximation for positive base values.
* Uses bit manipulation via the identity: x^y = 2^(y * log2(x))
* Optimized for embedded systems - approximately 5-10x faster than powf().
*
* Accuracy: ~1-3% error for typical ranges, sufficient for TPA calculations.
* Note: Only valid for base > 0. Returns 0 for invalid inputs.
*/
float fast_powf(float base, float exp)
{
// Handle common special cases for maximum speed
if (exp == 0.0f) {
return 1.0f;
}
Comment on lines +116 to +121
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

fast_powf() is new math behavior in a file that already has unit tests (src/test/unit/maths_unittest.cc). Please add coverage that checks fast_powf against powf() across the expected domain (e.g., base in ~[0.015, 60] and exp in ~[0, 2]) with an explicit acceptable error bound, so future changes don’t silently degrade PID scaling.

Copilot uses AI. Check for mistakes.
if (exp == 1.0f) {
return base;
}
if (base <= 0.0f) {
return 0.0f; // Invalid input
}
if (exp == 2.0f) {
return base * base;
}
if (exp == 0.5f) {
return fast_fsqrtf(base);
}

// For general case, use bit manipulation approximation
// Based on: x^y = 2^(y * log2(x))
// Using IEEE 754 floating point representation
union {
float f;
int32_t i;
} u;

u.f = base;
// Extract and compute: log2(x) ≈ (mantissa bits - 127) + normalized_mantissa
// IEEE 754: float = 2^(exponent-127) * (1 + mantissa/2^23)
// log2(x) ≈ (exponent - 127) + (mantissa / 2^23)

// Fast approximation: just use the exponent bits for log2
// More accurate version includes mantissa contribution
int32_t exp_bits = (u.i >> 23) & 0xFF;
int32_t mant_bits = u.i & 0x7FFFFF;

// log2(base) approximation with mantissa correction
float log2_base = (float)(exp_bits - 127) + (float)mant_bits / 8388608.0f;

Comment on lines +137 to +155
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The stated accuracy (~1–3% error) appears inconsistent with the current log2(base) approximation: using mantissa/2^23 as a linear proxy for log2(1+mantissa) can introduce much larger errors (e.g., base≈1.5, exp=2 gives >10% relative error). Please either tighten the approximation (e.g., add a better mantissa->log2 term) or update the accuracy/performance claims and limit the intended operating range accordingly.

Suggested change
// Using IEEE 754 floating point representation
union {
float f;
int32_t i;
} u;
u.f = base;
// Extract and compute: log2(x) ≈ (mantissa bits - 127) + normalized_mantissa
// IEEE 754: float = 2^(exponent-127) * (1 + mantissa/2^23)
// log2(x) ≈ (exponent - 127) + (mantissa / 2^23)
// Fast approximation: just use the exponent bits for log2
// More accurate version includes mantissa contribution
int32_t exp_bits = (u.i >> 23) & 0xFF;
int32_t mant_bits = u.i & 0x7FFFFF;
// log2(base) approximation with mantissa correction
float log2_base = (float)(exp_bits - 127) + (float)mant_bits / 8388608.0f;
// Use standard library log2f for accurate log2(base)
union {
float f;
int32_t i;
} u;
// Compute log2(base) accurately using libm
float log2_base = log2f(base);

Copilot uses AI. Check for mistakes.
// Compute result exponent: y * log2(x)
float result_exp = exp * log2_base;

// Convert back to float: 2^result_exp
int32_t result_exp_int = (int32_t)result_exp;
float result_exp_frac = result_exp - (float)result_exp_int;

// Reconstruct float from exponent
u.i = (result_exp_int + 127) << 23;

// Apply fractional part correction using polynomial approximation
// 2^x ≈ 1 + x*(0.69315 + x*(0.24023 + x*0.05550)) for x in [0,1]
float frac_mult = 1.0f + result_exp_frac * (0.69314718f + result_exp_frac * (0.24022650f + result_exp_frac * 0.05550410f));
Comment on lines +159 to +168
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

result_exp_frac can be negative when result_exp < 0 (which happens for base<1, common in airspeed scaling). The 2^x polynomial is documented as valid for x in [0,1], but the current int-cast decomposition allows frac outside that domain, which can increase error and introduce discontinuities around integer boundaries. Consider decomposing result_exp using floor (so 0<=frac<1) or otherwise ensuring the polynomial’s input range matches its documented validity.

Copilot uses AI. Check for mistakes.

return u.f * frac_mult;
}

int gcd(int num, int denom)
{
if (denom == 0) {
Expand Down
1 change: 1 addition & 0 deletions src/main/common/maths.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ float bellCurve(const float x, const float curveWidth);
float attenuation(const float input, const float width);
float gaussian(const float x, const float mu, const float sigma);
float fast_fsqrtf(const float value);
float fast_powf(float base, float exp);
float calc_length_pythagorean_2D(const float firstElement, const float secondElement);
float calc_length_pythagorean_3D(const float firstElement, const float secondElement, const float thirdElement);

Expand Down
4 changes: 2 additions & 2 deletions src/main/flight/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ float pidRcCommandToRate(int16_t stick, uint8_t rate)
static float calculateFixedWingAirspeedTPAFactor(void){
const float airspeed = constrainf(getAirspeedEstimate(), 100.0f, 20000.0f); // cm/s, clamped to 3.6-720 km/h
const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s
float tpaFactor= powf(referenceAirspeed/airspeed, currentControlProfile->throttle.apa_pow/100.0f);
float tpaFactor= fast_powf(referenceAirspeed/airspeed, currentControlProfile->throttle.apa_pow/100.0f);
tpaFactor= constrainf(tpaFactor, 0.3f, 2.0f);
return tpaFactor;
}
Expand All @@ -460,7 +460,7 @@ static float calculateFixedWingAirspeedITermFactor(void){
return 1.0f;
}

float iTermFactor = powf(referenceAirspeed/airspeed, (apa_pow/100.0f) - 1.0f);
float iTermFactor = fast_powf(referenceAirspeed/airspeed, (apa_pow/100.0f) - 1.0f);
iTermFactor = constrainf(iTermFactor, 0.3f, 1.5f);
return iTermFactor;
}
Expand Down
Loading