v2.0.0
Loading...
Searching...
No Matches
mne_ctf_comp_data_set.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
22#include "mne_ctf_comp_data.h"
23
24#include "mne_types.h"
25
27
28#include <fiff/fiff_types.h>
29
30#include <Eigen/Core>
31
32//=============================================================================================================
33// QT INCLUDES
34//=============================================================================================================
35
36#include <QFile>
37#include <QDebug>
38
39constexpr int FAIL = -1;
40constexpr int OK = 0;
41
42
43//=============================================================================================================
44// USED NAMESPACES
45//=============================================================================================================
46
47using namespace Eigen;
48using namespace FIFFLIB;
49using namespace MNELIB;
50
51//============================= mne_read_forward_solution.c =============================
52
53int mne_read_meg_comp_eeg_ch_info_32(const QString& name,
54 QList<FIFFLIB::FiffChInfo>& megp, /* MEG channels */
55 int* nmegp,
56 QList<FIFFLIB::FiffChInfo>& meg_compp,
57 int* nmeg_compp,
58 QList<FIFFLIB::FiffChInfo>& eegp, /* EEG channels */
59 int* neegp,
60 FiffCoordTrans* meg_head_t,
61 fiffId* idp) /* The measurement ID */
62/*
63 * Read the channel information and split it into three arrays,
64 * one for MEG, one for MEG compensation channels, and one for EEG
65 */
66{
67 QFile file(name);
68 FiffStream::SPtr stream(new FiffStream(&file));
69
70 QList<FIFFLIB::FiffChInfo> chs;
71 int nchan = 0;
72 QList<FIFFLIB::FiffChInfo> meg;
73 int nmeg = 0;
74 QList<FIFFLIB::FiffChInfo> meg_comp;
75 int nmeg_comp = 0;
76 QList<FIFFLIB::FiffChInfo> eeg;
77 int neeg = 0;
78 std::unique_ptr<FiffId> id;
79 QList<FiffDirNode::SPtr> nodes;
81 FiffTag::UPtr t_pTag;
82 FIFFLIB::FiffChInfo this_ch;
84 fiff_int_t kind, pos;
85 int j, k, to_find;
86
87 if (!stream->open()) {
88 stream->close();
89 return FIFF_FAIL;
90 }
91
92 nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MEAS_FILE);
93
94 if (nodes.size() == 0) {
95 nodes = stream->dirtree()->dir_tree_find(FIFFB_MEAS_INFO);
96 if (nodes.size() == 0) {
97 qCritical("Could not find the channel information.");
98 stream->close();
99 return FIFF_FAIL;
100 }
101 }
102 info = nodes[0];
103 to_find = 0;
104 for (k = 0; k < info->nent(); k++) {
105 kind = info->dir[k]->kind;
106 pos = info->dir[k]->pos;
107 switch (kind) {
108 case FIFF_NCHAN:
109 if (!stream->read_tag(t_pTag, pos)) {
110 stream->close();
111 return FIFF_FAIL;
112 }
113 nchan = *t_pTag->toInt();
114
115 for (j = 0; j < nchan; j++) {
116 chs.append(FiffChInfo());
117 chs[j].scanNo = -1;
118 }
119 to_find = nchan;
120 break;
121
123 if (!stream->read_tag(t_pTag, pos)) {
124 stream->close();
125 return FIFF_FAIL;
126 }
127 id = std::make_unique<FiffId>(*(FiffId*)t_pTag->data());
128 break;
129
130 case FIFF_COORD_TRANS:
131 if (!stream->read_tag(t_pTag, pos)) {
132 stream->close();
133 return FIFF_FAIL;
134 }
135 // t = t_pTag->toCoordTrans();
136 t = FiffCoordTrans::readFromTag(t_pTag);
138 t = FiffCoordTrans();
139 break;
140
141 case FIFF_CH_INFO: /* Information about one channel */
142 if (!stream->read_tag(t_pTag, pos)) {
143 stream->close();
144 return FIFF_FAIL;
145 }
146
147 this_ch = t_pTag->toChInfo();
148 if (this_ch.scanNo <= 0 || this_ch.scanNo > nchan) {
149 qCritical("FIFF_CH_INFO : scan # out of range %d (%d)!", this_ch.scanNo, nchan);
150 stream->close();
151 return FIFF_FAIL;
152 } else
153 chs[this_ch.scanNo - 1] = this_ch;
154 to_find--;
155 break;
156 }
157 }
158 if (to_find != 0) {
159 qCritical("Some of the channel information was missing.");
160 stream->close();
161 return FIFF_FAIL;
162 }
163 if (t.isEmpty() && meg_head_t != nullptr) {
164 /*
165 * Try again in a more general fashion
166 */
168 if (t.isEmpty()) {
169 qCritical("MEG -> head coordinate transformation not found.");
170 stream->close();
171 return FIFF_FAIL;
172 }
173 }
174 /*
175 * Sort out the channels
176 */
177 for (k = 0; k < nchan; k++) {
178 if (chs[k].kind == FIFFV_MEG_CH) {
179 meg.append(chs[k]);
180 nmeg++;
181 } else if (chs[k].kind == FIFFV_REF_MEG_CH) {
182 meg_comp.append(chs[k]);
183 nmeg_comp++;
184 } else if (chs[k].kind == FIFFV_EEG_CH) {
185 eeg.append(chs[k]);
186 neeg++;
187 }
188 }
189 // fiff_close(in);
190 stream->close();
191
192 megp = meg;
193 if (nmegp) {
194 *nmegp = nmeg;
195 }
196
197 meg_compp = meg_comp;
198 if (nmeg_compp) {
199 *nmeg_compp = nmeg_comp;
200 }
201
202 eegp = eeg;
203 if (neegp) {
204 *neegp = neeg;
205 }
206
207 if (idp == nullptr) {
208 /* id is auto-deleted by unique_ptr */
209 } else
210 *idp = id.release();
211 if (meg_head_t == nullptr) {
212 } else
213 *meg_head_t = t;
214
215 return FIFF_OK;
216}
217
218#define MNE_CTFV_COMP_UNKNOWN -1
219#define MNE_CTFV_COMP_NONE 0
220#define MNE_CTFV_COMP_G1BR 0x47314252
221#define MNE_CTFV_COMP_G2BR 0x47324252
222#define MNE_CTFV_COMP_G3BR 0x47334252
223#define MNE_CTFV_COMP_G2OI 0x47324f49
224#define MNE_CTFV_COMP_G3OI 0x47334f49
225
226static struct
227{
230} compMap[] = {{MNE_CTFV_NOGRAD, MNE_CTFV_COMP_NONE},
234 {MNE_4DV_COMP1, MNE_4DV_COMP1}, /* One-to-one mapping for 4D data */
236
238
239{
240 int k;
241
242 for (k = 0; compMap[k].grad_comp >= 0; k++)
243 if (ctf_comp == compMap[k].ctf_comp)
244 return compMap[k].grad_comp;
245 return ctf_comp;
246}
247
248std::unique_ptr<FiffSparseMatrix> mne_convert_to_sparse(const Eigen::MatrixXf& dense, /* The dense matrix to be converted */
249 int stor_type, /* Either FIFFTS_MC_CCS or FIFFTS_MC_RCS */
250 float small) /* How small elements should be ignored? */
251/*
252 * Convert a dense matrix to sparse using Eigen's sparseView.
253 */
254{
255 Q_UNUSED(stor_type);
256
257 if (small < 0) { /* Automatic scaling */
258 float maxval = dense.cwiseAbs().maxCoeff();
259 if (maxval > 0)
260 small = maxval * std::fabs(small);
261 else
262 small = std::fabs(small);
263 }
264
265 Eigen::SparseMatrix<float> eigenSparse = dense.sparseView(small, 1.0f);
266 eigenSparse.makeCompressed();
267
268 if (eigenSparse.nonZeros() <= 0) {
269 qWarning("No nonzero elements found.");
270 return nullptr;
271 }
272
273 return std::make_unique<FiffSparseMatrix>(std::move(eigenSparse), FIFFTS_MC_RCS);
274}
275
276int mne_sparse_vec_mult2_32(FiffSparseMatrix* mat, /* The sparse matrix */
277 float* vector, /* Vector to be multiplied */
278 float* res) /* Result of the multiplication */
279/*
280 * Multiply a vector by a sparse matrix using Eigen.
281 */
282{
283 Eigen::Map<const Eigen::VectorXf> vecIn(vector, mat->cols());
284 Eigen::Map<Eigen::VectorXf> vecOut(res, mat->rows());
285 vecOut = mat->eigen() * vecIn;
286 return 0;
287}
288
289//=============================================================================================================
290// DEFINE MEMBER METHODS
291//=============================================================================================================
292
294: ncomp(0)
295, nch(0)
296, undo(nullptr)
297, current(nullptr)
298{
299}
300
301//=============================================================================================================
302
304: ncomp(0)
305, nch(set.nch)
306, undo(nullptr)
307, current(nullptr)
308{
309 if (set.ncomp > 0) {
310 for (int k = 0; k < set.ncomp; k++)
311 if (set.comps[k])
312 this->comps.push_back(std::make_unique<MNECTFCompData>(*set.comps[k]));
313 this->ncomp = static_cast<int>(this->comps.size());
314 }
315
316 this->chs = set.chs;
317
318 if (set.undo)
319 this->undo = std::make_unique<MNECTFCompData>(*set.undo);
320
321 if (set.current)
322 this->current = std::make_unique<MNECTFCompData>(*set.current);
323}
324
325//=============================================================================================================
326
330
331//=============================================================================================================
332
333std::unique_ptr<MNECTFCompDataSet> MNECTFCompDataSet::read(const QString& name)
334/*
335 * Read all CTF compensation data from a given file
336 */
337{
338 QFile file(name);
339 FiffStream::SPtr stream(new FiffStream(&file));
340
341 std::unique_ptr<MNECTFCompDataSet> set;
342 QList<FiffDirNode::SPtr> nodes;
343 QList<FiffDirNode::SPtr> comps;
344 int ncomp;
345 int kind, k;
346 FiffTag::UPtr t_pTag;
347 QList<FiffChInfo> chs;
348 int nch = 0;
349 int calibrated;
350 /*
351 * Read the channel information
352 */
353 {
354 QList<FiffChInfo> comp_chs, temp;
355 int ncompch = 0;
356
358 chs,
359 &nch,
360 comp_chs,
361 &ncompch,
362 temp,
363 nullptr,
364 nullptr,
365 nullptr) == FAIL)
366 return nullptr;
367 if (ncompch > 0) {
368 for (k = 0; k < ncompch; k++)
369 chs.append(comp_chs[k]);
370 nch = nch + ncompch;
371 }
372 }
373 /*
374 * Read the rest of the stuff
375 */
376 if (!stream->open()) {
377 stream->close();
378 return nullptr;
379 }
380 set = std::make_unique<MNECTFCompDataSet>();
381 /*
382 * Locate the compensation data sets
383 */
384 nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_CTF_COMP);
385 if (nodes.size() == 0) {
386 stream->close();
387 return set;
388 }
389 comps = nodes[0]->dir_tree_find(FIFFB_MNE_CTF_COMP_DATA);
390 if (comps.size() == 0) {
391 stream->close();
392 return set;
393 }
394 ncomp = comps.size();
395 /*
396 * Set the channel info
397 */
398 set->chs = chs;
399 set->nch = nch;
400 /*
401 * Read each data set
402 */
403 for (k = 0; k < ncomp; k++) {
404 auto mat = MNENamedMatrix::read(stream, comps[k], FIFF_MNE_CTF_COMP_DATA);
405 if (!mat) {
406 stream->close();
407 return nullptr;
408 }
409 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_KIND, t_pTag);
410 if (t_pTag) {
411 kind = *t_pTag->toInt();
412 } else {
413 stream->close();
414 return nullptr;
415 }
416 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_CALIBRATED, t_pTag);
417 if (t_pTag) {
418 calibrated = *t_pTag->toInt();
419 } else
420 calibrated = 0;
421 /*
422 * Add these data to the set
423 */
424 auto one = std::make_unique<MNECTFCompData>();
425 one->data = std::move(mat);
426 one->kind = kind;
427 one->mne_kind = mne_unmap_ctf_comp_kind(one->kind);
428 one->calibrated = calibrated;
429
430 if (one->calibrate(set->chs, set->nch, true) == FAIL) {
431 qWarning("Warning: Compensation data for '%s' omitted\n", explain_comp(one->kind).toUtf8().constData());
432 } else {
433 set->comps.push_back(std::move(one));
434 set->ncomp++;
435 }
436 }
437#ifdef DEBUG
438 qInfo("%d CTF compensation data sets read from %s\n", set->ncomp, name);
439#endif
440 stream->close();
441 return set;
442}
443
444//=============================================================================================================
445
446int MNECTFCompDataSet::make_comp(const QList<FiffChInfo>& chList,
447 int nChan,
448 QList<FiffChInfo> compchs,
449 int nCompChan) /* How many of these */
450/*
451 * Make compensation data to apply to a set of channels to yield (or uncompensated) compensated data
452 */
453{
454 Eigen::VectorXi compGrades;
455 int need_comp;
456 int first_comp;
457 MNECTFCompData* this_comp;
458 Eigen::VectorXi comp_sel;
459 QStringList names;
460 QString name;
461 int j, k, p;
462
463 std::unique_ptr<FiffSparseMatrix> presel;
464 std::unique_ptr<FiffSparseMatrix> postsel;
465 std::unique_ptr<MNENamedMatrix> data;
466
467 QStringList emptyList;
468
469 if (compchs.isEmpty()) {
470 compchs = chList;
471 nCompChan = nChan;
472 }
473 qInfo("Setting up compensation data...\n");
474 if (nChan == 0)
475 return OK;
476 current.reset();
477 compGrades.resize(nChan);
478 for (k = 0, need_comp = 0, first_comp = MNE_CTFV_COMP_NONE; k < nChan; k++) {
479 if (chList[k].kind == FIFFV_MEG_CH) {
480 compGrades[k] = chList[k].chpos.coil_type >> 16;
481 if (compGrades[k] != MNE_CTFV_COMP_NONE) {
482 if (first_comp == MNE_CTFV_COMP_NONE)
483 first_comp = compGrades[k];
484 else {
485 if (compGrades[k] != first_comp) {
486 qCritical("We do not support nonuniform compensation yet.");
487 return FAIL;
488 }
489 }
490 need_comp++;
491 }
492 } else
493 compGrades[k] = MNE_CTFV_COMP_NONE;
494 }
495 if (need_comp == 0) {
496 qInfo("\tNo compensation set. Nothing more to do.\n");
497 return OK;
498 }
499 qInfo("\t%d out of %d channels have the compensation set.\n", need_comp, nChan);
500 /*
501 * Find the desired compensation data matrix
502 */
503 for (k = 0, this_comp = nullptr; k < this->ncomp; k++) {
504 if (this->comps[k]->mne_kind == first_comp) {
505 this_comp = this->comps[k].get();
506 break;
507 }
508 }
509 if (!this_comp) {
510 qCritical("Did not find the desired compensation data : %s",
511 explain_comp(map_comp_kind(first_comp)).toUtf8().constData());
512 return FAIL;
513 }
514 qInfo("\tDesired compensation data (%s) found.\n", explain_comp(map_comp_kind(first_comp)).toUtf8().constData());
515 /*
516 * Find the compensation channels
517 */
518 comp_sel.resize(this_comp->data->ncol);
519 for (k = 0; k < this_comp->data->ncol; k++) {
520 comp_sel[k] = -1;
521 name = this_comp->data->collist[k];
522 for (p = 0; p < nCompChan; p++)
523 if (QString::compare(name, compchs[p].ch_name) == 0) {
524 comp_sel[k] = p;
525 break;
526 }
527 if (comp_sel[k] < 0) {
528 qCritical("Compensation channel %s not found", name.toUtf8().constData());
529 return FAIL;
530 }
531 }
532 qInfo("\tAll compensation channels found.\n");
533 /*
534 * Create the preselector
535 */
536 {
537 Eigen::MatrixXf sel = Eigen::MatrixXf::Zero(this_comp->data->ncol, nCompChan);
538 for (j = 0; j < this_comp->data->ncol; j++)
539 sel(j, comp_sel[j]) = 1.0f;
540 presel = mne_convert_to_sparse(sel, FIFFTS_MC_RCS, 1e-30f);
541 if (!presel)
542 return FAIL;
543 qInfo("\tPreselector created.\n");
544 }
545 /*
546 * Pick the desired channels
547 */
548 for (k = 0; k < nChan; k++) {
549 if (compGrades[k] != MNE_CTFV_COMP_NONE)
550 names.append(chList[k].ch_name);
551 }
552
553 {
554 auto d = this_comp->data->pick(names, need_comp, emptyList, 0);
555 if (!d)
556 return FAIL;
557 data = std::move(d);
558 }
559 qInfo("\tCompensation data matrix created.\n");
560 /*
561 * Create the postselector
562 */
563 {
564 Eigen::MatrixXf sel = Eigen::MatrixXf::Zero(nChan, data->nrow);
565 for (j = 0, p = 0; j < nChan; j++) {
566 if (compGrades[j] != MNE_CTFV_COMP_NONE)
567 sel(j, p++) = 1.0f;
568 }
569 postsel = mne_convert_to_sparse(sel, FIFFTS_MC_RCS, 1e-30f);
570 if (!postsel)
571 return FAIL;
572 qInfo("\tPostselector created.\n");
573 }
574 current = std::make_unique<MNECTFCompData>();
575 current->kind = this_comp->kind;
576 current->mne_kind = this_comp->mne_kind;
577 current->data = std::move(data);
578 current->presel = std::move(presel);
579 current->postsel = std::move(postsel);
580
581 qInfo("\tCompensation set up.\n");
582 return OK;
583}
584
585//=============================================================================================================
586
587int MNECTFCompDataSet::set_comp(QList<FIFFLIB::FiffChInfo>& chs,
588 int nch,
589 int comp)
590/*
591 * Set the compensation bits to the desired value
592 */
593{
594 int k;
595 int nset;
596 for (k = 0, nset = 0; k < nch; k++) {
597 if (chs[k].kind == FIFFV_MEG_CH) {
598 chs[k].chpos.coil_type = (chs[k].chpos.coil_type & 0xFFFF) | (comp << 16);
599 nset++;
600 }
601 }
602 qInfo("A new compensation value (%s) was assigned to %d MEG channels.\n",
603 explain_comp(map_comp_kind(comp)).toUtf8().constData(), nset);
604 return nset;
605}
606
607//=============================================================================================================
608
609int MNECTFCompDataSet::apply(bool do_it, Eigen::Ref<Eigen::VectorXf> data)
610{
611 return apply(do_it, data, data);
612}
613
614//=============================================================================================================
615
616int MNECTFCompDataSet::apply(bool do_it, Eigen::Ref<Eigen::VectorXf> data, Eigen::Ref<const Eigen::VectorXf> compdata)
617/*
618 * Apply compensation or revert to uncompensated data
619 */
620{
621 MNECTFCompData* this_comp;
622 int ndata = static_cast<int>(data.size());
623 int ncompdata = static_cast<int>(compdata.size());
624
625 if (!current)
626 return OK;
627 this_comp = current.get();
628 /*
629 * Dimension checks
630 */
631 if (this_comp->presel) {
632 if (this_comp->presel->cols() != ncompdata) {
633 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
634 this_comp->presel->cols(), ncompdata);
635 return FAIL;
636 }
637 } else if (this_comp->data->ncol != ncompdata) {
638 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
639 this_comp->data->ncol, ncompdata);
640 return FAIL;
641 }
642 if (this_comp->postsel) {
643 if (this_comp->postsel->rows() != ndata) {
644 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
645 this_comp->postsel->rows(), ndata);
646 return FAIL;
647 }
648 } else if (this_comp->data->nrow != ndata) {
649 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
650 this_comp->data->nrow, ndata);
651 return FAIL;
652 }
653 /*
654 * Preselection is optional
655 */
656 const float* presel;
657 if (this_comp->presel) {
658 if (this_comp->presel_data.size() == 0)
659 this_comp->presel_data.resize(this_comp->presel->rows());
660 if (mne_sparse_vec_mult2_32(this_comp->presel.get(), const_cast<float*>(compdata.data()), this_comp->presel_data.data()) != OK)
661 return FAIL;
662 presel = this_comp->presel_data.data();
663 } else
664 presel = compdata.data();
665 /*
666 * This always happens
667 */
668 if (this_comp->comp_data.size() == 0)
669 this_comp->comp_data.resize(this_comp->data->nrow);
670 {
671 Eigen::Map<const Eigen::VectorXf> preselVec(presel, this_comp->data->ncol);
672 Eigen::Map<Eigen::VectorXf> compVec(this_comp->comp_data.data(), this_comp->data->nrow);
673 compVec = this_comp->data->data * preselVec;
674 }
675 /*
676 * Optional postselection
677 */
678 const float* comp;
679 if (!this_comp->postsel)
680 comp = this_comp->comp_data.data();
681 else {
682 if (this_comp->postsel_data.size() == 0)
683 this_comp->postsel_data.resize(this_comp->postsel->rows());
684 if (mne_sparse_vec_mult2_32(this_comp->postsel.get(), this_comp->comp_data.data(), this_comp->postsel_data.data()) != OK)
685 return FAIL;
686 comp = this_comp->postsel_data.data();
687 }
688 /*
689 * Compensate or revert compensation?
690 */
691 Eigen::Map<const Eigen::VectorXf> compVec(comp, ndata);
692 if (do_it)
693 data -= compVec;
694 else
695 data += compVec;
696 return OK;
697}
698
699//=============================================================================================================
700
701int MNECTFCompDataSet::apply_transpose(bool do_it, Eigen::MatrixXf& data)
702/*
703 * Apply compensation or revert to uncompensated data
704 */
705{
706 MNECTFCompData* this_comp;
707 int ndata = static_cast<int>(data.rows());
708 int ncompdata = ndata;
709
710 if (!current)
711 return OK;
712 this_comp = current.get();
713 /*
714 * Dimension checks
715 */
716 if (this_comp->presel) {
717 if (this_comp->presel->cols() != ncompdata) {
718 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
719 this_comp->presel->cols(), ncompdata);
720 return FAIL;
721 }
722 } else if (this_comp->data->ncol != ncompdata) {
723 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
724 this_comp->data->ncol, ncompdata);
725 return FAIL;
726 }
727 if (this_comp->postsel) {
728 if (this_comp->postsel->rows() != ndata) {
729 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
730 this_comp->postsel->rows(), ndata);
731 return FAIL;
732 }
733 } else if (this_comp->data->nrow != ndata) {
734 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
735 this_comp->data->nrow, ndata);
736 return FAIL;
737 }
738 /*
739 * Preselection is optional — sparse matrix * data
740 */
741 Eigen::MatrixXf preselMat;
742 if (this_comp->presel) {
743 preselMat = this_comp->presel->eigen() * data;
744 } else {
745 preselMat = data;
746 }
747 /*
748 * Compensation: comp = data_matrix * preselMat
749 * data_matrix is float** (contiguous row-major via mne_cmatrix)
750 */
751 Eigen::MatrixXf comp = this_comp->data->data * preselMat;
752 /*
753 * Optional postselection — sparse matrix * comp
754 */
755 if (this_comp->postsel) {
756 comp = this_comp->postsel->eigen() * comp;
757 }
758 /*
759 * Compensate or revert compensation?
760 */
761 if (do_it)
762 data -= comp;
763 else
764 data += comp;
765 return OK;
766}
767
768//=============================================================================================================
769
770int MNECTFCompDataSet::get_comp(const QList<FIFFLIB::FiffChInfo>& chs, int nch)
771{
772 int res = MNE_CTFV_NOGRAD;
773 int first_comp, comp;
774 int k;
775
776 for (k = 0, first_comp = -1; k < nch; k++) {
777 if (chs[k].kind == FIFFV_MEG_CH) {
778 comp = chs[k].chpos.coil_type >> 16;
779 if (first_comp < 0)
780 first_comp = comp;
781 else if (first_comp != comp) {
782 qCritical("Non uniform compensation not supported.");
783 return FAIL;
784 }
785 }
786 }
787 if (first_comp >= 0)
788 res = first_comp;
789 return res;
790}
791
792//=============================================================================================================
793
795/*
796 * Simple mapping
797 */
798{
799 int k;
800
801 for (k = 0; compMap[k].grad_comp >= 0; k++)
802 if (grad == compMap[k].grad_comp)
803 return compMap[k].ctf_comp;
804 return grad;
805}
806
807//=============================================================================================================
808
810{
811 static const struct
812 {
813 int kind;
814 const char* expl;
815 } explain[] = {{MNE_CTFV_COMP_NONE, "uncompensated"},
816 {MNE_CTFV_COMP_G1BR, "first order gradiometer"},
817 {MNE_CTFV_COMP_G2BR, "second order gradiometer"},
818 {MNE_CTFV_COMP_G3BR, "third order gradiometer"},
819 {MNE_4DV_COMP1, "4D comp 1"},
820 {MNE_CTFV_COMP_UNKNOWN, "unknown"}};
821 int k;
822
823 for (k = 0; explain[k].kind != MNE_CTFV_COMP_UNKNOWN; k++)
824 if (explain[k].kind == kind)
825 return QString::fromLatin1(explain[k].expl);
826 return QString::fromLatin1(explain[k].expl);
827}
828
829//=============================================================================================================
830
832 QList<FiffChInfo>& chList,
833 int nchan,
834 QList<FiffChInfo> comp_chs,
835 int ncomp_chan) /* How many */
836/*
837 * Make data which has the third-order gradient compensation applied
838 */
839{
840 int k;
841 int have_comp_chs;
842 int comp_was = MNE_CTFV_COMP_UNKNOWN;
843
844 if (comp_chs.isEmpty()) {
845 comp_chs = chList;
846 ncomp_chan = nchan;
847 }
848 undo.reset();
849 current.reset();
850 for (k = 0, have_comp_chs = 0; k < ncomp_chan; k++)
851 if (comp_chs[k].kind == FIFFV_REF_MEG_CH)
852 have_comp_chs++;
853 if (have_comp_chs == 0 && compensate_to != MNE_CTFV_NOGRAD) {
854 qWarning("No compensation channels in these data.");
855 return FAIL;
856 }
857 /*
858 * Update the 'current' field to reflect the compensation possibly present in the data now
859 */
860 if (make_comp(chList, nchan, comp_chs, ncomp_chan) == FAIL)
861 return FAIL;
862 /*
863 * Are we there already?
864 */
865 if (current && current->mne_kind == compensate_to) {
866 qInfo("No further compensation necessary (comp = %s)\n", explain_comp(current->kind).toUtf8().constData());
867 current.reset();
868 return OK;
869 }
870 undo = std::move(current);
871 if (compensate_to == MNE_CTFV_NOGRAD) {
872 qInfo("No compensation was requested.\n");
873 set_comp(chList, nchan, compensate_to);
874 return OK;
875 }
876 if (set_comp(chList, nchan, compensate_to) > 0) {
877 if (undo)
878 comp_was = undo->mne_kind;
879 else
880 comp_was = MNE_CTFV_NOGRAD;
881 if (make_comp(chList, nchan, comp_chs, ncomp_chan) == FAIL) {
882 if (comp_was != MNE_CTFV_COMP_UNKNOWN)
883 set_comp(chList, nchan, comp_was);
884 return FAIL;
885 }
886 qInfo("Compensation set up as requested (%s -> %s).\n",
887 explain_comp(map_comp_kind(comp_was)).toUtf8().constData(),
888 explain_comp(current->kind).toUtf8().constData());
889 }
890 return OK;
891}
#define FIFFV_EEG_CH
#define FIFF_OK
#define FIFF_MNE_CTF_COMP_KIND
#define FIFFV_COORD_DEVICE
#define FIFFB_MNE_CTF_COMP_DATA
#define FIFF_MNE_CTF_COMP_CALIBRATED
#define FIFF_FAIL
#define FIFFV_REF_MEG_CH
#define FIFFV_MEG_CH
#define FIFF_MNE_CTF_COMP_DATA
#define FIFFV_COORD_HEAD
#define FIFFB_MNE_CTF_COMP
#define FIFFB_MNE_PARENT_MEAS_FILE
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
#define FIFF_PARENT_BLOCK_ID
Definition fiff_file.h:326
#define FIFF_NCHAN
Definition fiff_file.h:446
#define FIFFTS_MC_RCS
Definition fiff_file.h:263
#define FIFF_COORD_TRANS
Definition fiff_file.h:468
#define FIFF_CH_INFO
Definition fiff_file.h:449
#define FIFFB_MEAS_INFO
Definition fiff_file.h:356
if(w.size() > 0)
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
Primitive scalar typedefs and forward-compatible aliases backing the FIFF type system.
constexpr int FAIL
constexpr int OK
Set of CTF compensation matrices plus the currently active grade.
Legacy MNE-C constants and shared typedefs used across MNELIB structures.
#define MNE_CTFV_GRAD1
Definition mne_types.h:110
#define MNE_CTFV_GRAD2
Definition mne_types.h:111
#define MNE_CTFV_GRAD3
Definition mne_types.h:112
#define MNE_4DV_COMP1
Definition mne_types.h:119
#define MNE_CTFV_NOGRAD
Definition mne_types.h:109
Single CTF reference-sensor compensation matrix labelled by kind.
#define MNE_CTFV_COMP_G2BR
#define MNE_CTFV_COMP_NONE
#define MNE_CTFV_COMP_UNKNOWN
#define MNE_CTFV_COMP_G3BR
#define MNE_CTFV_COMP_G1BR
std::unique_ptr< FiffSparseMatrix > mne_convert_to_sparse(const Eigen::MatrixXf &dense, int stor_type, float small)
int mne_unmap_ctf_comp_kind(int ctf_comp)
int mne_sparse_vec_mult2_32(FiffSparseMatrix *mat, float *vector, float *res)
int mne_read_meg_comp_eeg_ch_info_32(const QString &name, QList< FIFFLIB::FiffChInfo > &megp, int *nmegp, QList< FIFFLIB::FiffChInfo > &meg_compp, int *nmeg_compp, QList< FIFFLIB::FiffChInfo > &eegp, int *neegp, FiffCoordTrans *meg_head_t, fiffId *idp)
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
FiffId * fiffId
Backward-compatible pointer typedef for the old fiffId pointer.
Definition fiff_types.h:127
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans readMeasTransform(const QString &name)
static FiffCoordTrans readFromTag(const std::unique_ptr< FiffTag > &tag)
QSharedPointer< FiffDirNode > SPtr
128-bit FIFF identifier: hardware machine ID plus creation time, stamped on every file and block.
Definition fiff_id.h:69
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
Eigen::SparseMatrix< float > & eigen()
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165
Represents a single CTF compensation data element.
std::unique_ptr< FIFFLIB::FiffSparseMatrix > postsel
std::unique_ptr< MNENamedMatrix > data
std::unique_ptr< FIFFLIB::FiffSparseMatrix > presel
std::vector< std::unique_ptr< MNECTFCompData > > comps
std::unique_ptr< MNECTFCompData > undo
std::unique_ptr< MNECTFCompData > current
QList< FIFFLIB::FiffChInfo > chs
static int set_comp(QList< FIFFLIB::FiffChInfo > &chs, int nch, int comp)
static std::unique_ptr< MNECTFCompDataSet > read(const QString &name)
int make_comp(const QList< FIFFLIB::FiffChInfo > &chList, int nChan, QList< FIFFLIB::FiffChInfo > compchs, int nCompChan)
int apply(bool do_it, Eigen::Ref< Eigen::VectorXf > data, Eigen::Ref< const Eigen::VectorXf > compdata)
int set_compensation(int compensate_to, QList< FIFFLIB::FiffChInfo > &chList, int nchan, QList< FIFFLIB::FiffChInfo > comp_chs, int ncomp_chan)
int apply_transpose(bool do_it, Eigen::MatrixXf &data)
static QString explain_comp(int kind)
static int get_comp(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
static std::unique_ptr< MNENamedMatrix > read(QSharedPointer< FIFFLIB::FiffStream > &stream, const QSharedPointer< FIFFLIB::FiffDirNode > &node, int kind)
Factory: read a named matrix from a FIFF file.