Skip to content

Commit

Permalink
build in fileio support for the MEF format (#2394)
Browse files Browse the repository at this point in the history
* Add password option for reading specific file formats

* Add support for Dark Horse Neuro MED 1.0 file format

* Updated MAYO_MEF dependency with MEDFieldTrip_1p0 for MEF 2.1 and MEF 3.0

---------

Co-authored-by: Richard Jie Cui <richard.cui@utoronto.ca>
Co-authored-by: Richard Cui <Cui.Jie@mayo.edu>
  • Loading branch information
3 people committed Mar 4, 2024
1 parent 7ba0239 commit be34a25
Show file tree
Hide file tree
Showing 11 changed files with 576 additions and 266 deletions.
9 changes: 9 additions & 0 deletions fileio/ft_filetype.m
Expand Up @@ -1237,6 +1237,15 @@
% known Multiscale Electrophysiology Format (or Mayo EEG File, MEF)
% MEF 2.1, see: https://github.com/benbrinkmann/mef_lib_2_1
% MEF 3.0, see: https://msel.mayo.edu/codes.html
% MED 1.0, see: http://www.darkhorseneuro.com
elseif isfolder(filename) && any(filetype_check_extension(filename, {'.medd', '.tied', '.rdat','recd','.ridx'}))
type = 'dhn_med10';
manufacturer = 'Dark Horse Neuro';
content = 'Multiscale Electrophysiology Data 1.0';
elseif isfolder(filename) && any(endsWith({ls.name}, '.medd'))
type = 'dhn_med10';
manufacturer = 'Dark Horse Neuro';
content = 'Multiscale Electrophysiology Format 1.0';
elseif isfolder(filename) && any(filetype_check_extension(filename, {'.mefd', '.timd', '.segd'}))
type = 'mayo_mef30';
manufacturer = 'Mayo Clinic';
Expand Down
8 changes: 6 additions & 2 deletions fileio/ft_read_data.m
Expand Up @@ -23,7 +23,7 @@
% 'fallback' can be empty or 'biosig' (default = [])
% 'blocking' wait for the selected number of events (default = 'no')
% 'timeout' amount of time in seconds to wait when blocking (default = 5)
% 'password' password structure for encrypted data set (only for mayo_mef30 and mayo_mef21)
% 'password' password structure for encrypted data set (only for dhn_med10, mayo_mef30 and mayo_mef21)
%
% This function returns a 2-D matrix of size Nchans*Nsamples for continuous
% data when begevent and endevent are specified, or a 3-D matrix of size
Expand Down Expand Up @@ -569,6 +569,10 @@
case 'dataq_wdq'
dat = read_wdq_data(filename, hdr.orig, begsample, endsample, chanindx);

case 'dhn_med10'
hdr.sampleunit = 'index';
dat = read_dhn_med10(filename, password, false, hdr, begsample, endsample, chanindx);

case 'eeglab_set'
dat = read_eeglabdata(filename, 'header', hdr, 'begtrial', begtrial, 'endtrial', endtrial, 'chanindx', chanindx);
dimord = 'chans_samples_trials';
Expand Down Expand Up @@ -1021,7 +1025,7 @@

case 'mayo_mef30'
hdr.sampleunit = 'index';
dat = read_mayo_mef30(filename, password, sortchannel, hdr, begsample, endsample, chanindx);
dat = read_mayo_mef30(filename, password, [], hdr, begsample, endsample, chanindx);

case 'mayo_mef21'
hdr.sampleunit = 'index';
Expand Down
8 changes: 7 additions & 1 deletion fileio/ft_read_event.m
Expand Up @@ -19,7 +19,7 @@
% 'tolerance' = tolerance in samples when merging Neuromag analogue trigger channels (default = 1, meaning that a shift of one sample in both directions is compensated for)
% 'blocking' = wait for the selected number of events (default = 'no')
% 'timeout' = amount of time in seconds to wait when blocking (default = 5)
% 'password' = password structure for encrypted data set (only for mayo_mef30 and mayo_mef21)
% 'password' = password structure for encrypted data set (only for dhn_med10, mayo_mef30 and mayo_mef21)
% 'readbids' = 'yes', no', or 'ifmakessense', whether to read information from the BIDS sidecar files (default = 'ifmakessense')
%
% This function returns an event structure with the following fields
Expand Down Expand Up @@ -688,6 +688,12 @@
event(i).sample = iy(i);
end

case 'dhn_med10'
if isempty(hdr)
hdr = ft_read_header(filename, 'password', password);
end
event = read_dhn_med10(filename, password, false, hdr);

case 'edf'
% read the header
if isempty(hdr)
Expand Down
10 changes: 7 additions & 3 deletions fileio/ft_read_header.m
Expand Up @@ -15,7 +15,7 @@
% 'chantype' = string or cell-array with strings, channel types to be read (only for NeuroOmega and BlackRock)
% 'coordsys' = string, 'head' or 'dewar' (default = 'head')
% 'headerformat' = name of a MATLAB function that takes the filename as input (default is automatic)
% 'password' = password structure for encrypted data set (only for mayo_mef30 and mayo_mef21)
% 'password' = password structure for encrypted data set (for dhn_med10, mayo_mef30, mayo_mef21)
% 'readbids' = string, 'yes', no', or 'ifmakessense', whether to read information from the BIDS sidecar files (default = 'ifmakessense')
%
% This returns a header structure with the following fields
Expand Down Expand Up @@ -854,6 +854,10 @@
hdr.label = orig.label(:);
hdr.orig = orig; % remember the original details

case 'dhn_med10'
ft_hastoolbox('mayo_mef', 1); % make sure mayo_mef exists
hdr = read_dhn_med10(filename, password);

case 'edf'
% this reader is largely similar to the bdf reader
if isempty(chanindx)
Expand Down Expand Up @@ -1911,7 +1915,7 @@
[fid, tree] = fiff_open(filename);
[info, meas] = fiff_read_meas_info(fid, tree);
fclose(fid);

% convert to FieldTrip format header
hdr.label = info.ch_names(:);
hdr.nChans = info.nchan;
Expand Down Expand Up @@ -1982,7 +1986,7 @@
info.info = evoked_data.info; % keep all the details
info.vartriallength = 0;
end

catch
% this happens if fiff_read_evoked_all cannot find evoked
% responses, in which case it errors due to not assigning the
Expand Down
6 changes: 3 additions & 3 deletions fileio/private/ft_hastoolbox.m
Expand Up @@ -143,7 +143,7 @@
'MATLAB2BESA' 'see http://www.besa.de/downloads/matlab/ and get the "MATLAB to BESA Export functions"'
'MATNWB' 'see https://neurodatawithoutborders.github.io/matnwb/'
'MATPLOTLIB' 'see https://nl.mathworks.com/matlabcentral/fileexchange/62729-matplotlib-perceptually-uniform-colormaps'
'MAYO_MEF' 'see https://github.com/MultimodalNeuroimagingLab/mef_reader_fieldtrip and https://msel.mayo.edu/codes.html'
'MAYO_MEF' 'see http://darkhorseneuro.com/, https://github.com/jiecui/mef_reader_fieldtrip and https://msel.mayo.edu/codes.html'
'MEG-CALC' 'this is a commercial toolbox from Neuromag, see http://www.neuromag.com'
'MEG-PD' 'see http://www.kolumbus.fi/kuutela/programs/meg-pd'
'MENTAT' 'see http://robertoostenveld.nl, or contact Robert Oostenveld'
Expand Down Expand Up @@ -432,8 +432,8 @@
dependency = {'copnorm' 'mi_gg'};
case 'XSENS'
dependency = {'load_mvnx'};
case 'MAYO_MEF' % MEF 2.1 and MEF 3.0
dependency = {'MEFFieldTrip_2p1', 'MEFFieldTrip_3p0'};
case 'MAYO_MEF' % MED 1.0, MEF 2.1 and MEF 3.0
dependency = {'MEDFieldTrip_1p0', 'MEFFieldTrip_2p1', 'MEFFieldTrip_3p0'};
case 'MATNWB'
dependency = {'nwbRead', 'generateCore'};
case 'MATPLOTLIB'
Expand Down
96 changes: 96 additions & 0 deletions fileio/private/read_dhn_med10.m
@@ -0,0 +1,96 @@
function dhn_out = read_dhn_med10(filename, password, sortchannel, hdr, begsample, endsample, chanindx)
% READ_DHN_MED10 read header, event, and waveform data formated in Dark Horse Neuron MED 1.0
%
% Syntax:
% hdr = read_dhn_med10(filename)
% hdr = read_dhn_med10(filename, password)
% hdr = read_dhn_med10(filename, password, sortchannel)
% evt = read_dhn_med10(filename, password, sortchannel, hdr)
% dat = read_dhn_med10(filename, password, sortchannel, hdr, begsample, endsample, chanindx)
%
% Input(s):
% filename - [char] name of the file or folder of the dataset
% password - [struct] (opt) password structure of MED 1.0 data (see
% MEDSession_1p0)
% sortchannel - [char] (opt) sort channel order either alphabetically
% 'alphabet' or numerically 'number' (default = 'alphabet')
% hdr - [struct] (opt) header structure of the dataset (see FT_READ_HEADER; default = struct([]))
% begsample - [num] (opt) first sample to read (default = [])
% endsample - [num] (opt) last smaple to read (default = [])
% chanindx - [num] (opt) list of channel indices to read (default = [])
%
% Output(s):
% hdr - [struct] header structure of the dataset (see FT_READ_HEADER)
% evt - [struct] event structure of the dataset (see FT_READ_EVENT)
% dat - [num] data read in
%
% Example:
%
% Note:
%
% References:
%
% See also FT_FILETYPE, FT_READ_HEADER, FT_READ_EVENT, FT_READ_DATA.

% Copyright 2023 Richard J. Cui. Created: Sat 02/11/2023 5:47:28.254 PM
% $Revision: 0.3 $ $Date: Sun 10/08/2023 01:56:00.468 PM $
%
% Rocky Creek Dr. NE
% Rochester, MN 55906, USA
%
% Email: richard.cui@utoronto.ca

% ======================================================================
% parse inputs
% ======================================================================
arguments
filename (1, :) char
password (1, 1) struct = struct('Level1Password', '', 'Level2Password', '', ...
'AccessLevel', 1); % example_data password =='L1_password' or 'L2_password'
sortchannel (1, 1) logical = false
hdr (1, 1) struct = struct()
begsample (1, 1) double = nan
endsample (1, 1) double = nan
chanindx (1, :) double = nan
end % positional

% ======================================================================
% main
% ======================================================================
% check the consistency of SortChannel
% ------------------------------------
if ~isempty(fieldnames(hdr)) && hdr.SortChannel ~= sortchannel
warning('off', 'backtrace')
warning('dhn_med10:invalidSortChannel', ...
'SortChannel provided -%s- is not consistent with -%s- in header. use that in header', ...
sortchannel, hdr.SortChannel)
warning('on', 'backtrace')

sortchannel = hdr.SortChannel;
end % if

% setup the instance of MEDFieldTrip_1p0
% -------------------------------------
med_ft = MEDFieldTrip_1p0(filename, password, sortchannel); % dealing MED 1.0 data for FieldTrip
channames = med_ft.SelectedChannel;

% get the desired information
% ---------------------------
switch nargin
case {1, 2, 3} % get header
dhn_out = med_ft.getHeader(channames);
dhn_out.SortChannel = sortchannel;
case 4 % get event
dhn_out = med_ft.getEvent(channames);
case 7 % get data
dhn_out = med_ft.getData(channames, hdr.sampleunit, begsample, endsample, ...
chanindx);
otherwise
% error
error('FieldTrip:dhn_med10:invalidInput', ...
'invalid number of inputs of the function')
end % switch

end % function read_dhn_med10

% [EOF]

0 comments on commit be34a25

Please sign in to comment.