29#include <unsupported/Eigen/FFT>
48constexpr double CSD_PI = 3.14159265358979323846;
62 const int nChannels =
static_cast<int>(matData.rows());
63 const int nTimes =
static_cast<int>(matData.cols());
64 const int nFreqsFull = nTimes / 2 + 1;
71 const int nTap =
static_cast<int>(dpss.
matTapers.rows());
74 double weightSum = 0.0;
75 for (
int t = 0; t < nTap; ++t)
79 const double freqRes = sfreq /
static_cast<double>(nTimes);
82 const int iBinMin = std::max(0,
static_cast<int>(std::ceil(fmin / freqRes)));
83 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
84 const int nFreqsSel = iBinMax - iBinMin + 1;
87 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
88 MatrixXcd::Zero(nChannels, nChannels));
90 Eigen::FFT<double> fft;
93 for (
int t = 0; t < nTap; ++t) {
97 MatrixXcd spectra(nChannels, nFreqsFull);
99 for (
int ch = 0; ch < nChannels; ++ch) {
100 VectorXd tapered = matData.row(ch).transpose().array()
101 * dpss.
matTapers.row(t).transpose().array();
104 fft.fwd(spec, tapered);
106 spectra.row(ch) = spec.head(nFreqsFull).transpose();
110 for (
int fi = 0; fi < nFreqsSel; ++fi) {
111 const int fBin = iBinMin + fi;
112 VectorXcd col = spectra.col(fBin);
113 csdAccum[
static_cast<std::size_t
>(fi)] += w * (col * col.adjoint());
122 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
124 for (
int fi = 0; fi < nFreqsSel; ++fi) {
125 csdAccum[
static_cast<std::size_t
>(fi)] /= weightSum;
126 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
127 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
132 meanCsd /=
static_cast<double>(nFreqsSel);
147 const int nChannels =
static_cast<int>(matData.rows());
148 const int nTimes =
static_cast<int>(matData.cols());
153 const int nFreqsFull = nFft / 2 + 1;
158 const double freqRes = sfreq /
static_cast<double>(nFft);
159 const int iBinMin = std::max(0,
static_cast<int>(std::ceil(fmin / freqRes)));
160 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
161 const int nFreqsSel = iBinMax - iBinMin + 1;
164 VectorXd hannWin(nFft);
165 for (
int n = 0; n < nFft; ++n) {
166 const double t =
static_cast<double>(n) /
static_cast<double>(nFft - 1);
167 hannWin[n] = 0.5 * (1.0 - std::cos(2.0 * CSD_PI * t));
169 const double winPow = hannWin.squaredNorm();
171 const int iStep = std::max(1,
static_cast<int>(std::round(
172 static_cast<double>(nFft) * (1.0 - overlap))));
175 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
176 MatrixXcd::Zero(nChannels, nChannels));
178 Eigen::FFT<double> fft;
182 for (
int start = 0; start + nFft <= nTimes; start += iStep) {
184 MatrixXcd spectra(nChannels, nFreqsFull);
186 for (
int ch = 0; ch < nChannels; ++ch) {
187 VectorXd seg = matData.row(ch).segment(start, nFft).transpose().array()
193 spectra.row(ch) = spec.head(nFreqsFull).transpose();
197 for (
int fi = 0; fi < nFreqsSel; ++fi) {
198 const int fBin = iBinMin + fi;
199 VectorXcd col = spectra.col(fBin);
200 csdAccum[
static_cast<std::size_t
>(fi)] += col * col.adjoint();
211 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
213 const double dNorm = (nSeg > 0) ? 1.0 / (
static_cast<double>(nSeg) * winPow) : 0.0;
215 for (
int fi = 0; fi < nFreqsSel; ++fi) {
216 csdAccum[
static_cast<std::size_t
>(fi)] *= dNorm;
217 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
218 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
223 meanCsd /=
static_cast<double>(nFreqsSel);
233 const RowVectorXd& frequencies,
236 const int nChannels =
static_cast<int>(matData.rows());
237 const int nTimes =
static_cast<int>(matData.cols());
238 const int nFreqs =
static_cast<int>(frequencies.size());
244 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
246 for (
int fi = 0; fi < nFreqs; ++fi) {
247 const double f = frequencies[fi];
248 const double sigma =
static_cast<double>(nCycles) / (2.0 * CSD_PI * f);
251 const int halfLen =
static_cast<int>(std::ceil(3.0 * sigma * sfreq));
252 const int wavLen = 2 * halfLen + 1;
255 VectorXcd wavelet(wavLen);
256 double normFactor = 0.0;
257 for (
int n = 0; n < wavLen; ++n) {
258 const double t = (
static_cast<double>(n) -
static_cast<double>(halfLen)) / sfreq;
259 const double gauss = std::exp(-t * t / (2.0 * sigma * sigma));
260 wavelet[n] = std::complex<double>(gauss * std::cos(2.0 * CSD_PI * f * t),
261 gauss * std::sin(2.0 * CSD_PI * f * t));
262 normFactor += gauss * gauss;
265 normFactor = std::sqrt(normFactor);
266 if (normFactor > 0.0)
267 wavelet /= normFactor;
270 MatrixXcd analytic(nChannels, nTimes);
272 for (
int ch = 0; ch < nChannels; ++ch) {
273 for (
int t = 0; t < nTimes; ++t) {
274 std::complex<double> sum(0.0, 0.0);
275 for (
int k = 0; k < wavLen; ++k) {
276 const int idx = t - halfLen + k;
277 if (idx >= 0 && idx < nTimes) {
278 sum += matData(ch, idx) * std::conj(wavelet[k]);
281 analytic(ch, t) = sum;
286 MatrixXcd csdAtFreq = MatrixXcd::Zero(nChannels, nChannels);
287 for (
int t = 0; t < nTimes; ++t) {
288 VectorXcd col = analytic.col(t);
289 csdAtFreq += col * col.adjoint();
291 csdAtFreq /=
static_cast<double>(nTimes);
294 meanCsd += csdAtFreq;
298 meanCsd /=
static_cast<double>(nFreqs);
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 N, double halfBandwidth, int nTapers=-1)