v2.0.0
Loading...
Searching...
No Matches
multitaper_tfr.cpp
Go to the documentation of this file.
1//=============================================================================================================
33
34//=============================================================================================================
35// INCLUDES
36//=============================================================================================================
37
38#include "multitaper_tfr.h"
39#include "dpss.h"
40
41//=============================================================================================================
42// EIGEN INCLUDES
43//=============================================================================================================
44
45#include <Eigen/Core>
46//#ifndef EIGEN_FFTW_DEFAULT
47//#define EIGEN_FFTW_DEFAULT
48//#endif
49#include <unsupported/Eigen/FFT>
50
51//=============================================================================================================
52// STD INCLUDES
53//=============================================================================================================
54
55#include <cmath>
56
57//=============================================================================================================
58// USED NAMESPACES
59//=============================================================================================================
60
61using namespace UTILSLIB;
62using namespace Eigen;
63
64//=============================================================================================================
65// DEFINE MEMBER METHODS
66//=============================================================================================================
67
69 double sfreq,
70 int windowSize,
71 int stepSize,
72 double halfBandwidth,
73 int nTapers)
74{
75 const int nChannels = static_cast<int>(matData.rows());
76 const int nTimes = static_cast<int>(matData.cols());
77
78 if (stepSize < 0)
79 stepSize = windowSize / 2;
80
81 const int nFreqs = windowSize / 2 + 1;
82
83 // 1. Compute DPSS tapers for the window size
84 DpssResult dpss = Dpss::compute(windowSize, halfBandwidth, nTapers);
85 const int nTap = static_cast<int>(dpss.matTapers.rows());
86
87 // Compute eigenvalue weight sum
88 double weightSum = 0.0;
89 for (int t = 0; t < nTap; ++t)
90 weightSum += dpss.vecEigenvalues[t];
91
92 // 2. Determine number of time steps and build time/freq axes
93 int nSteps = 0;
94 for (int start = 0; start + windowSize <= nTimes; start += stepSize)
95 ++nSteps;
96
98
99 // Frequency axis
100 result.vecFreqs.resize(nFreqs);
101 for (int k = 0; k < nFreqs; ++k)
102 result.vecFreqs[k] = static_cast<double>(k) * sfreq / static_cast<double>(windowSize);
103
104 // Time axis: centre of each window in seconds
105 result.vecTimes.resize(nSteps);
106 for (int s = 0; s < nSteps; ++s) {
107 const int start = s * stepSize;
108 const double centre = static_cast<double>(start) + static_cast<double>(windowSize - 1) / 2.0;
109 result.vecTimes[s] = centre / sfreq;
110 }
111
112 // 3. Compute TFR for each channel
113 Eigen::FFT<double> fft;
114
115 result.tfrData.reserve(nChannels);
116 for (int ch = 0; ch < nChannels; ++ch) {
117 MatrixXd tfr = MatrixXd::Zero(nFreqs, nSteps);
118
119 int stepIdx = 0;
120 for (int start = 0; start + windowSize <= nTimes; start += stepSize) {
121 // Extract window segment
122 VectorXd segment = matData.row(ch).segment(start, windowSize).transpose();
123
124 // Apply each taper, FFT, accumulate weighted |X|^2
125 RowVectorXd psd = RowVectorXd::Zero(nFreqs);
126
127 for (int t = 0; t < nTap; ++t) {
128 VectorXd tapered = segment.array()
129 * dpss.matTapers.row(t).transpose().array();
130
131 VectorXcd spec;
132 fft.fwd(spec, tapered);
133
134 const double w = dpss.vecEigenvalues[t];
135 for (int k = 0; k < nFreqs; ++k)
136 psd[k] += w * std::norm(spec[k]);
137 }
138
139 // Normalise: divide by (weightSum × sfreq)
140 const double dNorm = 1.0 / (weightSum * sfreq);
141 psd *= dNorm;
142
143 // One-sided: double all non-DC, non-Nyquist bins
144 for (int k = 1; k < nFreqs - 1; ++k)
145 psd[k] *= 2.0;
146
147 tfr.col(stepIdx) = psd.transpose();
148 ++stepIdx;
149 }
150
151 result.tfrData.append(tfr);
152 }
153
154 return result;
155}
MultitaperTfr class declaration — sliding-window multitaper time-frequency representation.
Dpss class declaration — Discrete Prolate Spheroidal Sequences (Slepian tapers).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Result of a DPSS taper computation.
Definition dpss.h:62
Eigen::MatrixXd matTapers
nTapers × N, each row is a unit-norm taper
Definition dpss.h:63
Eigen::VectorXd vecEigenvalues
Concentration ratios, length nTapers.
Definition dpss.h:64
static DpssResult compute(int N, double halfBandwidth, int nTapers=-1)
Definition dpss.cpp:69
Result of a multitaper TFR computation.
QVector< Eigen::MatrixXd > tfrData
One matrix per channel, each: n_freqs × n_times.
Eigen::RowVectorXd vecTimes
Time axis in seconds (window centres).
Eigen::RowVectorXd vecFreqs
Frequency axis in Hz.
static MultitaperTfrResult compute(const Eigen::MatrixXd &matData, double sfreq, int windowSize=256, int stepSize=-1, double halfBandwidth=4.0, int nTapers=-1)