File syn_filter.c¶
File List > dsp > syn_filter.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_FILTER) || SYN_USE_FILTER
#include "../util/syn_assert.h"
#include "syn_filter.h"
#include <string.h>
/* ════════════════════════════════════════════════════════════════════════ */
/* Moving Average */
/* ════════════════════════════════════════════════════════════════════════ */
void syn_filter_ma_init(SYN_FilterMA *f, uint8_t window)
{
if (f == NULL || window == 0 || window > SYN_FILTER_MAX_WINDOW) {
return;
}
SYN_ASSERT(f != NULL);
SYN_ASSERT(window > 0 && window <= SYN_FILTER_MAX_WINDOW);
memset(f, 0, sizeof(*f));
f->window = window;
}
int16_t syn_filter_ma_update(SYN_FilterMA *f, int16_t sample)
{
if (f == NULL || f->window == 0) {
return sample;
}
SYN_ASSERT(f != NULL);
if (f->count >= f->window) {
/* Remove oldest sample from running sum */
f->sum -= f->buf[f->idx];
} else {
f->count++;
}
/* Insert new sample */
f->buf[f->idx] = sample;
f->sum += sample;
f->idx = (uint8_t)((f->idx + 1) % f->window);
return (int16_t)(f->sum / f->count);
}
void syn_filter_ma_reset(SYN_FilterMA *f)
{
if (f == NULL) {
return;
}
SYN_ASSERT(f != NULL);
uint8_t w = f->window;
memset(f, 0, sizeof(*f));
f->window = w;
}
/* ════════════════════════════════════════════════════════════════════════ */
/* Exponential Moving Average */
/* ════════════════════════════════════════════════════════════════════════ */
void syn_filter_ema_init(SYN_FilterEMA *f, uint8_t alpha)
{
if (f == NULL) {
return;
}
SYN_ASSERT(f != NULL);
f->value = 0;
f->alpha = alpha;
f->primed = false;
}
int16_t syn_filter_ema_update(SYN_FilterEMA *f, int16_t sample)
{
if (f == NULL) {
return sample;
}
SYN_ASSERT(f != NULL);
if (!f->primed) {
/* First sample — initialize directly */
f->value = (int32_t)sample << 8;
f->primed = true;
return sample;
}
/* EMA: out = alpha * sample + (256 - alpha) * prev, all in Q8 */
int32_t sample_q8 = (int32_t)sample << 8;
f->value += ((int32_t)f->alpha * (sample_q8 - f->value)) >> 8;
return (int16_t)(f->value >> 8);
}
void syn_filter_ema_reset(SYN_FilterEMA *f)
{
if (f == NULL) {
return;
}
SYN_ASSERT(f != NULL);
uint8_t a = f->alpha;
f->value = 0;
f->primed = false;
f->alpha = a;
}
/* ════════════════════════════════════════════════════════════════════════ */
/* Median Filter */
/* ════════════════════════════════════════════════════════════════════════ */
void syn_filter_median_init(SYN_FilterMedian *f, uint8_t window)
{
if (f == NULL || window == 0 || window > SYN_FILTER_MAX_WINDOW) {
return;
}
SYN_ASSERT(f != NULL);
SYN_ASSERT(window > 0 && window <= SYN_FILTER_MAX_WINDOW);
memset(f, 0, sizeof(*f));
f->window = window;
}
int16_t syn_filter_median_update(SYN_FilterMedian *f, int16_t sample)
{
if (f == NULL || f->window == 0) {
return sample;
}
SYN_ASSERT(f != NULL);
/* Insert into circular buffer */
f->buf[f->idx] = sample;
f->idx = (uint8_t)((f->idx + 1) % f->window);
if (f->count < f->window) {
f->count++;
}
/* Sort a copy to find median (insertion sort — fine for small windows) */
int16_t sorted[SYN_FILTER_MAX_WINDOW];
uint8_t n = f->count;
memcpy(sorted, f->buf, (size_t)n * sizeof(int16_t));
uint8_t i;
for (i = 1; i < n; i++) {
int16_t key = sorted[i];
uint8_t j = i;
while (j > 0 && sorted[j - 1] > key) {
sorted[j] = sorted[j - 1];
j--;
}
sorted[j] = key;
}
return sorted[n / 2];
}
void syn_filter_median_reset(SYN_FilterMedian *f)
{
SYN_ASSERT(f != NULL);
uint8_t w = f->window;
memset(f, 0, sizeof(*f));
f->window = w;
}
/* ════════════════════════════════════════════════════════════════════════ */
/* FIR Filter */
/* ════════════════════════════════════════════════════════════════════════ */
void syn_filter_fir_init(SYN_FilterFIR *f, const q16_t *taps, q16_t *history, uint16_t num_taps)
{
SYN_ASSERT(f != NULL && taps != NULL && history != NULL && num_taps > 0);
f->taps = taps;
f->history = history;
f->num_taps = num_taps;
f->head = 0;
memset(history, 0, (size_t)num_taps * sizeof(q16_t));
}
q16_t syn_filter_fir_update(SYN_FilterFIR *f, q16_t sample)
{
SYN_ASSERT(f != NULL && f->taps != NULL && f->history != NULL);
/* Insert new sample into circular history */
f->history[f->head] = sample;
int64_t acc = 0;
uint16_t idx = f->head;
for (uint16_t i = 0; i < f->num_taps; i++) {
acc += (int64_t)f->taps[i] * f->history[idx];
if (idx == 0) {
idx = f->num_taps - 1;
} else {
idx--;
}
}
if (f->num_taps > 0) {
f->head = (uint16_t)((f->head + 1) % f->num_taps);
}
return (q16_t)(acc >> Q16_SHIFT);
}
void syn_filter_fir_reset(SYN_FilterFIR *f)
{
SYN_ASSERT(f != NULL && f->history != NULL);
memset(f->history, 0, (size_t)f->num_taps * sizeof(q16_t));
f->head = 0;
}
#endif /* SYN_USE_FILTER */