37#include <unsupported/Eigen/FFT>
44#include <QtConcurrent>
59 const MatrixXd &matTaper,
64 fft.SetFlag(fft.HalfSpectrum);
67 if (vecData.cols() != matTaper.cols() || iNfft < vecData.cols()) {
72 RowVectorXd vecInputFFT;
73 RowVectorXcd vecTmpFreq;
74 MatrixXcd matTapSpectrum(matTaper.rows(),
int(floor(iNfft / 2.0)) + 1);
75 for (
int i=0; i < matTaper.rows(); i++) {
76 vecInputFFT = vecData.cwiseProduct(matTaper.row(i));
77 fft.fwd(vecTmpFreq, vecInputFFT, iNfft);
78 matTapSpectrum.row(i) = vecTmpFreq;
81 return matTapSpectrum;
87 const MatrixXd &matTaper,
91 #ifdef EIGEN_FFTW_DEFAULT
92 fftw_make_planner_thread_safe();
95 QVector<MatrixXcd> finalResult;
105 fft.SetFlag(fft.HalfSpectrum);
107 RowVectorXd vecInputFFT, rowData;
108 RowVectorXcd vecTmpFreq;
110 MatrixXcd matTapSpectrum(matTaper.rows(),
int(floor(iNfft / 2.0)) + 1);
112 for (
int i = 0; i < matData.rows(); ++i) {
113 rowData = matData.row(i);
116 for (j = 0; j < matTaper.rows(); j++) {
117 vecInputFFT = rowData.cwiseProduct(matTaper.row(j));
118 fft.fwd(vecTmpFreq, vecInputFFT, iNfft);
119 matTapSpectrum.row(j) = vecTmpFreq;
122 finalResult.append(matTapSpectrum);
133 QList<TaperedSpectraInputData> lData;
136 dataTemp.
iNfft = iNfft;
138 for (
int i = 0; i < matData.rows(); ++i) {
139 dataTemp.
vecData = matData.row(i);
141 lData.append(dataTemp);
144 QFuture<QVector<MatrixXcd> > result = QtConcurrent::mappedReduced(lData,
147 QtConcurrent::OrderedReduce);
148 result.waitForFinished();
149 finalResult = result.result();
168 const MatrixXcd& resultData)
171 finalData.append(resultData);
177 const Eigen::VectorXd &vecTapWeights,
182 if (matTapSpectrum.rows() != vecTapWeights.rows()) {
183 return Eigen::RowVectorXd();
189 double denom = vecTapWeights.cwiseAbs2().sum() * dSampFreq;
190 Eigen::RowVectorXd vecPsd = 2.0 * (vecTapWeights.asDiagonal() * matTapSpectrum).cwiseAbs2().colwise().sum() / denom;
194 vecPsd.tail(1) /= 2.0;
203 const Eigen::MatrixXcd &vecTapSpectrumTarget,
204 const Eigen::VectorXd &vecTapWeightsSeed,
205 const Eigen::VectorXd &vecTapWeightsTarget,
214 if (vecTapSpectrumSeed.rows() != vecTapSpectrumTarget.rows()) {
215 return Eigen::MatrixXcd();
217 if (vecTapSpectrumSeed.cols() != vecTapSpectrumTarget.cols()) {
218 return Eigen::MatrixXcd();
220 if (vecTapSpectrumSeed.rows() != vecTapWeightsSeed.rows()) {
221 return Eigen::MatrixXcd();
223 if (vecTapSpectrumTarget.rows() != vecTapWeightsTarget.rows()) {
224 return Eigen::MatrixXcd();
234 double denom = sqrt(vecTapWeightsSeed.cwiseAbs2().sum()) * sqrt(vecTapWeightsTarget.cwiseAbs2().sum()) * dSampFreq;
235 Eigen::RowVectorXcd vecCsd = 2.0 * (vecTapWeightsSeed.asDiagonal() * vecTapSpectrumSeed).cwiseProduct((vecTapWeightsTarget.asDiagonal() * vecTapSpectrumTarget).conjugate()).colwise().sum() / denom;
244 vecCsd.tail(1) /= 2.0;
259 RowVectorXd vecFFTFreqs;
261 vecFFTFreqs = (dSampFreq / iNfft) * RowVectorXd::LinSpaced(iNfft / 2.0 + 1, 0.0, iNfft / 2.0);
263 vecFFTFreqs = (dSampFreq / iNfft) * RowVectorXd::LinSpaced((iNfft - 1) / 2.0 + 1, 0.0, (iNfft - 1) / 2.0);
272 QPair<MatrixXd, VectorXd> pairOut;
273 if (sWindowType ==
"hanning") {
274 pairOut.first = hanningWindow(iSignalLength);
275 pairOut.second = VectorXd::Ones(1);
276 }
else if (sWindowType ==
"ones") {
277 pairOut.first = MatrixXd::Ones(1, iSignalLength) / double(iSignalLength);
278 pairOut.second = VectorXd::Ones(1);
280 pairOut.first = hanningWindow(iSignalLength);
281 pairOut.second = VectorXd::Ones(1);
290 std::pair<MatrixXd, VectorXd> pairOut;
291 if (sWindowType ==
"hanning") {
292 pairOut.first = hanningWindow(iSignalLength);
293 pairOut.second = VectorXd::Ones(1);
294 }
else if (sWindowType ==
"ones") {
295 pairOut.first = MatrixXd::Ones(1, iSignalLength) / double(iSignalLength);
296 pairOut.second = VectorXd::Ones(1);
298 pairOut.first = hanningWindow(iSignalLength);
299 pairOut.second = VectorXd::Ones(1);
306MatrixXd Spectral::hanningWindow(
int iSignalLength)
308 MatrixXd matHann = MatrixXd::Zero(1, iSignalLength);
311 for (
int n = 0; n < iSignalLength; n++) {
312 matHann(0, n) = 0.5 - 0.5 * cos(2.0 *
M_PI * n / (iSignalLength - 1.0));
314 matHann.array() /= matHann.row(0).norm();
Multi-taper spectral estimation: tapered FFT, power and cross-spectral density, DPSS weighting.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Per-row input bundle for parallel multi-taper spectral estimation (data row, taper matrix,...
Eigen::RowVectorXd vecData
static Eigen::RowVectorXcd csdFromTaperedSpectra(const Eigen::MatrixXcd &vecTapSpectrumSeed, const Eigen::MatrixXcd &vecTapSpectrumTarget, const Eigen::VectorXd &vecTapWeightsSeed, const Eigen::VectorXd &vecTapWeightsTarget, int iNfft, double dSampFreq=1.0)
static QVector< Eigen::MatrixXcd > computeTaperedSpectraMatrix(const Eigen::MatrixXd &matData, const Eigen::MatrixXd &matTaper, int iNfft, bool bUseThreads=true)
static Eigen::RowVectorXd psdFromTaperedSpectra(const Eigen::MatrixXcd &matTapSpectrum, const Eigen::VectorXd &vecTapWeights, int iNfft, double dSampFreq=1.0)
static QPair< Eigen::MatrixXd, Eigen::VectorXd > generateTapers(int iSignalLength, const QString &sWindowType="hanning")
static Eigen::MatrixXcd compute(const TaperedSpectraInputData &inputData)
static Eigen::MatrixXcd computeTaperedSpectraRow(const Eigen::RowVectorXd &vecData, const Eigen::MatrixXd &matTaper, int iNfft)
static Eigen::VectorXd calculateFFTFreqs(int iNfft, double dSampFreq)
static void reduce(QVector< Eigen::MatrixXcd > &finalData, const Eigen::MatrixXcd &resultData)