v2.0.0
Loading...
Searching...
No Matches
filterkernel.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "filterkernel.h"
42
43#include <math/numerics.h>
44
45#include "parksmcclellan.h"
46#include "cosinefilter.h"
47
48#include <iostream>
49
50//=============================================================================================================
51// QT INCLUDES
52//=============================================================================================================
53
54#include <QDebug>
55
56//=============================================================================================================
57// EIGEN INCLUDES
58//=============================================================================================================
59
60#include <Eigen/SparseCore>
61//#ifndef EIGEN_FFTW_DEFAULT
62//#define EIGEN_FFTW_DEFAULT
63//#endif
64#include <unsupported/Eigen/FFT>
65
66//=============================================================================================================
67// USED NAMESPACES
68//=============================================================================================================
69
70using namespace UTILSLIB;
71using namespace Eigen;
72
73//=============================================================================================================
74// INIT STATIC MEMBERS
75//=============================================================================================================
76
77QVector<UTILSLIB::FilterParameter> FilterKernel::m_designMethods ({
78 FilterParameter(QString("Cosine"), QString("A cosine filter")),
79 FilterParameter(QString("Tschebyscheff"), QString("A tschebyscheff filter"))
80// FilterParameter(QString("External"), QString("An external filter"))
81});
82QVector<UTILSLIB::FilterParameter> FilterKernel::m_filterTypes ({
83 FilterParameter(QString("LPF"), QString("An LPF filter")),
84 FilterParameter(QString("HPF"), QString("An HPF filter")),
85 FilterParameter(QString("BPF"), QString("A BPF filter")),
86 FilterParameter(QString("NOTCH"), QString("A NOTCH filter")),
87 FilterParameter(QString("UNKNOWN"), QString("An UNKNOWN filter"))
88});
89
90//=============================================================================================================
91// DEFINE MEMBER METHODS
92//=============================================================================================================
93
95: m_sFreq(1000)
96, m_dCenterFreq(0.5)
97, m_dBandwidth(0.1)
98, m_dParksWidth(0.1)
99, m_dLowpassFreq(40)
100, m_dHighpassFreq(4)
101, m_iFilterOrder(80)
102, m_iDesignMethod(m_designMethods.indexOf(FilterParameter("Cosine")))
103, m_iFilterType(m_filterTypes.indexOf(FilterParameter("BPF")))
104, m_sFilterName("Unknown")
105, m_sFilterShortDescription("")
106{
107 designFilter();
108}
109
110//=============================================================================================================
111
112FilterKernel::FilterKernel(const QString& sFilterName,
113 int iFilterType,
114 int iOrder,
115 double dCenterfreq,
116 double dBandwidth,
117 double dParkswidth,
118 double dSFreq,
119 int iDesignMethod)
120: m_sFreq(dSFreq)
121, m_dCenterFreq(dCenterfreq)
122, m_dBandwidth(dBandwidth)
123, m_dParksWidth(dParkswidth)
124, m_iFilterOrder(iOrder)
125, m_iDesignMethod(iDesignMethod)
126, m_iFilterType(iFilterType)
127, m_sFilterName(sFilterName)
128, m_sFilterShortDescription()
129{
130 if(iOrder < 9) {
131 qWarning() << "[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
132 }
133
134 designFilter();
135}
136
137//=============================================================================================================
138
140{
141 int iFftLength, exp;
142
143 iFftLength = iDataSize + m_vecCoeff.cols();
144 exp = ceil(Numerics::log2(iFftLength));
145 iFftLength = pow(2, exp);
146
147 // Transform coefficients anew if needed
148 if(m_vecCoeff.cols() != (iFftLength/2+1)) {
149 fftTransformCoeffs(iFftLength);
150 }
151}
152
153//=============================================================================================================
154
155RowVectorXd FilterKernel::applyConvFilter(const RowVectorXd& vecData,
156 bool bKeepOverhead) const
157{
158 //Do zero padding or mirroring depending on user input
159 RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
160 RowVectorXd vecFilteredTime = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
161
162 vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;
163
164 //Do the convolution
165 for(int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
166 vecFilteredTime(i-m_vecCoeff.cols()) = vecDataZeroPad.segment(i-m_vecCoeff.cols(),m_vecCoeff.cols()) * m_vecCoeff.transpose();
167 }
168
169 //Return filtered data
170 if(!bKeepOverhead) {
171 return vecFilteredTime.segment(m_vecCoeff.cols()/2, vecData.cols());
172 }
173
174 return vecFilteredTime.head(vecData.cols()+m_vecCoeff.cols());
175}
176
177//=============================================================================================================
178
179void FilterKernel::applyFftFilter(RowVectorXd& vecData,
180 bool bKeepOverhead)
181{
182 #ifdef EIGEN_FFTW_DEFAULT
183 fftw_make_planner_thread_safe();
184 #endif
185
186 // Make sure we always have the correct FFT length for the given input data and filter overlap
187 int iFftLength = vecData.cols() + m_vecCoeff.cols();
188 int exp = ceil(Numerics::log2(iFftLength));
189 iFftLength = pow(2, exp);
190
191 // Transform coefficients anew if needed
192 if(m_vecFftCoeff.cols() != (iFftLength/2+1)) {
193 fftTransformCoeffs(iFftLength);
194 }
195
196 //generate fft object
197 Eigen::FFT<double> fft;
198 fft.SetFlag(fft.HalfSpectrum);
199
200 // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
201 int iOriginalSize = vecData.cols();
202 if (vecData.cols() < iFftLength) {
203 int iResidual = iFftLength - vecData.cols();
204 vecData.conservativeResize(iFftLength);
205 vecData.tail(iResidual).setZero();
206 }
207
208 //fft-transform data sequence
209 RowVectorXcd vecFreqData;
210 fft.fwd(vecFreqData, vecData, iFftLength);
211
212 //perform frequency-domain filtering
213 vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();
214
215 //inverse-FFT
216 fft.inv(vecData, vecFreqData);
217
218 //Return filtered data
219 if(!bKeepOverhead) {
220 vecData = vecData.segment(m_vecCoeff.cols()/2, iOriginalSize).eval();
221 } else {
222 vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
223 }
224}
225
226//=============================================================================================================
227
229{
230 return m_sFilterName;
231}
232
233//=============================================================================================================
234
235void FilterKernel::setName(const QString& sFilterName)
236{
237 m_sFilterName = sFilterName;
238}
239
240//=============================================================================================================
241
243{
244 return m_sFreq;
245}
246
247//=============================================================================================================
248
250{
251 m_sFreq = dSFreq;
252}
253
254//=============================================================================================================
255
257{
258 return m_iFilterOrder;
259}
260
261//=============================================================================================================
262
264{
265 m_iFilterOrder = iOrder;
266}
267
268//=============================================================================================================
269
271{
272 return m_dCenterFreq;
273}
274
275//=============================================================================================================
276
277void FilterKernel::setCenterFrequency(double dCenterFreq)
278{
279 m_dCenterFreq = dCenterFreq;
280}
281
282//=============================================================================================================
283
285{
286 return m_dBandwidth;
287}
288
289//=============================================================================================================
290
291void FilterKernel::setBandwidth(double dBandwidth)
292{
293 m_dBandwidth = dBandwidth;
294}
295
296//=============================================================================================================
297
299{
300 return m_dParksWidth;
301}
302
303//=============================================================================================================
304
305void FilterKernel::setParksWidth(double dParksWidth)
306{
307 m_dParksWidth = dParksWidth;
308}
309
310//=============================================================================================================
311
313{
314 return m_dHighpassFreq;
315}
316
317//=============================================================================================================
318
319void FilterKernel::setHighpassFreq(double dHighpassFreq)
320{
321 m_dHighpassFreq = dHighpassFreq;
322}
323
324//=============================================================================================================
325
327{
328 return m_dLowpassFreq;
329}
330
331//=============================================================================================================
332
333void FilterKernel::setLowpassFreq(double dLowpassFreq)
334{
335 m_dLowpassFreq = dLowpassFreq;
336}
337
338//=============================================================================================================
339
340Eigen::RowVectorXd FilterKernel::getCoefficients() const
341{
342 return m_vecCoeff;
343}
344
345//=============================================================================================================
346
347void FilterKernel::setCoefficients(const Eigen::RowVectorXd& vecCoeff)
348{
349 m_vecCoeff = vecCoeff;
350}
351
352//=============================================================================================================
353
354Eigen::RowVectorXcd FilterKernel::getFftCoefficients() const
355{
356 return m_vecFftCoeff;
357}
358
359//=============================================================================================================
360
361void FilterKernel::setFftCoefficients(const Eigen::RowVectorXcd& vecFftCoeff)
362{
363 m_vecFftCoeff = vecFftCoeff;
364}
365
366//=============================================================================================================
367
368bool FilterKernel::fftTransformCoeffs(int iFftLength)
369{
370 #ifdef EIGEN_FFTW_DEFAULT
371 fftw_make_planner_thread_safe();
372 #endif
373
374 if(m_vecCoeff.cols() > iFftLength) {
375 std::cout <<"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length."<< std::endl;
376 return false;
377 }
378
379 //generate fft object
380 Eigen::FFT<double> fft;
381 fft.SetFlag(fft.HalfSpectrum);
382
383 // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
384 RowVectorXd vecInputFft;
385 if (m_vecCoeff.cols() < iFftLength) {
386 vecInputFft.setZero(iFftLength);
387 vecInputFft.block(0,0,1,m_vecCoeff.cols()) = m_vecCoeff;
388 } else {
389 vecInputFft = m_vecCoeff;
390 }
391
392 //fft-transform filter coeffs
393 RowVectorXcd vecFreqData;
394 fft.fwd(vecFreqData, vecInputFft, iFftLength);
395 m_vecFftCoeff = vecFreqData;;
396
397 return true;
398}
399
400//=============================================================================================================
401
402void FilterKernel::designFilter()
403{
404 // Make sure we only use a minimum needed FFT size
405 int iFftLength = m_iFilterOrder;
406 int exp = ceil(Numerics::log2(iFftLength));
407 iFftLength = pow(2, exp);
408
409 switch(m_iDesignMethod) {
410 case 1: {
411 ParksMcClellan filter(m_iFilterOrder,
412 m_dCenterFreq,
413 m_dBandwidth,
414 m_dParksWidth,
415 static_cast<ParksMcClellan::TPassType>(m_iFilterType));
416 m_vecCoeff = filter.FirCoeff;
417
418 //fft-transform m_vecCoeff in order to be able to perform frequency-domain filtering
419 fftTransformCoeffs(iFftLength);
420
421 break;
422 }
423
424 case 0: {
425 CosineFilter filtercos;
426
427 switch(m_iFilterType) {
428 case 0:
429 filtercos = CosineFilter(iFftLength,
430 (m_dCenterFreq)*(m_sFreq/2.),
431 m_dParksWidth*(m_sFreq/2),
432 (m_dCenterFreq)*(m_sFreq/2),
433 m_dParksWidth*(m_sFreq/2),
434 m_sFreq,
435 static_cast<CosineFilter::TPassType>(m_iFilterType));
436
437 break;
438
439 case 1:
440 filtercos = CosineFilter(iFftLength,
441 (m_dCenterFreq)*(m_sFreq/2),
442 m_dParksWidth*(m_sFreq/2),
443 (m_dCenterFreq)*(m_sFreq/2),
444 m_dParksWidth*(m_sFreq/2),
445 m_sFreq,
446 static_cast<CosineFilter::TPassType>(m_iFilterType));
447
448 break;
449
450 case 2:
451 filtercos = CosineFilter(iFftLength,
452 (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2),
453 m_dParksWidth*(m_sFreq/2),
454 (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2),
455 m_dParksWidth*(m_sFreq/2),
456 m_sFreq,
457 static_cast<CosineFilter::TPassType>(m_iFilterType));
458
459 break;
460 }
461
462 //This filter is designed in the frequency domain, hence the time domain impulse response need to be shortend by the users dependent number of taps
463 m_vecCoeff.resize(m_iFilterOrder);
464
465 m_vecCoeff.head(m_iFilterOrder/2) = filtercos.m_vecCoeff.tail(m_iFilterOrder/2);
466 m_vecCoeff.tail(m_iFilterOrder/2) = filtercos.m_vecCoeff.head(m_iFilterOrder/2);
467
468 //Now generate the fft version of the shortened impulse response
469 fftTransformCoeffs(iFftLength);
470
471 break;
472 }
473 }
474
475 switch(m_iFilterType) {
476 case 0:
477 m_dLowpassFreq = 0;
478 m_dHighpassFreq = m_dCenterFreq*(m_sFreq/2);
479 break;
480
481 case 1:
482 m_dLowpassFreq = m_dCenterFreq*(m_sFreq/2);
483 m_dHighpassFreq = 0;
484 break;
485
486 case 2:
487 m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2);
488 m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2);
489 break;
490 }
492}
493
494//=============================================================================================================
495
497{
498 QString description(m_designMethods.at(m_iDesignMethod).getName() + " - " + \
499 QString::number(m_dHighpassFreq,'g',4) + "Hz to " + QString::number(m_dLowpassFreq,'g',4) + "Hz - " \
500 "Ord: " + QString::number(m_iFilterOrder));
501 return description;
502}
503
504//=============================================================================================================
505
507{
508 if(m_iDesignMethod < 0){
509 return m_designMethods.at(0);
510 }
511 return m_designMethods.at(m_iDesignMethod);
512}
513
514//=============================================================================================================
515
517{
518 if(m_iFilterType < 0){
519 return m_filterTypes.at(0);
520 }
521 return m_filterTypes.at(m_iFilterType);
522}
523
524//=============================================================================================================
525
526void FilterKernel::setDesignMethod(int iDesignMethod)
527{
528 if(iDesignMethod < 0){
529 m_iDesignMethod = 0;
530 } else {
531 m_iDesignMethod = iDesignMethod;
532 }
533}
534
535//=============================================================================================================
536
537void FilterKernel::setFilterType(int iFilterType)
538{
539 if(iFilterType < 0){
540 m_iFilterType = 0;
541 } else {
542 m_iFilterType = iFilterType;
543 }
544}
545
546//=============================================================================================================
547
552
553//=============================================================================================================
554
556:FilterParameter(sName,"")
557{
558}
559
560//=============================================================================================================
561
563 QString sDescription)
564: m_sName(sName)
565, m_sDescription(sDescription)
566{
567
568}
569
570//=============================================================================================================
571
573{
574 return m_sName;
575}
576
577//=============================================================================================================
Declaration of the CosineFilter class.
The FilterKernel class represents a filter object that generates the FIR filter coefficients using Pa...
Numerics class declaration.
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)
QString getName() const
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)
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)
Definition numerics.h:205