37#include <Eigen/SparseCore>
41#include <unsupported/Eigen/FFT>
56 FilterParameter(QString(
"Tschebyscheff"), QString(
"A tschebyscheff filter"))
79, m_sFilterName(
"Unknown")
80, m_sFilterShortDescription(
"")
96, m_dCenterFreq(dCenterfreq)
97, m_dBandwidth(dBandwidth)
98, m_dParksWidth(dParkswidth)
99, m_iFilterOrder(iOrder)
100, m_iDesignMethod(iDesignMethod)
101, m_iFilterType(iFilterType)
102, m_sFilterName(sFilterName)
103, m_sFilterShortDescription()
106 qWarning() <<
"[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
118 iFftLength = iDataSize + m_vecCoeff.cols();
120 iFftLength = pow(2, exp);
123 if (m_vecCoeff.cols() != (iFftLength / 2 + 1)) {
124 fftTransformCoeffs(iFftLength);
131 bool bKeepOverhead)
const
134 RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2 * m_vecCoeff.cols() + vecData.cols());
135 RowVectorXd vecFilteredTime = RowVectorXd::Zero(2 * m_vecCoeff.cols() + vecData.cols());
137 vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;
140 for (
int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
141 vecFilteredTime(i - m_vecCoeff.cols()) = vecDataZeroPad.segment(i - m_vecCoeff.cols(), m_vecCoeff.cols()) * m_vecCoeff.transpose();
145 if (!bKeepOverhead) {
146 return vecFilteredTime.segment(m_vecCoeff.cols() / 2, vecData.cols());
149 return vecFilteredTime.head(vecData.cols() + m_vecCoeff.cols());
157#ifdef EIGEN_FFTW_DEFAULT
158 fftw_make_planner_thread_safe();
162 int iFftLength = vecData.cols() + m_vecCoeff.cols();
164 iFftLength = pow(2, exp);
167 if (m_vecFftCoeff.cols() != (iFftLength / 2 + 1)) {
168 fftTransformCoeffs(iFftLength);
172 Eigen::FFT<double> fft;
173 fft.SetFlag(fft.HalfSpectrum);
176 int iOriginalSize = vecData.cols();
177 if (vecData.cols() < iFftLength) {
178 int iResidual = iFftLength - vecData.cols();
179 vecData.conservativeResize(iFftLength);
180 vecData.tail(iResidual).setZero();
184 RowVectorXcd vecFreqData;
185 fft.fwd(vecFreqData, vecData, iFftLength);
188 vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();
191 fft.inv(vecData, vecFreqData);
194 if (!bKeepOverhead) {
195 vecData = vecData.segment(m_vecCoeff.cols() / 2, iOriginalSize).eval();
197 vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
205 return m_sFilterName;
212 m_sFilterName = sFilterName;
233 return m_iFilterOrder;
240 m_iFilterOrder = iOrder;
247 return m_dCenterFreq;
254 m_dCenterFreq = dCenterFreq;
268 m_dBandwidth = dBandwidth;
275 return m_dParksWidth;
282 m_dParksWidth = dParksWidth;
289 return m_dHighpassFreq;
296 m_dHighpassFreq = dHighpassFreq;
303 return m_dLowpassFreq;
310 m_dLowpassFreq = dLowpassFreq;
324 m_vecCoeff = vecCoeff;
331 return m_vecFftCoeff;
338 m_vecFftCoeff = vecFftCoeff;
343bool FilterKernel::fftTransformCoeffs(
int iFftLength)
345#ifdef EIGEN_FFTW_DEFAULT
346 fftw_make_planner_thread_safe();
349 if (m_vecCoeff.cols() > iFftLength) {
350 std::cout <<
"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length." << std::endl;
355 Eigen::FFT<double> fft;
356 fft.SetFlag(fft.HalfSpectrum);
359 RowVectorXd vecInputFft;
360 if (m_vecCoeff.cols() < iFftLength) {
361 vecInputFft.setZero(iFftLength);
362 vecInputFft.block(0, 0, 1, m_vecCoeff.cols()) = m_vecCoeff;
364 vecInputFft = m_vecCoeff;
368 RowVectorXcd vecFreqData;
369 fft.fwd(vecFreqData, vecInputFft, iFftLength);
370 m_vecFftCoeff = vecFreqData;
378void FilterKernel::designFilter()
381 int iFftLength = m_iFilterOrder;
383 iFftLength = pow(2, exp);
385 if (m_iDesignMethod == 0) {
386 double dSmallestFeatureHz = m_dParksWidth * (m_sFreq / 2.0);
387 const double dLowEdgeHz = std::max(0.0, (m_dCenterFreq - m_dBandwidth / 2.0) * (m_sFreq / 2.0));
388 const double dHighEdgeHz = std::max(0.0, (m_dCenterFreq + m_dBandwidth / 2.0) * (m_sFreq / 2.0));
389 const double dSingleCutoffHz = std::max(0.0, m_dCenterFreq * (m_sFreq / 2.0));
391 if (m_iFilterType == 0 || m_iFilterType == 1) {
392 if (dSingleCutoffHz > 0.0) {
393 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dSingleCutoffHz);
396 if (dLowEdgeHz > 0.0) {
397 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dLowEdgeHz);
399 if (dHighEdgeHz > 0.0) {
400 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dHighEdgeHz);
404 dSmallestFeatureHz = std::max(0.5, dSmallestFeatureHz);
406 const int iRecommendedFftLength =
static_cast<int>(std::ceil((m_sFreq / dSmallestFeatureHz) * 8.0));
407 if (iRecommendedFftLength > iFftLength) {
408 int iRecommendedExp = ceil(
Numerics::log2(iRecommendedFftLength));
409 iFftLength = pow(2, iRecommendedExp);
413 switch (m_iDesignMethod) {
415 ParksMcClellan filter(m_iFilterOrder,
420 m_vecCoeff = filter.FirCoeff;
423 fftTransformCoeffs(iFftLength);
429 CosineFilter filtercos;
431 switch (m_iFilterType) {
433 filtercos = CosineFilter(iFftLength,
434 (m_dCenterFreq) * (m_sFreq / 2.),
435 m_dParksWidth * (m_sFreq / 2),
436 (m_dCenterFreq) * (m_sFreq / 2),
437 m_dParksWidth * (m_sFreq / 2),
444 filtercos = CosineFilter(iFftLength,
445 (m_dCenterFreq) * (m_sFreq / 2),
446 m_dParksWidth * (m_sFreq / 2),
447 (m_dCenterFreq) * (m_sFreq / 2),
448 m_dParksWidth * (m_sFreq / 2),
455 filtercos = CosineFilter(iFftLength,
456 (m_dCenterFreq + m_dBandwidth / 2) * (m_sFreq / 2),
457 m_dParksWidth * (m_sFreq / 2),
458 (m_dCenterFreq - m_dBandwidth / 2) * (m_sFreq / 2),
459 m_dParksWidth * (m_sFreq / 2),
467 m_vecCoeff.resize(m_iFilterOrder);
469 m_vecCoeff.head(m_iFilterOrder / 2) = filtercos.
m_vecCoeff.tail(m_iFilterOrder / 2);
470 m_vecCoeff.tail(m_iFilterOrder / 2) = filtercos.
m_vecCoeff.head(m_iFilterOrder / 2);
473 fftTransformCoeffs(iFftLength);
479 switch (m_iFilterType) {
482 m_dHighpassFreq = m_dCenterFreq * (m_sFreq / 2);
486 m_dLowpassFreq = m_dCenterFreq * (m_sFreq / 2);
491 m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth / 2) * (m_sFreq / 2);
492 m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth / 2) * (m_sFreq / 2);
502 QString description(
m_designMethods.at(m_iDesignMethod).getName() +
" - " +
503 QString::number(m_dHighpassFreq,
'g', 4) +
"Hz to " + QString::number(m_dLowpassFreq,
'g', 4) +
"Hz - "
505 QString::number(m_iFilterOrder));
513 if (m_iDesignMethod < 0) {
523 if (m_iFilterType < 0) {
533 if (iDesignMethod < 0) {
536 m_iDesignMethod = iDesignMethod;
544 if (iFilterType < 0) {
547 m_iFilterType = iFilterType;
568 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)