From 7de913f19c54ee5be5c414b346745425cef3b0fd Mon Sep 17 00:00:00 2001 From: David Madl Date: Mon, 2 Mar 2026 15:29:36 +0100 Subject: [PATCH] feat: IIR filter impl --- google-tests/test1.cpp | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/google-tests/test1.cpp b/google-tests/test1.cpp index 6213c4b..f576e76 100644 --- a/google-tests/test1.cpp +++ b/google-tests/test1.cpp @@ -4,6 +4,7 @@ #include #include "library.h" #include "npy.hpp" +#include #include #include #include @@ -49,3 +50,103 @@ TEST(HelloTest, Save_npy_matrix) { const std::string path{"test1/data2.npy"}; npy::write_npy(path, d); } + +/** Shift register implemented as a circular buffer. */ +class Buf { +protected: + std::vector data; + size_t size; + size_t n; +public: + Buf(size_t N): size(N), n(0) { + data.resize(N); + data.assign(N, 0.0); + } + void push(double val) { + data[n] = val; + n = (n+1) % size; + } +}; + +/** Running filter base. */ +class Filt : Buf { +protected: + std::vector taps; + size_t shift; + size_t offset; +public: + Filt(size_t N, size_t shift, size_t offset, std::vector taps): Buf(N), shift(shift), offset(offset), taps(taps) { + if (taps.size() != N) throw std::invalid_argument("taps.size() != N"); + } + double filter(double val) { + this->push(val); + return this->peek(); + } + double peek() { + double sum = 0; + for (size_t i = offset; i < this->size; i++) { + //size_t n = (this->n - i + shift - 1) % this->size; // unsigned % size ... bad if u is negative + size_t n = (this->size + this->n - i + shift - 1) % this->size; + //std::cout << " t[" << i << "] * v[" << n << "]" << std::endl; + sum += this->data[n] * this->taps[i]; + } + return sum; + } + void push(double val) { + Buf::push(val); + } +}; + +class IirFilter { +protected: + Filt y; + Filt x; +public: + IirFilter(std::vector b, std::vector a) : x(b.size(), 0, 0, b), y(a.size(), 1, 1, a) { + if (b.size() != a.size()) throw std::invalid_argument("b.size() != a.size()"); + } + double filter(double val) { + //std::cout << "x.filter(" << val << ")" << std::endl; + double xv = x.filter(val); + //std::cout << "xv=" << xv << std::endl; + //std::cout << "y.peek()" << std::endl; + double yv = y.peek(); + //std::cout << "yv=" << yv << std::endl; + //std::cout << "---" << std::endl; + double yo = xv - yv; + y.push(yo); + return yo; + } +}; + +// TODO: copy npy files from CMake output folder +// TODO: add npy files to CMakeLists.txt so they are copied into output folder +TEST(HelloTest, Test_IIR_1_Apply_IIR) { + npy::npy_data x = npy::read_npy("test1/iir_t1_x.npy"); + npy::npy_data y_e = npy::read_npy("test1/iir_t1_y.npy"); + size_t N = x.shape[0]; + EXPECT_EQ(x.shape[0], y_e.shape[0]); + + npy::npy_data a = npy::read_npy("test1/iir_t1_a.npy"); + npy::npy_data b = npy::read_npy("test1/iir_t1_b.npy"); + + //EXPECT_EQ(6, b.data.size()); + + IirFilter filter(b.data, a.data); + std::vector y; + y.resize(N); + for (size_t i = 0; i < N; i++) { + y[i] = filter.filter(x.data[i]); + } + // assert y == y_e, nb. upto 5 digits + double abs_err = 1e-5; + for (size_t i = 0; i < N; i++) { + ASSERT_NEAR(y_e.data[i], y[i], abs_err); + } + + npy::npy_data_ptr d; + d.data_ptr = y.data(); + d.shape = {(unsigned long)N}; + const std::string path{"test1/iir_t1_y_out.npy"}; + npy::write_npy(path, d); +}