perf(lf): drop double-precision sqrt from FSK demod hot path

The build targets the Cortex-M4F single-precision FPU (-mfpu=fpv4-sp-d16),
which has no double-precision hardware. goertzel_mag() used sqrt() (double):
the float result was promoted to double for the call and converted back, and
a software double-sqrt routine ran -- twice per decoded bit.

The bit decision only compares the two Goertzel outputs, and power is
monotonic with magnitude, so the sqrt is unnecessary. goertzel_power()
returns the squared magnitude and fsk_feed() compares that directly. This
also removes a latent sqrt(NaN): the magnitude argument can round slightly
negative near zero signal, which sqrt() turned into NaN (and NaN comparisons
make the bit decision unreliable); comparing the raw power is well-defined.

The old goertzel_mag() had no callers anywhere and was not declared in the
header, so it is removed rather than kept.

Shared by all FSK readers (HID Prox, ioProx, Pyramid): bit decisions are
identical, the soft-float double dependency is gone, and the time-sensitive
demod loop is slightly faster.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wereii
2026-06-24 14:06:18 +02:00
co-authored by Claude Opus 4.8
parent 6e2a902d0e
commit ab59e7af00
@@ -7,7 +7,7 @@
#define PI 3.14159265358979f
#define GOERTZEL(FREQ, SAMPLE_RATE) (2.0 * cos((2.0 * PI * FREQ) / (SAMPLE_RATE)))
float goertzel_mag(float coef, uint16_t samples[], int n) {
static float goertzel_power(float coef, const uint16_t samples[], int n) {
float z1 = 0;
float z2 = 0;
for (int i = 0; i < n; i++) {
@@ -15,7 +15,11 @@ float goertzel_mag(float coef, uint16_t samples[], int n) {
z2 = z1;
z1 = z0;
}
return sqrt(z1 * z1 + z2 * z2 - coef * z1 * z2);
// Squared magnitude (|X|^2). The bit decision only compares two of these,
// and power is monotonic with magnitude, so the sqrt is dropped entirely.
// Result can be slightly negative from rounding; harmless for a comparison
// (and avoids the sqrt(NaN) the old magnitude version could hit near zero).
return z1 * z1 + z2 * z2 - coef * z1 * z2;
}
void fsk_free(fsk_t *m) {
@@ -33,8 +37,8 @@ bool fsk_feed(fsk_t *m, uint16_t sample, bool *bit) {
if (m->c < m->bitrate) {
return false;
}
float bit0 = goertzel_mag(m->goertzel_fc_8, m->samples, m->bitrate);
float bit1 = goertzel_mag(m->goertzel_fc_10, m->samples, m->bitrate);
float bit0 = goertzel_power(m->goertzel_fc_8, m->samples, m->bitrate);
float bit1 = goertzel_power(m->goertzel_fc_10, m->samples, m->bitrate);
*bit = (bit1 > bit0);
// Reset counter and clear sample buffer for the next bit