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