DiFipp/include/Butterworth.tpp

272 wiersze
9.2 KiB
Plaintext

2019-01-04 08:45:22 +00:00
// Copyright (c) 2019, Vincent SAMY
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
2019-10-10 02:31:00 +00:00
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
2019-01-04 08:45:22 +00:00
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2019-10-10 02:31:00 +00:00
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2019-01-04 08:45:22 +00:00
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2019-10-10 02:31:00 +00:00
// The views and conclusions contained in the software and documentation are those
// of the authors and should not be interpreted as representing official policies,
// either expressed or implied, of the FreeBSD Project.
2018-12-14 11:30:44 +00:00
#include "BilinearTransform.h"
2018-12-14 09:58:52 +00:00
#include "polynome_functions.h"
2019-01-04 08:45:22 +00:00
namespace difi {
2018-12-14 09:58:52 +00:00
template <typename T>
std::pair<int, T> Butterworth<T>::findMinimumButter(T wPass, T wStop, T APass, T AStop)
{
2019-01-11 10:07:11 +00:00
Expects(wPass > T(0) && wPass < T(1));
Expects(wStop > T(0) && wPass < T(1));
T num = std::log10((std::pow(T(10), T(0.1) * std::abs(AStop)) - 1) / (std::pow(T(10), T(0.1) * std::abs(APass)) - 1));
// pre-warp
T fwPass = std::tan(T(0.5) * pi<T> * wPass);
T fwStop = std::tan(T(0.5) * pi<T> * wStop);
T w;
if (wPass < wStop)
w = std::abs(fwStop / fwPass);
else
w = std::abs(fwPass / fwStop);
T denum = T(2) * std::log10(w);
int order = static_cast<int>(std::ceil(num / denum));
T ctf = w / std::pow(std::pow(T(10), T(0.1) * std::abs(AStop)) - 1, T(1) / T(2 * order));
if (wPass < wStop)
ctf *= fwPass;
else
ctf = fwPass / ctf;
return std::pair<int, T>(order, T(2) * std::atan(ctf) / pi<T>);
}
2018-12-14 09:58:52 +00:00
template <typename T>
Butterworth<T>::Butterworth(Type type)
: m_type(type)
{
}
template <typename T>
2018-12-17 05:48:44 +00:00
Butterworth<T>::Butterworth(int order, T fc, T fs, Type type)
2018-12-14 09:58:52 +00:00
: m_type(type)
{
2019-01-11 10:07:11 +00:00
setFilterParameters(order, fc, fs);
2018-12-14 09:58:52 +00:00
}
template <typename T>
2018-12-18 09:23:09 +00:00
Butterworth<T>::Butterworth(int order, T fLower, T fUpper, T fs, Type type)
2018-12-18 07:16:47 +00:00
: m_type(type)
2018-12-14 09:58:52 +00:00
{
2019-01-11 10:07:11 +00:00
setFilterParameters(order, fLower, fUpper, fs);
2018-12-18 07:16:47 +00:00
}
template <typename T>
2018-12-18 09:23:09 +00:00
void Butterworth<T>::setFilterParameters(int order, T fc, T fs)
2018-12-18 07:16:47 +00:00
{
2019-01-11 10:07:11 +00:00
Expects(fc < fs / T(2));
2018-12-18 09:23:09 +00:00
initialize(order, fc, 0, fs);
2018-12-14 09:58:52 +00:00
}
template <typename T>
void Butterworth<T>::setFilterParameters(int order, T fLower, T fUpper, T fs)
2018-12-18 09:23:09 +00:00
{
2019-01-11 10:07:11 +00:00
Expects(fLower < fUpper);
initialize(order, fLower, fUpper, fs);
2018-12-18 09:23:09 +00:00
}
template <typename T>
void Butterworth<T>::initialize(int order, T f1, T f2, T fs)
2018-12-14 09:58:52 +00:00
{
2019-01-04 08:15:03 +00:00
// f1 = fc for LowPass/HighPass filter
// f1 = fLower, f2 = fUpper for BandPass/BandReject filter
2019-01-11 10:07:11 +00:00
Expects(order > 0);
Expects(f1 > 0 && fs > 0); // f2 must be > f1 check in setFilterParameters
2018-12-14 09:58:52 +00:00
m_order = order;
m_fs = fs;
2018-12-18 07:16:47 +00:00
if (m_type == Type::LowPass || m_type == Type::HighPass)
computeDigitalRep(f1);
2018-12-18 07:16:47 +00:00
else
computeBandDigitalRep(f1, f2); // For band-like filters
2018-12-14 09:58:52 +00:00
}
template <typename T>
2018-12-18 07:16:47 +00:00
void Butterworth<T>::computeDigitalRep(T fc)
2018-12-14 09:58:52 +00:00
{
// Continuous pre-warped frequency
T fpw = (m_fs / pi<T>)*std::tan(pi<T> * fc / m_fs);
2018-12-14 09:58:52 +00:00
// Compute poles
2018-12-18 07:16:47 +00:00
vectXc_t<T> poles(m_order);
2018-12-14 11:30:44 +00:00
std::complex<T> analogPole;
2018-12-18 07:16:47 +00:00
for (int k = 0; k < m_order; ++k) {
analogPole = generateAnalogPole(k + 1, fpw);
BilinearTransform<std::complex<T>>::SToZ(m_fs, analogPole, poles(k));
2018-12-14 09:58:52 +00:00
}
2018-12-18 07:16:47 +00:00
vectXc_t<T> zeros = generateAnalogZeros();
vectXc_t<T> a = VietaAlgo<std::complex<T>>::polyCoeffFromRoot(poles);
vectXc_t<T> b = VietaAlgo<std::complex<T>>::polyCoeffFromRoot(zeros);
2018-12-17 08:16:01 +00:00
vectX_t<T> aCoeff(m_order + 1);
vectX_t<T> bCoeff(m_order + 1);
2018-12-17 05:48:44 +00:00
for (int i = 0; i < m_order + 1; ++i) {
aCoeff(i) = a(i).real();
bCoeff(i) = b(i).real();
2018-12-14 09:58:52 +00:00
}
2018-12-17 05:48:44 +00:00
scaleAmplitude(aCoeff, bCoeff);
2020-03-30 08:15:34 +00:00
this->setCoeffs(std::move(aCoeff), std::move(bCoeff));
2018-12-14 09:58:52 +00:00
}
2018-12-14 11:30:44 +00:00
template <typename T>
2018-12-18 09:23:09 +00:00
void Butterworth<T>::computeBandDigitalRep(T fLower, T fUpper)
2018-12-14 11:30:44 +00:00
{
T fpw1 = (m_fs / pi<T>)*std::tan(pi<T> * fLower / m_fs);
T fpw2 = (m_fs / pi<T>)*std::tan(pi<T> * fUpper / m_fs);
2018-12-18 09:23:09 +00:00
T fpw0 = std::sqrt(fpw1 * fpw2);
2018-12-18 07:16:47 +00:00
vectXc_t<T> poles(2 * m_order);
std::pair<std::complex<T>, std::complex<T>> analogPoles;
for (int k = 0; k < m_order; ++k) {
2018-12-18 09:23:09 +00:00
analogPoles = generateBandAnalogPole(k + 1, fpw0, fpw2 - fpw1);
2019-01-04 04:23:22 +00:00
BilinearTransform<std::complex<T>>::SToZ(m_fs, analogPoles.first, poles(k));
BilinearTransform<std::complex<T>>::SToZ(m_fs, analogPoles.second, poles(m_order + k));
2018-12-18 07:16:47 +00:00
}
2018-12-18 09:23:09 +00:00
vectXc_t<T> zeros = generateAnalogZeros(fpw0);
2018-12-18 07:16:47 +00:00
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);
vectX_t<T> bCoeff(2 * m_order + 1);
for (int i = 0; i < 2 * m_order + 1; ++i) {
aCoeff(i) = a(i).real();
bCoeff(i) = b(i).real();
}
2018-12-18 09:23:09 +00:00
if (m_type == Type::BandPass)
scaleAmplitude(aCoeff, bCoeff, std::exp(std::complex<T>(T(0), T(2) * pi<T> * std::sqrt(fLower * fUpper) / m_fs)));
2018-12-18 09:23:09 +00:00
else
scaleAmplitude(aCoeff, bCoeff);
2020-03-30 08:15:34 +00:00
this->setCoeffs(std::move(aCoeff), std::move(bCoeff));
2018-12-18 07:16:47 +00:00
}
2018-12-14 11:30:44 +00:00
2018-12-18 07:16:47 +00:00
template <typename T>
std::complex<T> Butterworth<T>::generateAnalogPole(int k, T fpw1)
{
auto thetaK = [pi = pi<T>, order = m_order](int k) -> T {
2020-03-30 08:15:34 +00:00
return static_cast<float>(2 * k - 1) * pi / static_cast<float>(2 * order);
2018-12-14 11:30:44 +00:00
};
std::complex<T> analogPole(-std::sin(thetaK(k)), std::cos(thetaK(k)));
switch (m_type) {
case Type::HighPass:
return T(2) * pi<T> * fpw1 / analogPole;
2018-12-14 11:30:44 +00:00
case Type::LowPass:
return T(2) * pi<T> * fpw1 * analogPole;
2019-01-11 10:07:11 +00:00
default:
GSL_ASSUME(0);
2018-12-14 11:30:44 +00:00
}
}
template <typename T>
2018-12-18 09:23:09 +00:00
std::pair<std::complex<T>, std::complex<T>> Butterworth<T>::generateBandAnalogPole(int k, T fpw0, T bw)
2018-12-14 11:30:44 +00:00
{
auto thetaK = [pi = pi<T>, order = m_order](int k) -> T {
2020-03-30 08:15:34 +00:00
return static_cast<float>(2 * k - 1) * pi / static_cast<float>(2 * order);
2018-12-18 07:16:47 +00:00
};
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<T> * fpw0;
2018-12-18 09:23:09 +00:00
std::complex<T> s = T(0.5) * bw / fpw0;
2018-12-14 11:30:44 +00:00
switch (m_type) {
2018-12-18 07:16:47 +00:00
case Type::BandReject:
2018-12-18 09:23:09 +00:00
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));
2018-12-18 07:16:47 +00:00
return poles;
case Type::BandPass:
2018-12-18 09:23:09 +00:00
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));
2018-12-18 07:16:47 +00:00
return poles;
2019-01-11 10:07:11 +00:00
default:
GSL_ASSUME(0);
2018-12-18 07:16:47 +00:00
}
}
2018-12-14 11:30:44 +00:00
2018-12-18 07:16:47 +00:00
template <typename T>
2018-12-18 09:23:09 +00:00
vectXc_t<T> Butterworth<T>::generateAnalogZeros(T fpw0)
2018-12-18 07:16:47 +00:00
{
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();
2018-12-18 09:23:09 +00:00
case Type::BandReject: {
T w0 = T(2) * std::atan(pi<T> * fpw0 / m_fs);
2019-01-04 04:23:22 +00:00
return (vectXc_t<T>(2 * m_order) << vectXc_t<T>::Constant(m_order, std::exp(std::complex<T>(0, w0))), vectXc_t<T>::Constant(m_order, std::exp(std::complex<T>(0, -w0)))).finished();
2018-12-18 09:23:09 +00:00
}
2018-12-14 11:30:44 +00:00
case Type::LowPass:
default:
2018-12-18 07:16:47 +00:00
return vectXc_t<T>::Constant(m_order, std::complex<T>(-1));
2018-12-14 11:30:44 +00:00
}
}
template <typename T>
2018-12-18 09:23:09 +00:00
void Butterworth<T>::scaleAmplitude(const vectX_t<T>& aCoeff, Eigen::Ref<vectX_t<T>> bCoeff, const std::complex<T>& bpS)
2018-12-14 11:30:44 +00:00
{
2018-12-18 07:16:47 +00:00
T num = 0;
T denum = 0;
2018-12-14 11:30:44 +00:00
switch (m_type) {
case Type::HighPass:
2018-12-17 05:48:44 +00:00
for (int i = 0; i < m_order + 1; ++i) {
2018-12-14 11:30:44 +00:00
if (i % 2 == 0) {
2018-12-18 07:16:47 +00:00
num += aCoeff(i);
denum += bCoeff(i);
2018-12-14 11:30:44 +00:00
} else {
2018-12-18 07:16:47 +00:00
num -= aCoeff(i);
denum -= bCoeff(i);
2018-12-14 11:30:44 +00:00
}
}
break;
2018-12-18 09:23:09 +00:00
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:
2018-12-14 11:30:44 +00:00
case Type::LowPass:
default:
2018-12-18 07:16:47 +00:00
num = aCoeff.sum();
denum = bCoeff.sum();
2018-12-14 11:30:44 +00:00
break;
}
2018-12-18 07:16:47 +00:00
bCoeff *= num / denum;
2018-12-14 11:30:44 +00:00
}
2019-01-04 08:45:22 +00:00
} // namespace difi