From 29da451f5234e624590846dc8127aad2a08bf0e0 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Tue, 31 Oct 2023 14:09:49 +0800 Subject: [PATCH 01/20] Add CMakeLists.txt; Add tests --- .gitignore | 2 ++ Butterworth.cpp | 2 +- CMakeLists.txt | 14 ++++++++++++++ tests/CMakeLists.txt | 5 +++++ tests/test-butter.cpp | 10 ++++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test-butter.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..653fa51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build/ +/.vscode/ diff --git a/Butterworth.cpp b/Butterworth.cpp index 9a7a672..180c2b9 100644 --- a/Butterworth.cpp +++ b/Butterworth.cpp @@ -28,7 +28,7 @@ */ -#import +#include #include #include "Butterworth.h" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fe4948e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.5) +project(butter_worth_filter_design) + +include_directories(${CMAKE_SOURCE_DIR}) + +# lib_comfort库 +add_library(butterworth-filter-design SHARED + Butterworth.cpp + Biquad.cpp + ) + +# 添加测试 +enable_testing() +add_subdirectory(tests) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..cc2eae6 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +enable_testing() + +add_executable(test-butter test-butter.cpp) +add_test(NAME test-butter COMMAND test-butter) + diff --git a/tests/test-butter.cpp b/tests/test-butter.cpp new file mode 100644 index 0000000..c03eda9 --- /dev/null +++ b/tests/test-butter.cpp @@ -0,0 +1,10 @@ +#include "Butterworth.h" +#include + + +using namespace std; + +int main() +{ + cout << "Butter-Worth Filter: " << endl; +} \ No newline at end of file From ed362d1b3a341da7df520f343f2389a5b2b0e3a6 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Wed, 1 Nov 2023 14:31:03 +0800 Subject: [PATCH 02/20] Move main test to tests/ directory. Add bandpass test. Found a1,a2 are negative compare to the matlab results. --- CMakeLists.txt | 6 +- main.cpp | 1 + tests/CMakeLists.txt | 5 + tests/main-test.cpp | 698 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 709 insertions(+), 1 deletion(-) create mode 100644 tests/main-test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fe4948e..bf1b28e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,15 @@ project(butter_worth_filter_design) include_directories(${CMAKE_SOURCE_DIR}) # lib_comfort库 -add_library(butterworth-filter-design SHARED +add_library(butterworth SHARED Butterworth.cpp Biquad.cpp ) +# 编译测试主程序 main +add_executable(test-main main.cpp) +target_link_libraries(test-main PRIVATE butterworth -lsndfile) + # 添加测试 enable_testing() add_subdirectory(tests) diff --git a/main.cpp b/main.cpp index c3fdc17..15c909e 100644 --- a/main.cpp +++ b/main.cpp @@ -34,6 +34,7 @@ #define CATCH_CONFIG_MAIN // Tells Catch to provide a main() #include "catch.hpp" // Catch Unit test framework +#include using namespace std; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc2eae6..c4db1b4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,10 @@ enable_testing() +add_executable(main-test main-test.cpp) +add_test(NAME main-test COMMAND main-test) + +target_link_libraries(main-test PRIVATE butterworth -lsndfile) + add_executable(test-butter test-butter.cpp) add_test(NAME test-butter COMMAND test-butter) diff --git a/tests/main-test.cpp b/tests/main-test.cpp new file mode 100644 index 0000000..98b37f7 --- /dev/null +++ b/tests/main-test.cpp @@ -0,0 +1,698 @@ +/* + + This file is part of Butterworth Filter Design, a pair C++ classes and an + accompanying suite of unit tests for designing high order Butterworth IIR & + EQ filters using the bilinear transform. + The generated filter coefficients are split out into cascaded biquad sections, + for easy use in your garden variety biquad or second-order section (SOS). + + Reference: http://en.wikipedia.org/wiki/Butterworth_filter + http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + + + Copyright (C) 2013, iroro orife + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + */ + + +#include +#include +#include "Butterworth.h" + +#define CATCH_CONFIG_MAIN // Tells Catch to provide a main() +#include "catch.hpp" // Catch Unit test framework +#include + +using namespace std; + + +TEST_CASE("Analogue lowpass prototype generation", " Verify poles match MATLAB buttap(8)"){ + + //****************************************************************************** + // + // baseline_poles values generated with MATLAB command >> buttap(8); + // + //****************************************************************************** + + int filterOrder = 8; + + vector poles; + poles.resize(filterOrder); + vector baseline_poles; + baseline_poles.resize(filterOrder); + + // Butterworth butter; + vector tempPoleStorage = Butterworth::prototypeAnalogLowPass(filterOrder); + + // copy tmppole into poles + int index = 0; + for(vector ::iterator itr = tempPoleStorage.begin(); itr != tempPoleStorage.end(); itr++){ + poles[index] = *itr; + index++; + } + + baseline_poles[0] = complex_double(-0.19509032201613, 0.98078528040323); + baseline_poles[1] = complex_double(-0.19509032201613, -0.98078528040323); + baseline_poles[2] = complex_double(-0.55557023301960, 0.83146961230255); + baseline_poles[3] = complex_double(-0.55557023301960, -0.83146961230255); + baseline_poles[4] = complex_double(-0.83146961230255, 0.55557023301960); + baseline_poles[5] = complex_double(-0.83146961230255, -0.55557023301960); + baseline_poles[6] = complex_double(-0.98078528040323, 0.19509032201613); + baseline_poles[7] = complex_double(-0.98078528040323, -0.19509032201613); + + // Tolerance + const double EPSILON = 1.0e-14; + + // Test + for(uint32_t i = 0; i < filterOrder; i++){ + CAPTURE(i); + REQUIRE(abs(baseline_poles[i].real() - poles[i].real()) <= EPSILON); + } +} + + +TEST_CASE("Lowpass filter coefficients", " Verify coefficients for a lowpass filter"){ + + //****************************************************************************** + // MATLAB (vR14) coefficients generated with the following code: + // + // [z, p, k] = butter(8, 500, 's'); + // [Zd, Pd, Kd] = bilinear(z, p, k, 44100); + // [sos, g] = zpk2sos(Zd, Pd, Kd) + //****************************************************************************** + + int filterOrder = 8; + double overallGain = 1.0; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + + bool designedCorrectly = butterworth.loPass(44100, // fs + 500, // freq1 + 0, // freq2. N/A for lowpass + filterOrder, + coeffs, + overallGain); + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + double b0 = 1.0; + double b1 = 2.0; + double b2 = 1.0; + // double a0 = 1.0; + double a1 = -1.96762058043629 * (-1); // to convert to DF2T (direct form II transpose) + double a2 = 0.97261960500367 * (-1); // to convert to DF2T (direct form II transpose) + + int i = 0; + + const double EPSILON = 1.0e-4; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.0; + b1 = 2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.91907529383978 * (-1); + a2 = 0.92395098208778 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: third section + //****************************************************************************** + + b0 = 1.0; + b1 = 2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.88350864178159 * (-1); + a2 = 0.88829396780773 * (-1); + + i = 2; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: fourth section + //****************************************************************************** + + b0 = 1.0; + b1 = 2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.86480445083537 * (-1); + a2 = 0.86954225616013 * (-1); + + i = 3; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); +} + + +TEST_CASE("Highpass filter coefficients", " Verify coefficients for a highpass filter"){ + + //****************************************************************************** + // MATLAB (vR14) coefficients generated with the following code: + // + // [z, p, k] = butter(8, 500, 'high', 's'); + // [Zd, Pd, Kd] = bilinear(z, p, k, 44100); + // [sos, g] = zpk2sos(Zd, Pd, Kd) + //****************************************************************************** + + int filterOrder = 8; + double overallGain = 1.0; + const double EPSILON = 1.0e-4; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + + bool designedCorrectly = butterworth.hiPass(44100, // fs + 500, // freq1 + 0, // freq2 + filterOrder, + coeffs, + overallGain); + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + double b0 = 1.0; + double b1 = -2.0; + double b2 = 1.0; + // double a0 = 1.0; + double a1 = -1.96762058043629 * (-1); + double a2 = 0.97261960500367 * (-1); + + int i = 0; + + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.0; + b1 = -2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.91907529383978 * (-1); + a2 = 0.92395098208778 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: third section + //****************************************************************************** + + b0 = 1.0; + b1 = -2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.88350864178159 * (-1); + a2 = 0.88829396780773 * (-1); + + i = 2; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: fourth section + //****************************************************************************** + + b0 = 1.0; + b1 = -2.0; + b2 = 1.0; + // a0 = 1.0; + a1 = -1.86480445083537 * (-1); + a2 = 0.86954225616013 * (-1); + + i = 3; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); +} + + +TEST_CASE("Parametric EQ coefficients", " Verify coefficients for a parametric filter"){ + + //****************************************************************************** + // MATLAB coefficients were generated with the following code in + // MATLAB R14 with code hpeq1.m from + // + // Reference: Sophocles J. Orfanidis, "High-Order Digital Parametric Equalizer + // Design," J. Audio Eng. Soc., vol.53, pp. 1026-1046, November 2005. + //****************************************************************************** + + int filterOrder = 8; + double overallGain = 12.0; + const double EPSILON = 1.0e-4; + + vector coeffs; + Butterworth butterworth; + + bool designedCorrectly = butterworth.coefficientsEQ(Butterworth::kParametric, + 40000, // fs + 3500, // freq1 + 6500, // freq2 Df = 3kHz + filterOrder, + coeffs, + overallGain); + + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + double b0 = 1.0356; + double b1 = -2.6699; + double b2 = 3.4403; + double b3 = -2.3905; + double b4 = 0.8435; + // double a0 = 1.0; + double a1 = -2.6478 * (-1); + double a2 = 3.4810 * (-1); + double a3 = -2.4127 * (-1); + double a4 = 0.8384 * (-1); + + int i = 0; + + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(b3 - coeffs[i].b3) <= EPSILON); + REQUIRE(abs(b4 - coeffs[i].b4) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + REQUIRE(abs(a3 - coeffs[i].a3) <= EPSILON); + REQUIRE(abs(a4 - coeffs[i].a4) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.0555; + b1 = -2.5476; + b2 = 2.9933; + b3 = -1.8553; + b4 = 0.5794; + // a0 = 1.0; + a1 = -2.4927 * (-1); + a2 = 3.0287 * (-1); + a3 = -1.9102 * (-1); + a4 = 0.5995 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(b3 - coeffs[i].b3) <= EPSILON); + REQUIRE(abs(b4 - coeffs[i].b4) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + REQUIRE(abs(a3 - coeffs[i].a3) <= EPSILON); + REQUIRE(abs(a4 - coeffs[i].a4) <= EPSILON); +} + + +TEST_CASE("Low shelf EQ coefficients", " Verify coefficients for a low shelf filter"){ + + //****************************************************************************** + // MATLAB coefficients were generated with the following code in + // MATLAB R14 with code hpeq1.m from + // + // Reference: Sophocles J. Orfanidis, "High-Order Digital Parametric Equalizer + // Design," J. Audio Eng. Soc., vol.53, pp. 1026-1046, November 2005. + //****************************************************************************** + + int filterOrder = 4; + double overallGain = 12.0; + const double EPSILON = 1.0e-4; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + bool designedCorrectly = butterworth.coefficientsEQ(Butterworth::kLoShelf, + 40000, // fs + 0, // freq1 + 13000, // freq2 Df = 13kHz + filterOrder, + coeffs, + overallGain); + + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + double b0 = 1.6392; + double b1 = 1.7241; + double b2 = 0.9170; + // double a0 = 1.0; + double a1 = 0.6565 * (-1); + double a2 = 0.4887 * (-1); + + int i = 0; + + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.5790; + b1 = 1.2663; + b2 = 0.2984; + // a0 = 1.0; + a1 = 0.4822 * (-1); + a2 = 0.0934 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + + //****************************************************************************** + // test another set of lowshelf design requirements + //****************************************************************************** + + designedCorrectly = butterworth.coefficientsEQ(Butterworth::kLoShelf, + 40000, // fs + 2000, // freq1 + 2800, // freq2 Df = 0.8kHz + filterOrder, + coeffs, + overallGain); + + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + b0 = 1.0222; + b1 = -1.8880; + b2 = 0.8949; + // a0 = 1.0; + a1 = -1.8953 * (-1); + a2 = 0.9099 * (-1); + + i = 0; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.0456; + b1 = -1.7749; + b2 = 0.7566; + // a0 = 1.0; + a1 = -1.7817 * (-1); + a2 = 0.7954 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); +} + + +//------------------------------------------------------------------------------------------------- +TEST_CASE("High shelf EQ coefficients", " Verify coefficients for a high shelf filter"){ + + //****************************************************************************** + // MATLAB coefficients were generated with the following code in + // MATLAB R14 with code hpeq1.m from + // + // Reference: Sophocles J. Orfanidis, "High-Order Digital Parametric Equalizer + // Design," J. Audio Eng. Soc., vol.53, pp. 1026-1046, November 2005. + //****************************************************************************** + + int filterOrder = 4; + double overallGain = 12.0; + const double EPSILON = 1.0e-4; + + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + bool designedCorrectly = butterworth.coefficientsEQ(Butterworth::kHiShelf, + 40000, // fs + 0, // freq1 + 13000, // freq2 Df = 13kHz + filterOrder, + coeffs, + overallGain); + + + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + double b0 = 1.6392; + double b1 = -1.7241; + double b2 = 0.9170; + // double a0 = 1.0; + double a1 = -0.6565 * (-1); + double a2 = 0.4887 * (-1); + + int i = 0; + + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.5790; + b1 = -1.2663; + b2 = 0.2984; + // a0 = 1.0; + a1 = -0.4822 * (-1); + a2 = 0.0934 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + + + //****************************************************************************** + // test another set of highshelf design requirements + //****************************************************************************** + + designedCorrectly = butterworth.coefficientsEQ(Butterworth::kHiShelf, + 40000, // fs + 2000, // freq1 + 2800, // freq2 Df = 13kHz + filterOrder, + coeffs, + overallGain); + + REQUIRE(designedCorrectly == true); + + //****************************************************************************** + // MATLAB coefficients: first section + //****************************************************************************** + + b0 = 1.0222; + b1 = 1.8880; + b2 = 0.8949; + // a0 = 1.0; + a1 = 1.8953 * (-1); + a2 = 0.9099 * (-1); + + i = 0; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); + + //****************************************************************************** + // MATLAB coefficients: second section + //****************************************************************************** + + b0 = 1.0456; + b1 = 1.7749; + b2 = 0.7566; + // a0 = 1.0; + a1 = 1.7817 * (-1); + a2 = 0.7954 * (-1); + + i = 1; + REQUIRE(abs(b0 - coeffs[i].b0) <= EPSILON); + REQUIRE(abs(b1 - coeffs[i].b1) <= EPSILON); + REQUIRE(abs(b2 - coeffs[i].b2) <= EPSILON); + REQUIRE(abs(a1 - coeffs[i].a1) <= EPSILON); + REQUIRE(abs(a2 - coeffs[i].a2) <= EPSILON); +} + + +//------------------------------------------------------------------------------------------------- +#define BUFFER_LEN 1024 +#define MAX_CHANNELS 6 + +static void process_data (double *data, int count, int channels) +{ + int k; + + /* Process the data here. + ** If the soundfile contains more then 1 channel you need to take care of + ** the data interleaving youself. + ** Current we just apply a channel dependant gain. + */ + + for (k = 0 ; k < count ; k+= 1) + data [k] *= 1.0; + + return ; +} + +static void read_write_file (const char *infilename, const char *outfilename) +{ + static double data [BUFFER_LEN] ; + SNDFILE *infile, *outfile ; + SF_INFO sfinfo ; + int readcount ; + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + if (! (infile = sf_open (infilename, SFM_READ, &sfinfo))) + { + printf ("Not able to open input file %s.\n", infilename) ; + puts (sf_strerror (NULL)) ; + return; + } + + if (sfinfo.channels > MAX_CHANNELS) + { printf ("Not able to process more than %d channels\n", MAX_CHANNELS) ; + return; + } + if (! (outfile = sf_open (outfilename, SFM_WRITE, &sfinfo))) + { printf ("Not able to open output file %s.\n", outfilename) ; + puts (sf_strerror (NULL)) ; + return; + } + + while ((readcount = sf_read_double (infile, data, BUFFER_LEN))) + { process_data (data, readcount, sfinfo.channels) ; + sf_write_double (outfile, data, readcount) ; + } + + sf_close (infile); sf_close (outfile) ; + return; +} + + +TEST_CASE("Test gain control issue #3"){ + + //****************************************************************************** + // For this test you will need libsndfile.a which lives in + // /opt/local/lib/libsndfile.a installed on MacOS via macports + // https://trac.macports.org/browser/trunk/dports/audio/libsndfile/Portfile + //****************************************************************************** + + const char * infilename = "sweep_0_20K.wav" ; + const char * outfilename = "sweep_0_20K_filtered.wav" ; + read_write_file(infilename,outfilename); +} + +TEST_CASE("Bandpass test") { + + int filterOrder = 4; + const double EPSILON = 1.0e-4; + double gain; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + bool designedCorrectly = butterworth.bandPass( + 100, // fs + 10, // freq1 + 0.5, // freq2 + filterOrder, + coeffs, gain); + + cout << "gain: " << gain << endl; + for(int i=0; i<4; i++) { + cout << " === section " << i << " ===" << endl; + cout << "\tBiquad parameters b0: " << coeffs[i].b0 << endl; + cout << "\tBiquad parameters b1: " << coeffs[i].b1 << endl; + cout << "\tBiquad parameters b2: " << coeffs[i].b2 << endl; + cout << "\tBiquad parameters a1: " << coeffs[i].a1 << endl; + cout << "\tBiquad parameters a2: " << coeffs[i].a2 << endl; + } +} From 7a404f51239ea7d850ca0f6a547606b16646f2d5 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Wed, 1 Nov 2023 14:48:36 +0800 Subject: [PATCH 03/20] fix main-test file path issue --- tests/main-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/main-test.cpp b/tests/main-test.cpp index 98b37f7..809a395 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -666,7 +666,7 @@ TEST_CASE("Test gain control issue #3"){ // https://trac.macports.org/browser/trunk/dports/audio/libsndfile/Portfile //****************************************************************************** - const char * infilename = "sweep_0_20K.wav" ; + const char * infilename = "../../sweep_0_20K.wav" ; const char * outfilename = "sweep_0_20K_filtered.wav" ; read_write_file(infilename,outfilename); } From 73af9f6117a5368b9fcacdb84cb071abf1884877 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Wed, 1 Nov 2023 15:55:58 +0800 Subject: [PATCH 04/20] add conv() and pass test. --- Biquad.h | 2 ++ CMakeLists.txt | 1 + conv.cpp | 32 ++++++++++++++++++++++++++++++++ conv.hpp | 15 +++++++++++++++ sos2tf.cpp | 0 tests/CMakeLists.txt | 4 ++++ tests/test-conv.cpp | 22 ++++++++++++++++++++++ 7 files changed, 76 insertions(+) create mode 100644 conv.cpp create mode 100644 conv.hpp create mode 100644 sos2tf.cpp create mode 100644 tests/test-conv.cpp diff --git a/Biquad.h b/Biquad.h index c4f63a1..6521bec 100644 --- a/Biquad.h +++ b/Biquad.h @@ -36,6 +36,8 @@ using namespace std; // A biquad filter expression: // y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]; +// DF2T: Direct Form II Transposed, It is one of the standard structures used to implement digital filters, including Butterworth filters. + class Biquad { public: diff --git a/CMakeLists.txt b/CMakeLists.txt index bf1b28e..f3f7506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories(${CMAKE_SOURCE_DIR}) add_library(butterworth SHARED Butterworth.cpp Biquad.cpp + conv.cpp ) # 编译测试主程序 main diff --git a/conv.cpp b/conv.cpp new file mode 100644 index 0000000..c4bb0fc --- /dev/null +++ b/conv.cpp @@ -0,0 +1,32 @@ + +#include "conv.hpp" + +/** + * Convolution of u and v vector. + */ +void conv(const vectord &u, const vectord &v, vectord &c) +{ + // fill with zeros + c.resize(u.size() + v.size() - 1, 0); + // use convolution machine implements convolution + for (int n = 0; n < c.size(); n++) + { + // iterate input signal u, kernal is v + for (int k = 0; k < v.size(); k++) + { + // just as the Math Equation of Convolution + int iu = n - k; + double uu; + if (iu < 0 || iu >= u.size()) + { + uu = 0; + } + else + { + uu = u[iu]; + } + // iterate kernel and cumulate it + c[n] = c[n] + v[k] * uu; + } + } +} diff --git a/conv.hpp b/conv.hpp new file mode 100644 index 0000000..0885522 --- /dev/null +++ b/conv.hpp @@ -0,0 +1,15 @@ +#ifndef CONV_H +#define CONV_H + +#include + +typedef std::vector vectord; + +/** + * Convolution of u and v vector. + * Return result vector c. + * length(c) = length(u) + length(v) - 1 + */ +void conv(const vectord& u, const vectord& v, vectord& c); + +#endif \ No newline at end of file diff --git a/sos2tf.cpp b/sos2tf.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c4db1b4..b7c60dd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,3 +8,7 @@ target_link_libraries(main-test PRIVATE butterworth -lsndfile) add_executable(test-butter test-butter.cpp) add_test(NAME test-butter COMMAND test-butter) +add_executable(test-conv test-conv.cpp) +add_test(NAME test-conv COMMAND test-conv) +target_link_libraries(test-conv PRIVATE butterworth -lsndfile) + diff --git a/tests/test-conv.cpp b/tests/test-conv.cpp new file mode 100644 index 0000000..8d279dc --- /dev/null +++ b/tests/test-conv.cpp @@ -0,0 +1,22 @@ +#define CATCH_CONFIG_MAIN // Tells Catch to provide a main() +#include "catch.hpp" // Catch Unit test framework +#include +#include "conv.hpp" + +using namespace std; + +const double EPSILON = 1E-4; + +TEST_CASE("Convolution u and v", " Verify convolution is correct") +{ + vectord u = {1, 2, 3, 4, 5}; + vectord v = {1, 2, 3}; + vectord c; + conv(u, v, c); + + vectord r = {1, 4, 10, 16, 22, 22, 15}; + for (size_t i = 0; i < c.size(); i++) + { + REQUIRE(abs(r[i] - c[i]) <= EPSILON); + } +} \ No newline at end of file From d6a4e9444f7a5066acfa2531e4fa32a53b16185a Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Wed, 1 Nov 2023 17:51:31 +0800 Subject: [PATCH 05/20] Added sos2tf() and passed the test --- Biquad.cpp | 6 ++++ Biquad.h | 1 + CMakeLists.txt | 1 + sos2tf.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++ sos2tf.hpp | 45 ++++++++++++++++++++++++++++++ tests/test-conv.cpp | 28 ++++++++++++++++++- 6 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 sos2tf.hpp diff --git a/Biquad.cpp b/Biquad.cpp index ba22b07..3957a80 100644 --- a/Biquad.cpp +++ b/Biquad.cpp @@ -36,6 +36,12 @@ Biquad::Biquad(){ } + +Biquad::Biquad(double b0, double b1, double b2, double a1, double a2): + b0(b0),b1(b1),b2(b2),a1(a1),a2(a2) +{ +} + Biquad::~Biquad(){ } diff --git a/Biquad.h b/Biquad.h index 6521bec..1835b5e 100644 --- a/Biquad.h +++ b/Biquad.h @@ -42,6 +42,7 @@ class Biquad { public: Biquad(); + Biquad(double b0, double b1, double b2, double a1, double a2); ~Biquad(); diff --git a/CMakeLists.txt b/CMakeLists.txt index f3f7506..8e7dbef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(butterworth SHARED Butterworth.cpp Biquad.cpp conv.cpp + sos2tf.cpp ) # 编译测试主程序 main diff --git a/sos2tf.cpp b/sos2tf.cpp index e69de29..88198cd 100644 --- a/sos2tf.cpp +++ b/sos2tf.cpp @@ -0,0 +1,67 @@ +#include "sos2tf.hpp" +#include "conv.hpp" + +// implement matlab statement: b(1:3) = sos(1, 1:3); +// exclude end index. +void slice_assign(vector &lhs, int lstart, int lend, vector &rhs, int rstart, int rend) +{ + for (int i = 0; i < lend - lstart; i++) + { + lhs[lstart + i] = rhs[rstart + i]; + } +} + +void sos2tf(const vector &sos, double gain, vector &b, vector &a) +{ + // Step1: Decompose b and a coefficients + int L = sos.size(); + + b.resize(2 * L + 1, 0); + a.resize(2 * L + 1, 0); + + // initializes the first polynomial coefficient + // b(1 : 3) = sos(1, 1 : 3); + // a(1 : 3) = sos(1, 4 : 6); + b[0] = sos[0].b0; + b[1] = sos[0].b1; + b[2] = sos[0].b2; + a[0] = 1; + a[1] = sos[0].a1; + a[2] = sos[0].a2; + + // iterate every Biquad + for (int r = 1; r < L; r++) + { + // get b and a of SOS + vectord b1(3), a1(3); + b1[0] = sos[r].b0; + b1[1] = sos[r].b1; + b1[2] = sos[r].b2; + a1[0] = 1; + a1[1] = sos[r].a1; + a1[2] = sos[r].a2; + // polynomial multiply by convolve. + // b(1:2*(r+1)+1) = myconv(b(1:2*r+1), b1); + std::vector::const_iterator b_begin = b.begin(); + std::vector::const_iterator b_end = b.begin() + 2 * r + 1; + vectord b0(b_begin, b_end); + vectord bc; + conv(b0, b1, bc); + slice_assign(b, 0, bc.size(), bc, 0, bc.size()); + + // a(1:2*(r+1)+1) = myconv(a(1:2*r+1), a1); + std::vector::const_iterator a_begin = a.begin(); + std::vector::const_iterator a_end = a.begin() + 2 * r + 1; + vectord a0(a_begin, a_end); + vectord ac; + conv(a0, a1, ac); + slice_assign(a, 0, ac.size(), ac, 0, ac.size()); + } + // cut size to 2L+1, because the convolution adds two zeros to the end + b.resize(2*L+1); + a.resize(2*L+1); + // multiply gain + for (int i = 0; i < b.size(); i++) { + b[i] *= gain; + } +} \ No newline at end of file diff --git a/sos2tf.hpp b/sos2tf.hpp new file mode 100644 index 0000000..5972914 --- /dev/null +++ b/sos2tf.hpp @@ -0,0 +1,45 @@ +#ifndef SOS2TF_H +#define SOS2TF_H + +#include "Biquad.h" + +/** + * My 2nd-order sections to transfer function model conversion. + * See matlab `sos2tf`. + * + * [B,A] = MYSOS2TF(SOS,GAIN) returns the numerator and denominator + * coefficients B and A of the discrete-time linear system given + * by the gain G and the matrix SOS in second-order sections form. + * + * SOS is an L by 6 matrix which contains the coefficients of each + * second-order section in its rows: + * SOS = [ b01 b11 b21 1 a11 a21 + * b02 b12 b22 1 a12 a22 + * ... + * b0L b1L b2L 1 a1L a2L ] + * + * The system transfer function is the product of the second-order + * transfer functions of the sections times the gain G. If G is not + * specified, it defaults to 1. Each row of the SOS matrix describes + * a 2nd order transfer function as + * b0k + b1k z^-1 + b2k z^-2 + * ---------------------------- + * 1 + a1k z^-1 + a2k z^-2 + * where k is the row index. + * + * In each row of the sos matrix, the first three elements form the + * coefficients of the numerator polynomial of a transfer function and + * the next three elements form the coefficients of the denominator + * polynomial. B and A are formed by multiplying all numerator and + * denominator polynomials respectively. Since each polynomial is of + * degree two, multiplying L such polynomials results in a polynomial of + * degree 2*L. + * + * Use convolution to multiply two polynomials. + * w = conv(u,v) Returns the convolution of the vectors u and v. + * If u and v are vectors of polynomial coefficients, convolving them is equivalent to multiplying the two polynomials. + * + */ +void sos2tf(const vector &sos, double gain, vector &b, vector &a); + +#endif \ No newline at end of file diff --git a/tests/test-conv.cpp b/tests/test-conv.cpp index 8d279dc..55e465e 100644 --- a/tests/test-conv.cpp +++ b/tests/test-conv.cpp @@ -2,6 +2,7 @@ #include "catch.hpp" // Catch Unit test framework #include #include "conv.hpp" +#include "sos2tf.hpp" using namespace std; @@ -19,4 +20,29 @@ TEST_CASE("Convolution u and v", " Verify convolution is correct") { REQUIRE(abs(r[i] - c[i]) <= EPSILON); } -} \ No newline at end of file +} + +TEST_CASE("SOS To Transfer Function coefficients", " Verify coefficients is correct") +{ + vector sos(4); + sos[0] = Biquad(1, 2, 1, -1.11879834477625, 0.336688180692519); + sos[1] = Biquad(1, 2, 1, -1.35028339447298, 0.660543824885074); + sos[2] = Biquad(1, -2, 1, -1.93859735792819, 0.939736057085694); + sos[3] = Biquad(1, -2, 1, -1.97691413926085, 0.977914024909786); + + double gain = 0.00404923315402386; + + vectord b, a; + sos2tf(sos, gain, b, a); + + // matlab b,a + vectord mb = {0.00404923315402386, 0, -0.0161969326160954, 0, 0.0242953989241431, 0, -0.0161969326160954, 0, 0.00404923315402386}; + vectord ma = {1, -6.38459323643827, 17.9257353790401, -28.9644589090336, 29.5037393371934, -19.4170269899449, 8.06393935848502, -1.93171376981280, 0.204378907481348}; + + for (int i = 0; i < b.size(); i++) + { + CAPTURE(i) + REQUIRE(abs(b[i] - mb[i]) <= EPSILON); + REQUIRE(abs(a[i] - ma[i]) <= EPSILON); + } +} From 5ee6046c70487879f7ced984cf922f6d989ae9b7 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Wed, 1 Nov 2023 17:54:55 +0800 Subject: [PATCH 06/20] Move tests to main-test.cpp --- tests/main-test.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/main-test.cpp b/tests/main-test.cpp index 809a395..0719497 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -31,6 +31,7 @@ #include #include #include "Butterworth.h" +#include "sos2tf.hpp" #define CATCH_CONFIG_MAIN // Tells Catch to provide a main() #include "catch.hpp" // Catch Unit test framework @@ -696,3 +697,45 @@ TEST_CASE("Bandpass test") { cout << "\tBiquad parameters a2: " << coeffs[i].a2 << endl; } } + + +const double EPSILON = 1E-4; + +TEST_CASE("Convolution u and v", " Verify convolution is correct") +{ + vectord u = {1, 2, 3, 4, 5}; + vectord v = {1, 2, 3}; + vectord c; + conv(u, v, c); + + vectord r = {1, 4, 10, 16, 22, 22, 15}; + for (size_t i = 0; i < c.size(); i++) + { + REQUIRE(abs(r[i] - c[i]) <= EPSILON); + } +} + +TEST_CASE("SOS To Transfer Function coefficients", " Verify coefficients is correct") +{ + vector sos(4); + sos[0] = Biquad(1, 2, 1, -1.11879834477625, 0.336688180692519); + sos[1] = Biquad(1, 2, 1, -1.35028339447298, 0.660543824885074); + sos[2] = Biquad(1, -2, 1, -1.93859735792819, 0.939736057085694); + sos[3] = Biquad(1, -2, 1, -1.97691413926085, 0.977914024909786); + + double gain = 0.00404923315402386; + + vectord b, a; + sos2tf(sos, gain, b, a); + + // matlab b,a + vectord mb = {0.00404923315402386, 0, -0.0161969326160954, 0, 0.0242953989241431, 0, -0.0161969326160954, 0, 0.00404923315402386}; + vectord ma = {1, -6.38459323643827, 17.9257353790401, -28.9644589090336, 29.5037393371934, -19.4170269899449, 8.06393935848502, -1.93171376981280, 0.204378907481348}; + + for (int i = 0; i < b.size(); i++) + { + CAPTURE(i) + REQUIRE(abs(b[i] - mb[i]) <= EPSILON); + REQUIRE(abs(a[i] - ma[i]) <= EPSILON); + } +} From e86179be648e4df447074ae01acb5529ec0c52cd Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 11:56:08 +0800 Subject: [PATCH 07/20] Fix compile issue. The reason is wrong to use #undefin instead of #define --- Biquad.h | 4 ++++ Butterworth.h | 3 +++ tests/main-test.cpp | 9 ++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Biquad.h b/Biquad.h index 1835b5e..f1146ed 100644 --- a/Biquad.h +++ b/Biquad.h @@ -27,6 +27,8 @@ */ +#ifndef BIQUAD_H +#define BIQUAD_H #include #include @@ -96,3 +98,5 @@ class BiquadChain { void allocate(int count); }; + +#endif \ No newline at end of file diff --git a/Butterworth.h b/Butterworth.h index 9debe3b..cddd486 100644 --- a/Butterworth.h +++ b/Butterworth.h @@ -27,6 +27,8 @@ */ +#ifndef BUTTERWORTH_H +#define BUTTERWORTH_H #include "Biquad.h" @@ -116,3 +118,4 @@ class Butterworth { double * ba; }; +#endif diff --git a/tests/main-test.cpp b/tests/main-test.cpp index 0719497..21d0033 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -30,8 +30,10 @@ #include #include + #include "Butterworth.h" #include "sos2tf.hpp" +#include "conv.hpp" #define CATCH_CONFIG_MAIN // Tells Catch to provide a main() #include "catch.hpp" // Catch Unit test framework @@ -698,11 +700,10 @@ TEST_CASE("Bandpass test") { } } - -const double EPSILON = 1E-4; - TEST_CASE("Convolution u and v", " Verify convolution is correct") { + const double EPSILON = 1E-4; + vectord u = {1, 2, 3, 4, 5}; vectord v = {1, 2, 3}; vectord c; @@ -717,6 +718,8 @@ TEST_CASE("Convolution u and v", " Verify convolution is correct") TEST_CASE("SOS To Transfer Function coefficients", " Verify coefficients is correct") { + const double EPSILON = 1E-4; + vector sos(4); sos[0] = Biquad(1, 2, 1, -1.11879834477625, 0.336688180692519); sos[1] = Biquad(1, 2, 1, -1.35028339447298, 0.660543824885074); From 7b6baf9b19cf5e4db1e631813d70cf9e7f0944e0 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 13:00:13 +0800 Subject: [PATCH 08/20] Add Butterworth + sos2tf tests With a1,a2 * -1. --- tests/main-test.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/main-test.cpp b/tests/main-test.cpp index 21d0033..b2632d8 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -742,3 +742,46 @@ TEST_CASE("SOS To Transfer Function coefficients", " Verify coefficients is corr REQUIRE(abs(a[i] - ma[i]) <= EPSILON); } } + +TEST_CASE("Butter-Worth Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") +{ + int filterOrder = 4; + double gain = 1.0; + double Fs = 100; + double fp1 = 0.5; + double fs1 = 10; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + + bool designedCorrectly = butterworth.bandPass(Fs, // fs + fp1, // freq1 + fs1, // freq2. N/A for lowpass + filterOrder, + coeffs, + gain); + REQUIRE(designedCorrectly == true); + + // Inverse sign of a1, a2 + // Because we dont know why Butterworth class always gets negative of a1 and a2 + for (int i=0; i Date: Thu, 2 Nov 2023 13:02:55 +0800 Subject: [PATCH 09/20] Add 200Hz Fs test --- tests/main-test.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/main-test.cpp b/tests/main-test.cpp index b2632d8..91d8932 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -785,3 +785,46 @@ TEST_CASE("Butter-Worth Filter Design Output Transfer Function Coefficients", "V REQUIRE(abs(a[i] - ma[i]) <= EPSILON); } } + +TEST_CASE("200Hz Fs Butter-Worth Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") +{ + int filterOrder = 4; + double gain = 1.0; + double Fs = 200; + double fp1 = 0.5; + double fs1 = 10; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + + bool designedCorrectly = butterworth.bandPass(Fs, // fs + fp1, // freq1 + fs1, // freq2. N/A for lowpass + filterOrder, + coeffs, + gain); + REQUIRE(designedCorrectly == true); + + // Inverse sign of a1, a2 + // Because we dont know why Butterworth class always gets negative of a1 and a2 + for (int i=0; i Date: Thu, 2 Nov 2023 13:31:44 +0800 Subject: [PATCH 10/20] Add low pass tests --- tests/main-test.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/main-test.cpp b/tests/main-test.cpp index 91d8932..122c968 100644 --- a/tests/main-test.cpp +++ b/tests/main-test.cpp @@ -743,7 +743,7 @@ TEST_CASE("SOS To Transfer Function coefficients", " Verify coefficients is corr } } -TEST_CASE("Butter-Worth Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") +TEST_CASE("100Hz Butter-Worth Band-Pass Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") { int filterOrder = 4; double gain = 1.0; @@ -786,7 +786,7 @@ TEST_CASE("Butter-Worth Filter Design Output Transfer Function Coefficients", "V } } -TEST_CASE("200Hz Fs Butter-Worth Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") +TEST_CASE("200Hz Fs Butter-Worth Band-Pass Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") { int filterOrder = 4; double gain = 1.0; @@ -828,3 +828,46 @@ TEST_CASE("200Hz Fs Butter-Worth Filter Design Output Transfer Function Coeffici REQUIRE(abs(a[i] - ma[i]) <= EPSILON); } } + +TEST_CASE("100Hz Fs Butter-Worth LowPass Filter Design Output Transfer Function Coefficients", "Verify coefficients with matlab values") +{ + int filterOrder = 4; + double gain = 1.0; + double Fs = 100; + double fp1 = 0.5; + double fs1 = 10; + + vector coeffs; // second-order sections (sos) + Butterworth butterworth; + + bool designedCorrectly = butterworth.loPass(Fs, // fs + fp1, // freq1 + fs1, // freq2. N/A for lowpass + filterOrder, + coeffs, + gain); + REQUIRE(designedCorrectly == true); + + // Inverse sign of a1, a2 + // Because we dont know why Butterworth class always gets negative of a1 and a2 + for (int i=0; i Date: Thu, 2 Nov 2023 13:38:02 +0800 Subject: [PATCH 11/20] Add sos2tf usage document. --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c411d4..760a766 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,24 @@ To generate the same set of coefficients in MATLAB (R14) as a comparison, to dou [sos, g] = zpk2sos(Zd, Pd, Kd) % zero-pole-gain form to second-order sections (SOS) ``` - +#### To convert from SOS to Transfer Function Form + +The Butterworth class get the Second Order Sections form coefficients of the Butter-Worth filter bank, but you can use `sos2tf()` to get the Transfer Function coefficients. + +``` +vector coeffs; // array of biquad filters (for this case, array size = 4 ) +Butterworth butterworth; +bool designedCorrectly = butterworth.loPass(44100, // fs + 500, // freq1 + 0, // freq2. N/A for lowpass + 8, // filter order, + coeffs, // coefficient array being filled + 1.0); // overall gain +// get Transfer Function coefficients +vectord b, a; +sos2tf(coeffs, gain, b, a); +// you can use b and a now. +``` ### Other filter design repos on GitHub * Vinnie Falco [DSP Filters](https://github.com/vinniefalco/DSPFilters) From f4a12f100667e7152d207413eceb0aeb4ffa21cf Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 14:18:42 +0800 Subject: [PATCH 12/20] Add -fPIC compile option --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e7dbef..9ead1eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,8 @@ add_library(butterworth SHARED conv.cpp sos2tf.cpp ) +# 添加编译选项,按位置无关代码编译 +target_compile_options(butterworth PRIVATE -fPIC) # 编译测试主程序 main add_executable(test-main main.cpp) From 708dfa6d081d3054ea4125496c12f252f48fb7cf Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 14:41:31 +0800 Subject: [PATCH 13/20] Add arm64 cross-compile --- .gitignore | 1 + CMakeLists.txt | 17 +++++++++++++++++ toolchain-arm64.cmake | 23 +++++++++++++++++++++++ toolchain-armv7-32b.cmake | 22 ++++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 toolchain-arm64.cmake create mode 100644 toolchain-armv7-32b.cmake diff --git a/.gitignore b/.gitignore index 653fa51..cd71fc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build/ /.vscode/ +/build-*/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ead1eb..d72440f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,23 @@ +# 交叉编译为 arm 架构程序。注意: 本块必须放在最前面,否则不生效 +if(ARM64) + message(STATUS "Using ARM64 Tool Chain!") + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchain-arm64.cmake) + # 总是 Release 版本 + set(CMAKE_BUILD_TYPE Release) + message(STATUS "Compiler:${CMAKE_CXX_COMPILER}") +else() + message(STATUS "Using X64 Tool Chain!") + # 指定编译器选项 + # 为了 gdb 调试,不优化、带上debug符号 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -rdynamic") +endif() + cmake_minimum_required(VERSION 3.5) project(butter_worth_filter_design) +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchain-arm64.cmake) + + include_directories(${CMAKE_SOURCE_DIR}) # lib_comfort库 diff --git a/toolchain-arm64.cmake b/toolchain-arm64.cmake new file mode 100644 index 0000000..642805c --- /dev/null +++ b/toolchain-arm64.cmake @@ -0,0 +1,23 @@ +set(CMAKE_SYSTEM_NAME Linux) # 操作系统类型 +set(CMAKE_SYSTEM_PROCESSOR aarch64) # CPU 架构 +set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu) + +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) # gcc 编译器程序名 +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) # g++ 编译器程序名 +set(CMAKE_Fortran_COMPILER aarch64-linux-gnu-gfortran) # g++ 编译器程序名 + +set(CMAKE_FIND_ROOT_PATH /opt/cross-gcc-linaro-6.3.1-arm64/) # g++ 所在根目录 +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # 告诉CMake要从 ROOT_PATH 下查找g++等程序,但也可以从其他地方查找 +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 告诉CMake要从 ROOT_PATH 下查找library库文件 +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 告诉CMake要从 ROOT_PATH 下查找头文件 +# set(CMAKE_PREFIX_PATH /opt/cross-gcc-linaro-6.3.1-arm64/arm-linux-gnueabihf) + +# 是否静态链接 libc 和 libstdc++ +# set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++") + +# 设置编译选项 +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=vfpv3") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=vfpv3") # fpu版本 + +# 设置链接选项 +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,/opt/cross-gcc-linaro-6.3.1-arm64/aarch64-linux-gnu/lib") diff --git a/toolchain-armv7-32b.cmake b/toolchain-armv7-32b.cmake new file mode 100644 index 0000000..dd4eb9a --- /dev/null +++ b/toolchain-armv7-32b.cmake @@ -0,0 +1,22 @@ +set(CMAKE_SYSTEM_NAME Linux) # 操作系统类型 +set(CMAKE_SYSTEM_PROCESSOR arm) # CPU 架构 + +set(CMAKE_C_COMPILER arm-linux-musleabihf-gcc) # gcc 编译器程序名 +set(CMAKE_CXX_COMPILER arm-linux-musleabihf-g++) # g++ 编译器程序名 + +set(CMAKE_FIND_ROOT_PATH /opt/cross/) # musl g++ 所在根目录 +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # 告诉CMake要从 ROOT_PATH 下查找g++等程序,但也可以从其他地方查找 +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 告诉CMake要从 ROOT_PATH 下查找library库文件 +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 告诉CMake要从 ROOT_PATH 下查找头文件 + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_C_COMPILER_TARGET arm-linux-musleabihf) # 使用musl libc 库的目标程序,如果是标准gnu库,可以注释掉本行 + +# set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++") + +# 设置编译选项 +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=vfpv3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=vfpv3") # fpu版本 + +# 设置链接选项 +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,/opt/cross/arm-linux-musleabihf/lib") From 321557d6a45572efd787fc0d1252aba22be5cd4e Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 14:48:25 +0800 Subject: [PATCH 14/20] exclude generate tests when target is arm64 --- CMakeLists.txt | 4 ---- tests/CMakeLists.txt | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d72440f..0f8ad2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,10 +30,6 @@ add_library(butterworth SHARED # 添加编译选项,按位置无关代码编译 target_compile_options(butterworth PRIVATE -fPIC) -# 编译测试主程序 main -add_executable(test-main main.cpp) -target_link_libraries(test-main PRIVATE butterworth -lsndfile) - # 添加测试 enable_testing() add_subdirectory(tests) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b7c60dd..ef5e573 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,14 +1,14 @@ enable_testing() -add_executable(main-test main-test.cpp) -add_test(NAME main-test COMMAND main-test) - -target_link_libraries(main-test PRIVATE butterworth -lsndfile) +if(NOT ARM64) + add_executable(main-test main-test.cpp) + add_test(NAME main-test COMMAND main-test) + target_link_libraries(main-test PRIVATE butterworth -lsndfile) +endif() add_executable(test-butter test-butter.cpp) add_test(NAME test-butter COMMAND test-butter) add_executable(test-conv test-conv.cpp) add_test(NAME test-conv COMMAND test-conv) -target_link_libraries(test-conv PRIVATE butterworth -lsndfile) - +target_link_libraries(test-conv PRIVATE butterworth) From 54652863e22712da945fbf550f323cfb70598352 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 15:09:03 +0800 Subject: [PATCH 15/20] =?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=9D=99=E6=80=81=E9=93=BE=E6=8E=A5=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f8ad2a..dec2278 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,13 +20,14 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchain-arm64.cmake) include_directories(${CMAKE_SOURCE_DIR}) -# lib_comfort库 -add_library(butterworth SHARED +# 生产动态库 +add_library(butterworth STATIC Butterworth.cpp Biquad.cpp conv.cpp sos2tf.cpp ) + # 添加编译选项,按位置无关代码编译 target_compile_options(butterworth PRIVATE -fPIC) From acb9f10c023081da7388247118dd4502d3b6184a Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 15:15:25 +0800 Subject: [PATCH 16/20] =?UTF-8?q?=E5=8E=BB=E6=8E=89=20-fPIC=20=E9=80=89?= =?UTF-8?q?=E9=A1=B9=EF=BC=8C=E5=9B=A0=E4=B8=BA=E9=9D=99=E6=80=81=E5=BA=93?= =?UTF-8?q?=E4=B8=8D=E9=9C=80=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dec2278..a1c700d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(butterworth STATIC ) # 添加编译选项,按位置无关代码编译 -target_compile_options(butterworth PRIVATE -fPIC) +# target_compile_options(butterworth PRIVATE -fPIC) # 添加测试 enable_testing() From 354b068afd38f3fe3920626fa072b80e53eef663 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 16:27:07 +0800 Subject: [PATCH 17/20] Need -fPIC for relocatable dynamic lib --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1c700d..dec2278 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(butterworth STATIC ) # 添加编译选项,按位置无关代码编译 -# target_compile_options(butterworth PRIVATE -fPIC) +target_compile_options(butterworth PRIVATE -fPIC) # 添加测试 enable_testing() From 0c4c045e0da2c58bdf6926b172aee7c7d7b1bbc6 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 16:52:38 +0800 Subject: [PATCH 18/20] Remove the execute attribute of .hpp --- catch.hpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 catch.hpp diff --git a/catch.hpp b/catch.hpp old mode 100755 new mode 100644 From deccd731b9d9e05d24a48c02b5d72b8e4d998535 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Thu, 2 Nov 2023 17:17:21 +0800 Subject: [PATCH 19/20] add document --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 760a766..feea772 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ The generated filter coefficients are split out into cascaded biquad sections, f * High order Parametric boost/cut EQ filter design *Biquad and Biquad Chain implementations (for filtering audio buffers with cascaded biquad sections) * Compact, readable and well-commented codebase - + * Convert from SOS to Transfer Function coefficients + * Convoluttion function ### Unit tests As with any good audio signal processing toolkit, there are unit tests that provide basic proof of correctness. There are currently 6 primary test cases that check 113 different conditions. @@ -19,9 +20,7 @@ Unit tests live in `main.cpp` and are written using the compact [Catch](https:// ### Prerequisites - * [SCONS](http://scons.org) as a cross-platform build system to build, test and run examples. - * On MacOS use [Homebrew](https://brew.sh): `$brew install scons` or [MacPorts](https://www.macports.org) `port install scons` - * On Linux: `apt-get install scons` + * cmake * [libsndfile](http://www.mega-nerd.com/libsndfile): `brew install libsndfile` ### Usage From 51ebe7d515fa53af2d15f960267aadcc7ed3e5d8 Mon Sep 17 00:00:00 2001 From: "bo.yang" Date: Sat, 4 Nov 2023 14:40:26 +0800 Subject: [PATCH 20/20] Change CMake comments to English --- toolchain-arm64.cmake | 24 ++++++++++++------------ toolchain-armv7-32b.cmake | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/toolchain-arm64.cmake b/toolchain-arm64.cmake index 642805c..92004c7 100644 --- a/toolchain-arm64.cmake +++ b/toolchain-arm64.cmake @@ -1,23 +1,23 @@ -set(CMAKE_SYSTEM_NAME Linux) # 操作系统类型 -set(CMAKE_SYSTEM_PROCESSOR aarch64) # CPU 架构 +set(CMAKE_SYSTEM_NAME Linux) # Operating system type +set(CMAKE_SYSTEM_PROCESSOR aarch64) # CPU architecture set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu) -set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) # gcc 编译器程序名 -set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) # g++ 编译器程序名 -set(CMAKE_Fortran_COMPILER aarch64-linux-gnu-gfortran) # g++ 编译器程序名 +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) # gcc compiler program name +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) # g++ compiler program name +set(CMAKE_Fortran_COMPILER aarch64-linux-gnu-gfortran) # g++ compiler program name -set(CMAKE_FIND_ROOT_PATH /opt/cross-gcc-linaro-6.3.1-arm64/) # g++ 所在根目录 -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # 告诉CMake要从 ROOT_PATH 下查找g++等程序,但也可以从其他地方查找 -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 告诉CMake要从 ROOT_PATH 下查找library库文件 -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 告诉CMake要从 ROOT_PATH 下查找头文件 +set(CMAKE_FIND_ROOT_PATH /opt/cross-gcc-linaro-6.3.1-arm64/) # Root directory of g++ +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # Tell CMake to look for programs like g++ from under ROOT_PATH, but it can also look elsewhere +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Tell CMake to look for library files from under ROOT_PATH +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Tell CMake to look for header files from under ROOT_PATH # set(CMAKE_PREFIX_PATH /opt/cross-gcc-linaro-6.3.1-arm64/arm-linux-gnueabihf) -# 是否静态链接 libc 和 libstdc++ +# Whether to statically link libc and libstdc++ # set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++") -# 设置编译选项 +# Set compilation options # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=vfpv3") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=vfpv3") # fpu版本 -# 设置链接选项 +# Set link options set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,/opt/cross-gcc-linaro-6.3.1-arm64/aarch64-linux-gnu/lib") diff --git a/toolchain-armv7-32b.cmake b/toolchain-armv7-32b.cmake index dd4eb9a..f18fd11 100644 --- a/toolchain-armv7-32b.cmake +++ b/toolchain-armv7-32b.cmake @@ -1,22 +1,22 @@ -set(CMAKE_SYSTEM_NAME Linux) # 操作系统类型 -set(CMAKE_SYSTEM_PROCESSOR arm) # CPU 架构 +set(CMAKE_SYSTEM_NAME Linux) # Operating system type +set(CMAKE_SYSTEM_PROCESSOR arm) # CPU architecture -set(CMAKE_C_COMPILER arm-linux-musleabihf-gcc) # gcc 编译器程序名 -set(CMAKE_CXX_COMPILER arm-linux-musleabihf-g++) # g++ 编译器程序名 +set(CMAKE_C_COMPILER arm-linux-musleabihf-gcc) # gcc compiler program name +set(CMAKE_CXX_COMPILER arm-linux-musleabihf-g++) # g++ compiler program name -set(CMAKE_FIND_ROOT_PATH /opt/cross/) # musl g++ 所在根目录 -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # 告诉CMake要从 ROOT_PATH 下查找g++等程序,但也可以从其他地方查找 -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 告诉CMake要从 ROOT_PATH 下查找library库文件 -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 告诉CMake要从 ROOT_PATH 下查找头文件 +set(CMAKE_FIND_ROOT_PATH /opt/cross/) # Root directory of g++ +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) # Tell CMake to look for programs like g++ from under ROOT_PATH, but it can also look elsewhere +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Tell CMake to look for library files from under ROOT_PATH +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Tell CMake to look for header files from under ROOT_PATH set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_C_COMPILER_TARGET arm-linux-musleabihf) # 使用musl libc 库的目标程序,如果是标准gnu库,可以注释掉本行 +set(CMAKE_C_COMPILER_TARGET arm-linux-musleabihf) # Target programs that use the musl libc library, if they are standard gnu libraries, can comment out this line # set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++") -# 设置编译选项 +# Set compilation options set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=vfpv3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=vfpv3") # fpu版本 -# 设置链接选项 +# Set link options set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,/opt/cross/arm-linux-musleabihf/lib")