28#include <unsupported/Eigen/FFT>
56 const int nChannels =
static_cast<int>(matData.rows());
57 const int nTimes =
static_cast<int>(matData.cols());
60 stepSize = windowSize / 2;
62 const int nFreqs = windowSize / 2 + 1;
66 const int nTap =
static_cast<int>(dpss.
matTapers.rows());
69 double weightSum = 0.0;
70 for (
int t = 0; t < nTap; ++t)
75 for (
int start = 0; start + windowSize <= nTimes; start += stepSize)
82 for (
int k = 0; k < nFreqs; ++k)
83 result.
vecFreqs[k] =
static_cast<double>(k) * sfreq /
static_cast<double>(windowSize);
87 for (
int s = 0; s < nSteps; ++s) {
88 const int start = s * stepSize;
89 const double centre =
static_cast<double>(start) +
static_cast<double>(windowSize - 1) / 2.0;
94 Eigen::FFT<double> fft;
96 result.
tfrData.reserve(nChannels);
97 for (
int ch = 0; ch < nChannels; ++ch) {
98 MatrixXd tfr = MatrixXd::Zero(nFreqs, nSteps);
101 for (
int start = 0; start + windowSize <= nTimes; start += stepSize) {
103 VectorXd segment = matData.row(ch).segment(start, windowSize).transpose();
106 RowVectorXd psd = RowVectorXd::Zero(nFreqs);
108 for (
int t = 0; t < nTap; ++t) {
109 VectorXd tapered = segment.array() * dpss.
matTapers.row(t).transpose().array();
112 fft.fwd(spec, tapered);
115 for (
int k = 0; k < nFreqs; ++k)
116 psd[k] += w * std::norm(spec[k]);
120 const double dNorm = 1.0 / (weightSum * sfreq);
124 for (
int k = 1; k < nFreqs - 1; ++k)
127 tfr.col(stepIdx) = psd.transpose();
141 const RowVectorXd& vecFreqs,
143 double timeBandwidth,
146 const int nChannels =
static_cast<int>(matData.rows());
147 const int nTimes =
static_cast<int>(matData.cols());
148 const int nFreqs =
static_cast<int>(vecFreqs.size());
149 const int nTap =
static_cast<int>(std::floor(timeBandwidth - 1.0));
153 result.
vecTimes = RowVectorXd::LinSpaced(nTimes, 0.0, (nTimes - 1) / sfreq);
154 for (
int ch = 0; ch < nChannels; ++ch)
155 result.
tfrData.append(MatrixXd::Zero(nFreqs, nTimes));
157 for (
int fi = 0; fi < nFreqs; ++fi) {
158 const double f = vecFreqs[fi];
159 const double tWin = nCycles / f;
161 const int nWav =
static_cast<int>(std::ceil(tWin / (1.0 / sfreq)));
164 double weightSum = 0.0;
165 for (
int m = 0; m < nTap; ++m) {
167 VectorXcd wavelet(nWav);
168 for (
int k = 0; k < nWav; ++k) {
169 const double phase = 2.0 * std::numbers::pi * f * (k * (1.0 / sfreq) - tWin / 2.0);
170 wavelet[k] = dpss.
matTapers(m, k) * std::complex<double>(std::cos(phase), std::sin(phase));
173 wavelet.array() -= wavelet.mean();
174 wavelet /= std::sqrt(0.5) * wavelet.norm();
180 const int offset = (nWav - 1) / 2;
181 for (
int ch = 0; ch < nChannels; ++ch) {
182 for (
int t = 0; t < nTimes; ++t) {
183 std::complex<double> sum(0.0, 0.0);
184 const int n = t + offset;
185 for (
int k = std::max(0, n - nTimes + 1); k <= std::min(nWav - 1, n); ++k)
186 sum += matData(ch, n - k) * wavelet[k];
187 result.
tfrData[ch](fi, t) += weight * std::norm(sum);
192 for (
int ch = 0; ch < nChannels; ++ch)
193 result.
tfrData[ch].row(fi) *= 2.0 / weightSum;
Sliding-window multitaper time-frequency representation.
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.
Eigen::MatrixXd matTapers
nTapers × N, each row is a unit-norm taper
Eigen::VectorXd vecEigenvalues
Concentration ratios, length nTapers.
static DpssResult compute(int nSamples, double halfBandwidth, int nTapers=-1, bool sym=true, bool lowBias=false)
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)
static MultitaperTfrResult computeArray(const Eigen::MatrixXd &matData, double sfreq, const Eigen::RowVectorXd &vecFreqs, double nCycles=7.0, double timeBandwidth=4.0, bool zeroMean=true)