kopia lustrzana https://github.com/vsamy/DiFipp
Add BandReject
rodzic
3ce8bb3a4f
commit
3ff2f09bdb
|
@ -10,6 +10,7 @@ namespace fratio {
|
|||
// https://www.dsprelated.com/showarticle/1119.php
|
||||
// https://www.dsprelated.com/showarticle/1135.php
|
||||
// https://www.dsprelated.com/showarticle/1128.php
|
||||
// https://www.dsprelated.com/showarticle/1131.php
|
||||
template <typename T>
|
||||
class Butterworth : public DigitalFilter<T> {
|
||||
public:
|
||||
|
@ -28,26 +29,24 @@ public:
|
|||
public:
|
||||
Butterworth(Type type = Type::LowPass);
|
||||
Butterworth(int order, T fc, T fs, Type type = Type::LowPass);
|
||||
Butterworth(int order, T bw, T fs, T fCenter, Type type = Type::BandPass);
|
||||
Butterworth(int order, T fLower, T fUpper, T fs, Type type = Type::BandPass);
|
||||
|
||||
void setFilterParameters(int order, T fc, T fs, T fCenter = T(0));
|
||||
void setFilterParameters(int order, T fc, T fs);
|
||||
void setFilterParameters(int order, T f0, T fLimit, T fs);
|
||||
|
||||
private:
|
||||
void initialize(int order, T fc, T fs, T fCenter = T(0)); // fc = bw for bandPass filter
|
||||
void initialize(int order, T f0, T fLimit, T fs); // f0 = fc for LowPass/HighPass filter
|
||||
void computeDigitalRep(T fc);
|
||||
void computeBandDigitalRep(T bw, T fCenter);
|
||||
void computeBandDigitalRep(T fLower, T fUpper);
|
||||
std::complex<T> generateAnalogPole(int k, T fpw);
|
||||
std::pair<std::complex<T>, std::complex<T>> generateBandAnalogPole(int k, T fpw1, T fpw2);
|
||||
vectXc_t<T> generateAnalogZeros();
|
||||
void scaleAmplitude(Eigen::Ref<vectX_t<T>> aCoeff, Eigen::Ref<vectX_t<T>> bCoeff);
|
||||
std::pair<std::complex<T>, std::complex<T>> generateBandAnalogPole(int k, T fpw0, T bw);
|
||||
vectXc_t<T> generateAnalogZeros(T f0 = T());
|
||||
void scaleAmplitude(const vectX_t<T>& aCoeff, Eigen::Ref<vectX_t<T>> bCoeff, const std::complex<T>& bpS = T());
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
int m_order;
|
||||
T m_fc;
|
||||
T m_bw;
|
||||
T m_fs;
|
||||
T m_fCenter;
|
||||
vectXc_t<T> m_poles;
|
||||
};
|
||||
|
||||
|
|
|
@ -13,41 +13,47 @@ template <typename T>
|
|||
Butterworth<T>::Butterworth(int order, T fc, T fs, Type type)
|
||||
: m_type(type)
|
||||
{
|
||||
initialize(order, fc, fs);
|
||||
initialize(order, fc, 0, fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Butterworth<T>::Butterworth(int order, T fc, T fs, T fCenter, Type type)
|
||||
Butterworth<T>::Butterworth(int order, T fLower, T fUpper, T fs, Type type)
|
||||
: m_type(type)
|
||||
{
|
||||
initialize(order, fc, fs, fCenter);
|
||||
initialize(order, fLower, fUpper, fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Butterworth<T>::setFilterParameters(int order, T fc, T fs, T fCenter)
|
||||
void Butterworth<T>::setFilterParameters(int order, T fc, T fs)
|
||||
{
|
||||
initialize(order, fc, fs, fCenter);
|
||||
initialize(order, fc, 0, fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Butterworth<T>::initialize(int order, T fc, T fs, T fCenter)
|
||||
void Butterworth<T>::setFilterParameters(int order, T f0, T fLimit, T fs)
|
||||
{
|
||||
initialize(order, f0, fLimit, fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Butterworth<T>::initialize(int order, T f0, T fLimit, T fs)
|
||||
{
|
||||
if (order <= 0) {
|
||||
m_status = FilterStatus::BAD_ORDER_SIZE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fc <= 0 || fs <= 0) {
|
||||
if (f0 <= 0 || fs <= 0) {
|
||||
m_status = FilterStatus::BAD_FREQUENCY_VALUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_type == Type::BandPass || m_type == Type::BandReject) && fCenter - fc / 2. <= 0) {
|
||||
if ((m_type == Type::BandPass || m_type == Type::BandReject) && f0 >= fLimit) {
|
||||
m_status = FilterStatus::BAD_BAND_FREQUENCY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_fc > m_fs / 2.) {
|
||||
if ((m_type == Type::LowPass || m_type == Type::HighPass) && f0 > fs / 2.) {
|
||||
m_status = FilterStatus::BAD_CUTOFF_FREQUENCY;
|
||||
return;
|
||||
}
|
||||
|
@ -55,9 +61,9 @@ void Butterworth<T>::initialize(int order, T fc, T fs, T fCenter)
|
|||
m_order = order;
|
||||
m_fs = fs;
|
||||
if (m_type == Type::LowPass || m_type == Type::HighPass)
|
||||
computeDigitalRep(fc);
|
||||
computeDigitalRep(f0);
|
||||
else
|
||||
computeBandDigitalRep(fc, fCenter); // For band-like filters
|
||||
computeBandDigitalRep(f0, fLimit); // For band-like filters
|
||||
|
||||
resetFilter();
|
||||
}
|
||||
|
@ -65,9 +71,8 @@ void Butterworth<T>::initialize(int order, T fc, T fs, T fCenter)
|
|||
template <typename T>
|
||||
void Butterworth<T>::computeDigitalRep(T fc)
|
||||
{
|
||||
m_fc = fc;
|
||||
// Continuous pre-warped frequency
|
||||
T fpw = (m_fs / PI) * std::tan(PI * m_fc / m_fs);
|
||||
T fpw = (m_fs / PI) * std::tan(PI * fc / m_fs);
|
||||
|
||||
// Compute poles
|
||||
vectXc_t<T> poles(m_order);
|
||||
|
@ -92,24 +97,21 @@ void Butterworth<T>::computeDigitalRep(T fc)
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void Butterworth<T>::computeBandDigitalRep(T bw, T fCenter)
|
||||
void Butterworth<T>::computeBandDigitalRep(T fLower, T fUpper)
|
||||
{
|
||||
m_bw = bw;
|
||||
m_fCenter = fCenter;
|
||||
T f1 = m_fCenter - m_bw / T(2);
|
||||
T f2 = m_fCenter + m_bw / T(2);
|
||||
T fpw1 = (m_fs / PI) * std::tan(PI * f1 / m_fs);
|
||||
T fpw2 = (m_fs / PI) * std::tan(PI * f2 / m_fs);
|
||||
T fpw1 = (m_fs / PI) * std::tan(PI * fLower / m_fs);
|
||||
T fpw2 = (m_fs / PI) * std::tan(PI * fUpper / m_fs);
|
||||
T fpw0 = std::sqrt(fpw1 * fpw2);
|
||||
|
||||
vectXc_t<T> poles(2 * m_order);
|
||||
std::pair<std::complex<T>, std::complex<T>> analogPoles;
|
||||
for (int k = 0; k < m_order; ++k) {
|
||||
analogPoles = generateBandAnalogPole(k + 1, fpw1, fpw2);
|
||||
analogPoles = generateBandAnalogPole(k + 1, fpw0, fpw2 - fpw1);
|
||||
BilinearTransform<std::complex<T>>::SToZ(m_fs, analogPoles.first, poles(2 * k));
|
||||
BilinearTransform<std::complex<T>>::SToZ(m_fs, analogPoles.second, poles(2 * k + 1));
|
||||
}
|
||||
|
||||
vectXc_t<T> zeros = generateAnalogZeros();
|
||||
vectXc_t<T> zeros = generateAnalogZeros(fpw0);
|
||||
vectXc_t<T> a = VietaAlgo<std::complex<T>>::polyCoeffFromRoot(poles);
|
||||
vectXc_t<T> b = VietaAlgo<std::complex<T>>::polyCoeffFromRoot(zeros);
|
||||
vectX_t<T> aCoeff(2 * m_order + 1);
|
||||
|
@ -119,15 +121,11 @@ void Butterworth<T>::computeBandDigitalRep(T bw, T fCenter)
|
|||
bCoeff(i) = b(i).real();
|
||||
}
|
||||
|
||||
std::complex<T> s = std::exp(std::complex<T>(T(0), T(2) * PI * std::sqrt(f1 * f2) / m_fs));
|
||||
std::complex<T> num(b(0));
|
||||
std::complex<T> denum(a(0));
|
||||
for (int i = 1; i < 2 * m_order + 1; ++i) {
|
||||
num = num * s + b(i);
|
||||
denum = denum * s + a(i);
|
||||
}
|
||||
std::complex<T> hf0 = num / denum;
|
||||
bCoeff *= T(1) / std::abs(hf0); // bCoeff *= 1 / abs(num / denum)
|
||||
if (m_type == Type::BandPass)
|
||||
scaleAmplitude(aCoeff, bCoeff, std::exp(std::complex<T>(T(0), T(2) * PI * std::sqrt(fLower * fUpper) / m_fs)));
|
||||
else
|
||||
scaleAmplitude(aCoeff, bCoeff);
|
||||
|
||||
setCoeffs(std::move(aCoeff), std::move(bCoeff));
|
||||
}
|
||||
|
||||
|
@ -149,7 +147,7 @@ std::complex<T> Butterworth<T>::generateAnalogPole(int k, T fpw1)
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::pair<std::complex<T>, std::complex<T>> Butterworth<T>::generateBandAnalogPole(int k, T fpw1, T fpw2)
|
||||
std::pair<std::complex<T>, std::complex<T>> Butterworth<T>::generateBandAnalogPole(int k, T fpw0, T bw)
|
||||
{
|
||||
auto thetaK = [pi = PI, order = m_order](int k) -> T {
|
||||
return (2 * k - 1) * pi / (2 * order);
|
||||
|
@ -157,28 +155,35 @@ std::pair<std::complex<T>, std::complex<T>> Butterworth<T>::generateBandAnalogPo
|
|||
|
||||
std::complex<T> analogPole(-std::sin(thetaK(k)), std::cos(thetaK(k)));
|
||||
std::pair<std::complex<T>, std::complex<T>> poles;
|
||||
std::complex<T> s0 = T(2) * PI * fpw0;
|
||||
std::complex<T> s = T(0.5) * bw / fpw0;
|
||||
switch (m_type) {
|
||||
case Type::BandReject:
|
||||
s /= analogPole;
|
||||
poles.first = s0 * (s + std::complex<T>(T(0), T(1)) * std::sqrt(T(1) - s * s));
|
||||
poles.second = s0 * (s - std::complex<T>(T(0), T(1)) * std::sqrt(T(1) - s * s));
|
||||
return poles;
|
||||
case Type::BandPass:
|
||||
default: {
|
||||
std::complex<T> fpw0 = std::sqrt(fpw1 * fpw2);
|
||||
std::complex<T> s = T(0.5) * (fpw2 - fpw1) * analogPole / fpw0;
|
||||
poles.first = T(2) * PI * fpw0 * (s + std::complex<T>(T(0), T(1)) * std::sqrt(std::complex<T>(T(1), T(0)) - s * s));
|
||||
poles.second = T(2) * PI * fpw0 * (s - std::complex<T>(T(0), T(1)) * std::sqrt(std::complex<T>(T(1), T(0)) - s * s));
|
||||
default:
|
||||
s *= analogPole;
|
||||
poles.first = s0 * (s + std::complex<T>(T(0), T(1)) * std::sqrt(T(1) - s * s));
|
||||
poles.second = s0 * (s - std::complex<T>(T(0), T(1)) * std::sqrt(T(1) - s * s));
|
||||
return poles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
vectXc_t<T> Butterworth<T>::generateAnalogZeros()
|
||||
vectXc_t<T> Butterworth<T>::generateAnalogZeros(T fpw0)
|
||||
{
|
||||
switch (m_type) {
|
||||
case Type::HighPass:
|
||||
return vectXc_t<T>::Constant(m_order, std::complex<T>(1));
|
||||
case Type::BandPass:
|
||||
return (vectXc_t<T>(2 * m_order) << vectXc_t<T>::Constant(m_order, std::complex<T>(-1)), vectXc_t<T>::Constant(m_order, std::complex<T>(1))).finished();
|
||||
case Type::BandReject: {
|
||||
T w0 = T(2) * std::atan2(PI * fpw0, m_fs); // 2 * atan2(fpw0, 4)??
|
||||
return (vectXc_t<T>(2 * m_order) << vectXc_t<T>::Constant(m_order, std::exp(std::complex<T>(0, fpw0))), vectXc_t<T>::Constant(m_order, std::exp(std::complex<T>(0, -fpw0)))).finished();
|
||||
}
|
||||
case Type::LowPass:
|
||||
default:
|
||||
return vectXc_t<T>::Constant(m_order, std::complex<T>(-1));
|
||||
|
@ -186,7 +191,7 @@ vectXc_t<T> Butterworth<T>::generateAnalogZeros()
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void Butterworth<T>::scaleAmplitude(Eigen::Ref<vectX_t<T>> aCoeff, Eigen::Ref<vectX_t<T>> bCoeff)
|
||||
void Butterworth<T>::scaleAmplitude(const vectX_t<T>& aCoeff, Eigen::Ref<vectX_t<T>> bCoeff, const std::complex<T>& bpS)
|
||||
{
|
||||
T num = 0;
|
||||
T denum = 0;
|
||||
|
@ -203,6 +208,17 @@ void Butterworth<T>::scaleAmplitude(Eigen::Ref<vectX_t<T>> aCoeff, Eigen::Ref<ve
|
|||
}
|
||||
}
|
||||
break;
|
||||
case Type::BandPass: {
|
||||
std::complex<T> numC(bCoeff(0));
|
||||
std::complex<T> denumC(aCoeff(0));
|
||||
for (int i = 1; i < 2 * m_order + 1; ++i) {
|
||||
denumC = denumC * bpS + aCoeff(i);
|
||||
numC = numC * bpS + bCoeff(i);
|
||||
}
|
||||
num = std::abs(denumC);
|
||||
denum = std::abs(numC);
|
||||
} break;
|
||||
case Type::BandReject:
|
||||
case Type::LowPass:
|
||||
default:
|
||||
num = aCoeff.sum();
|
||||
|
|
|
@ -15,8 +15,10 @@ struct System {
|
|||
int order = 5;
|
||||
T fc = 10;
|
||||
T fs = 100;
|
||||
T bw = 10;
|
||||
T fCenter = 10;
|
||||
T fLower = 5;
|
||||
T fUpper = 15;
|
||||
T f0 = 10;
|
||||
T fU = 11;
|
||||
// LP
|
||||
fratio::vectX_t<T> lpACoeffRes = (fratio::vectX_t<T>(6) << 1, -2.975422109745684, 3.806018119320413, -2.545252868330468, 0.881130075437837, -0.125430622155356).finished();
|
||||
fratio::vectX_t<T> lpBCoeffRes = (fratio::vectX_t<T>(6) << 0.001282581078961, 0.006412905394803, 0.012825810789607, 0.012825810789607, 0.006412905394803, 0.001282581078961).finished();
|
||||
|
@ -29,6 +31,10 @@ struct System {
|
|||
fratio::vectX_t<T> bpACoeffRes = (fratio::vectX_t<T>(11) << 1, -6.784299264603903, 21.577693329895588, -42.338550072279737, 56.729081385507655, -54.208087151300411, 37.399203252161037, -18.397491390111661, 6.180883710485754, -1.283022311577260, 0.125430622155356).finished();
|
||||
fratio::vectX_t<T> bpBCoeffRes = (fratio::vectX_t<T>(11) << 0.001282581078963, 0, -0.006412905394817, 0, 0.012825810789633, 0, -0.012825810789633, 0, 0.006412905394817, 0, -0.001282581078963).finished();
|
||||
fratio::vectX_t<T> bpResults = (fratio::vectX_t<T>(8) << 0.001282581078963, 0.011266576028733, 0.046195520115810, 0.116904647483408, 0.200574194600111, 0.232153315136604, 0.141350142008155, -0.086403129422609).finished();
|
||||
// BR
|
||||
fratio::vectX_t<T> brACoeffRes = (fratio::vectX_t<T>(11) << 1.000000000000000, -6.784299264603897, 21.577693329895553, -42.338550072279631, 56.729081385507484, -54.208087151300205, 37.399203252160873, -18.397491390111572, 6.180883710485723, -1.283022311577253, 0.125430622155356).finished();
|
||||
fratio::vectX_t<T> brBCoeffRes = (fratio::vectX_t<T>(11) << 0.354164181088899, -3.012700469326103, 12.021845263663774, -29.490886190815772, 49.130136704563000, -58.004276868015168, 49.130136704563000, -29.490886190815772, 12.021845263663774, -3.012700469326103, 0.354164181088899).finished();
|
||||
fratio::vectX_t<T> brResults = (fratio::vectX_t<T>(8) << 0.354164181088899, 0.098383686162154, 0.084355149987331, 0.375555141082278, 0.735622022349639, 1.008089442365644, 1.229578363722674, 1.537000959441760).finished();
|
||||
};
|
||||
|
||||
DISABLE_CONVERSION_WARNING_END
|
||||
|
@ -67,7 +73,7 @@ BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_HP_FILTER_DOUBLE, System<double>)
|
|||
|
||||
BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_BP_FILTER_FLOAT, System<float>)
|
||||
{
|
||||
auto bf = fratio::Butterworthf(order, bw, fs, fCenter);
|
||||
auto bf = fratio::Butterworthf(order, fLower, fUpper, fs);
|
||||
BOOST_REQUIRE_EQUAL(bf.aOrder(), bf.bOrder());
|
||||
test_coeffs(bpACoeffRes, bpBCoeffRes, bf, std::numeric_limits<float>::epsilon() * 1000);
|
||||
test_results(bpResults, data, bf, std::numeric_limits<float>::epsilon() * 10000);
|
||||
|
@ -75,8 +81,24 @@ BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_BP_FILTER_FLOAT, System<float>)
|
|||
|
||||
BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_BP_FILTER_DOUBLE, System<double>)
|
||||
{
|
||||
auto bf = fratio::Butterworthd(order, bw, fs, fCenter);
|
||||
auto bf = fratio::Butterworthd(order, fLower, fUpper, fs);
|
||||
BOOST_REQUIRE_EQUAL(bf.aOrder(), bf.bOrder());
|
||||
test_coeffs(bpACoeffRes, bpBCoeffRes, bf, std::numeric_limits<double>::epsilon() * 1000);
|
||||
test_results(bpResults, data, bf, std::numeric_limits<double>::epsilon() * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_BR_FILTER_FLOAT, System<float>)
|
||||
{
|
||||
auto bf = fratio::Butterworthf(order, fLower, fUpper, fs, fratio::Butterworthf::Type::BandReject);
|
||||
BOOST_REQUIRE_EQUAL(bf.aOrder(), bf.bOrder());
|
||||
test_coeffs(brACoeffRes, brBCoeffRes, bf, std::numeric_limits<float>::epsilon() * 10);
|
||||
test_results(brResults, data, bf, std::numeric_limits<float>::epsilon() * 10);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(BUTTERWORTH_BR_FILTER_DOUBLE, System<double>)
|
||||
{
|
||||
auto bf = fratio::Butterworthd(order, fLower, fUpper, fs, fratio::Butterworthd::Type::BandReject);
|
||||
BOOST_REQUIRE_EQUAL(bf.aOrder(), bf.bOrder());
|
||||
test_coeffs(brACoeffRes, brBCoeffRes, bf, std::numeric_limits<double>::epsilon() * 10);
|
||||
test_results(brResults, data, bf, std::numeric_limits<double>::epsilon() * 10);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@ BOOST_AUTO_TEST_CASE(FILTER_FAILURES)
|
|||
BOOST_REQUIRE(mad.status() == fratio::FilterStatus::BAD_ORDER_SIZE);
|
||||
auto bfd = fratio::Butterworthd(0, 10, 100);
|
||||
BOOST_REQUIRE(bfd.status() == fratio::FilterStatus::BAD_ORDER_SIZE);
|
||||
bfd = fratio::Butterworthd(5, 10, 100, 3);
|
||||
bfd = fratio::Butterworthd(5, 5, 6, 100);
|
||||
BOOST_REQUIRE(bfd.status() == fratio::FilterStatus::BAD_BAND_FREQUENCY);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue