Use Case
Embedded Signal Processing
Signal processing is where embedded systems meet the physical world. ADC streams, sensor fusion pipelines, vibration analysis, acoustic detection. numx handles all of it without touching the heap.
The signal module
Every primitive a DSP pipeline needs, without a dependency on anything except C99. FIR and IIR filters use caller-provided state buffers so they are safe to call from RTOS tasks and ISRs simultaneously.
numx_signal_fir FIR filter convolution. Direct-form, any tap count.
numx_signal_iir Direct-form II IIR with caller-provided state. Streaming-safe.
numx_signal_convolve Full linear convolution, output length na + nb - 1.
numx_signal_correlate Cross-correlation. Time-delay estimation.
numx_signal_window Hann, Hamming, Blackman, Bartlett, rectangular.
numx_signal_ema Exponential moving average for online smoothing.
numx_signal_peaks Local maxima above threshold. Index-based output.
The fft module
Cooley-Tukey radix-2 in float32 and Q15 fixed-point. All transforms are in-place. The Q15 path is for targets without hardware FPU where fixed-point is faster. Use float32 everywhere else.
numx_fft_f32 In-place float32 FFT. N must be power of two.
numx_fft_q15 In-place Q15 FFT. Interleaved int16 pairs.
numx_ifft_f32 In-place inverse FFT, 1/N normalized.
numx_fft_magnitude Magnitude spectrum from complex output. N/2+1 bins.
Complete pipeline example
FIR low-pass filter followed by Hann-windowed FFT for frequency analysis. Runs block-by-block from an ADC stream on ESP32 or any Cortex-M target.
#include "numx/numx.h"
#define BLOCK_SIZE 256
#define TAPS 32
#define FS 10000.0f /* 10 kHz sample rate */
/* 32-tap Hamming-windowed low-pass FIR (Fc = 1 kHz) */
static const numx_real_t lpf_h[TAPS] = { /* precomputed */ };
/* IIR state persists between blocks for streaming */
static numx_real_t iir_state[4]; /* max(nb, na) - 1 */
/* Output buffers */
static numx_real_t filtered[BLOCK_SIZE];
static numx_complex_f32_t fft_buf[BLOCK_SIZE];
static numx_real_t window_buf[BLOCK_SIZE];
static numx_real_t mag[BLOCK_SIZE / 2 + 1];
void process_block(const numx_real_t *adc, size_t n) {
/* Stage 1: FIR low-pass filter */
numx_signal_fir(lpf_h, TAPS, adc, n, filtered);
/* Stage 2: Apply Hann window for spectral analysis */
numx_signal_window(NUMX_WINDOW_HANN, n, window_buf);
for (size_t i = 0; i < n; i++) {
fft_buf[i].re = filtered[i] * window_buf[i];
fft_buf[i].im = 0.0f;
}
/* Stage 3: FFT for frequency analysis */
numx_fft_f32(fft_buf, n);
numx_fft_magnitude(fft_buf, n, mag);
/* mag[k] = amplitude at k * (FS / n) Hz */
}