#include "../sources/OnePoleFilter.h" #include "catch2/catch.hpp" #include "cnpy.h" #include "gsl/gsl-lite.hpp" #include #include #include using namespace Catch::literals; template inline bool approxEqual(const std::vector& lhs, const std::vector& rhs) { if (lhs.size() != rhs.size()) return false; for (size_t i = 0; i < rhs.size(); ++i) if (lhs[i] != Approx(rhs[i]).epsilon(1e-3)) { std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; return false; } return true; } template void testLowpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain) { const auto input = cnpy::npy_load(inputNumpyFile.string()); REQUIRE( input.word_size == 8 ); const auto inputSpan = gsl::make_span(input.data(), input.shape[0]); const auto output = cnpy::npy_load(outputNumpyFile.string()); REQUIRE( output.word_size == 8 ); const auto outputSpan = gsl::make_span(output.data(), output.shape[0]); auto size = std::min(outputSpan.size(), inputSpan.size()); REQUIRE( size > 0 ); std::vector inputData; std::vector expectedData; inputData.reserve(size); expectedData.reserve(size); for (auto& data: inputSpan) inputData.push_back(static_cast(data)); for (auto& data: outputSpan) expectedData.push_back(static_cast(data)); OnePoleFilter filter { gain }; std::vector outputData (size); filter.processLowpass(inputData, outputData); REQUIRE( approxEqual(outputData, expectedData) ); filter.reset(); std::fill(outputData.begin(), outputData.end(), 0.0); std::vector gains(size); std::fill(gains.begin(), gains.end(), gain); filter.processLowpassVariableGain(inputData, outputData, gains); REQUIRE( approxEqual(outputData, expectedData) ); } template void testHighpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain) { const auto input = cnpy::npy_load(inputNumpyFile.string()); REQUIRE( input.word_size == 8 ); const auto inputSpan = gsl::make_span(input.data(), input.shape[0]); const auto output = cnpy::npy_load(outputNumpyFile.string()); REQUIRE( output.word_size == 8 ); const auto outputSpan = gsl::make_span(output.data(), output.shape[0]); auto size = std::min(outputSpan.size(), inputSpan.size()); REQUIRE( size > 0 ); std::vector inputData; std::vector expectedData; inputData.reserve(size); expectedData.reserve(size); for (auto& data: inputSpan) inputData.push_back(static_cast(data)); for (auto& data: outputSpan) expectedData.push_back(static_cast(data)); OnePoleFilter filter { gain }; std::vector outputData (size); filter.processHighpass(inputData, outputData); REQUIRE( approxEqual(outputData, expectedData) ); filter.reset(); std::fill(outputData.begin(), outputData.end(), 0.0); std::vector gains(size); std::fill(gains.begin(), gains.end(), gain); filter.processHighpassVariableGain(inputData, outputData, gains); REQUIRE( approxEqual(outputData, expectedData) ); } TEST_CASE("[OnePoleFilter] Lowpass Float") { testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy", 0.1f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy", 0.3f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy", 0.5f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy", 0.7f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy", 0.9f ); } TEST_CASE("[OnePoleFilter] Lowpass Double") { testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy", 0.1f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy", 0.3f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy", 0.5f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy", 0.7f ); testLowpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy", 0.9f ); } TEST_CASE("[OnePoleFilter] Highpass Float") { testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy", 0.1f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy", 0.3f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy", 0.5f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy", 0.7f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy", 0.9f ); } TEST_CASE("[OnePoleFilter] Highpass Double") { testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy", 0.1f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy", 0.3f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy", 0.5f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy", 0.7f ); testHighpass( std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy", 0.9f ); }