51#include <unsupported/Eigen/FFT>
70constexpr double CSD_PI = 3.14159265358979323846;
84 const int nChannels =
static_cast<int>(matData.rows());
85 const int nTimes =
static_cast<int>(matData.cols());
86 const int nFreqsFull = nTimes / 2 + 1;
93 const int nTap =
static_cast<int>(dpss.
matTapers.rows());
96 double weightSum = 0.0;
97 for (
int t = 0; t < nTap; ++t)
101 const double freqRes = sfreq /
static_cast<double>(nTimes);
104 const int iBinMin = std::max(0,
static_cast<int>(std::ceil(fmin / freqRes)));
105 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
106 const int nFreqsSel = iBinMax - iBinMin + 1;
109 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
110 MatrixXcd::Zero(nChannels, nChannels));
112 Eigen::FFT<double> fft;
115 for (
int t = 0; t < nTap; ++t) {
119 MatrixXcd spectra(nChannels, nFreqsFull);
121 for (
int ch = 0; ch < nChannels; ++ch) {
122 VectorXd tapered = matData.row(ch).transpose().array()
123 * dpss.
matTapers.row(t).transpose().array();
126 fft.fwd(spec, tapered);
128 spectra.row(ch) = spec.head(nFreqsFull).transpose();
132 for (
int fi = 0; fi < nFreqsSel; ++fi) {
133 const int fBin = iBinMin + fi;
134 VectorXcd col = spectra.col(fBin);
135 csdAccum[
static_cast<std::size_t
>(fi)] += w * (col * col.adjoint());
144 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
146 for (
int fi = 0; fi < nFreqsSel; ++fi) {
147 csdAccum[
static_cast<std::size_t
>(fi)] /= weightSum;
148 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
149 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
154 meanCsd /=
static_cast<double>(nFreqsSel);
169 const int nChannels =
static_cast<int>(matData.rows());
170 const int nTimes =
static_cast<int>(matData.cols());
175 const int nFreqsFull = nFft / 2 + 1;
180 const double freqRes = sfreq /
static_cast<double>(nFft);
181 const int iBinMin = std::max(0,
static_cast<int>(std::ceil(fmin / freqRes)));
182 const int iBinMax = std::min(nFreqsFull - 1,
static_cast<int>(std::floor(fmax / freqRes)));
183 const int nFreqsSel = iBinMax - iBinMin + 1;
186 VectorXd hannWin(nFft);
187 for (
int n = 0; n < nFft; ++n) {
188 const double t =
static_cast<double>(n) /
static_cast<double>(nFft - 1);
189 hannWin[n] = 0.5 * (1.0 - std::cos(2.0 * CSD_PI * t));
191 const double winPow = hannWin.squaredNorm();
193 const int iStep = std::max(1,
static_cast<int>(std::round(
194 static_cast<double>(nFft) * (1.0 - overlap))));
197 std::vector<MatrixXcd> csdAccum(
static_cast<std::size_t
>(nFreqsSel),
198 MatrixXcd::Zero(nChannels, nChannels));
200 Eigen::FFT<double> fft;
204 for (
int start = 0; start + nFft <= nTimes; start += iStep) {
206 MatrixXcd spectra(nChannels, nFreqsFull);
208 for (
int ch = 0; ch < nChannels; ++ch) {
209 VectorXd seg = matData.row(ch).segment(start, nFft).transpose().array()
215 spectra.row(ch) = spec.head(nFreqsFull).transpose();
219 for (
int fi = 0; fi < nFreqsSel; ++fi) {
220 const int fBin = iBinMin + fi;
221 VectorXcd col = spectra.col(fBin);
222 csdAccum[
static_cast<std::size_t
>(fi)] += col * col.adjoint();
233 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
235 const double dNorm = (nSeg > 0) ? 1.0 / (
static_cast<double>(nSeg) * winPow) : 0.0;
237 for (
int fi = 0; fi < nFreqsSel; ++fi) {
238 csdAccum[
static_cast<std::size_t
>(fi)] *= dNorm;
239 result.
csdByFreq[fi] = csdAccum[
static_cast<std::size_t
>(fi)];
240 result.
vecFreqs[fi] = (iBinMin + fi) * freqRes;
245 meanCsd /=
static_cast<double>(nFreqsSel);
255 const RowVectorXd& frequencies,
258 const int nChannels =
static_cast<int>(matData.rows());
259 const int nTimes =
static_cast<int>(matData.cols());
260 const int nFreqs =
static_cast<int>(frequencies.size());
266 MatrixXcd meanCsd = MatrixXcd::Zero(nChannels, nChannels);
268 for (
int fi = 0; fi < nFreqs; ++fi) {
269 const double f = frequencies[fi];
270 const double sigma =
static_cast<double>(nCycles) / (2.0 * CSD_PI * f);
273 const int halfLen =
static_cast<int>(std::ceil(3.0 * sigma * sfreq));
274 const int wavLen = 2 * halfLen + 1;
277 VectorXcd wavelet(wavLen);
278 double normFactor = 0.0;
279 for (
int n = 0; n < wavLen; ++n) {
280 const double t = (
static_cast<double>(n) -
static_cast<double>(halfLen)) / sfreq;
281 const double gauss = std::exp(-t * t / (2.0 * sigma * sigma));
282 wavelet[n] = std::complex<double>(gauss * std::cos(2.0 * CSD_PI * f * t),
283 gauss * std::sin(2.0 * CSD_PI * f * t));
284 normFactor += gauss * gauss;
287 normFactor = std::sqrt(normFactor);
288 if (normFactor > 0.0)
289 wavelet /= normFactor;
292 MatrixXcd analytic(nChannels, nTimes);
294 for (
int ch = 0; ch < nChannels; ++ch) {
295 for (
int t = 0; t < nTimes; ++t) {
296 std::complex<double> sum(0.0, 0.0);
297 for (
int k = 0; k < wavLen; ++k) {
298 const int idx = t - halfLen + k;
299 if (idx >= 0 && idx < nTimes) {
300 sum += matData(ch, idx) * std::conj(wavelet[k]);
303 analytic(ch, t) = sum;
308 MatrixXcd csdAtFreq = MatrixXcd::Zero(nChannels, nChannels);
309 for (
int t = 0; t < nTimes; ++t) {
310 VectorXcd col = analytic.col(t);
311 csdAtFreq += col * col.adjoint();
313 csdAtFreq /=
static_cast<double>(nTimes);
316 meanCsd += csdAtFreq;
320 meanCsd /=
static_cast<double>(nFreqs);
Csd class declaration — Cross-Spectral Density computation.
Dpss class declaration — Discrete Prolate Spheroidal Sequences (Slepian tapers).
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)