From eefc41fe300c7409d1583582a8ef657c1abbebee Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 12 Jun 2022 18:38:10 +1000 Subject: [PATCH] Filter: added a way to plot attenuation and phase lag for complex filters --- libraries/Filter/HarmonicNotchFilter.cpp | 2 + libraries/Filter/tests/plot_harmonics.gnu | 11 +++ libraries/Filter/tests/test_notchfilter.cpp | 89 ++++++++++++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100755 libraries/Filter/tests/plot_harmonics.gnu diff --git a/libraries/Filter/HarmonicNotchFilter.cpp b/libraries/Filter/HarmonicNotchFilter.cpp index 20733e58ec..9617b3af49 100644 --- a/libraries/Filter/HarmonicNotchFilter.cpp +++ b/libraries/Filter/HarmonicNotchFilter.cpp @@ -311,3 +311,5 @@ void HarmonicNotchFilterParams::save_params() instantiate template classes */ template class HarmonicNotchFilter; +template class HarmonicNotchFilter; + diff --git a/libraries/Filter/tests/plot_harmonics.gnu b/libraries/Filter/tests/plot_harmonics.gnu new file mode 100755 index 0000000000..a534c4994d --- /dev/null +++ b/libraries/Filter/tests/plot_harmonics.gnu @@ -0,0 +1,11 @@ +#!/usr/bin/gnuplot -persist +set y2tics 0,10 +set ytics nomirror +set style data linespoints +set key autotitle +set datafile separator "," +set key autotitle columnhead +set xlabel "Freq(Hz)" +set ylabel "Attenuation" +#set ylabel2 "PhaseLag(deg)" +plot "harmonicnotch_test.csv" using 1:2 axis x1y1, "harmonicnotch_test.csv" using 1:3 axis x1y2 diff --git a/libraries/Filter/tests/test_notchfilter.cpp b/libraries/Filter/tests/test_notchfilter.cpp index 1c44fddf49..4d1b90a149 100644 --- a/libraries/Filter/tests/test_notchfilter.cpp +++ b/libraries/Filter/tests/test_notchfilter.cpp @@ -2,6 +2,7 @@ #include #include +#include const AP_HAL::HAL& hal = AP_HAL::get_HAL(); @@ -30,7 +31,7 @@ TEST(NotchFilterTest, SineTest) const float test_freq = 47; const float attenuation_dB = 43; const float rate_hz = 2000; - double dt = 1.0 / rate_hz; + const double dt = 1.0 / rate_hz; const uint32_t period_samples = rate_hz / test_freq; const uint32_t periods = 1000; const float test_amplitude = 0.7; @@ -65,4 +66,90 @@ TEST(NotchFilterTest, SineTest) EXPECT_LE(err_pct, 1); } +/* + test attentuation versus frequency + This is a way to get a graph of the attenuation and phase lag for a complex filter setup + */ +TEST(NotchFilterTest, HarmonicNotchTest) +{ + const uint8_t num_test_freq = 150; + const uint8_t harmonics = 15; + const uint8_t num_harmonics = __builtin_popcount(harmonics); + const float base_freq = 46; + const float bandwidth = base_freq/2; + const float attenuation_dB = 60; + // number of identical filters chained together, simulating + // usage of per-motor notch filtering + const uint8_t chained_filters = 8; + const uint16_t rate_hz = 2000; + const uint32_t samples = 50000; + const float test_amplitude = 0.7; + const double dt = 1.0 / rate_hz; + + bool double_notch = false; + HarmonicNotchFilter filters[num_test_freq][chained_filters] {}; + struct { + double in; + double out; + double last_in; + double last_out; + uint32_t last_crossing; + uint32_t total_lag_samples; + uint32_t lag_count; + float get_lag_degrees(const float freq) const { + const float lag_avg = total_lag_samples/float(lag_count); + return (360.0 * lag_avg * freq) / rate_hz; + } + } integrals[num_test_freq] {}; + + for (uint8_t i=0; i= s/10) { + integrals[i].in += fabsf(sample) * dt; + integrals[i].out += fabsf(v) * dt; + } + if (sample >= 0 && integrals[i].last_in < 0) { + integrals[i].last_crossing = s; + } + if (v >= 0 && integrals[i].last_out < 0 && integrals[i].last_crossing != 0) { + integrals[i].total_lag_samples += s - integrals[i].last_crossing; + integrals[i].lag_count++; + } + integrals[i].last_in = sample; + integrals[i].last_out = v; + } + } + const char *csv_file = "harmonicnotch_test.csv"; + FILE *f = fopen(csv_file, "w"); + fprintf(f, "Freq(Hz),Ratio,Lag(deg)\n"); + for (uint8_t i=0; i