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