29#include <unsupported/Eigen/FFT>
65 const double nyquist = dSFreq / 2.0;
67 double dCenterfreq = 0.0;
68 double dBandwidth = 0.0;
69 const double dParkswidth = dTransition / nyquist;
71 int iFilterType =
static_cast<int>(type);
76 dCenterfreq = dCutoffLow / nyquist;
81 dCenterfreq = (dCutoffLow + dCutoffHigh) / dSFreq;
82 dBandwidth = (dCutoffHigh - dCutoffLow) / nyquist;
89 sName = QStringLiteral(
"LP_%1Hz").arg(dCutoffLow);
92 sName = QStringLiteral(
"HP_%1Hz").arg(dCutoffLow);
95 sName = QStringLiteral(
"BP_%1-%2Hz").arg(dCutoffLow).arg(dCutoffHigh);
98 sName = QStringLiteral(
"BS_%1-%2Hz").arg(dCutoffLow).arg(dCutoffHigh);
109 static_cast<int>(method));
117 RowVectorXd work = vecData;
128 RowVectorXd work = vecData;
132 RowVectorXd rev = work.reverse();
135 return rev.reverse();
142 const RowVectorXi& vecPicks)
144 MatrixXd result = matData;
146 if (vecPicks.size() == 0) {
148 for (
int i = 0; i < result.rows(); ++i) {
149 RowVectorXd row = result.row(i);
153 for (
int k = 0; k < vecPicks.size(); ++k) {
155 if (i < 0 || i >= result.rows())
157 RowVectorXd row = result.row(i);
171 const double nyquist = dSFreq / 2.0;
172 const bool highPass = dLFreq > 0.0;
173 const bool lowPass = dHFreq > 0.0;
174 const double lTrans = highPass ? std::min(std::max(0.25 * dLFreq, 2.0), dLFreq) : 0.0;
175 const double hTrans = lowPass ? std::min(std::max(0.25 * dHFreq, 2.0), nyquist - dHFreq) : 0.0;
176 const double narrowest = std::min(highPass ? lTrans : INFINITY, lowPass ? hTrans : INFINITY);
178 int nTaps = std::max(
static_cast<int>(std::ceil(3.3 / narrowest * dSFreq)), 1);
179 nTaps += (nTaps - 1) % 2;
182 std::vector<double> freq{0.0};
183 std::vector<double> gain{highPass ? 0.0 : 1.0};
185 freq.insert(freq.end(), {dLFreq - lTrans, dLFreq});
186 gain.insert(gain.end(), {0.0, 1.0});
189 freq.insert(freq.end(), {dHFreq, dHFreq + hTrans});
190 gain.insert(gain.end(), {1.0, 0.0});
192 freq.push_back(nyquist);
193 gain.push_back(gain.back());
196 RowVectorXd h = RowVectorXd::Zero(nTaps);
197 if (gain.back() == 1.0) {
200 for (
int k =
static_cast<int>(freq.size()) - 2; k >= 0; --k) {
201 if (gain[k] == gain[k + 1]) {
204 const double transition = (freq[k + 1] - freq[k]) / nyquist / 2.0;
205 int nStep =
static_cast<int>(std::nearbyint(3.3 / transition));
206 nStep += 1 - nStep % 2;
207 const double cutoff = (freq[k + 1] + freq[k]) / 2.0 / nyquist;
208 RowVectorXd step(nStep);
209 for (
int n = 0; n < nStep; ++n) {
210 const double m = n - (nStep - 1) / 2.0;
211 const double sinc = m == 0.0 ? 1.0 : std::sin(M_PI * cutoff * m) / (M_PI * cutoff * m);
212 const double window = nStep > 1 ? 0.54 - 0.46 * std::cos(2.0 * M_PI * n / (nStep - 1)) : 1.0;
213 step(n) = cutoff * sinc * window;
216 const int offset = (nTaps - nStep) / 2;
217 h.segment(offset, nStep) += (gain[k] == 0.0 ? -1.0 : 1.0) * step;
226 const RowVectorXd h =
designMne(dSFreq, dLFreq, dHFreq);
227 return overlapAddZeroPhase(matData, h, std::min<Index>(h.size(), matData.cols()) - 1);
235 const int nFreqs = 1 + (1 <<
static_cast<int>(std::ceil(std::log2(nTaps))));
236 RowVectorXcd fx(nFreqs);
238 for (
int i = 0; i < nFreqs; ++i) {
239 const double x =
static_cast<double>(i) / (nFreqs - 1);
240 while (k + 2 < freq.size() && x > freq[k + 1])
242 const double span = freq[k + 1] - freq[k];
243 const double g = span > 0.0 ? gain[k] + (gain[k + 1] - gain[k]) * (x - freq[k]) / span : gain[k + 1];
244 const double phase = -(nTaps - 1) / 2.0 * M_PI * x;
245 fx(i) = g * std::complex<double>(std::cos(phase), std::sin(phase));
248 fft.SetFlag(FFT<double>::HalfSpectrum);
250 fft.inv(full, fx, 2 * (nFreqs - 1));
253 RowVectorXd h(nTaps);
254 for (
int n = 0; n < nTaps; ++n) {
255 const double w = nTaps > 1 ? a0 - (1.0 - a0) * std::cos(2.0 * M_PI * n / (nTaps - 1)) : 1.0;
267 const int nTaps = 1 <<
static_cast<int>(std::ceil(std::log2(std::ceil(10.0 * dSFreq))));
268 const double nyquist = dSFreq / 2.0;
269 const RowVectorXd h =
firwin2(nTaps,
270 {0.0, (dLFreq - 0.5) / nyquist, dLFreq / nyquist, dHFreq / nyquist, (dHFreq + 0.5) / nyquist, 1.0},
271 {0.0, 0.0, 1.0, 1.0, 0.0, 0.0},
275 const Index nH = h.size();
277 RowVectorXd padded = RowVectorXd::Zero(2 * nH);
280 fft.fwd(spec, padded);
281 spec = spec.cwiseAbs2().cast<std::complex<double>>();
282 RowVectorXd autocorr;
283 fft.inv(autocorr, spec);
285 RowVectorXd hh(2 * nH - 1);
286 hh << autocorr.tail(nH - 1), autocorr.head(nH);
287 return overlapAddZeroPhase(matData, hh, std::min<Index>(nH, matData.cols()) - 1);
292MatrixXd FirFilter::overlapAddZeroPhase(
const MatrixXd& matData,
const RowVectorXd& h, Index nEdge)
295 const Index nTimes = matData.cols();
296 const Index nH = h.size();
297 if (nTimes == 0 || nH == 1) {
298 return matData * (nH == 1 ? h(0) : 1.0);
300 const Index nExt = nTimes + 2 * nEdge;
303 while (nFft < nExt + nH - 1)
307 RowVectorXd hPadded = RowVectorXd::Zero(nFft);
308 hPadded.head(nH) = h;
310 fft.fwd(hSpec, hPadded);
312 MatrixXd out(matData.rows(), nTimes);
313 RowVectorXd ext = RowVectorXd::Zero(nFft);
316 for (Index r = 0; r < matData.rows(); ++r) {
317 const RowVectorXd x = matData.row(r);
320 for (Index i = 0; i < nEdge; ++i) {
321 ext(i) = 2.0 * x(0) - x(nEdge - i);
322 ext(nEdge + nTimes + i) = 2.0 * x(nTimes - 1) - x(nTimes - 2 - i);
324 ext.segment(nEdge, nTimes) = x;
326 spec = spec.cwiseProduct(hSpec);
328 out.row(r) = conv.segment(nEdge + (nH - 1) / 2, nTimes);
Discoverable design / apply façade over the UTILSLIB::FilterKernel FIR engine.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
The FilterKernel class provides methods to create/design a FIR filter kernel.
void applyFftFilter(Eigen::RowVectorXd &vecData, bool bKeepOverhead=false)
FilterType
FIR filter type — mirrors IirFilter::FilterType for a uniform API.
static Eigen::MatrixXd filterData(const Eigen::MatrixXd &matData, double dSFreq, double dLFreq, double dHFreq)
static Eigen::MatrixXd applyZeroPhaseMatrix(const Eigen::MatrixXd &matData, FilterKernel &kernel, const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi())
static Eigen::MatrixXd filterDataZeroDouble(const Eigen::MatrixXd &matData, double dSFreq, double dLFreq, double dHFreq, FirWindow window=FirWindow::Hann)
static Eigen::RowVectorXd designMne(double dSFreq, double dLFreq, double dHFreq)
DesignMethod
FIR design method — wraps FilterKernel's iDesignMethod integer codes.
static Eigen::RowVectorXd applyZeroPhase(const Eigen::RowVectorXd &vecData, FilterKernel &kernel)
static Eigen::RowVectorXd firwin2(int nTaps, const std::vector< double > &freq, const std::vector< double > &gain, FirWindow window=FirWindow::Hann)
static FilterKernel design(int iOrder, FilterType type, double dCutoffLow, double dCutoffHigh, double dSFreq, double dTransition=5.0, DesignMethod method=Cosine)
static Eigen::RowVectorXd apply(const Eigen::RowVectorXd &vecData, FilterKernel &kernel)