v2.0.0
Loading...
Searching...
No Matches
fiff_info.cpp
Go to the documentation of this file.
1//=============================================================================================================
21
22//=============================================================================================================
23// INCLUDES
24//=============================================================================================================
25
26#include "fiff_info.h"
27#include "fiff_stream.h"
28#include "fiff_file.h"
29
30#include <utils/ioutils.h>
31#include <iostream>
32#include <QDebug>
33
34#include <Eigen/LU>
35
36//=============================================================================================================
37// USED NAMESPACES
38//=============================================================================================================
39
40using namespace FIFFLIB;
41using namespace UTILSLIB;
42using namespace Eigen;
43
44//=============================================================================================================
45// DEFINE MEMBER METHODS
46//=============================================================================================================
47
49: FiffInfoBase() //nchan(-1)
50, sfreq(-1.0)
51, linefreq(-1.0)
52, highpass(-1.0)
53, lowpass(-1.0)
54, gantry_angle(-1)
55, acq_pars("")
56, acq_stim("")
57{
58 meas_date[0] = -1;
59}
60
61//=============================================================================================================
62
63FiffInfo::FiffInfo(const FiffInfo& p_FiffInfo)
64: FiffInfoBase(p_FiffInfo)
65, file_id(p_FiffInfo.file_id)
66, sfreq(p_FiffInfo.sfreq)
67, linefreq(p_FiffInfo.linefreq)
68, highpass(p_FiffInfo.highpass)
69, lowpass(p_FiffInfo.lowpass)
70, proj_id(p_FiffInfo.proj_id)
71, proj_name(p_FiffInfo.proj_name)
73, experimenter(p_FiffInfo.experimenter)
74, description(p_FiffInfo.description)
75, utc_offset(p_FiffInfo.utc_offset)
76, gantry_angle(p_FiffInfo.gantry_angle)
77, dev_ctf_t(p_FiffInfo.dev_ctf_t)
78, dig(p_FiffInfo.dig)
79, dig_trans(p_FiffInfo.dig_trans)
80, projs(p_FiffInfo.projs)
81, comps(p_FiffInfo.comps)
82, acq_pars(p_FiffInfo.acq_pars)
83, acq_stim(p_FiffInfo.acq_stim)
85{
86 meas_date[0] = p_FiffInfo.meas_date[0];
87 meas_date[1] = p_FiffInfo.meas_date[1];
88}
89
90//=============================================================================================================
91
95
96//=============================================================================================================
97
99{
101 file_id = FiffId();
102 meas_date[0] = -1;
103 sfreq = -1.0;
104 linefreq = -1.0;
105 highpass = -1.0;
106 lowpass = -1.0;
107 proj_id = -1;
108 proj_name = "";
109 xplotter_layout = "";
110 experimenter = "";
111 description = "";
112 utc_offset = "";
113 gantry_angle = -1;
114 dev_ctf_t.clear();
115 dig.clear();
116 dig_trans.clear();
117 projs.clear();
118 comps.clear();
119 acq_pars = "";
120 acq_stim = "";
121 hpi_coil_freqs.clear();
122}
123
124//=============================================================================================================
125
127{
128 qint32 comp = 0;
129 qint32 first_comp = -1;
130
131 qint32 k = 0;
132 for (k = 0; k < this->nchan; ++k) {
133 if (this->chs[k].kind == FIFFV_MEG_CH) {
134 comp = this->chs[k].chpos.coil_type >> 16;
135 if (first_comp < 0)
136 first_comp = comp;
137 else if (comp != first_comp)
138 qWarning("Compensation is not set equally on all MEG channels");
139 }
140 }
141 return comp;
142}
143
144//=============================================================================================================
145
146bool FiffInfo::make_compensator(fiff_int_t from, fiff_int_t to, FiffCtfComp& ctf_comp, bool exclude_comp_chs) const
147{
148 MatrixXd C1, C2, comp_tmp;
149
150 // if(ctf_comp.data)
151 // delete ctf_comp.data;
152 ctf_comp.data->clear();
153
154 if (from == to) {
155 ctf_comp.data->data = MatrixXd::Identity(this->nchan, this->nchan);
156 return false;
157 }
158
159 if (from == 0)
160 C1 = MatrixXd::Zero(this->nchan, this->nchan);
161 else {
162 if (!this->make_compensator(from, C1)) {
163 qWarning("Cannot create compensator C1\n");
164 qWarning("Desired compensation matrix (kind = %d) not found\n", from);
165 return false;
166 }
167 }
168
169 if (to == 0)
170 C2 = MatrixXd::Zero(this->nchan, this->nchan);
171 else {
172 if (!this->make_compensator(to, C2)) {
173 qWarning("Cannot create compensator C2\n");
174 qWarning("Desired compensation matrix (kind = %d) not found\n", to);
175 return false;
176 }
177 }
178 //
179 // s_from = (I - C1)*s_orig => s_orig = (I - C1)^-1 * s_from
180 // s_to = (I - C2)*s_orig
181 // (I + C1) is only the inverse when C1*C1 = 0, which fails as soon as
182 // reference channels are themselves compensated (e.g. CTF grade 1).
183 //
184 const MatrixXd eye = MatrixXd::Identity(this->nchan, this->nchan);
185 comp_tmp = (eye - C2) * (eye - C1).partialPivLu().inverse();
186
187 qint32 k;
188 if (exclude_comp_chs) {
189 VectorXi pick = VectorXi::Zero(this->nchan);
190 qint32 npick = 0;
191 for (k = 0; k < this->nchan; ++k) {
192 if (this->chs[k].kind != FIFFV_REF_MEG_CH) {
193 pick(npick) = k;
194 ++npick;
195 }
196 }
197 if (npick == 0) {
198 qWarning("Nothing remains after excluding the compensation channels\n");
199 return false;
200 }
201
202 ctf_comp.data->data.resize(npick, this->nchan);
203 for (k = 0; k < npick; ++k)
204 ctf_comp.data->data.row(k) = comp_tmp.block(pick(k), 0, 1, this->nchan);
205 } else {
206 ctf_comp.data->data = comp_tmp;
207 }
208 // FiffRawData applies the compensator only when kind is set.
209 ctf_comp.kind = to;
210
211 return true;
212}
213
214//=============================================================================================================
215
216bool FiffInfo::make_compensator(fiff_int_t kind, MatrixXd& this_comp) const //private method
217{
218 FiffNamedMatrix::SDPtr this_data;
219 MatrixXd presel, postsel;
220 qint32 k, col, c, ch = 0, row, row_ch = 0, channelAvailable;
221
222 for (k = 0; k < this->comps.size(); ++k) {
223 if (this->comps[k].kind == kind) {
224 this_data = this->comps[k].data;
225
226 //
227 // Create the preselector
228 //
229 presel = MatrixXd::Zero(this_data->ncol, this->nchan);
230
231 for (col = 0; col < this_data->ncol; ++col) {
232 channelAvailable = 0;
233 for (c = 0; c < this->ch_names.size(); ++c) {
234 if (QString::compare(this_data->col_names.at(col), this->ch_names.at(c)) == 0) {
235 ++channelAvailable;
236 ch = c;
237 }
238 }
239 if (channelAvailable == 0) {
240 qWarning("Channel %s is not available in data\n", this_data->col_names.at(col).toUtf8().constData());
241 return false;
242 } else if (channelAvailable > 1) {
243 qWarning("Ambiguous channel %s", this_data->col_names.at(col).toUtf8().constData());
244 return false;
245 }
246 presel(col, ch) = 1.0;
247 }
248 //
249 // Create the postselector
250 //
251 postsel = MatrixXd::Zero(this->nchan, this_data->nrow);
252
253 for (c = 0; c < this->nchan; ++c) {
254 channelAvailable = 0;
255 for (row = 0; row < this_data->row_names.size(); ++row) {
256 if (QString::compare(this->ch_names.at(c), this_data->row_names.at(row)) == 0) {
257 ++channelAvailable;
258 row_ch = row;
259 }
260 }
261 if (channelAvailable > 1) {
262 qWarning("Ambiguous channel %s", this->ch_names.at(c).toUtf8().constData());
263 return false;
264 } else if (channelAvailable == 1) {
265 postsel(c, row_ch) = 1.0;
266 }
267 }
268 this_comp = postsel * this_data->data * presel;
269 return true;
270 }
271 }
272 this_comp = defaultMatrixXd;
273 return false;
274}
275
276//=============================================================================================================
277
278FiffInfo FiffInfo::pick_info(const RowVectorXi& sel) const
279{
280 FiffInfo res = *this; //new FiffInfo(this);
281 if (sel.size() == 0)
282 return res;
283
284 //ToDo when pointer List do delation
285 res.chs.clear();
286 res.ch_names.clear();
287
288 qint32 idx;
289 for (qint32 i = 0; i < sel.size(); ++i) {
290 idx = sel[i];
291 res.chs.append(this->chs[idx]);
292 res.ch_names.append(this->ch_names[idx]);
293 }
294 res.nchan = static_cast<int>(sel.size());
295
296 return res;
297}
298
299//=============================================================================================================
300
301QList<FiffChInfo> FiffInfo::set_current_comp(QList<FiffChInfo>& listFiffChInfo, fiff_int_t value)
302{
303 QList<FiffChInfo> newList;
304 qint32 k;
305 fiff_int_t coil_type;
306
307 for (k = 0; k < listFiffChInfo.size(); ++k)
308 newList.append(listFiffChInfo[k]);
309
310 qint32 lower_half = 65535; // hex2dec('FFFF');
311 for (k = 0; k < listFiffChInfo.size(); ++k) {
312 if (listFiffChInfo[k].kind == FIFFV_MEG_CH) {
313 coil_type = listFiffChInfo[k].chpos.coil_type & lower_half;
314 newList[k].chpos.coil_type = (coil_type | (value << 16));
315 }
316 }
317 return newList;
318}
319
320//=============================================================================================================
321
322bool FiffInfo::readMegEegChannels(const QString& name,
323 bool do_meg,
324 bool do_eeg,
325 const QStringList& bads,
326 QList<FiffChInfo>& chsp,
327 int& nmegp,
328 int& neegp)
329{
330 QFile file(name);
331 FiffStream::SPtr stream(new FiffStream(&file));
332
333 if (!stream->open())
334 return false;
335
336 FiffInfo info;
337 FiffDirNode::SPtr infoNode;
338 if (!stream->read_meas_info(stream->dirtree(), info, infoNode)) {
339 qCritical("%s : could not read measurement info", name.toUtf8().data());
340 stream->close();
341 return false;
342 }
343 stream->close();
344
345 QList<FiffChInfo> meg;
346 QList<FiffChInfo> eeg;
347
348 for (int k = 0; k < info.chs.size(); k++) {
349 if (bads.contains(info.chs[k].ch_name))
350 continue;
351 if (do_meg && info.chs[k].kind == FIFFV_MEG_CH)
352 meg.append(info.chs[k]);
353 else if (do_eeg && info.chs[k].isValidEeg())
354 eeg.append(info.chs[k]);
355 }
356
357 chsp.clear();
358 chsp.reserve(meg.size() + eeg.size());
359 chsp.append(meg);
360 chsp.append(eeg);
361
362 nmegp = meg.size();
363 neegp = eeg.size();
364 return true;
365}
366
367//=============================================================================================================
368
370{
371 //
372 // We will always write floats
373 //
374 fiff_int_t data_type = 4;
375 QList<FiffChInfo> chsToWrite;
376
377 for (qint32 k = 0; k < this->nchan; ++k)
378 chsToWrite << this->chs[k];
379
380 fiff_int_t nchanToWrite = chsToWrite.size();
381
382 //
383 // write the essentials
384 //
385 p_pStream->start_block(FIFFB_MEAS); //4
386 p_pStream->write_id(FIFF_BLOCK_ID); //5
387 if (this->meas_id.version != -1) {
388 p_pStream->write_id(FIFF_PARENT_BLOCK_ID, this->meas_id); //6
389 }
390 //
391 // Measurement info
392 //
393 p_pStream->start_block(FIFFB_MEAS_INFO); //7
394
395 //
396 // Blocks from the original -> skip this
397 //
398 // QList<fiff_int_t> blocks;
399 // blocks << FIFFB_SUBJECT << FIFFB_HPI_MEAS << FIFFB_HPI_RESULT << FIFFB_ISOTRAK << FIFFB_PROCESSING_HISTORY;
400 bool have_hpi_result = false;
401 bool have_isotrak = false;
402 //
403 // megacq parameters
404 //
405 if (!this->acq_pars.isEmpty() || !this->acq_stim.isEmpty()) {
406 p_pStream->start_block(FIFFB_DACQ_PARS);
407 if (!this->acq_pars.isEmpty())
408 p_pStream->write_string(FIFF_DACQ_PARS, this->acq_pars);
409
410 if (!this->acq_stim.isEmpty())
411 p_pStream->write_string(FIFF_DACQ_STIM, this->acq_stim);
412
413 p_pStream->end_block(FIFFB_DACQ_PARS);
414 }
415 //
416 // Coordinate transformations if the HPI result block was not there
417 //
418 if (!have_hpi_result) {
419 if (!this->dev_head_t.isEmpty())
420 p_pStream->write_coord_trans(this->dev_head_t);
421
422 if (!this->ctf_head_t.isEmpty())
423 p_pStream->write_coord_trans(this->ctf_head_t);
424 }
425 //
426 // Polhemus data
427 //
428 if (this->dig.size() > 0 && !have_isotrak) {
429 p_pStream->start_block(FIFFB_ISOTRAK);
430 for (qint32 k = 0; k < this->dig.size(); ++k)
431 p_pStream->write_dig_point(this->dig[k]);
432
433 p_pStream->end_block(FIFFB_ISOTRAK);
434 }
435 //
436 // Projectors
437 //
438 p_pStream->write_proj(this->projs);
439 //
440 // CTF compensation info
441 //
442 p_pStream->write_ctf_comp(this->comps);
443 //
444 // Bad channels
445 //
446 if (this->bads.size() > 0) {
448 p_pStream->write_name_list(FIFF_MNE_CH_NAME_LIST, this->bads);
450 }
451 //
452 // General
453 //
454 p_pStream->write_float(FIFF_SFREQ, &this->sfreq);
455 p_pStream->write_float(FIFF_LINE_FREQ, &this->linefreq);
456 p_pStream->write_float(FIFF_HIGHPASS, &this->highpass);
457 p_pStream->write_float(FIFF_LOWPASS, &this->lowpass);
459 p_pStream->write_string(FIFF_DESCRIPTION, this->description);
460 p_pStream->write_string(FIFF_UTC_OFFSET, this->utc_offset);
461 p_pStream->write_string(FIFF_PROJ_NAME, this->proj_name);
462 p_pStream->write_int(FIFF_PROJ_ID, &this->proj_id);
463 p_pStream->write_int(FIFF_GANTRY_ANGLE, &this->gantry_angle);
464 p_pStream->write_int(FIFF_NCHAN, &nchanToWrite);
465 p_pStream->write_int(FIFF_DATA_PACK, &data_type);
466 if (this->meas_date[0] != -1)
467 p_pStream->write_int(FIFF_MEAS_DATE, this->meas_date, 2);
468 //
469 // Channel info
470 //
471 MatrixXd cals(1, nchanToWrite);
472
473 for (qint32 k = 0; k < nchanToWrite; ++k) {
474 //
475 // Scan numbers may have been messed up
476 //
477 chsToWrite[k].scanNo = k + 1; //+1 because
478 // chs[k].range = 1.0f;//Why? -> cause its already calibrated through reading
479 cals(0, k) = static_cast<double>(chsToWrite[k].cal); //ToDo whats going on with cals?
480 p_pStream->write_ch_info(chsToWrite[k]);
481 }
482 //
483 //
484 p_pStream->end_block(FIFFB_MEAS_INFO);
485}
486
487//=============================================================================================================
488
489void FiffInfo::print() const
490{
491 std::cout << "Sample frequency: " << sfreq << "\n";
492 std::cout << "LineFreq: " << linefreq << " | Highpass: " << highpass << " | Lowpass: " << lowpass << "\n";
493 std::cout << "Number of digitizer points: " << dig.size() << "\n";
494 for (auto& point : dig) {
495 if (point.kind == FIFFV_POINT_HPI) {
496 std::cout << "HPI Point " << point.ident << " - " << point.r[0] << ", " << point.r[1] << ", " << point.r[2] << "\n";
497 }
498 }
499}
#define FIFFV_REF_MEG_CH
#define FIFFV_MEG_CH
#define FIFF_MNE_CH_NAME_LIST
#define FIFFB_MNE_BAD_CHANNELS
#define FIFFV_POINT_HPI
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
#define FIFF_GANTRY_ANGLE
Definition fiff_file.h:548
#define FIFF_DACQ_STIM
Definition fiff_file.h:344
#define FIFF_PARENT_BLOCK_ID
Definition fiff_file.h:326
#define FIFF_NCHAN
Definition fiff_file.h:446
#define FIFF_PROJ_ID
Definition fiff_file.h:568
#define FIFFB_ISOTRAK
Definition fiff_file.h:363
#define FIFF_HIGHPASS
Definition fiff_file.h:469
#define FIFF_EXPERIMENTER
Definition fiff_file.h:458
#define FIFF_PROJ_NAME
Definition fiff_file.h:569
#define FIFF_DATA_PACK
Definition fiff_file.h:448
#define FIFF_DESCRIPTION
Definition fiff_file.h:479
#define FIFF_LINE_FREQ
Definition fiff_file.h:482
#define FIFFB_MEAS
Definition fiff_file.h:355
#define FIFF_UTC_OFFSET
Definition fiff_file.h:444
#define FIFFB_DACQ_PARS
Definition fiff_file.h:372
#define FIFF_BLOCK_ID
Definition fiff_file.h:319
#define FIFF_DACQ_PARS
Definition fiff_file.h:343
#define FIFF_MEAS_DATE
Definition fiff_file.h:450
#define FIFF_LOWPASS
Definition fiff_file.h:465
#define FIFFB_MEAS_INFO
Definition fiff_file.h:356
#define FIFF_SFREQ
Definition fiff_file.h:447
Header-only Eigen matrix text I/O — round-trips dense matrices to whitespace-separated ASCII for cros...
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
One CTF software-gradient compensation matrix: grade kind, calibration flag and the gradiometer × ref...
QSharedPointer< FiffDirNode > SPtr
128-bit FIFF identifier: hardware machine ID plus creation time, stamped on every file and block.
Definition fiff_id.h:69
FiffInfo pick_info(const Eigen::RowVectorXi &sel=defaultVectorXi) const
void set_current_comp(fiff_int_t value)
Definition fiff_info.h:317
void print() const
static bool readMegEegChannels(const QString &name, bool do_meg, bool do_eeg, const QStringList &bads, QList< FiffChInfo > &chsp, int &nmegp, int &neegp)
QString description
Definition fiff_info.h:286
FiffCoordTrans dev_ctf_t
Definition fiff_info.h:289
QList< float > hpi_coil_freqs
Definition fiff_info.h:296
fiff_int_t gantry_angle
Definition fiff_info.h:288
void writeToStream(FiffStream *p_pStream) const
FiffCoordTrans dig_trans
Definition fiff_info.h:291
QList< FiffCtfComp > comps
Definition fiff_info.h:293
qint32 get_current_comp()
QString xplotter_layout
Definition fiff_info.h:284
fiff_int_t meas_date[2]
Definition fiff_info.h:277
bool make_compensator(fiff_int_t from, fiff_int_t to, FiffCtfComp &ctf_comp, bool exclude_comp_chs=false) const
QList< FiffDigPoint > dig
Definition fiff_info.h:290
QString experimenter
Definition fiff_info.h:285
QList< FiffProj > projs
Definition fiff_info.h:292
QList< FiffChInfo > chs
FiffCoordTrans ctf_head_t
FiffCoordTrans dev_head_t
QSharedDataPointer< FiffNamedMatrix > SDPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t start_block(fiff_int_t kind)
fiff_long_t write_proj(const QList< FiffProj > &projs)
QSharedPointer< FiffStream > SPtr
fiff_long_t write_dig_point(const FiffDigPoint &dig)
fiff_long_t write_int(fiff_int_t kind, const fiff_int_t *data, fiff_int_t nel=1, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_float(fiff_int_t kind, const float *data, fiff_int_t nel=1)
fiff_long_t write_id(fiff_int_t kind, const FiffId &id=FiffId::getDefault())
fiff_long_t write_coord_trans(const FiffCoordTrans &trans)
fiff_long_t write_name_list(fiff_int_t kind, const QStringList &data)
fiff_long_t write_string(fiff_int_t kind, const QString &data)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_ch_info(const FiffChInfo &ch)
fiff_long_t write_ctf_comp(const QList< FiffCtfComp > &comps)