36#include <Eigen/SparseCore>
40#include <unsupported/Eigen/FFT>
55 FilterParameter(QString(
"Tschebyscheff"), QString(
"A tschebyscheff filter"))
78, m_sFilterName(
"Unknown")
79, m_sFilterShortDescription(
"")
95, m_dCenterFreq(dCenterfreq)
96, m_dBandwidth(dBandwidth)
97, m_dParksWidth(dParkswidth)
98, m_iFilterOrder(iOrder)
99, m_iDesignMethod(iDesignMethod)
100, m_iFilterType(iFilterType)
101, m_sFilterName(sFilterName)
102, m_sFilterShortDescription()
105 qWarning() <<
"[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
117 iFftLength = iDataSize + m_vecCoeff.cols();
119 iFftLength = pow(2, exp);
122 if (m_vecCoeff.cols() != (iFftLength / 2 + 1)) {
123 fftTransformCoeffs(iFftLength);
130 bool bKeepOverhead)
const
133 RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2 * m_vecCoeff.cols() + vecData.cols());
134 RowVectorXd vecFilteredTime = RowVectorXd::Zero(2 * m_vecCoeff.cols() + vecData.cols());
136 vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;
139 for (
int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
140 vecFilteredTime(i - m_vecCoeff.cols()) = vecDataZeroPad.segment(i - m_vecCoeff.cols(), m_vecCoeff.cols()) * m_vecCoeff.transpose();
144 if (!bKeepOverhead) {
145 return vecFilteredTime.segment(m_vecCoeff.cols() / 2, vecData.cols());
148 return vecFilteredTime.head(vecData.cols() + m_vecCoeff.cols());
156#ifdef EIGEN_FFTW_DEFAULT
157 fftw_make_planner_thread_safe();
161 int iFftLength = vecData.cols() + m_vecCoeff.cols();
163 iFftLength = pow(2, exp);
166 if (m_vecFftCoeff.cols() != (iFftLength / 2 + 1)) {
167 fftTransformCoeffs(iFftLength);
171 Eigen::FFT<double> fft;
172 fft.SetFlag(fft.HalfSpectrum);
175 int iOriginalSize = vecData.cols();
176 if (vecData.cols() < iFftLength) {
177 int iResidual = iFftLength - vecData.cols();
178 vecData.conservativeResize(iFftLength);
179 vecData.tail(iResidual).setZero();
183 RowVectorXcd vecFreqData;
184 fft.fwd(vecFreqData, vecData, iFftLength);
187 vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();
190 fft.inv(vecData, vecFreqData);
193 if (!bKeepOverhead) {
194 vecData = vecData.segment(m_vecCoeff.cols() / 2, iOriginalSize).eval();
196 vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
204 return m_sFilterName;
211 m_sFilterName = sFilterName;
232 return m_iFilterOrder;
239 m_iFilterOrder = iOrder;
246 return m_dCenterFreq;
253 m_dCenterFreq = dCenterFreq;
267 m_dBandwidth = dBandwidth;
274 return m_dParksWidth;
281 m_dParksWidth = dParksWidth;
288 return m_dHighpassFreq;
295 m_dHighpassFreq = dHighpassFreq;
302 return m_dLowpassFreq;
309 m_dLowpassFreq = dLowpassFreq;
323 m_vecCoeff = vecCoeff;
330 return m_vecFftCoeff;
337 m_vecFftCoeff = vecFftCoeff;
342bool FilterKernel::fftTransformCoeffs(
int iFftLength)
344#ifdef EIGEN_FFTW_DEFAULT
345 fftw_make_planner_thread_safe();
348 if (m_vecCoeff.cols() > iFftLength) {
349 qWarning().noquote().nospace() <<
"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length.";
354 Eigen::FFT<double> fft;
355 fft.SetFlag(fft.HalfSpectrum);
358 RowVectorXd vecInputFft;
359 if (m_vecCoeff.cols() < iFftLength) {
360 vecInputFft.setZero(iFftLength);
361 vecInputFft.block(0, 0, 1, m_vecCoeff.cols()) = m_vecCoeff;
363 vecInputFft = m_vecCoeff;
367 RowVectorXcd vecFreqData;
368 fft.fwd(vecFreqData, vecInputFft, iFftLength);
369 m_vecFftCoeff = vecFreqData;
377void FilterKernel::designFilter()
380 int iFftLength = m_iFilterOrder;
382 iFftLength = pow(2, exp);
384 if (m_iDesignMethod == 0) {
385 double dSmallestFeatureHz = m_dParksWidth * (m_sFreq / 2.0);
386 const double dLowEdgeHz = std::max(0.0, (m_dCenterFreq - m_dBandwidth / 2.0) * (m_sFreq / 2.0));
387 const double dHighEdgeHz = std::max(0.0, (m_dCenterFreq + m_dBandwidth / 2.0) * (m_sFreq / 2.0));
388 const double dSingleCutoffHz = std::max(0.0, m_dCenterFreq * (m_sFreq / 2.0));
390 if (m_iFilterType == 0 || m_iFilterType == 1) {
391 if (dSingleCutoffHz > 0.0) {
392 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dSingleCutoffHz);
395 if (dLowEdgeHz > 0.0) {
396 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dLowEdgeHz);
398 if (dHighEdgeHz > 0.0) {
399 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dHighEdgeHz);
403 dSmallestFeatureHz = std::max(0.5, dSmallestFeatureHz);
405 const int iRecommendedFftLength =
static_cast<int>(std::ceil((m_sFreq / dSmallestFeatureHz) * 8.0));
406 if (iRecommendedFftLength > iFftLength) {
407 int iRecommendedExp = ceil(
Numerics::log2(iRecommendedFftLength));
408 iFftLength = pow(2, iRecommendedExp);
412 switch (m_iDesignMethod) {
414 ParksMcClellan filter(m_iFilterOrder,
419 m_vecCoeff = filter.FirCoeff;
422 fftTransformCoeffs(iFftLength);
428 CosineFilter filtercos;
430 switch (m_iFilterType) {
432 filtercos = CosineFilter(iFftLength,
433 (m_dCenterFreq) * (m_sFreq / 2.),
434 m_dParksWidth * (m_sFreq / 2),
435 (m_dCenterFreq) * (m_sFreq / 2),
436 m_dParksWidth * (m_sFreq / 2),
443 filtercos = CosineFilter(iFftLength,
444 (m_dCenterFreq) * (m_sFreq / 2),
445 m_dParksWidth * (m_sFreq / 2),
446 (m_dCenterFreq) * (m_sFreq / 2),
447 m_dParksWidth * (m_sFreq / 2),
454 filtercos = CosineFilter(iFftLength,
455 (m_dCenterFreq + m_dBandwidth / 2) * (m_sFreq / 2),
456 m_dParksWidth * (m_sFreq / 2),
457 (m_dCenterFreq - m_dBandwidth / 2) * (m_sFreq / 2),
458 m_dParksWidth * (m_sFreq / 2),
466 m_vecCoeff.resize(m_iFilterOrder);
468 m_vecCoeff.head(m_iFilterOrder / 2) = filtercos.
m_vecCoeff.tail(m_iFilterOrder / 2);
469 m_vecCoeff.tail(m_iFilterOrder / 2) = filtercos.
m_vecCoeff.head(m_iFilterOrder / 2);
472 fftTransformCoeffs(iFftLength);
478 switch (m_iFilterType) {
481 m_dHighpassFreq = m_dCenterFreq * (m_sFreq / 2);
485 m_dLowpassFreq = m_dCenterFreq * (m_sFreq / 2);
490 m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth / 2) * (m_sFreq / 2);
491 m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth / 2) * (m_sFreq / 2);
501 QString description(
m_designMethods.at(m_iDesignMethod).getName() +
" - " +
502 QString::number(m_dHighpassFreq,
'g', 4) +
"Hz to " + QString::number(m_dLowpassFreq,
'g', 4) +
"Hz - "
504 QString::number(m_iFilterOrder));
512 if (m_iDesignMethod < 0) {
522 if (m_iFilterType < 0) {
532 m_iDesignMethod = (iDesignMethod >= 0 && iDesignMethod <
m_designMethods.size()) ? iDesignMethod : 0;
539 m_iFilterType = (iFilterType >= 0 && iFilterType <
m_filterTypes.size()) ? iFilterType : 0;
559 QString sDescription)
Parks–McClellan equiripple FIR design via the Remez exchange algorithm.
Frequency-domain cosine-tapered (raised-cosine) FIR filter design.
Linear-phase FIR filter kernel with overlap-add FFT convolution back-end.
General numerical helpers: GCD, log2, histogram binning, baseline rescaling, sparsity tests.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Eigen::RowVectorXd m_vecCoeff
Named filter-design parameter descriptor holding a human-readable name and description (e....
double getBandwidth() const
void setSamplingFrequency(double dSFreq)
double getParksWidth() const
double getCenterFrequency() const
void setCenterFrequency(double dCenterFreq)
double getSamplingFrequency() const
QString getShortDescription() const
Eigen::RowVectorXcd getFftCoefficients() const
void applyFftFilter(Eigen::RowVectorXd &vecData, bool bKeepOverhead=false)
void setHighpassFreq(double dHighpassFreq)
void setCoefficients(const Eigen::RowVectorXd &vecCoeff)
static QVector< FilterParameter > m_designMethods
Eigen::RowVectorXd applyConvFilter(const Eigen::RowVectorXd &vecData, bool bKeepOverhead=false) const
void setBandwidth(double dBandwidth)
void prepareFilter(int iDataSize)
void setFilterOrder(int iOrder)
void setFftCoefficients(const Eigen::RowVectorXcd &vecFftCoeff)
void setParksWidth(double dParksWidth)
FilterKernel()
FilterKernel creates a default FilterKernel object.
void setLowpassFreq(double dLowpassFreq)
static QVector< FilterParameter > m_filterTypes
void setName(const QString &sFilterName)
int getFilterOrder() const
Eigen::RowVectorXd getCoefficients() const
double getLowpassFreq() const
void setFilterType(int iFilterType)
double getHighpassFreq() const
FilterParameter getDesignMethod() const
void setDesignMethod(int iDesignMethod)
FilterParameter getFilterType() const
static double log2(const T d)