57static constexpr double SSS_PI =
M_PI;
70MatrixXd regPinv(
const MatrixXd& A,
double reg = 1e-5)
72 JacobiSVD<MatrixXd>
svd(A, ComputeThinU | ComputeThinV);
73 const VectorXd& sv =
svd.singularValues();
74 double threshold = reg * sv(0);
76 for (
int i = 0; i < invSv.size(); ++i) {
77 invSv(i) = (sv(i) > threshold) ? 1.0 / sv(i) : 0.0;
79 return svd.matrixV() * invSv.asDiagonal() *
svd.matrixU().transpose();
86MatrixXd orthonormalCols(
const MatrixXd& A)
89 return MatrixXd(A.rows(), 0);
92 ColPivHouseholderQR<MatrixXd> qr(A);
93 qr.setThreshold(1e-12);
94 const int rank = qr.rank();
96 return MatrixXd(A.rows(), 0);
99 return qr.householderQ() * MatrixXd::Identity(A.rows(), rank);
108void SSS::computeNormALP(
int lmax,
double cosTheta,
double sinTheta,
109 MatrixXd& P, MatrixXd& dP)
112 if (sinTheta < 1e-12) {
116 const int sz = lmax + 2;
131 MatrixXd Praw(sz, sz);
135 Praw(1, 0) = cosTheta;
136 Praw(1, 1) = -sinTheta;
138 for (
int l = 2; l <= lmax + 1; ++l) {
139 Praw(l, l) = -(2 * l - 1) * sinTheta * Praw(l - 1, l - 1);
140 Praw(l, l - 1) = (2 * l - 1) * cosTheta * Praw(l - 1, l - 1);
141 for (
int m = 0; m <= l - 2; ++m) {
142 Praw(l, m) = ((2 * l - 1) * cosTheta * Praw(l - 1, m)
143 - (l - 1 + m) * Praw(l - 2, m))
144 /
static_cast<double>(l - m);
149 for (
int l = 1; l <= lmax; ++l) {
150 for (
int m = 0; m <= l; ++m) {
154 for (
int k = l - m + 1; k <= l + m; ++k) {
155 fac /=
static_cast<double>(k);
157 double norm = (m == 0)
158 ? std::sqrt((2.0 * l + 1.0) / (4.0 * SSS_PI) * fac)
159 : std::sqrt(2.0 * (2.0 * l + 1.0) / (4.0 * SSS_PI) * fac);
161 P(l, m) = norm * Praw(l, m);
166 double sinThetaDeriv =
static_cast<double>(l) * cosTheta * Praw(l, m);
168 sinThetaDeriv -=
static_cast<double>(l + m) * Praw(l - 1, m);
170 dP(l, m) = norm * sinThetaDeriv / sinTheta;
177Vector3d SSS::basisGradCart(
int l,
int m,
bool bInternal,
178 const Vector3d& rPos,
179 const MatrixXd& P,
const MatrixXd& dP,
180 double cosTheta,
double sinTheta,
181 double cosPhi,
double sinPhi)
184 double r = rPos.norm();
186 return Vector3d::Zero();
189 const int absM = std::abs(m);
202 double Plm = P(l, absM);
203 double dPlm = dP(l, absM);
205 double angFactor, dAngFactor_theta, dAngFactor_phi;
208 dAngFactor_theta = 0.0;
209 dAngFactor_phi = 0.0;
211 double cosmPhi = std::cos(
static_cast<double>(m) * std::atan2(sinPhi, cosPhi));
212 double sinmPhi = std::sin(
static_cast<double>(m) * std::atan2(sinPhi, cosPhi));
214 dAngFactor_theta = 0.0;
215 dAngFactor_phi = -
static_cast<double>(m) * sinmPhi;
218 double cosmPhi = std::cos(
static_cast<double>(absM) * std::atan2(sinPhi, cosPhi));
219 double sinmPhi = std::sin(
static_cast<double>(absM) * std::atan2(sinPhi, cosPhi));
221 dAngFactor_theta = 0.0;
222 dAngFactor_phi =
static_cast<double>(absM) * cosmPhi;
226 double Ylm = Plm * angFactor;
229 double dYdTheta = dPlm * angFactor;
232 double dYdPhi = Plm * dAngFactor_phi;
245 double radPow, Gr_coeff, Gtu_coeff;
247 radPow = std::pow(r, l - 1);
248 Gr_coeff =
static_cast<double>(l) * radPow;
251 radPow = std::pow(r, -(l + 2));
252 Gr_coeff = -
static_cast<double>(l + 1) * radPow;
256 double Gr = Gr_coeff * Ylm;
257 double Gtheta = Gtu_coeff * dYdTheta;
258 double GphiTimesSin = Gtu_coeff * dYdPhi;
269 double Gphi = (sinTheta > 1e-12) ? GphiTimesSin / sinTheta : 0.0;
271 double gx = Gr * sinTheta * cosPhi + Gtheta * cosTheta * cosPhi - Gphi * sinPhi;
272 double gy = Gr * sinTheta * sinPhi + Gtheta * cosTheta * sinPhi + Gphi * cosPhi;
273 double gz = Gr * cosTheta - Gtheta * sinTheta;
275 return Vector3d(gx, gy, gz);
289 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
290 int kind = fiffInfo.
chs[i].kind;
298 qWarning() <<
"SSS::computeBasis: no MEG channels found in FiffInfo.";
310 for (
int si = 0; si < nMeg; ++si) {
314 Vector3d rPos(
static_cast<double>(ch.
chpos.
r0(0)) - params.
origin(0),
319 Vector3d normal(
static_cast<double>(ch.
chpos.
ez(0)),
320 static_cast<double>(ch.
chpos.
ez(1)),
321 static_cast<double>(ch.
chpos.
ez(2)));
324 double nNorm = normal.norm();
331 double r = rPos.norm();
335 double cosTheta = rPos(2) / r;
336 double sinTheta = std::sqrt(rPos(0) * rPos(0) + rPos(1) * rPos(1)) / r;
337 if (sinTheta < 1e-12) sinTheta = 1e-12;
338 double phi = std::atan2(rPos(1), rPos(0));
339 double cosPhi = std::cos(phi);
340 double sinPhi = std::sin(phi);
344 computeNormALP(lmax, cosTheta, sinTheta, P, dP);
348 for (
int l = 1; l <= params.
iOrderIn; ++l) {
349 for (
int m = -l; m <= l; ++m) {
350 Vector3d grad = basisGradCart(l, m,
true,
352 cosTheta, sinTheta, cosPhi, sinPhi);
353 basis.
matSin(si, colIn) = normal.dot(grad);
360 for (
int l = 1; l <= params.
iOrderOut; ++l) {
361 for (
int m = -l; m <= l; ++m) {
362 Vector3d grad = basisGradCart(l, m,
false,
364 cosTheta, sinTheta, cosPhi, sinPhi);
365 basis.
matSout(si, colOut) = normal.dot(grad);
385 const MatrixXd Qin = orthonormalCols(basis.
matSin);
386 const MatrixXd Qout = orthonormalCols(basis.
matSout);
388 if (Qin.cols() == 0) {
389 basis.
matProjIn = MatrixXd::Zero(nMeg, nMeg);
393 const MatrixXd I = MatrixXd::Identity(nMeg, nMeg);
394 const MatrixXd QoutPerp = Qout.cols() > 0 ? (I - Qout * Qout.transpose()) : I;
395 const MatrixXd gram = Qin.transpose() * QoutPerp * Qin;
396 basis.
matProjIn = Qin * regPinv(gram, params.
dRegIn) * Qin.transpose() * QoutPerp;
410 MatrixXd matOut = matData;
413 MatrixXd megData(nMeg, matData.cols());
414 for (
int i = 0; i < nMeg; ++i) {
419 MatrixXd megSss = basis.
matProjIn * megData;
422 for (
int i = 0; i < nMeg; ++i) {
441 const int nSamp =
static_cast<int>(matData.cols());
442 const int bufLen = std::min(iBufferLength, nSamp);
444 MatrixXd matOut = matData;
447 MatrixXd megData(nMeg, nSamp);
448 for (
int i = 0; i < nMeg; ++i) {
460 while (offset < nSamp) {
461 int winLen = std::min(bufLen, nSamp - offset);
464 MatrixXd cInWin = cIn.middleCols(offset, winLen);
465 MatrixXd cOutWin = cOut.middleCols(offset, winLen);
471 JacobiSVD<MatrixXd>
svd(cOutWin, ComputeThinU | ComputeThinV);
472 const VectorXd& sv =
svd.singularValues();
473 const MatrixXd& V =
svd.matrixV();
476 double svMax = (sv.size() > 0) ? sv(0) : 0.0;
487 for (
int k = 0; k < sv.size(); ++k) {
488 if (sv(k) / svMax > dCorrLimit) {
496 Vr = V.leftCols(nRemove);
498 MatrixXd cInWinClean = cInWin - cInWin * (Vr * Vr.transpose());
499 cIn.middleCols(offset, winLen) = cInWinClean;
506 MatrixXd megTsss = basis.
matSin * cIn;
509 for (
int i = 0; i < nMeg; ++i) {
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Signal-Space Separation (SSS) and temporal SSS (tSSS) for MEG data.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
static Basis computeBasis(const FIFFLIB::FiffInfo &fiffInfo, const Params ¶ms=Params())
static Eigen::MatrixXd apply(const Eigen::MatrixXd &matData, const Basis &basis)
static Eigen::MatrixXd applyTemporal(const Eigen::MatrixXd &matData, const Basis &basis, int iBufferLength=10000, double dCorrLimit=0.98)
Precomputed SSS basis and projectors for a given sensor array.
Eigen::MatrixXd matPinvAll
QVector< int > megChannelIdx
Eigen::MatrixXd matProjIn
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...