30#include <unsupported/Eigen/FFT>
49constexpr double CSD_PI = 3.14159265358979323846;
63 const int nChannels =
static_cast<int>(matData.rows());
64 const int nTimes =
static_cast<int>(matData.cols());
65 const int nFreqsFull = nTimes / 2 + 1;
71 const bool lowBias = nTapers < 0;
72 DpssResult dpss =
Dpss::compute(nTimes, halfBandwidth, lowBias ?
static_cast<int>(2.0 * halfBandwidth) : nTapers,
false, lowBias);
73 const int nTap =
static_cast<int>(dpss.
matTapers.rows());
76 double weightSum = 0.0;
77 for (
int t = 0; t < nTap; ++t)
81 const double freqRes = sfreq /
static_cast<double>(nTimes);
84 const int iBinMin = std::max(1,
static_cast<int>(std::ceil(fmin / freqRes)));
85 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
86 const int nFreqsSel = std::max(0, iBinMax - iBinMin + 1);
89 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
90 MatrixXcd::Zero(nChannels, nChannels));
92 Eigen::FFT<double> fft;
95 for (
int t = 0; t < nTap; ++t) {
99 MatrixXcd spectra(nChannels, nFreqsFull);
101 for (
int ch = 0; ch < nChannels; ++ch) {
102 VectorXd tapered = (matData.row(ch).array() - matData.row(ch).mean()).transpose() * dpss.
matTapers.row(t).transpose().array();
105 fft.fwd(spec, tapered);
107 spectra.row(ch) = spec.head(nFreqsFull).transpose();
110 spectra.col(nFreqsFull - 1) /= std::sqrt(2.0);
113 for (
int fi = 0; fi < nFreqsSel; ++fi) {
114 const int fBin = iBinMin + fi;
115 VectorXcd col = spectra.col(fBin);
116 csdAccum[
static_cast<std::size_t
>(fi)] += w * (col * col.adjoint());
125 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
127 for (
int fi = 0; fi < nFreqsSel; ++fi) {
129 csdAccum[
static_cast<std::size_t
>(fi)] *= 2.0 / (weightSum * sfreq);
130 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
131 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
136 meanCsd /=
static_cast<double>(nFreqsSel);
151 const int nChannels =
static_cast<int>(matData.rows());
152 const int nTimes =
static_cast<int>(matData.cols());
157 const int nFreqsFull = nFft / 2 + 1;
162 const double freqRes = sfreq /
static_cast<double>(nFft);
163 const int iBinMin = std::max(1,
static_cast<int>(std::ceil(fmin / freqRes)));
164 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
165 const int nFreqsSel = std::max(0, iBinMax - iBinMin + 1);
168 VectorXd hannWin(nFft);
169 for (
int n = 0; n < nFft; ++n) {
170 const double t =
static_cast<double>(n) /
static_cast<double>(nFft - 1);
171 hannWin[n] = 0.5 * (1.0 - std::cos(2.0 * CSD_PI * t));
174 const int iStep = std::max(1,
static_cast<int>(std::round(
static_cast<double>(nFft) * (1.0 - overlap))));
177 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
178 MatrixXcd::Zero(nChannels, nChannels));
180 Eigen::FFT<double> fft;
184 for (
int start = 0; start + nFft <= nTimes; start += iStep) {
186 MatrixXcd spectra(nChannels, nFreqsFull);
188 for (
int ch = 0; ch < nChannels; ++ch) {
189 VectorXd seg = matData.row(ch).segment(start, nFft).transpose();
190 seg = (seg.array() - seg.mean()) * hannWin.array();
195 spectra.row(ch) = spec.head(nFreqsFull).transpose();
198 spectra.col(0) /= std::sqrt(2.0);
200 spectra.col(nFreqsFull - 1) /= std::sqrt(2.0);
203 for (
int fi = 0; fi < nFreqsSel; ++fi) {
204 const int fBin = iBinMin + fi;
205 VectorXcd col = spectra.col(fBin);
206 csdAccum[
static_cast<std::size_t
>(fi)] += col * col.adjoint();
217 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
220 const double dNorm = (nSeg > 0) ? 2.0 * 8.0 / 3.0 / (
static_cast<double>(nSeg) * nFft * sfreq) : 0.0;
222 for (
int fi = 0; fi < nFreqsSel; ++fi) {
223 csdAccum[
static_cast<std::size_t
>(fi)] *= dNorm;
224 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
225 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
230 meanCsd /=
static_cast<double>(nFreqsSel);
240 const RowVectorXd& frequencies,
243 const int nChannels =
static_cast<int>(matData.rows());
244 const int nTimes =
static_cast<int>(matData.cols());
245 const int nFreqs =
static_cast<int>(frequencies.size());
251 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
253 for (
int fi = 0; fi < nFreqs; ++fi) {
256 const int wavLen =
static_cast<int>(wavelet.size());
259 MatrixXcd analytic(nChannels, nTimes);
261 for (
int ch = 0; ch < nChannels; ++ch) {
262 for (
int t = 0; t < nTimes; ++t) {
263 std::complex<double> sum(0.0, 0.0);
264 for (
int k = 0; k < wavLen; ++k) {
265 const int idx = t - halfLen + k;
266 if (idx >= 0 && idx < nTimes) {
267 sum += matData(ch, idx) * std::conj(wavelet[k]);
270 analytic(ch, t) = sum;
275 MatrixXcd csdAtFreq = MatrixXcd::Zero(nChannels, nChannels);
276 for (
int t = 0; t < nTimes; ++t) {
277 VectorXcd col = analytic.col(t);
278 csdAtFreq += col * col.adjoint();
281 csdAtFreq /=
static_cast<double>(nTimes) * sfreq;
284 meanCsd += csdAtFreq;
288 meanCsd /=
static_cast<double>(nFreqs);
Complex Morlet wavelet time-frequency representation (TFR).
Discrete Prolate Spheroidal Sequences (Slepian tapers) for multitaper spectral estimation.
Cross-spectral density (CSD) estimation via averaged windowed FFTs.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Result of a Cross-Spectral Density computation.
Eigen::MatrixXcd matCsd
n_channels × n_channels mean CSD across selected frequencies
Eigen::RowVectorXd vecFreqs
Frequency axis (Hz) for csdByFreq entries.
QVector< Eigen::MatrixXcd > csdByFreq
One n_ch × n_ch CSD matrix per selected frequency bin.
static CsdResult computeMultitaper(const Eigen::MatrixXd &matData, double sfreq, double fmin=0.0, double fmax=-1.0, double halfBandwidth=4.0, int nTapers=-1)
static CsdResult computeMorlet(const Eigen::MatrixXd &matData, double sfreq, const Eigen::RowVectorXd &frequencies, int nCycles=7)
static CsdResult computeFourier(const Eigen::MatrixXd &matData, double sfreq, double fmin=0.0, double fmax=-1.0, int nFft=256, double overlap=0.5)
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)
static Eigen::VectorXcd buildWavelet(double dFreq, double dSFreq, double dNCycles, int &halfLen, bool bZeroMean=false)