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
218static struct
219{
222} compMap[] = {{MNE_CTFV_NOGRAD, MNE_CTFV_COMP_NONE},
226 {MNE_4DV_COMP1, MNE_4DV_COMP1}, /* One-to-one mapping for 4D data */
228
230
231{
232 int k;
233
234 for (k = 0; compMap[k].grad_comp >= 0; k++)
235 if (ctf_comp == compMap[k].ctf_comp)
236 return compMap[k].grad_comp;
237 return ctf_comp;
238}
239
240std::unique_ptr<FiffSparseMatrix> mne_convert_to_sparse(const Eigen::MatrixXf& dense, /* The dense matrix to be converted */
241 int stor_type, /* Either FIFFTS_MC_CCS or FIFFTS_MC_RCS */
242 float small) /* How small elements should be ignored? */
243/*
244 * Convert a dense matrix to sparse using Eigen's sparseView.
245 */
246{
247 Q_UNUSED(stor_type);
248
249 if (small < 0) { /* Automatic scaling */
250 float maxval = dense.cwiseAbs().maxCoeff();
251 if (maxval > 0)
252 small = maxval * std::fabs(small);
253 else
254 small = std::fabs(small);
255 }
256
257 Eigen::SparseMatrix<float> eigenSparse = dense.sparseView(small, 1.0f);
258 eigenSparse.makeCompressed();
259
260 if (eigenSparse.nonZeros() <= 0) {
261 qWarning("No nonzero elements found.");
262 return nullptr;
263 }
264
265 return std::make_unique<FiffSparseMatrix>(std::move(eigenSparse), FIFFTS_MC_RCS);
266}
267
268int mne_sparse_vec_mult2_32(FiffSparseMatrix* mat, /* The sparse matrix */
269 float* vector, /* Vector to be multiplied */
270 float* res) /* Result of the multiplication */
271/*
272 * Multiply a vector by a sparse matrix using Eigen.
273 */
274{
275 Eigen::Map<const Eigen::VectorXf> vecIn(vector, mat->cols());
276 Eigen::Map<Eigen::VectorXf> vecOut(res, mat->rows());
277 vecOut = mat->eigen() * vecIn;
278 return 0;
279}
280
281//=============================================================================================================
282// DEFINE MEMBER METHODS
283//=============================================================================================================
284
286: ncomp(0)
287, nch(0)
288, undo(nullptr)
289, current(nullptr)
290{
291}
292
293//=============================================================================================================
294
296: ncomp(0)
297, nch(set.nch)
298, undo(nullptr)
299, current(nullptr)
300{
301 if (set.ncomp > 0) {
302 for (int k = 0; k < set.ncomp; k++)
303 if (set.comps[k])
304 this->comps.push_back(std::make_unique<MNECTFCompData>(*set.comps[k]));
305 this->ncomp = static_cast<int>(this->comps.size());
306 }
307
308 this->chs = set.chs;
309
310 if (set.undo)
311 this->undo = std::make_unique<MNECTFCompData>(*set.undo);
312
313 if (set.current)
314 this->current = std::make_unique<MNECTFCompData>(*set.current);
315}
316
317//=============================================================================================================
318
322
323//=============================================================================================================
324
325std::unique_ptr<MNECTFCompDataSet> MNECTFCompDataSet::read(const QString& name)
326/*
327 * Read all CTF compensation data from a given file
328 */
329{
330 QFile file(name);
331 FiffStream::SPtr stream(new FiffStream(&file));
332
333 std::unique_ptr<MNECTFCompDataSet> set;
334 QList<FiffDirNode::SPtr> nodes;
335 QList<FiffDirNode::SPtr> comps;
336 int ncomp;
337 int kind, k;
338 FiffTag::UPtr t_pTag;
339 QList<FiffChInfo> chs;
340 int nch = 0;
341 int calibrated;
342 /*
343 * Read the channel information
344 */
345 {
346 QList<FiffChInfo> comp_chs, temp;
347 int ncompch = 0;
348
350 chs,
351 &nch,
352 comp_chs,
353 &ncompch,
354 temp,
355 nullptr,
356 nullptr,
357 nullptr) == FAIL)
358 return nullptr;
359 if (ncompch > 0) {
360 for (k = 0; k < ncompch; k++)
361 chs.append(comp_chs[k]);
362 nch = nch + ncompch;
363 }
364 }
365 /*
366 * Read the rest of the stuff
367 */
368 if (!stream->open()) {
369 stream->close();
370 return nullptr;
371 }
372 set = std::make_unique<MNECTFCompDataSet>();
373 /*
374 * Locate the compensation data sets
375 */
376 nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_CTF_COMP);
377 if (nodes.size() == 0) {
378 stream->close();
379 return set;
380 }
381 comps = nodes[0]->dir_tree_find(FIFFB_MNE_CTF_COMP_DATA);
382 if (comps.size() == 0) {
383 stream->close();
384 return set;
385 }
386 ncomp = comps.size();
387 /*
388 * Set the channel info
389 */
390 set->chs = chs;
391 set->nch = nch;
392 /*
393 * Read each data set
394 */
395 for (k = 0; k < ncomp; k++) {
396 auto mat = MNENamedMatrix::read(stream, comps[k], FIFF_MNE_CTF_COMP_DATA);
397 if (!mat) {
398 stream->close();
399 return nullptr;
400 }
401 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_KIND, t_pTag);
402 if (t_pTag) {
403 kind = *t_pTag->toInt();
404 } else {
405 stream->close();
406 return nullptr;
407 }
408 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_CALIBRATED, t_pTag);
409 if (t_pTag) {
410 calibrated = *t_pTag->toInt();
411 } else
412 calibrated = 0;
413 /*
414 * Add these data to the set
415 */
416 auto one = std::make_unique<MNECTFCompData>();
417 one->data = std::move(mat);
418 one->kind = kind;
419 one->mne_kind = mne_unmap_ctf_comp_kind(one->kind);
420 one->calibrated = calibrated;
421
422 if (one->calibrate(set->chs, set->nch, true) == FAIL) {
423 qWarning("Warning: Compensation data for '%s' omitted\n", explain_comp(one->kind).toUtf8().constData());
424 } else {
425 set->comps.push_back(std::move(one));
426 set->ncomp++;
427 }
428 }
429#ifdef DEBUG
430 qInfo("%d CTF compensation data sets read from %s\n", set->ncomp, name);
431#endif
432 stream->close();
433 return set;
434}
435
436//=============================================================================================================
437
438int MNECTFCompDataSet::make_comp(const QList<FiffChInfo>& chList,
439 int nChan,
440 QList<FiffChInfo> compchs,
441 int nCompChan) /* How many of these */
442/*
443 * Make compensation data to apply to a set of channels to yield (or uncompensated) compensated data
444 */
445{
446 Eigen::VectorXi compGrades;
447 int need_comp;
448 int first_comp;
449 MNECTFCompData* this_comp;
450 Eigen::VectorXi comp_sel;
451 QStringList names;
452 QString name;
453 int j, k, p;
454
455 std::unique_ptr<FiffSparseMatrix> presel;
456 std::unique_ptr<FiffSparseMatrix> postsel;
457 std::unique_ptr<MNENamedMatrix> data;
458
459 QStringList emptyList;
460
461 if (compchs.isEmpty()) {
462 compchs = chList;
463 nCompChan = nChan;
464 }
465 qInfo("Setting up compensation data...\n");
466 if (nChan == 0)
467 return OK;
468 current.reset();
469 compGrades.resize(nChan);
470 for (k = 0, need_comp = 0, first_comp = MNE_CTFV_COMP_NONE; k < nChan; k++) {
471 if (chList[k].kind == FIFFV_MEG_CH) {
472 compGrades[k] = chList[k].chpos.coil_type >> 16;
473 if (compGrades[k] != MNE_CTFV_COMP_NONE) {
474 if (first_comp == MNE_CTFV_COMP_NONE)
475 first_comp = compGrades[k];
476 else {
477 if (compGrades[k] != first_comp) {
478 qCritical("We do not support nonuniform compensation yet.");
479 return FAIL;
480 }
481 }
482 need_comp++;
483 }
484 } else
485 compGrades[k] = MNE_CTFV_COMP_NONE;
486 }
487 if (need_comp == 0) {
488 qInfo("\tNo compensation set. Nothing more to do.\n");
489 return OK;
490 }
491 qInfo("\t%d out of %d channels have the compensation set.\n", need_comp, nChan);
492 /*
493 * Find the desired compensation data matrix
494 */
495 for (k = 0, this_comp = nullptr; k < this->ncomp; k++) {
496 if (this->comps[k]->mne_kind == first_comp) {
497 this_comp = this->comps[k].get();
498 break;
499 }
500 }
501 if (!this_comp) {
502 qCritical("Did not find the desired compensation data : %s",
503 explain_comp(map_comp_kind(first_comp)).toUtf8().constData());
504 return FAIL;
505 }
506 qInfo("\tDesired compensation data (%s) found.\n", explain_comp(map_comp_kind(first_comp)).toUtf8().constData());
507 /*
508 * Find the compensation channels
509 */
510 comp_sel.resize(this_comp->data->ncol);
511 for (k = 0; k < this_comp->data->ncol; k++) {
512 comp_sel[k] = -1;
513 name = this_comp->data->collist[k];
514 for (p = 0; p < nCompChan; p++)
515 if (QString::compare(name, compchs[p].ch_name) == 0) {
516 comp_sel[k] = p;
517 break;
518 }
519 if (comp_sel[k] < 0) {
520 qCritical("Compensation channel %s not found", name.toUtf8().constData());
521 return FAIL;
522 }
523 }
524 qInfo("\tAll compensation channels found.\n");
525 /*
526 * Create the preselector
527 */
528 {
529 Eigen::MatrixXf sel = Eigen::MatrixXf::Zero(this_comp->data->ncol, nCompChan);
530 for (j = 0; j < this_comp->data->ncol; j++)
531 sel(j, comp_sel[j]) = 1.0f;
532 presel = mne_convert_to_sparse(sel, FIFFTS_MC_RCS, 1e-30f);
533 if (!presel)
534 return FAIL;
535 qInfo("\tPreselector created.\n");
536 }
537 /*
538 * Pick the desired channels
539 */
540 for (k = 0; k < nChan; k++) {
541 if (compGrades[k] != MNE_CTFV_COMP_NONE)
542 names.append(chList[k].ch_name);
543 }
544
545 {
546 auto d = this_comp->data->pick(names, need_comp, emptyList, 0);
547 if (!d)
548 return FAIL;
549 data = std::move(d);
550 }
551 qInfo("\tCompensation data matrix created.\n");
552 /*
553 * Create the postselector
554 */
555 {
556 Eigen::MatrixXf sel = Eigen::MatrixXf::Zero(nChan, data->nrow);
557 for (j = 0, p = 0; j < nChan; j++) {
558 if (compGrades[j] != MNE_CTFV_COMP_NONE)
559 sel(j, p++) = 1.0f;
560 }
561 postsel = mne_convert_to_sparse(sel, FIFFTS_MC_RCS, 1e-30f);
562 if (!postsel)
563 return FAIL;
564 qInfo("\tPostselector created.\n");
565 }
566 current = std::make_unique<MNECTFCompData>();
567 current->kind = this_comp->kind;
568 current->mne_kind = this_comp->mne_kind;
569 current->data = std::move(data);
570 current->presel = std::move(presel);
571 current->postsel = std::move(postsel);
572
573 qInfo("\tCompensation set up.\n");
574 return OK;
575}
576
577//=============================================================================================================
578
579int MNECTFCompDataSet::set_comp(QList<FIFFLIB::FiffChInfo>& chs,
580 int nch,
581 int comp)
582/*
583 * Set the compensation bits to the desired value
584 */
585{
586 int k;
587 int nset;
588 for (k = 0, nset = 0; k < nch; k++) {
589 if (chs[k].kind == FIFFV_MEG_CH) {
590 chs[k].chpos.coil_type = (chs[k].chpos.coil_type & 0xFFFF) | (comp << 16);
591 nset++;
592 }
593 }
594 qInfo("A new compensation value (%s) was assigned to %d MEG channels.\n",
595 explain_comp(map_comp_kind(comp)).toUtf8().constData(), nset);
596 return nset;
597}
598
599//=============================================================================================================
600
601int MNECTFCompDataSet::apply(bool do_it, Eigen::Ref<Eigen::VectorXf> data)
602{
603 return apply(do_it, data, data);
604}
605
606//=============================================================================================================
607
608int MNECTFCompDataSet::apply(bool do_it, Eigen::Ref<Eigen::VectorXf> data, Eigen::Ref<const Eigen::VectorXf> compdata)
609/*
610 * Apply compensation or revert to uncompensated data
611 */
612{
613 MNECTFCompData* this_comp;
614 int ndata = static_cast<int>(data.size());
615 int ncompdata = static_cast<int>(compdata.size());
616
617 if (!current)
618 return OK;
619 this_comp = current.get();
620 /*
621 * Dimension checks
622 */
623 if (this_comp->presel) {
624 if (this_comp->presel->cols() != ncompdata) {
625 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
626 this_comp->presel->cols(), ncompdata);
627 return FAIL;
628 }
629 } else if (this_comp->data->ncol != ncompdata) {
630 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
631 this_comp->data->ncol, ncompdata);
632 return FAIL;
633 }
634 if (this_comp->postsel) {
635 if (this_comp->postsel->rows() != ndata) {
636 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
637 this_comp->postsel->rows(), ndata);
638 return FAIL;
639 }
640 } else if (this_comp->data->nrow != ndata) {
641 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
642 this_comp->data->nrow, ndata);
643 return FAIL;
644 }
645 /*
646 * Preselection is optional
647 */
648 const float* presel;
649 if (this_comp->presel) {
650 if (this_comp->presel_data.size() == 0)
651 this_comp->presel_data.resize(this_comp->presel->rows());
652 if (mne_sparse_vec_mult2_32(this_comp->presel.get(), const_cast<float*>(compdata.data()), this_comp->presel_data.data()) != OK)
653 return FAIL;
654 presel = this_comp->presel_data.data();
655 } else
656 presel = compdata.data();
657 /*
658 * This always happens
659 */
660 if (this_comp->comp_data.size() == 0)
661 this_comp->comp_data.resize(this_comp->data->nrow);
662 {
663 Eigen::Map<const Eigen::VectorXf> preselVec(presel, this_comp->data->ncol);
664 Eigen::Map<Eigen::VectorXf> compVec(this_comp->comp_data.data(), this_comp->data->nrow);
665 compVec = this_comp->data->data * preselVec;
666 }
667 /*
668 * Optional postselection
669 */
670 const float* comp;
671 if (!this_comp->postsel)
672 comp = this_comp->comp_data.data();
673 else {
674 if (this_comp->postsel_data.size() == 0)
675 this_comp->postsel_data.resize(this_comp->postsel->rows());
676 if (mne_sparse_vec_mult2_32(this_comp->postsel.get(), this_comp->comp_data.data(), this_comp->postsel_data.data()) != OK)
677 return FAIL;
678 comp = this_comp->postsel_data.data();
679 }
680 /*
681 * Compensate or revert compensation?
682 */
683 Eigen::Map<const Eigen::VectorXf> compVec(comp, ndata);
684 if (do_it)
685 data -= compVec;
686 else
687 data += compVec;
688 return OK;
689}
690
691//=============================================================================================================
692
693int MNECTFCompDataSet::apply_transpose(bool do_it, Eigen::MatrixXf& data)
694/*
695 * Apply compensation or revert to uncompensated data
696 */
697{
698 MNECTFCompData* this_comp;
699 int ndata = static_cast<int>(data.rows());
700 int ncompdata = ndata;
701
702 if (!current)
703 return OK;
704 this_comp = current.get();
705 /*
706 * Dimension checks
707 */
708 if (this_comp->presel) {
709 if (this_comp->presel->cols() != ncompdata) {
710 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
711 this_comp->presel->cols(), ncompdata);
712 return FAIL;
713 }
714 } else if (this_comp->data->ncol != ncompdata) {
715 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
716 this_comp->data->ncol, ncompdata);
717 return FAIL;
718 }
719 if (this_comp->postsel) {
720 if (this_comp->postsel->rows() != ndata) {
721 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
722 this_comp->postsel->rows(), ndata);
723 return FAIL;
724 }
725 } else if (this_comp->data->nrow != ndata) {
726 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
727 this_comp->data->nrow, ndata);
728 return FAIL;
729 }
730 /*
731 * Preselection is optional — sparse matrix * data
732 */
733 Eigen::MatrixXf preselMat;
734 if (this_comp->presel) {
735 preselMat = this_comp->presel->eigen() * data;
736 } else {
737 preselMat = data;
738 }
739 /*
740 * Compensation: comp = data_matrix * preselMat
741 * data_matrix is float** (contiguous row-major via mne_cmatrix)
742 */
743 Eigen::MatrixXf comp = this_comp->data->data * preselMat;
744 /*
745 * Optional postselection — sparse matrix * comp
746 */
747 if (this_comp->postsel) {
748 comp = this_comp->postsel->eigen() * comp;
749 }
750 /*
751 * Compensate or revert compensation?
752 */
753 if (do_it)
754 data -= comp;
755 else
756 data += comp;
757 return OK;
758}
759
760//=============================================================================================================
761
762int MNECTFCompDataSet::get_comp(const QList<FIFFLIB::FiffChInfo>& chs, int nch)
763{
764 int res = MNE_CTFV_NOGRAD;
765 int first_comp, comp;
766 int k;
767
768 for (k = 0, first_comp = -1; k < nch; k++) {
769 if (chs[k].kind == FIFFV_MEG_CH) {
770 comp = chs[k].chpos.coil_type >> 16;
771 if (first_comp < 0)
772 first_comp = comp;
773 else if (first_comp != comp) {
774 qCritical("Non uniform compensation not supported.");
775 return FAIL;
776 }
777 }
778 }
779 if (first_comp >= 0)
780 res = first_comp;
781 return res;
782}
783
784//=============================================================================================================
785
787/*
788 * Simple mapping
789 */
790{
791 int k;
792
793 for (k = 0; compMap[k].grad_comp >= 0; k++)
794 if (grad == compMap[k].grad_comp)
795 return compMap[k].ctf_comp;
796 return grad;
797}
798
799//=============================================================================================================
800
802{
803 static const struct
804 {
805 int kind;
806 const char* expl;
807 } explain[] = {{MNE_CTFV_COMP_NONE, "uncompensated"},
808 {MNE_CTFV_COMP_G1BR, "first order gradiometer"},
809 {MNE_CTFV_COMP_G2BR, "second order gradiometer"},
810 {MNE_CTFV_COMP_G3BR, "third order gradiometer"},
811 {MNE_4DV_COMP1, "4D comp 1"},
812 {MNE_CTFV_COMP_UNKNOWN, "unknown"}};
813 int k;
814
815 for (k = 0; explain[k].kind != MNE_CTFV_COMP_UNKNOWN; k++)
816 if (explain[k].kind == kind)
817 return QString::fromLatin1(explain[k].expl);
818 return QString::fromLatin1(explain[k].expl);
819}
820
821//=============================================================================================================
822
824 QList<FiffChInfo>& chList,
825 int nchan,
826 QList<FiffChInfo> comp_chs,
827 int ncomp_chan) /* How many */
828/*
829 * Make data which has the third-order gradient compensation applied
830 */
831{
832 int k;
833 int have_comp_chs;
834 int comp_was = MNE_CTFV_COMP_UNKNOWN;
835
836 if (comp_chs.isEmpty()) {
837 comp_chs = chList;
838 ncomp_chan = nchan;
839 }
840 undo.reset();
841 current.reset();
842 for (k = 0, have_comp_chs = 0; k < ncomp_chan; k++)
843 if (comp_chs[k].kind == FIFFV_REF_MEG_CH)
844 have_comp_chs++;
845 if (have_comp_chs == 0 && compensate_to != MNE_CTFV_NOGRAD) {
846 qWarning("No compensation channels in these data.");
847 return FAIL;
848 }
849 /*
850 * Update the 'current' field to reflect the compensation possibly present in the data now
851 */
852 if (make_comp(chList, nchan, comp_chs, ncomp_chan) == FAIL)
853 return FAIL;
854 /*
855 * Are we there already?
856 */
857 if (current && current->mne_kind == compensate_to) {
858 qInfo("No further compensation necessary (comp = %s)\n", explain_comp(current->kind).toUtf8().constData());
859 current.reset();
860 return OK;
861 }
862 undo = std::move(current);
863 if (compensate_to == MNE_CTFV_NOGRAD) {
864 qInfo("No compensation was requested.\n");
865 set_comp(chList, nchan, compensate_to);
866 return OK;
867 }
868 if (set_comp(chList, nchan, compensate_to) > 0) {
869 if (undo)
870 comp_was = undo->mne_kind;
871 else
872 comp_was = MNE_CTFV_NOGRAD;
873 if (make_comp(chList, nchan, comp_chs, ncomp_chan) == FAIL) {
874 if (comp_was != MNE_CTFV_COMP_UNKNOWN)
875 set_comp(chList, nchan, comp_was);
876 return FAIL;
877 }
878 qInfo("Compensation set up as requested (%s -> %s).\n",
879 explain_comp(map_comp_kind(comp_was)).toUtf8().constData(),
880 explain_comp(current->kind).toUtf8().constData());
881 }
882 return OK;
883}
constexpr auto FIFFB_MNE_PARENT_MEAS_FILE
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFB_MNE_CTF_COMP
constexpr auto FIFF_MNE_CTF_COMP_KIND
#define FIFFV_EEG_CH
constexpr auto FIFFV_COORD_DEVICE
#define FIFFV_MEG_CH
constexpr auto FIFF_FAIL
constexpr auto FIFF_MNE_CTF_COMP_CALIBRATED
constexpr auto FIFF_MNE_CTF_COMP_DATA
constexpr auto FIFF_OK
constexpr auto FIFFB_MNE_CTF_COMP_DATA
constexpr auto FIFFV_REF_MEG_CH
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
constexpr auto FIFFTS_MC_RCS
Definition fiff_file.h:261
constexpr auto FIFF_NCHAN
Definition fiff_file.h:444
constexpr auto FIFF_COORD_TRANS
Definition fiff_file.h:466
constexpr auto FIFF_PARENT_BLOCK_ID
Definition fiff_file.h:324
constexpr auto FIFFB_MEAS_INFO
Definition fiff_file.h:354
constexpr auto FIFF_CH_INFO
Definition fiff_file.h:447
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.
Single CTF reference-sensor compensation matrix labelled by kind.
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).
constexpr auto MNE_CTFV_COMP_G2BR
Definition mne_types.h:122
constexpr auto MNE_CTFV_NOGRAD
Definition mne_types.h:109
constexpr auto MNE_CTFV_GRAD2
Definition mne_types.h:111
constexpr auto MNE_CTFV_COMP_NONE
Definition mne_types.h:120
constexpr auto MNE_4DV_COMP1
Definition mne_types.h:132
constexpr auto MNE_CTFV_COMP_G1BR
Definition mne_types.h:121
constexpr auto MNE_CTFV_GRAD3
Definition mne_types.h:112
constexpr auto MNE_CTFV_COMP_G3BR
Definition mne_types.h:123
constexpr auto MNE_CTFV_COMP_UNKNOWN
Definition mne_types.h:119
constexpr auto MNE_CTFV_GRAD1
Definition mne_types.h:110
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.