Skip to content

DSP & Digital Signal Processing Modules

SyntropicOS provides fixed-point (integer-only) Digital Signal Processing (DSP) components designed for microcontrollers without Hardware Floating-Point Units (FPUs).


Technical Specifications

Feature Specification
Math Representation Q16.16 Fixed-Point (q16_t). 16 bits integer, 16 bits fractional.
Accumulator Resolution 64-bit integer (int64_t) to prevent overflow during sum/square accumulation.
Memory Allocation 100% Static / Zero Heap. Buffers are caller-owned.

1. Digital Filters (dsp/syn_filter.h & dsp/syn_biquad.h)

SyntropicOS includes Moving Average, Exponential Moving Average (EMA), Median spike rejection, Direct-Form FIR, and Butterworth Biquad IIR filters.

Signal Processing Flow

flowchart LR
    RawADC["Raw ADC Sample"] --> MedianFilter["Median Filter (Spike Removal)"]
    MedianFilter --> EMAFilter["EMA / Biquad Filter (Noise Reduction)"]
    EMAFilter --> SignalStats["Signal Statistics (Min, Max, Mean, RMS)"]

Complete Code Example (Biquad Butterworth Lowpass Filter)

#include <syntropic/dsp/syn_biquad.h>
#include <syntropic/dsp/syn_filter.h>

static SYN_FilterBiquad lpf;
static SYN_FilterEMA    ema;

void dsp_init(void) {
    // 1. Initialize EMA Filter (alpha = 64/256 = 0.25)
    syn_filter_ema_init(&ema, 64);

    // 2. Initialize 2nd-order Butterworth Lowpass Filter: 100 Hz cutoff, 1000 Hz sample rate
    syn_filter_biquad_lowpass(&lpf, Q16_FROM_INT(100), Q16_FROM_INT(1000));
}

int16_t process_adc_sample(int16_t raw_sample) {
    // Convert sample to Q16.16 fixed-point format
    q16_t in_q16 = Q16_FROM_INT(raw_sample);

    // Filter through lowpass Biquad
    q16_t filtered_q16 = syn_filter_biquad_update(&lpf, in_q16);

    // Return integer result
    return (int16_t)Q16_TO_INT(filtered_q16);
}

2. Signal Statistics (dsp/syn_signal.h)

Provides real-time sliding window statistics (min, max, mean, variance, standard deviation, RMS) over a caller-owned circular buffer.

#include <syntropic/dsp/syn_signal.h>

static int32_t stats_buffer[64];
static SYN_Signal sig;

void stats_init(void) {
    syn_signal_init(&sig, stats_buffer, 64);
}

void on_adc_sample(int32_t val) {
    syn_signal_push(&sig, val);

    int32_t min_val = syn_signal_min(&sig);
    int32_t max_val = syn_signal_max(&sig);
    int32_t mean_val = syn_signal_mean(&sig);
    int32_t rms_val  = syn_signal_rms_q16(&sig);
}

3. Fast Fourier Transform & Peak Detection (dsp/syn_fft.h)

Provides fixed-point Radix-2 FFT spectral analysis, Hanning/Hamming windowing, peak frequency identification, and Total Harmonic Distortion (THD) calculation.

#include <syntropic/dsp/syn_fft.h>

#define FFT_SIZE 64

static q16_t real_buf[FFT_SIZE];
static q16_t imag_buf[FFT_SIZE];
static q16_t mag_buf[FFT_SIZE / 2];

void analyze_spectrum(void) {
    // Apply Hanning window
    syn_fft_apply_window(real_buf, FFT_SIZE, SYN_FFT_WINDOW_HANNING);

    // Perform Radix-2 FFT
    syn_fft_perform(real_buf, imag_buf, FFT_SIZE);

    // Compute magnitude spectrum
    syn_fft_magnitude_spectrum(real_buf, imag_buf, mag_buf, FFT_SIZE / 2);

    // Find dominant frequency peak index
    uint16_t peak_bin = syn_fft_find_peak(mag_buf, FFT_SIZE / 2);
}

4. TinyML & Fixed-Point Neural Networks (util/syn_nn.h)

SyntropicOS features a zero-heap, fixed-point TinyML inference engine tailored for edge sensor processing (e.g., Human Activity Recognition, vibration fault detection, anomaly classification).

Key Neural Network Features

  • Quantized 1D Convolution (syn_nn_conv1d_quant_q7): Multi-channel temporal feature extraction with TFLite-style affine quantization scaling.
  • Quantized Dense Layers (syn_nn_dense_quant_q7): Fully connected layers supporting arbitrary activation functions.
  • 1D Max & Average Pooling (syn_nn_maxpool1d_q7 / syn_nn_avgpool1d_q7): INT8 temporal downsampling.
  • Self-Attention Transformer Engine (syn_nn_attention_q7): Multi-head / single-head QKV dot-product self-attention mechanism.
  • Stackless Protothread Coroutines (syn_nn_conv1d_pt / syn_nn_dense_pt): Time-sliced inference that yields execution back to the RTOS event loop every chunk_size steps without stack memory overhead.

Complete Code Example (Affine-Quantized 1D-CNN Inference)

#include <syntropic/util/syn_nn.h>

// Scaled affine quantization parameters
static const syn_nn_quant_t layer1_quant = {
    .multiplier = 32768, // Fixed-point multiplier (Q15)
    .shift = 1,          // Bit-shift
    .zero_point = 0      // INT8 zero-point offset
};

void run_sensor_inference(const q7_t *sensor_data, q7_t *class_probabilities) {
    q7_t conv_out[16 * 32];
    q7_t pool_out[8 * 32];

    // 1. 1D Convolution: 16 time steps, 3 accelerometer channels -> 32 filters, 3x1 kernel
    syn_nn_conv1d_quant_q7(sensor_data, 16, 3, conv1_weights, conv1_biases,
                           conv_out, 32, 3, 1, SYN_NN_ACT_RELU, &layer1_quant);

    // 2. 1D Max Pooling: Pool size 2, Stride 2 (16 steps -> 8 steps)
    syn_nn_maxpool1d_q7(conv_out, 16, 32, pool_out, 2, 2);

    // 3. Dense Classifier Output
    syn_nn_dense_quant_q7(pool_out, 8 * 32, dense_weights, dense_biases,
                          class_probabilities, 6, SYN_NN_ACT_NONE, &layer1_quant);

    // 4. ArgMax Class Index Selection
    size_t predicted_activity = syn_nn_argmax_q7(class_probabilities, 6);
}