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(); return FIFF_FAIL;
89 }
90
91 nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MEAS_FILE);
92
93 if (nodes.size() == 0) {
94 nodes = stream->dirtree()->dir_tree_find(FIFFB_MEAS_INFO);
95 if (nodes.size() == 0) {
96 qCritical ("Could not find the channel information.");
97 stream->close(); return FIFF_FAIL;
98 }
99 }
100 info = nodes[0];
101 to_find = 0;
102 for (k = 0; k < info->nent(); k++) {
103 kind = info->dir[k]->kind;
104 pos = info->dir[k]->pos;
105 switch (kind) {
106 case FIFF_NCHAN :
107 if (!stream->read_tag(t_pTag,pos)) {
108 stream->close(); return FIFF_FAIL;
109 }
110 nchan = *t_pTag->toInt();
111
112 for (j = 0; j < nchan; j++) {
113 chs.append(FiffChInfo());
114 chs[j].scanNo = -1;
115 }
116 to_find = nchan;
117 break;
118
120 if(!stream->read_tag(t_pTag, pos)) {
121 stream->close(); return FIFF_FAIL;
122 }
123 id = std::make_unique<FiffId>(*(FiffId*)t_pTag->data());
124 break;
125
126 case FIFF_COORD_TRANS :
127 if(!stream->read_tag(t_pTag, pos)) {
128 stream->close(); return FIFF_FAIL;
129 }
130// t = t_pTag->toCoordTrans();
131 t = FiffCoordTrans::readFromTag( t_pTag );
133 t = FiffCoordTrans();
134 break;
135
136 case FIFF_CH_INFO : /* Information about one channel */
137 if(!stream->read_tag(t_pTag, pos)) {
138 stream->close(); return FIFF_FAIL;
139 }
140
141 this_ch = t_pTag->toChInfo();
142 if (this_ch.scanNo <= 0 || this_ch.scanNo > nchan) {
143 qCritical ("FIFF_CH_INFO : scan # out of range %d (%d)!",this_ch.scanNo,nchan);
144 stream->close(); return FIFF_FAIL;
145 }
146 else
147 chs[this_ch.scanNo-1] = this_ch;
148 to_find--;
149 break;
150 }
151 }
152 if (to_find != 0) {
153 qCritical("Some of the channel information was missing.");
154 stream->close(); return FIFF_FAIL;
155 }
156 if (t.isEmpty() && meg_head_t != nullptr) {
157 /*
158 * Try again in a more general fashion
159 */
161 if (t.isEmpty()) {
162 qCritical("MEG -> head coordinate transformation not found.");
163 stream->close(); return FIFF_FAIL;
164 }
165 }
166 /*
167 * Sort out the channels
168 */
169 for (k = 0; k < nchan; k++) {
170 if (chs[k].kind == FIFFV_MEG_CH) {
171 meg.append(chs[k]);
172 nmeg++;
173 } else if (chs[k].kind == FIFFV_REF_MEG_CH) {
174 meg_comp.append(chs[k]);
175 nmeg_comp++;
176 } else if (chs[k].kind == FIFFV_EEG_CH) {
177 eeg.append(chs[k]);
178 neeg++;
179 }
180 }
181// fiff_close(in);
182 stream->close();
183
184 megp = meg;
185 if(nmegp) {
186 *nmegp = nmeg;
187 }
188
189 meg_compp = meg_comp;
190 if(nmeg_compp) {
191 *nmeg_compp = nmeg_comp;
192 }
193
194 eegp = eeg;
195 if(neegp) {
196 *neegp = neeg;
197 }
198
199 if (idp == nullptr) {
200 /* id is auto-deleted by unique_ptr */
201 }
202 else
203 *idp = id.release();
204 if (meg_head_t == nullptr) {
205 }
206 else
207 *meg_head_t = t;
208
209 return FIFF_OK;
210}
211
212#define MNE_CTFV_COMP_UNKNOWN -1
213#define MNE_CTFV_COMP_NONE 0
214#define MNE_CTFV_COMP_G1BR 0x47314252
215#define MNE_CTFV_COMP_G2BR 0x47324252
216#define MNE_CTFV_COMP_G3BR 0x47334252
217#define MNE_CTFV_COMP_G2OI 0x47324f49
218#define MNE_CTFV_COMP_G3OI 0x47334f49
219
220static struct {
223} compMap[] = { { MNE_CTFV_NOGRAD, MNE_CTFV_COMP_NONE },
227{ MNE_4DV_COMP1, MNE_4DV_COMP1 }, /* One-to-one mapping for 4D data */
229
231
232{
233 int k;
234
235 for (k = 0; compMap[k].grad_comp >= 0; k++)
236 if (ctf_comp == compMap[k].ctf_comp)
237 return compMap[k].grad_comp;
238 return ctf_comp;
239}
240
241std::unique_ptr<FiffSparseMatrix> mne_convert_to_sparse(const Eigen::MatrixXf& dense, /* The dense matrix to be converted */
242 int stor_type, /* Either FIFFTS_MC_CCS or FIFFTS_MC_RCS */
243 float small) /* How small elements should be ignored? */
244/*
245 * Convert a dense matrix to sparse using Eigen's sparseView.
246 */
247{
248 Q_UNUSED(stor_type);
249
250 if (small < 0) { /* Automatic scaling */
251 float maxval = dense.cwiseAbs().maxCoeff();
252 if (maxval > 0)
253 small = maxval*std::fabs(small);
254 else
255 small = std::fabs(small);
256 }
257
258 Eigen::SparseMatrix<float> eigenSparse = dense.sparseView(small, 1.0f);
259 eigenSparse.makeCompressed();
260
261 if (eigenSparse.nonZeros() <= 0) {
262 qWarning("No nonzero elements found.");
263 return nullptr;
264 }
265
266 return std::make_unique<FiffSparseMatrix>(std::move(eigenSparse), FIFFTS_MC_RCS);
267}
268
269int mne_sparse_vec_mult2_32(FiffSparseMatrix* mat, /* The sparse matrix */
270 float *vector, /* Vector to be multiplied */
271 float *res) /* Result of the multiplication */
272/*
273 * Multiply a vector by a sparse matrix using Eigen.
274 */
275{
276 Eigen::Map<const Eigen::VectorXf> vecIn(vector, mat->cols());
277 Eigen::Map<Eigen::VectorXf> vecOut(res, mat->rows());
278 vecOut = mat->eigen() * vecIn;
279 return 0;
280}
281
282//=============================================================================================================
283// DEFINE MEMBER METHODS
284//=============================================================================================================
285
287:ncomp(0)
288,nch(0)
289,undo(nullptr)
290,current(nullptr)
291{
292}
293
294//=============================================================================================================
295
297:ncomp(0)
298,nch(set.nch)
299,undo(nullptr)
300,current(nullptr)
301{
302 if (set.ncomp > 0) {
303 for (int k = 0; k < set.ncomp; k++)
304 if(set.comps[k])
305 this->comps.push_back(std::make_unique<MNECTFCompData>(*set.comps[k]));
306 this->ncomp = this->comps.size();
307 }
308
309 this->chs = set.chs;
310
311 if(set.undo)
312 this->undo = std::make_unique<MNECTFCompData>(*set.undo);
313
314 if(set.current)
315 this->current = std::make_unique<MNECTFCompData>(*set.current);
316}
317
318//=============================================================================================================
319
323
324//=============================================================================================================
325
326std::unique_ptr<MNECTFCompDataSet> MNECTFCompDataSet::read(const QString &name)
327/*
328 * Read all CTF compensation data from a given file
329 */
330{
331 QFile file(name);
332 FiffStream::SPtr stream(new FiffStream(&file));
333
334 std::unique_ptr<MNECTFCompDataSet> set;
335 QList<FiffDirNode::SPtr> nodes;
336 QList<FiffDirNode::SPtr> comps;
337 int ncomp;
338 int kind,k;
339 FiffTag::UPtr t_pTag;
340 QList<FiffChInfo> chs;
341 int nch = 0;
342 int calibrated;
343 /*
344 * Read the channel information
345 */
346 {
347 QList<FiffChInfo> comp_chs, temp;
348 int ncompch = 0;
349
351 chs,
352 &nch,
353 comp_chs,
354 &ncompch,
355 temp,
356 nullptr,
357 nullptr,
358 nullptr) == FAIL)
359 return nullptr;
360 if (ncompch > 0) {
361 for (k = 0; k < ncompch; k++)
362 chs.append(comp_chs[k]);
363 nch = nch + ncompch;
364 }
365 }
366 /*
367 * Read the rest of the stuff
368 */
369 if(!stream->open()) {
370 stream->close(); 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(); return set;
379 }
380 comps = nodes[0]->dir_tree_find(FIFFB_MNE_CTF_COMP_DATA);
381 if (comps.size() == 0) {
382 stream->close(); return set;
383 }
384 ncomp = comps.size();
385 /*
386 * Set the channel info
387 */
388 set->chs = chs;
389 set->nch = nch;
390 /*
391 * Read each data set
392 */
393 for (k = 0; k < ncomp; k++) {
395 if (!mat) {
396 stream->close(); return nullptr;
397 }
398 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_KIND, t_pTag);
399 if (t_pTag) {
400 kind = *t_pTag->toInt();
401 }
402 else {
403 stream->close(); return nullptr;
404 }
405 comps[k]->find_tag(stream, FIFF_MNE_CTF_COMP_CALIBRATED, t_pTag);
406 if (t_pTag) {
407 calibrated = *t_pTag->toInt();
408 }
409 else
410 calibrated = 0;
411 /*
412 * Add these data to the set
413 */
414 auto one = std::make_unique<MNECTFCompData>();
415 one->data = std::move(mat);
416 one->kind = kind;
417 one->mne_kind = mne_unmap_ctf_comp_kind(one->kind);
418 one->calibrated = calibrated;
419
420 if (one->calibrate(set->chs,set->nch,true) == FAIL) {
421 qWarning("Warning: Compensation data for '%s' omitted\n", explain_comp(one->kind).toUtf8().constData());
422 }
423 else {
424 set->comps.push_back(std::move(one));
425 set->ncomp++;
426 }
427 }
428#ifdef DEBUG
429 qInfo("%d CTF compensation data sets read from %s\n",set->ncomp,name);
430#endif
431 stream->close();
432 return set;
433}
434
435//=============================================================================================================
436
437int MNECTFCompDataSet::make_comp(const QList<FiffChInfo>& chs,
438 int nch,
439 QList<FiffChInfo> compchs,
440 int ncomp) /* How many of these */
441/*
442 * Make compensation data to apply to a set of channels to yield (or uncompensated) compensated data
443 */
444{
445 Eigen::VectorXi comps;
446 int need_comp;
447 int first_comp;
448 MNECTFCompData* this_comp;
449 Eigen::VectorXi comp_sel;
450 QStringList names;
451 QString name;
452 int j,k,p;
453
454 std::unique_ptr<FiffSparseMatrix> presel;
455 std::unique_ptr<FiffSparseMatrix> postsel;
456 std::unique_ptr<MNENamedMatrix> data;
457
458 QStringList emptyList;
459
460 if (compchs.isEmpty()) {
461 compchs = chs;
462 ncomp = nch;
463 }
464 qInfo("Setting up compensation data...\n");
465 if (nch == 0)
466 return OK;
467 current.reset();
468 comps.resize(nch);
469 for (k = 0, need_comp = 0, first_comp = MNE_CTFV_COMP_NONE; k < nch; k++) {
470 if (chs[k].kind == FIFFV_MEG_CH) {
471 comps[k] = chs[k].chpos.coil_type >> 16;
472 if (comps[k] != MNE_CTFV_COMP_NONE) {
473 if (first_comp == MNE_CTFV_COMP_NONE)
474 first_comp = comps[k];
475 else {
476 if (comps[k] != first_comp) {
477 qCritical("We do not support nonuniform compensation yet.");
478 return FAIL;
479 }
480 }
481 need_comp++;
482 }
483 }
484 else
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,nch);
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 < ncomp; 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, ncomp);
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 < nch; k++) {
541 if (comps[k] != MNE_CTFV_COMP_NONE)
542 names.append(chs[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(nch, data->nrow);
557 for (j = 0, p = 0; j < nch; j++) {
558 if (comps[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 }
630 else if (this_comp->data->ncol != ncompdata) {
631 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
632 this_comp->data->ncol,ncompdata);
633 return FAIL;
634 }
635 if (this_comp->postsel) {
636 if (this_comp->postsel->rows() != ndata) {
637 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
638 this_comp->postsel->rows(),ndata);
639 return FAIL;
640 }
641 }
642 else if (this_comp->data->nrow != ndata) {
643 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
644 this_comp->data->nrow,ndata);
645 return FAIL;
646 }
647 /*
648 * Preselection is optional
649 */
650 const float *presel;
651 if (this_comp->presel) {
652 if (this_comp->presel_data.size() == 0)
653 this_comp->presel_data.resize(this_comp->presel->rows());
654 if (mne_sparse_vec_mult2_32(this_comp->presel.get(),const_cast<float*>(compdata.data()),this_comp->presel_data.data()) != OK)
655 return FAIL;
656 presel = this_comp->presel_data.data();
657 }
658 else
659 presel = compdata.data();
660 /*
661 * This always happens
662 */
663 if (this_comp->comp_data.size() == 0)
664 this_comp->comp_data.resize(this_comp->data->nrow);
665 {
666 Eigen::Map<const Eigen::VectorXf> preselVec(presel, this_comp->data->ncol);
667 Eigen::Map<Eigen::VectorXf> compVec(this_comp->comp_data.data(), this_comp->data->nrow);
668 compVec = this_comp->data->data * preselVec;
669 }
670 /*
671 * Optional postselection
672 */
673 const float *comp;
674 if (!this_comp->postsel)
675 comp = this_comp->comp_data.data();
676 else {
677 if (this_comp->postsel_data.size() == 0)
678 this_comp->postsel_data.resize(this_comp->postsel->rows());
679 if (mne_sparse_vec_mult2_32(this_comp->postsel.get(),this_comp->comp_data.data(),this_comp->postsel_data.data()) != OK)
680 return FAIL;
681 comp = this_comp->postsel_data.data();
682 }
683 /*
684 * Compensate or revert compensation?
685 */
686 Eigen::Map<const Eigen::VectorXf> compVec(comp, ndata);
687 if (do_it)
688 data -= compVec;
689 else
690 data += compVec;
691 return OK;
692}
693
694//=============================================================================================================
695
696int MNECTFCompDataSet::apply_transpose(bool do_it, Eigen::MatrixXf& data)
697/*
698 * Apply compensation or revert to uncompensated data
699 */
700{
701 using RowMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
702
703 MNECTFCompData* this_comp;
704 int ndata = static_cast<int>(data.rows());
705 int ns = static_cast<int>(data.cols());
706 int ncompdata = ndata;
707
708 if (!current)
709 return OK;
710 this_comp = current.get();
711 /*
712 * Dimension checks
713 */
714 if (this_comp->presel) {
715 if (this_comp->presel->cols() != ncompdata) {
716 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
717 this_comp->presel->cols(),ncompdata);
718 return FAIL;
719 }
720 }
721 else if (this_comp->data->ncol != ncompdata) {
722 qCritical("Compensation data dimension mismatch. Expected %d, got %d channels.",
723 this_comp->data->ncol,ncompdata);
724 return FAIL;
725 }
726 if (this_comp->postsel) {
727 if (this_comp->postsel->rows() != ndata) {
728 qCritical("Data dimension mismatch. Expected %d, got %d channels.",
729 this_comp->postsel->rows(),ndata);
730 return FAIL;
731 }
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 }
745 else {
746 preselMat = data;
747 }
748 /*
749 * Compensation: comp = data_matrix * preselMat
750 * data_matrix is float** (contiguous row-major via mne_cmatrix)
751 */
752 Eigen::MatrixXf comp = this_comp->data->data * preselMat;
753 /*
754 * Optional postselection — sparse matrix * comp
755 */
756 if (this_comp->postsel) {
757 comp = this_comp->postsel->eigen() * comp;
758 }
759 /*
760 * Compensate or revert compensation?
761 */
762 if (do_it)
763 data -= comp;
764 else
765 data += comp;
766 return OK;
767}
768
769//=============================================================================================================
770
771int MNECTFCompDataSet::get_comp(const QList<FIFFLIB::FiffChInfo> &chs, int nch)
772{
773 int res = MNE_CTFV_NOGRAD;
774 int first_comp,comp;
775 int k;
776
777 for (k = 0, first_comp = -1; k < nch; k++) {
778 if (chs[k].kind == FIFFV_MEG_CH) {
779 comp = chs[k].chpos.coil_type >> 16;
780 if (first_comp < 0)
781 first_comp = comp;
782 else if (first_comp != comp) {
783 qCritical("Non uniform compensation not supported.");
784 return FAIL;
785 }
786 }
787 }
788 if (first_comp >= 0)
789 res = first_comp;
790 return res;
791}
792
793//=============================================================================================================
794
796/*
797 * Simple mapping
798 */
799{
800 int k;
801
802 for (k = 0; compMap[k].grad_comp >= 0; k++)
803 if (grad == compMap[k].grad_comp)
804 return compMap[k].ctf_comp;
805 return grad;
806}
807
808//=============================================================================================================
809
811{
812 static const struct {
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>& chs,
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 = chs;
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(chs,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(chs,nchan,compensate_to);
874 return OK;
875 }
876 if (set_comp(chs,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(chs,nchan,comp_chs,ncomp_chan) == FAIL) {
882 if (comp_was != MNE_CTFV_COMP_UNKNOWN)
883 set_comp(chs,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}
constexpr int FAIL
constexpr int OK
Set of CTF compensation matrices plus the currently active grade.
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)
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
Legacy MNE-C constants and shared typedefs used across MNELIB structures.
#define MNE_CTFV_GRAD1
Definition mne_types.h:109
#define MNE_CTFV_GRAD2
Definition mne_types.h:110
#define MNE_CTFV_GRAD3
Definition mne_types.h:111
#define MNE_4DV_COMP1
Definition mne_types.h:118
#define MNE_CTFV_NOGRAD
Definition mne_types.h:108
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
#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
#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
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
Primitive scalar typedefs and forward-compatible aliases backing the FIFF type system.
FiffId * fiffId
Backward-compatible pointer typedef for the old fiffId pointer.
Definition fiff_types.h:127
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
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:66
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:164
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
int make_comp(const QList< FIFFLIB::FiffChInfo > &chs, int nch, QList< FIFFLIB::FiffChInfo > compchs, int ncomp)
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 apply(bool do_it, Eigen::Ref< Eigen::VectorXf > data, Eigen::Ref< const Eigen::VectorXf > compdata)
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)
int set_compensation(int compensate_to, QList< FIFFLIB::FiffChInfo > &chs, int nchan, QList< FIFFLIB::FiffChInfo > comp_chs, int ncomp_chan)
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.