v2.0.0
Loading...
Searching...
No Matches
spectral.cpp
Go to the documentation of this file.
1//=============================================================================================================
25
26//=============================================================================================================
27// INCLUDES
28//=============================================================================================================
29
30#include "spectral.h"
31#include "math.h"
32
33//=============================================================================================================
34// EIGEN INCLUDES
35//=============================================================================================================
36
37#include <unsupported/Eigen/FFT>
38
39//=============================================================================================================
40// QT INCLUDES
41//=============================================================================================================
42
43#include <QtMath>
44#include <QtConcurrent>
45#include <QVector>
46
47//=============================================================================================================
48// USED NAMESPACES
49//=============================================================================================================
50
51using namespace UTILSLIB;
52using namespace Eigen;
53
54//=============================================================================================================
55// DEFINE MEMBER METHODS
56//=============================================================================================================
57
58MatrixXcd Spectral::computeTaperedSpectraRow(const RowVectorXd &vecData,
59 const MatrixXd &matTaper,
60 int iNfft)
61{
62 //qDebug() << "Spectral::computeTaperedSpectra Matrixwise";
63 FFT<double> fft;
64 fft.SetFlag(fft.HalfSpectrum);
65
66 //Check inputs
67 if (vecData.cols() != matTaper.cols() || iNfft < vecData.cols()) {
68 return MatrixXcd();
69 }
70
71 //FFT for freq domain returning the half spectrum
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;
79 }
80
81 return matTapSpectrum;
82}
83
84//=============================================================================================================
85
86QVector<MatrixXcd> Spectral::computeTaperedSpectraMatrix(const MatrixXd &matData,
87 const MatrixXd &matTaper,
88 int iNfft,
89 bool bUseThreads)
90{
91 #ifdef EIGEN_FFTW_DEFAULT
92 fftw_make_planner_thread_safe();
93 #endif
94
95 QVector<MatrixXcd> finalResult;
96
97 if(!bUseThreads) {
98 // Sequential
99// QElapsedTimer timer;
100// int iTime = 0;
101// int iTimeAll = 0;
102// timer.start();
103
104 FFT<double> fft;
105 fft.SetFlag(fft.HalfSpectrum);
106
107 RowVectorXd vecInputFFT, rowData;
108 RowVectorXcd vecTmpFreq;
109
110 MatrixXcd matTapSpectrum(matTaper.rows(), int(floor(iNfft / 2.0)) + 1);
111 int j;
112 for (int i = 0; i < matData.rows(); ++i) {
113 rowData = matData.row(i);
114
115 //FFT for freq domain returning the half spectrum
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;
120 }
121
122 finalResult.append(matTapSpectrum);
123
124// iTime = timer.elapsed();
125// qDebug() << QThread::currentThreadId() << "Spectral::computeTaperedSpectraMatrix - Row-wise computation:" << iTime;
126// iTimeAll += iTime;
127// timer.restart();
128 }
129
130// qDebug() << QThread::currentThreadId() << "Spectral::computeTaperedSpectraMatrix - Complete computation:" << iTimeAll;
131 } else {
132 // Parallel
133 QList<TaperedSpectraInputData> lData;
135 dataTemp.matTaper = matTaper;
136 dataTemp.iNfft = iNfft;
137
138 for (int i = 0; i < matData.rows(); ++i) {
139 dataTemp.vecData = matData.row(i);
140
141 lData.append(dataTemp);
142 }
143
144 QFuture<QVector<MatrixXcd> > result = QtConcurrent::mappedReduced(lData,
145 compute,
146 reduce,
147 QtConcurrent::OrderedReduce);
148 result.waitForFinished();
149 finalResult = result.result();
150 }
151
152 return finalResult;
153}
154
155//=============================================================================================================
156
158{
159 //qDebug() << "Spectral::compute";
160 return computeTaperedSpectraRow(inputData.vecData,
161 inputData.matTaper,
162 inputData.iNfft);
163}
164
165//=============================================================================================================
166
167void Spectral::reduce(QVector<MatrixXcd>& finalData,
168 const MatrixXcd& resultData)
169{
170 //qDebug() << "Spectral::reduce";
171 finalData.append(resultData);
172}
173
174//=============================================================================================================
175
176Eigen::RowVectorXd Spectral::psdFromTaperedSpectra(const Eigen::MatrixXcd &matTapSpectrum,
177 const Eigen::VectorXd &vecTapWeights,
178 int iNfft,
179 double dSampFreq)
180{
181 //Check inputs
182 if (matTapSpectrum.rows() != vecTapWeights.rows()) {
183 return Eigen::RowVectorXd();
184 }
185
186 //Compute PSD (average over tapers if necessary)
187 //Normalization via sFreq
188 //multiply by 2 due to half spectrum
189 double denom = vecTapWeights.cwiseAbs2().sum() * dSampFreq;
190 Eigen::RowVectorXd vecPsd = 2.0 * (vecTapWeights.asDiagonal() * matTapSpectrum).cwiseAbs2().colwise().sum() / denom;
191
192 vecPsd(0) /= 2.0;
193 if (iNfft % 2 == 0){
194 vecPsd.tail(1) /= 2.0;
195 }
196
197 return vecPsd;
198}
199
200//=============================================================================================================
201
202Eigen::RowVectorXcd Spectral::csdFromTaperedSpectra(const Eigen::MatrixXcd &vecTapSpectrumSeed,
203 const Eigen::MatrixXcd &vecTapSpectrumTarget,
204 const Eigen::VectorXd &vecTapWeightsSeed,
205 const Eigen::VectorXd &vecTapWeightsTarget,
206 int iNfft,
207 double dSampFreq)
208{
209// QElapsedTimer timer;
210// int iTime = 0;
211// timer.start();
212
213 //Check inputs
214 if (vecTapSpectrumSeed.rows() != vecTapSpectrumTarget.rows()) {
215 return Eigen::MatrixXcd();
216 }
217 if (vecTapSpectrumSeed.cols() != vecTapSpectrumTarget.cols()) {
218 return Eigen::MatrixXcd();
219 }
220 if (vecTapSpectrumSeed.rows() != vecTapWeightsSeed.rows()) {
221 return Eigen::MatrixXcd();
222 }
223 if (vecTapSpectrumTarget.rows() != vecTapWeightsTarget.rows()) {
224 return Eigen::MatrixXcd();
225 }
226
227// iTime = timer.elapsed();
228// qDebug() << QThread::currentThreadId() << "Spectral::csdFromTaperedSpectra timer - Prepare:" << iTime;
229// timer.restart();
230
231 // Compute PSD (average over tapers if necessary)
232 // Multiply by 2 due to half spectrum
233 // Normalize via sFreq
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;
236
237// iTime = timer.elapsed();
238// qDebug() << QThread::currentThreadId() << "Spectral::csdFromTaperedSpectra timer - compute PSD:" << iTime;
239// timer.restart();
240
241 //multiply first and last element by 2 due to half spectrum
242 vecCsd(0) /= 2.0;
243 if (iNfft % 2 == 0){
244 vecCsd.tail(1) /= 2.0;
245 }
246
247// iTime = timer.elapsed();
248// qDebug() << QThread::currentThreadId() << "Spectral::csdFromTaperedSpectra timer - half spectrum:" << iTime;
249// timer.restart();
250
251 return vecCsd;
252}
253
254//=============================================================================================================
255
256VectorXd Spectral::calculateFFTFreqs(int iNfft, double dSampFreq)
257{
258 //Compute FFT frequencies
259 RowVectorXd vecFFTFreqs;
260 if (iNfft % 2 == 0){
261 vecFFTFreqs = (dSampFreq / iNfft) * RowVectorXd::LinSpaced(iNfft / 2.0 + 1, 0.0, iNfft / 2.0);
262 } else {
263 vecFFTFreqs = (dSampFreq / iNfft) * RowVectorXd::LinSpaced((iNfft - 1) / 2.0 + 1, 0.0, (iNfft - 1) / 2.0);
264 }
265 return vecFFTFreqs;
266}
267
268//=============================================================================================================
269
270QPair<MatrixXd, VectorXd> Spectral::generateTapers(int iSignalLength, const QString &sWindowType)
271{
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);
279 } else {
280 pairOut.first = hanningWindow(iSignalLength);
281 pairOut.second = VectorXd::Ones(1);
282 }
283 return pairOut;
284}
285
286//=============================================================================================================
287
288std::pair<MatrixXd, VectorXd> Spectral::generateTapers(int iSignalLength, const std::string &sWindowType)
289{
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);
297 } else {
298 pairOut.first = hanningWindow(iSignalLength);
299 pairOut.second = VectorXd::Ones(1);
300 }
301 return pairOut;
302}
303
304//=============================================================================================================
305
306MatrixXd Spectral::hanningWindow(int iSignalLength)
307{
308 MatrixXd matHann = MatrixXd::Zero(1, iSignalLength);
309
310 //Main step of building the hanning window
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));
313 }
314 matHann.array() /= matHann.row(0).norm();
315
316 return matHann;
317}
#define M_PI
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,...
Definition spectral.h:70
Eigen::RowVectorXd vecData
Definition spectral.h:71
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)
Definition spectral.cpp:202
static QVector< Eigen::MatrixXcd > computeTaperedSpectraMatrix(const Eigen::MatrixXd &matData, const Eigen::MatrixXd &matTaper, int iNfft, bool bUseThreads=true)
Definition spectral.cpp:86
static Eigen::RowVectorXd psdFromTaperedSpectra(const Eigen::MatrixXcd &matTapSpectrum, const Eigen::VectorXd &vecTapWeights, int iNfft, double dSampFreq=1.0)
Definition spectral.cpp:176
static QPair< Eigen::MatrixXd, Eigen::VectorXd > generateTapers(int iSignalLength, const QString &sWindowType="hanning")
Definition spectral.cpp:270
static Eigen::MatrixXcd compute(const TaperedSpectraInputData &inputData)
Definition spectral.cpp:157
static Eigen::MatrixXcd computeTaperedSpectraRow(const Eigen::RowVectorXd &vecData, const Eigen::MatrixXd &matTaper, int iNfft)
Definition spectral.cpp:58
static Eigen::VectorXd calculateFFTFreqs(int iNfft, double dSampFreq)
Definition spectral.cpp:256
static void reduce(QVector< Eigen::MatrixXcd > &finalData, const Eigen::MatrixXcd &resultData)
Definition spectral.cpp:167