diff --git a/_includes/shared/particular_data.md b/_includes/shared/particular_data.md index 2bdb7c78e..77525c5a4 100644 --- a/_includes/shared/particular_data.md +++ b/_includes/shared/particular_data.md @@ -11,6 +11,8 @@ In general you should get started with the [tutorials](/tutorial). The pages tha - [Getting started with Yokogawa data](/getting_started/yokogawa) - [Getting started with Ricoh data](/getting_started/ricoh) - [Getting started with OPM data recorded at the FIL](/getting_started/opm_fil) +- [Getting started with Cerca OPM data](/getting_started/cerca) +- [Getting started with FieldLine OPM data](/getting_started/fieldline) ## Getting started with particular EEG data types diff --git a/getting_started/cerca.md b/getting_started/cerca.md new file mode 100644 index 000000000..e79a893c7 --- /dev/null +++ b/getting_started/cerca.md @@ -0,0 +1,10 @@ +--- +title: Getting started with Cerca OPM data +tags: [dataformat, meg, opm, cerca] +--- + +# Getting started with Cerca OPM data + +[Cerca Magnetics](https://www.cercamagnetics.com) is a spin-off company from the University of Nottingham, UK, that that develops OPM systems. + +As promised in , there will be more content for this page. diff --git a/getting_started/fieldline.md b/getting_started/fieldline.md index 3a0bc23a1..4967bff6e 100644 --- a/getting_started/fieldline.md +++ b/getting_started/fieldline.md @@ -1,15 +1,169 @@ --- title: Getting started with FieldLine OPM data -tags: [dataformat, meg, fieldline] +tags: [dataformat, meg, opm, fieldline] --- # Getting started with FieldLine OPM data [FieldLine](https://fieldlineinc.com/) is a company located in Boulder, Colorado that develops OPM sensors and complete OPM-based MEG systems. The data from their current systems is stored in the \*.fif format, which is developed and still used by [Neuromag/Elekta/Megin](/getting_started/neuromag). The \*.fif file format is already supported by FieldTrip, which means that no special functions are needed for reading the data. -There are a number of differences though, which are relevant in processing FieldLine data. One difference is how events or triggers are detected and represented. Another difference is in the procedure used to record the position of the OPM sensors relative to the head, which is relevant in the coregistration procedure between MEG and MRI. + cfg = []; + cfg.dataset = 'RestingStateEyesClosed_raw.fif'; + data = ft_preprocessing(cfg); + + cfg = []; + cfg.preproc.demean = 'yes'; + cfg.ylim = [-1 1] * 1e-11; + cfg.blocksize = 30; % seconds + ft_databrowser(cfg, data); + +There are a number of differences though, which are relevant for processing FieldLine data in more detail. One difference is how events or triggers are represented and detected. Another difference is in the procedure used to record the position of the OPM sensors relative to the head, which is relevant for topographic plotting and for the coregistration between MEG and MRI. Furthermore, it can be the case that you have multiple recordings with a small number of OPM sensors on different locations, and that you want to merge those recordings. ## Reading and processing triggers +Depending on the generation of the FieldLine OPM system that you are working with, the triggers may be represented as an analog channel (with voltages) or as a digital channel. In this case the experiment consisted of a median nerve stimulation on the right wrist. Each stimulus is coded with an analog trigger represented in the `Input-1` channel. + + filename = 'MedianNerve_wCoils_11mA_3Hz_StimBreakStim2min_Pos1_raw.fif'; + hdr = ft_read_header(filename); + + disp(hdr.label) + {'Input-1' } + {'00:01-BZ_OL'} + {'00:02-BZ_OL'} + {'00:03-BZ_OL'} + {'00:04-BZ_OL'} + {'00:05-BZ_OL'} + {'00:06-BZ_OL'} + {'00:07-BZ_OL'} + {'00:08-BZ_OL'} + + % determine the segments of interest around the triggers (aka the trials) + cfg = []; + cfg.dataset = filename; + cfg.trialdef.eventtype = 'Input-1'; + cfg.trialdef.prestim = 0.1; + cfg.trialdef.poststim = 0.3; + cfg = ft_definetrial(cfg); + + % read the segments of interest (aka the trials) + cfg.channel = '00*'; + cfg.demean = 'yes'; + cfg.baselinewindow = [-inf 0]; + data = ft_preprocessing(cfg); + + % remove large variance trials + cfg = []; + cfg.method = 'summary'; + data_clean = ft_rejectvisual(cfg, data); + + % average over trials and plot the ERF + cfg = []; + timelock = ft_timelockanalysis(cfg, data_clean); + + plot(timelock.time, timelock.avg) + +As there is quite some drift in the signal, it is better to apply a filter to the continuous data prior to segmenting. That results in the following alternate preprocessing script, similar to the [tutorial on processing continuous EEG or MEG data](/tutorial/continuous/). + + % read the continuous data + cfg = []; + cfg.dataset = filename; + cfg.channel = '00*'; + cfg.demean = 'yes'; + cfg.baselinewindow = [-inf inf]; + cfg.hpfilter = 'yes'; + cfg.hpfreq = 1; + data_continuous = ft_preprocessing(cfg); + + % determine the segments of interest around the triggers (aka the trials) + cfg = []; + cfg.dataset = filename; + cfg.trialdef.eventtype = 'Input-1'; + cfg.trialdef.prestim = 0.1; + cfg.trialdef.poststim = 0.3; + cfg = ft_definetrial(cfg); + + % do the actual segmentation of the data + data_segmented = ft_redefinetrial(cfg, data_continuous); + + % remove large variance trials + cfg = []; + cfg.method = 'summary'; + data_clean = ft_rejectvisual(cfg, data_segmented); + + % average over trials and plot the ERF + cfg = []; + timelock = ft_timelockanalysis(cfg, data_clean); + + plot(timelock.time, timelock.avg) + +## Coregistration and topographic plotting + +When we look at this recording, we see channel names like `00:01-BZ_OL` and `00:02-BZ_OL`. These channels correspond to the hardware sensors, i.e. the OPMs, and in this case each OPM has only one channel in the axial (or normal) direction, indicated by "BZ". + +The data structure returned by **[ft_preprocessing](/reference/ft_preprocessing)** or the header from **[ft_read_header](/reference/fileio/ft_read_header)** include the `grad` field, which describes the sensor positions (see also [this FAQ](/faq/how_are_electrodes_magnetometers_or_gradiometers_described)). However, in this case the recording software did not know in which slots of the 3D-printed helmet the OPMs were placed. The `data.grad.coilpos` and `data.grad.coilori` fields that are obtained from the fif file also don't make sense. + +In this specific recording the OPM sensors were placed in an early version of the FieldLine smart helmet that the company refers to as the "Alpha-1" version. The FieldTrip [templates](/template/gradiometer) includes a detailed description of this sensor, and there is a [layout](/template/layout/#fieldline) for 2D topographic plotting. + +The researchers acquiring the data maintained a table in an Excel file that maps the channel numbers in the electronics chassis, to the OPM sensors, and to the slots in the 3D-printed helmet. + + +| channel | OPM sensor | helmet position | +|---------|------------|-----------------| +| 1 | 338 | 'FL30' | +| 2 | 119 | 'FL30' | +| 3 | 323 | 'FL30' | +| 4 | 111 | 'FL30' | +| 5 | 62 | 'FL30' | +| 6 | 336 | 'FL30' | +| 7 | 22 | 'FL30' | +| 8 | 246 | 'FL30' | + + +{% include markup/info %} +Note that the OPM sensor number is not used here, but it might be relevant if you want to apply fine calibration, or if you want to account for the different noise characteristics of the differentt OPMs. +{% include markup/end %} + +For topographic plotting the channel names (in `data.label`) should match the locations on the head. To keep the script clean and reproducible, we will **not** go in the data structure and manually rename the channels to match, but we will use a [montage](/example/rereference/#montage), similar to how EEG and iEEG channels can be renamed, combined, added, and subtracted. + + montage = []; + montage.labelold = { + '00:01-BZ_OL' + '00:02-BZ_OL' + '00:03-BZ_OL' + '00:04-BZ_OL' + '00:05-BZ_OL' + '00:06-BZ_OL' + '00:07-BZ_OL' + '00:08-BZ_OL' + }'; + montage.labelnew = { + 'FL30' + 'FL21' + 'FL20' + 'FL23' + 'FL36' + 'FL35' + 'FL34' + 'FL84' + }; + montage.tra = eye(8); % the data is simply copied one-to-one + + % rename the channels in the raw data + cfg = []; + cfg.montage = montage; + data_helmetpos = ft_preprocessing(cfg, data); + + % or rename the channels in the timelocked ERF data + timelock_helmetpos = ft_preprocessing(cfg, timelock) + + % plot the ERFs on the corresponding position in the helmet + cfg = []; + cfg.layout = 'fieldlinealpha1_helmet.mat'; + cfg.showoutline = 'yes'; + ft_multiplotER(cfg, timelock_helmetpos) + +## Merging multiple recordings + +The researchers acquiring the data performed three subsequent recordings on the same participant in which the 8 OPM sensors were moved to different positions in the Apha1 helmet. Each recording is stored in a different fif file. Using the example above, we can construct three raw data structures with the trials. After removing the noisy trials in each of the three recordings, we can average each of the recordings. -## Coregistration with anatomical MRI +Since channels in a FieldTrip data structure are required have a unique channel name, we cannot immediately use **[ft_appenddata](/reference/ft_appenddata)** or **[ft_appendtimelock](/reference/ft_appendtimelock)** to concatenate the data along the channel direction; we first have to rename the channels to make them unique. Using three separate montages to map (rename) the channels in each recording onto the corresponding names of the slots in the 3D printed helmet, we get raw data (or timelock) structures with unique channel names that can be appended. diff --git a/getting_started/opm_fil.md b/getting_started/opm_fil.md index 41eb0a558..b56d2f5d8 100644 --- a/getting_started/opm_fil.md +++ b/getting_started/opm_fil.md @@ -1,6 +1,6 @@ --- title: Getting started with OPM data recorded at the FIL -tags: [dataformat, opm, fil, meg] +tags: [dataformat, meg, opm, fil] --- # Getting started with OPM data recorded at the FIL