37#include <Eigen/SparseCore>
41#include <unsupported/Eigen/FFT>
56 FilterParameter(QString(
"Tschebyscheff"), QString(
"A tschebyscheff filter"))
81, m_sFilterName(
"Unknown")
82, m_sFilterShortDescription(
"")
98, m_dCenterFreq(dCenterfreq)
99, m_dBandwidth(dBandwidth)
100, m_dParksWidth(dParkswidth)
101, m_iFilterOrder(iOrder)
102, m_iDesignMethod(iDesignMethod)
103, m_iFilterType(iFilterType)
104, m_sFilterName(sFilterName)
105, m_sFilterShortDescription()
108 qWarning() <<
"[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
120 iFftLength = iDataSize + m_vecCoeff.cols();
122 iFftLength = pow(2, exp);
125 if(m_vecCoeff.cols() != (iFftLength/2+1)) {
126 fftTransformCoeffs(iFftLength);
133 bool bKeepOverhead)
const
136 RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
137 RowVectorXd vecFilteredTime = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
139 vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;
142 for(
int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
143 vecFilteredTime(i-m_vecCoeff.cols()) = vecDataZeroPad.segment(i-m_vecCoeff.cols(),m_vecCoeff.cols()) * m_vecCoeff.transpose();
148 return vecFilteredTime.segment(m_vecCoeff.cols()/2, vecData.cols());
151 return vecFilteredTime.head(vecData.cols()+m_vecCoeff.cols());
159 #ifdef EIGEN_FFTW_DEFAULT
160 fftw_make_planner_thread_safe();
164 int iFftLength = vecData.cols() + m_vecCoeff.cols();
166 iFftLength = pow(2, exp);
169 if(m_vecFftCoeff.cols() != (iFftLength/2+1)) {
170 fftTransformCoeffs(iFftLength);
174 Eigen::FFT<double> fft;
175 fft.SetFlag(fft.HalfSpectrum);
178 int iOriginalSize = vecData.cols();
179 if (vecData.cols() < iFftLength) {
180 int iResidual = iFftLength - vecData.cols();
181 vecData.conservativeResize(iFftLength);
182 vecData.tail(iResidual).setZero();
186 RowVectorXcd vecFreqData;
187 fft.fwd(vecFreqData, vecData, iFftLength);
190 vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();
193 fft.inv(vecData, vecFreqData);
197 vecData = vecData.segment(m_vecCoeff.cols()/2, iOriginalSize).eval();
199 vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
207 return m_sFilterName;
214 m_sFilterName = sFilterName;
235 return m_iFilterOrder;
242 m_iFilterOrder = iOrder;
249 return m_dCenterFreq;
256 m_dCenterFreq = dCenterFreq;
270 m_dBandwidth = dBandwidth;
277 return m_dParksWidth;
284 m_dParksWidth = dParksWidth;
291 return m_dHighpassFreq;
298 m_dHighpassFreq = dHighpassFreq;
305 return m_dLowpassFreq;
312 m_dLowpassFreq = dLowpassFreq;
326 m_vecCoeff = vecCoeff;
333 return m_vecFftCoeff;
340 m_vecFftCoeff = vecFftCoeff;
345bool FilterKernel::fftTransformCoeffs(
int iFftLength)
347 #ifdef EIGEN_FFTW_DEFAULT
348 fftw_make_planner_thread_safe();
351 if(m_vecCoeff.cols() > iFftLength) {
352 std::cout <<
"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length."<< std::endl;
357 Eigen::FFT<double> fft;
358 fft.SetFlag(fft.HalfSpectrum);
361 RowVectorXd vecInputFft;
362 if (m_vecCoeff.cols() < iFftLength) {
363 vecInputFft.setZero(iFftLength);
364 vecInputFft.block(0,0,1,m_vecCoeff.cols()) = m_vecCoeff;
366 vecInputFft = m_vecCoeff;
370 RowVectorXcd vecFreqData;
371 fft.fwd(vecFreqData, vecInputFft, iFftLength);
372 m_vecFftCoeff = vecFreqData;;
379void FilterKernel::designFilter()
382 int iFftLength = m_iFilterOrder;
384 iFftLength = pow(2, exp);
386 if(m_iDesignMethod == 0) {
387 double dSmallestFeatureHz = m_dParksWidth * (m_sFreq / 2.0);
388 const double dLowEdgeHz = std::max(0.0, (m_dCenterFreq - m_dBandwidth / 2.0) * (m_sFreq / 2.0));
389 const double dHighEdgeHz = std::max(0.0, (m_dCenterFreq + m_dBandwidth / 2.0) * (m_sFreq / 2.0));
390 const double dSingleCutoffHz = std::max(0.0, m_dCenterFreq * (m_sFreq / 2.0));
392 if(m_iFilterType == 0 || m_iFilterType == 1) {
393 if(dSingleCutoffHz > 0.0) {
394 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dSingleCutoffHz);
397 if(dLowEdgeHz > 0.0) {
398 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dLowEdgeHz);
400 if(dHighEdgeHz > 0.0) {
401 dSmallestFeatureHz = std::min(dSmallestFeatureHz, dHighEdgeHz);
405 dSmallestFeatureHz = std::max(0.5, dSmallestFeatureHz);
407 const int iRecommendedFftLength =
static_cast<int>(std::ceil((m_sFreq / dSmallestFeatureHz) * 8.0));
408 if(iRecommendedFftLength > iFftLength) {
409 int iRecommendedExp = ceil(
Numerics::log2(iRecommendedFftLength));
410 iFftLength = pow(2, iRecommendedExp);
414 switch(m_iDesignMethod) {
416 ParksMcClellan filter(m_iFilterOrder,
421 m_vecCoeff = filter.FirCoeff;
424 fftTransformCoeffs(iFftLength);
430 CosineFilter filtercos;
432 switch(m_iFilterType) {
434 filtercos = CosineFilter(iFftLength,
435 (m_dCenterFreq)*(m_sFreq/2.),
436 m_dParksWidth*(m_sFreq/2),
437 (m_dCenterFreq)*(m_sFreq/2),
438 m_dParksWidth*(m_sFreq/2),
445 filtercos = CosineFilter(iFftLength,
446 (m_dCenterFreq)*(m_sFreq/2),
447 m_dParksWidth*(m_sFreq/2),
448 (m_dCenterFreq)*(m_sFreq/2),
449 m_dParksWidth*(m_sFreq/2),
456 filtercos = CosineFilter(iFftLength,
457 (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2),
458 m_dParksWidth*(m_sFreq/2),
459 (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2),
460 m_dParksWidth*(m_sFreq/2),
468 m_vecCoeff.resize(m_iFilterOrder);
470 m_vecCoeff.head(m_iFilterOrder/2) = filtercos.
m_vecCoeff.tail(m_iFilterOrder/2);
471 m_vecCoeff.tail(m_iFilterOrder/2) = filtercos.
m_vecCoeff.head(m_iFilterOrder/2);
474 fftTransformCoeffs(iFftLength);
480 switch(m_iFilterType) {
483 m_dHighpassFreq = m_dCenterFreq*(m_sFreq/2);
487 m_dLowpassFreq = m_dCenterFreq*(m_sFreq/2);
492 m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2);
493 m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2);
503 QString description(
m_designMethods.at(m_iDesignMethod).getName() +
" - " + \
504 QString::number(m_dHighpassFreq,
'g',4) +
"Hz to " + QString::number(m_dLowpassFreq,
'g',4) +
"Hz - " \
505 "Ord: " + QString::number(m_iFilterOrder));
513 if(m_iDesignMethod < 0){
523 if(m_iFilterType < 0){
533 if(iDesignMethod < 0){
536 m_iDesignMethod = iDesignMethod;
547 m_iFilterType = iFilterType;
568 QString sDescription)
General numerical helpers: GCD, log2, histogram binning, baseline rescaling, sparsity tests.
Frequency-domain cosine-tapered (raised-cosine) FIR filter design.
Linear-phase FIR filter kernel with overlap-add FFT convolution back-end.
Parks–McClellan equiripple FIR design via the Remez exchange algorithm.
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)