v2.0.0
Loading...
Searching...
No Matches
multitaper_psd.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "multitaper_psd.h"
18#include "dpss.h"
19
20//=============================================================================================================
21// EIGEN INCLUDES
22//=============================================================================================================
23
24#include <Eigen/Core>
25//#ifndef EIGEN_FFTW_DEFAULT
26//#define EIGEN_FFTW_DEFAULT
27//#endif
28#include <unsupported/Eigen/FFT>
29
30//=============================================================================================================
31// STD INCLUDES
32//=============================================================================================================
33
34#include <cmath>
35
36//=============================================================================================================
37// USED NAMESPACES
38//=============================================================================================================
39
40using namespace UTILSLIB;
41using namespace Eigen;
42
43//=============================================================================================================
44// DEFINE MEMBER METHODS
45//=============================================================================================================
46
48 double sfreq,
49 double halfBandwidth,
50 int nTapers,
51 int nFft)
52{
53 const int nChannels = static_cast<int>(matData.rows());
54 const int nTimes = static_cast<int>(matData.cols());
55
56 if (nFft < 0)
57 nFft = nTimes;
58
59 const int nFreqs = nFft / 2 + 1;
60
61 // 1. Compute DPSS tapers; by default those mne.time_frequency.psd_array_multitaper uses
62 const bool lowBias = nTapers < 0;
63 DpssResult dpss = Dpss::compute(nTimes, halfBandwidth, lowBias ? static_cast<int>(2.0 * halfBandwidth) : nTapers, false, lowBias);
64 const int nTap = static_cast<int>(dpss.matTapers.rows());
65
66 // Compute eigenvalue weights for weighted averaging
67 double weightSum = 0.0;
68 for (int t = 0; t < nTap; ++t)
69 weightSum += dpss.vecEigenvalues[t];
70
72 result.matPsd = MatrixXd::Zero(nChannels, nFreqs);
73
74 // Build frequency axis
75 result.vecFreqs.resize(nFreqs);
76 for (int k = 0; k < nFreqs; ++k)
77 result.vecFreqs[k] = static_cast<double>(k) * sfreq / static_cast<double>(nFft);
78
79 Eigen::FFT<double> fft;
80
81 // 2. For each channel, apply each taper, FFT, accumulate weighted |X|^2
82 for (int ch = 0; ch < nChannels; ++ch) {
83 RowVectorXd psd = RowVectorXd::Zero(nFreqs);
84
85 for (int t = 0; t < nTap; ++t) {
86 // Apply taper element-wise
87 VectorXd tapered = (matData.row(ch).array() - matData.row(ch).mean()).transpose() * dpss.matTapers.row(t).transpose().array();
88
89 // Zero-pad to nFft if needed
90 VectorXd padded = VectorXd::Zero(nFft);
91 const int copyLen = std::min(nTimes, nFft);
92 padded.head(copyLen) = tapered.head(copyLen);
93
94 // Forward FFT
95 VectorXcd spec;
96 fft.fwd(spec, padded);
97
98 // Accumulate weighted |X[k]|^2
99 const double w = dpss.vecEigenvalues[t];
100 for (int k = 0; k < nFreqs; ++k)
101 psd[k] += w * std::norm(spec[k]);
102 }
103
104 // Normalise: divide by (weightSum × sfreq)
105 const double dNorm = 1.0 / (weightSum * sfreq);
106 psd *= dNorm;
107
108 // One-sided: double all bins that have a mirror image (not DC, not an even-length Nyquist)
109 const int kLast = (nFft % 2 == 0) ? nFreqs - 1 : nFreqs;
110 for (int k = 1; k < kLast; ++k)
111 psd[k] *= 2.0;
112
113 result.matPsd.row(ch) = psd;
114 }
115
116 return result;
117}
Thomson multitaper power-spectral-density estimator.
Discrete Prolate Spheroidal Sequences (Slepian tapers) for multitaper spectral estimation.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Result of a DPSS taper computation.
Definition dpss.h:56
Eigen::MatrixXd matTapers
nTapers × N, each row is a unit-norm taper
Definition dpss.h:57
Eigen::VectorXd vecEigenvalues
Concentration ratios, length nTapers.
Definition dpss.h:58
static DpssResult compute(int nSamples, double halfBandwidth, int nTapers=-1, bool sym=true, bool lowBias=false)
Definition dpss.cpp:48
Result of a multitaper PSD computation.
Eigen::RowVectorXd vecFreqs
Frequency axis in Hz, length nFft/2+1.
Eigen::MatrixXd matPsd
n_channels × n_freqs; one-sided PSD in unit²/Hz
static MultitaperPsdResult compute(const Eigen::MatrixXd &matData, double sfreq, double halfBandwidth=4.0, int nTapers=-1, int nFft=-1)