Skip to content
Permalink
release
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
function [hdr] = ft_read_header(filename, varargin)
% FT_READ_HEADER reads header information from a variety of EEG, MEG and other time
% series data files and represents the header information in a common
% data-independent structure. The supported formats are listed below.
%
% Use as
% hdr = ft_read_header(filename, ...)
%
% Additional options should be specified in key-value pairs and can be
% 'headerformat' = string
% 'fallback' = can be empty or 'biosig' (default = [])
% 'checkmaxfilter' = boolean, whether to check that maxfilter has been correctly applied (default = true)
% 'chanindx' = list with channel indices in case of different sampling frequencies (only for EDF)
% '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)
% '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
% hdr.Fs = sampling frequency
% hdr.nChans = number of channels
% hdr.nSamples = number of samples per trial
% hdr.nSamplesPre = number of pre-trigger samples in each trial
% hdr.nTrials = number of trials
% hdr.label = Nx1 cell-array with the label of each channel
% hdr.chantype = Nx1 cell-array with the channel type, see FT_CHANTYPE
% hdr.chanunit = Nx1 cell-array with the physical units, see FT_CHANUNIT
%
% For continuously recorded data, this will return nSamplesPre=0 and nTrials=1.
%
% For some data formats that are recorded on animal electrophysiology
% systems (e.g. Neuralynx, Plexon), the following optional fields are
% returned, which allows for relating the timing of spike and LFP data
% hdr.FirstTimeStamp number, represented as 32-bit or 64-bit unsigned integer
% hdr.TimeStampPerSample number, represented in double precision
%
% Depending on the file format, additional header information can be
% returned in the hdr.orig subfield.
%
% To use an external reading function, you can specify an external function as the
% 'headerformat' option. This function should take the filename as input argument.
% Please check the code of this function for details, and search for BIDS_TSV as
% example.
%
% The following MEG dataformats are supported
% CTF (*.ds, *.res4, *.meg4)
% Neuromag/Elekta/Megin (*.fif)
% BTi/4D (*.m4d, *.pdf, *.xyz)
% Yokogawa/Ricoh (*.ave, *.con, *.raw)
% NetMEG (*.nc)
% ITAB - Chieti (*.mhd)
% Tristan Babysquid (*.fif)
% York Instruments (*.meghdf5)
%
% The following EEG dataformats are supported
% ANT - Advanced Neuro Technology, EEProbe (*.avr, *.eeg, *.cnt)
% BCI2000 (*.dat)
% Biosemi (*.bdf)
% BrainVision (*.eeg, *.seg, *.dat, *.vhdr, *.vmrk)
% CED - Cambridge Electronic Design (*.smr)
% EGI - Electrical Geodesics, Inc. (*.egis, *.ave, *.gave, *.ses, *.raw, *.sbin, *.mff)
% GTec (*.mat, *.hdf5)
% Generic data formats (*.edf, *.gdf)
% Megis/BESA (*.avr, *.swf, *.besa)
% NeuroScan (*.eeg, *.cnt, *.avg)
% Nexstim (*.nxe)
% TMSi (*.Poly5)
% Mega Neurone (directory)
% Natus/Nicolet/Nervus (.e files)
% Nihon Kohden (*.m00, *.EEG)
% Bitalino OpenSignals (*.txt)
% OpenBCI (*.txt)
%
% The following spike and LFP dataformats are supported
% Neuralynx (*.ncs, *.nse, *.nts, *.nev, *.nrd, *.dma, *.log)
% Plextor (*.nex, *.plx, *.ddt)
% CED - Cambridge Electronic Design (*.smr)
% MPI - Max Planck Institute (*.dap)
% Neurosim (neurosim_spikes, neurosim_signals, neurosim_ds)
% Windaq (*.wdq)
% NeuroOmega (*.mat transformed from *.mpx)
% Neurodata Without Borders: Neurophysiology (*.nwb)
%
% The following NIRS dataformats are supported
% Artinis - Artinis Medical Systems B.V. (*.oxy3, *.oxy4, *.oxy5, *.oxyproj)
% BUCN - Birkbeck college, London (*.txt)
% SNIRF - Society for functional near-infrared spectroscopy (*.snirf)
%
% The following Eyetracker dataformats are supported
% EyeLink - SR Research (*.asc)
% SensoMotoric Instruments - (*.txt)
% Tobii - (*.tsv)
%
% See also FT_READ_DATA, FT_READ_EVENT, FT_WRITE_DATA, FT_WRITE_EVENT,
% FT_CHANTYPE, FT_CHANUNIT
% Copyright (C) 2003-2021 Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$
% TODO channel renaming should be made a general option (see bham_bdf)
persistent cacheheader % for caching the full header
persistent cachechunk % for caching the res4 chunk when doing realtime analysis on the CTF scanner
persistent db_blob % for fcdc_mysql
if isempty(db_blob)
db_blob = false;
end
if iscell(filename)
% use recursion to read the header from multiple files
ft_warning('concatenating header from %d files', numel(filename));
hdr = cell(size(filename));
for i=1:numel(filename)
hdr{i} = ft_read_header(filename{i}, varargin{:});
end
allhdr = cat(1, hdr{:});
if numel(unique([allhdr.label]))==sum([allhdr.nChans])
% each file has different channels, concatenate along the channel dimension
for i=1:numel(filename)
assert(isequal(hdr{i}.Fs, hdr{1}.Fs), 'sampling rates are not consistent over files');
assert(isequal(hdr{i}.nSamples, hdr{1}.nSamples), 'number of samples is not consistent over files');
assert(isequal(hdr{i}.nTrials, hdr{1}.nTrials), 'number of trials is not consistent over files');
end
combined = hdr{1}; % copy the first header as the general one
combined.label = [allhdr.label];
combined.chanunit = [allhdr.chanunit];
combined.chantype = [allhdr.chantype];
combined.nChans = sum([allhdr.nChans]);
combined.orig = hdr; % store the original header details of all files
else
% each file has the same channels, concatenate along the time dimension
ntrl = nan(size(filename));
nsmp = nan(size(filename));
for i=1:numel(filename)
assert(isequal(hdr{i}.Fs, hdr{1}.Fs), 'sampling rates are not consistent over files');
assert(isequal(hdr{i}.label, hdr{1}.label), 'channels are not consistent over files');
ntrl(i) = hdr{i}.nTrials;
nsmp(i) = hdr{i}.nSamples;
end
% the subsequent code concatenates the files over time, i.e. each file has the same channels
combined = hdr{1}; % copy the first header as the general one
combined.orig = hdr; % store the original header details of all files
if all(ntrl==1)
% each file is a continuous recording
combined.nTrials = ntrl(1);
combined.nSamples = sum(nsmp);
elseif all(nsmp==nsmp(1))
% each file holds segments of the same length
combined.nTrials = sum(ntrl);
combined.nSamples = nsmp(1);
else
ft_error('cannot concatenate files');
end
end % concatenate over channels or over time
% return the header of the concatenated datafiles
hdr = combined;
return
end
% get the options
headerformat = ft_getopt(varargin, 'headerformat');
retry = ft_getopt(varargin, 'retry', false); % the default is not to retry reading the header
chanindx = ft_getopt(varargin, 'chanindx'); % this is used for EDF with different sampling rates
coordsys = ft_getopt(varargin, 'coordsys', 'head'); % this is used for ctf and neuromag_mne, it can be head or dewar
coilaccuracy = ft_getopt(varargin, 'coilaccuracy'); % empty, or a number between 0-2
chantype = ft_getopt(varargin, 'chantype', {});
password = ft_getopt(varargin, 'password', struct([]));
readbids = ft_getopt(varargin, 'readbids', 'ifmakessense');
% this should be a cell array
if ~iscell(chantype); chantype = {chantype}; end
% optionally get the data from the URL and make a temporary local copy
filename = fetch_url(filename);
if isempty(headerformat)
% only do the autodetection if the format was not specified
headerformat = ft_filetype(filename);
end
if iscell(headerformat)
% this happens for datasets specified as cell-array for concatenation
headerformat = headerformat{1};
end
if strcmp(headerformat, 'compressed')
% the file is compressed, unzip on the fly
inflated = true;
filename = inflate_file(filename);
headerformat = ft_filetype(filename);
else
inflated = false;
end
% for backward compatibility with https://github.com/fieldtrip/fieldtrip/issues/1585
if islogical(readbids)
% it should be either yes/no/ifmakessense
if readbids
readbids = 'yes';
else
readbids = 'no';
end
end
realtime = any(strcmp(headerformat, {'fcdc_buffer', 'ctf_shm', 'fcdc_mysql'}));
% The checkUniqueLabels flag is used for the realtime buffer in case
% it contains fMRI data. It prevents 1000000 voxel names to be checked
% for uniqueness. fMRI users will probably never use channel names
% for anything.
if realtime
% skip the rest of the initial checks to increase the speed for realtime operation
checkUniqueLabels = false;
% the cache and fallback option should always be false for realtime processing
cache = false;
fallback = false;
else
% check whether the file or directory exists
if ~exist(filename, 'file')
ft_error('file or directory ''%s'' does not exist', filename);
end
checkUniqueLabels = true;
% get the rest of the options, this is skipped for realtime operation
cache = ft_getopt(varargin, 'cache');
fallback = ft_getopt(varargin, 'fallback');
checkmaxfilter = ft_getopt(varargin, 'checkmaxfilter', true);
if isempty(cache)
if any(strcmp(headerformat, {'bci2000_dat', 'eyelink_asc', 'gtec_mat', 'gtec_hdf5', 'mega_neurone', 'nihonkohden_m00', 'smi_txt', 'biosig'}))
cache = true;
else
cache = false;
end
end
% ensure that the headerfile and datafile are defined, which are sometimes different than the name of the dataset
[filename, headerfile, datafile] = dataset2files(filename, headerformat);
if ~strcmp(filename, headerfile) && ~ft_filetype(filename, 'ctf_ds') && ~ft_filetype(filename, 'fcdc_buffer_offline') && ~ft_filetype(filename, 'fcdc_matbin')
filename = headerfile; % this function should read the headerfile, not the dataset
headerformat = ft_filetype(filename); % update the filetype
end
end % if skip initial check
% implement the caching in a data-format independent way
if cache && exist(headerfile, 'file') && ~isempty(cacheheader)
% try to get the header from cache
details = dir(headerfile);
if isequal(details, cacheheader.details)
% the header file has not been updated, fetch it from the cache
% fprintf('got header from cache\n');
hdr = rmfield(cacheheader, 'details');
switch ft_filetype(datafile)
case {'ctf_ds' 'ctf_meg4' 'ctf_old' 'read_ctf_res4'}
% for realtime analysis end-of-file-chasing the res4 does not correctly
% estimate the number of samples, so we compute it on the fly
sz = 0;
files = dir([filename '/*.*meg4']);
for j=1:numel(files)
sz = sz + files(j).bytes;
end
hdr.nTrials = floor((sz - 8) / (hdr.nChans*4) / hdr.nSamples);
end
return
end % if the details correspond
end % if cache
% the support for head/dewar coordinates is still limited
if strcmp(coordsys, 'dewar') && ~any(strcmp(headerformat, {'fcdc_buffer', 'ctf_ds', 'ctf_meg4', 'ctf_res4', 'neuromag_fif', 'neuromag_mne'}))
ft_error('dewar coordinates are not supported for %s', headerformat);
end
% deal with data that is organized according to BIDS
if strcmp(readbids, 'yes') || strcmp(readbids, 'ifmakessense')
[p, f, x] = fileparts(filename);
% check whether it is a BIDS dataset with json and tsv sidecar files
% data in a BIDS tsv file (like physio and stim) will be explicitly dealt with in BIDS_TSV
isbids = startsWith(f, 'sub-') && ~strcmp(x, '.tsv');
if isbids
% try to read the metadata from the BIDS sidecar files
sidecar = bids_sidecar(filename);
if ~isempty(sidecar)
data_json = ft_read_json(sidecar);
end
sidecar = bids_sidecar(filename, 'channels');
if ~isempty(sidecar)
channels_tsv = ft_read_tsv(sidecar);
end
sidecar = bids_sidecar(filename, 'electrodes');
if ~isempty(sidecar)
electrodes_tsv = ft_read_tsv(sidecar);
end
sidecar = bids_sidecar(filename, 'optodes');
if ~isempty(sidecar)
optodes_tsv = ft_read_tsv(sidecar);
end
sidecar = bids_sidecar(filename, 'coordsystem');
if ~isempty(sidecar)
coordsystem_json = ft_read_json(sidecar);
end
end
end
% start with an empty header
hdr = [];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% read the data with the low-level reading function
% please maintain this list in alphabetical order
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch headerformat
case '4d'
orig = read_4d_hdr(datafile);
hdr.Fs = orig.header_data.SampleFrequency;
hdr.nChans = orig.header_data.TotalChannels;
hdr.nSamples = orig.header_data.SlicesPerEpoch;
hdr.nSamplesPre = round(orig.header_data.FirstLatency*orig.header_data.SampleFrequency);
hdr.nTrials = orig.header_data.TotalEpochs;
%hdr.label = {orig.channel_data(:).chan_label}';
hdr.label = orig.Channel;
[hdr.grad, elec] = bti2grad(orig);
if ~isempty(elec)
hdr.elec = elec;
end
% remember original header details
hdr.orig = orig;
case {'4d_pdf', '4d_m4d', '4d_xyz'}
orig = read_bti_m4d(filename);
hdr.Fs = orig.SampleFrequency;
hdr.nChans = orig.TotalChannels;
hdr.nSamples = orig.SlicesPerEpoch;
hdr.nSamplesPre = round(orig.FirstLatency*orig.SampleFrequency);
hdr.nTrials = orig.TotalEpochs;
hdr.label = orig.ChannelOrder(:);
[hdr.grad, elec] = bti2grad(orig);
if ~isempty(elec)
hdr.elec = elec;
end
% remember original header details
hdr.orig = orig;
case 'AnyWave'
orig = read_ahdf5_hdr(datafile);
hdr.orig = orig;
hdr.Fs = orig.channels(1).samplingRate;
hdr.nChans = numel(orig.channels);
hdr.nSamples = orig.numberOfSamples;
hdr.nTrials = orig.numberOfBlocks;
hdr.nSamplesPre = 0;
hdr.label = orig.label;
hdr.reference = orig.reference(:);
hdr.chanunit = orig.unit(:);
hdr.chantype = orig.type(:);
case 'bci2000_dat'
% this requires the load_bcidat mex file to be present on the path
ft_hastoolbox('BCI2000', 1);
% this is inefficient, since it reads the complete data
[signal, states, parameters, total_samples] = load_bcidat(filename);
% convert into a FieldTrip-like header
hdr = [];
hdr.nChans = size(signal,2);
hdr.nSamples = total_samples;
hdr.nSamplesPre = 0; % it is continuous
hdr.nTrials = 1; % it is continuous
hdr.Fs = parameters.SamplingRate.NumericValue;
% there are some differences in the fields that are present in the
% *.dat files, probably due to different BCI2000 versions
if isfield(parameters, 'ChannelNames') && isfield(parameters.ChannelNames, 'Value') && ~isempty(parameters.ChannelNames.Value)
hdr.label = parameters.ChannelNames.Value;
elseif isfield(parameters, 'ChannelNames') && isfield(parameters.ChannelNames, 'Values') && ~isempty(parameters.ChannelNames.Values)
hdr.label = parameters.ChannelNames.Values;
else
% give this warning only once
ft_warning('creating fake channel names');
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
end
% remember the original header details
hdr.orig.parameters = parameters;
% also remember the complete data upon request
if cache
hdr.orig.signal = signal;
hdr.orig.states = states;
hdr.orig.total_samples = total_samples;
end
case 'besa_besa'
if isempty(chanindx)
hdr = read_besa_besa(filename);
else
% this will keep the non-selected channels hidden from the user
hdr = read_besa_besa(filename, [], chanindx);
end
case 'besa_avr'
orig = read_besa_avr(filename);
hdr.Fs = 1000/orig.di;
hdr.nChans = size(orig.data,1);
hdr.nSamples = size(orig.data,2);
hdr.nSamplesPre = -(hdr.Fs * orig.tsb/1000); % convert from ms to samples
hdr.nTrials = 1;
if isfield(orig, 'label') && iscell(orig.label)
hdr.label = orig.label;
elseif isfield(orig, 'label') && ischar(orig.label)
hdr.label = tokenize(orig.label, ' ');
else
% give this warning only once
ft_warning('creating fake channel names');
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
end
case 'besa_swf'
orig = read_besa_swf(filename);
hdr.Fs = 1000/orig.di;
hdr.nChans = size(orig.data,1);
hdr.nSamples = size(orig.data,2);
hdr.nSamplesPre = -(hdr.Fs * orig.tsb/1000); % convert from ms to samples
hdr.nTrials = 1;
hdr.label = orig.label;
case 'biosig'
% this requires the biosig toolbox
ft_hastoolbox('BIOSIG', 1);
hdr = read_biosig_header(filename);
case {'biosemi_bdf', 'bham_bdf'}
hdr = read_biosemi_bdf(filename);
if any(diff(hdr.orig.SampleRate))
ft_error('channels with different sampling rate not supported');
end
if ~ft_senstype(hdr, 'ext1020')
% assign the channel type and units for the known channels
hdr.chantype = repmat({'unknown'}, size(hdr.label));
hdr.chanunit = repmat({'unknown'}, size(hdr.label));
chan = ~cellfun(@isempty, regexp(hdr.label, '^[A-D]\d*$'));
hdr.chantype(chan) = {'eeg'};
hdr.chanunit(chan) = {'uV'};
end
if ft_filetype(filename, 'bham_bdf')
% TODO channel renaming should be made a general option
% this is for the Biosemi system used at the University of Birmingham
labelold = { 'A1' 'A2' 'A3' 'A4' 'A5' 'A6' 'A7' 'A8' 'A9' 'A10' 'A11' 'A12' 'A13' 'A14' 'A15' 'A16' 'A17' 'A18' 'A19' 'A20' 'A21' 'A22' 'A23' 'A24' 'A25' 'A26' 'A27' 'A28' 'A29' 'A30' 'A31' 'A32' 'B1' 'B2' 'B3' 'B4' 'B5' 'B6' 'B7' 'B8' 'B9' 'B10' 'B11' 'B12' 'B13' 'B14' 'B15' 'B16' 'B17' 'B18' 'B19' 'B20' 'B21' 'B22' 'B23' 'B24' 'B25' 'B26' 'B27' 'B28' 'B29' 'B30' 'B31' 'B32' 'C1' 'C2' 'C3' 'C4' 'C5' 'C6' 'C7' 'C8' 'C9' 'C10' 'C11' 'C12' 'C13' 'C14' 'C15' 'C16' 'C17' 'C18' 'C19' 'C20' 'C21' 'C22' 'C23' 'C24' 'C25' 'C26' 'C27' 'C28' 'C29' 'C30' 'C31' 'C32' 'D1' 'D2' 'D3' 'D4' 'D5' 'D6' 'D7' 'D8' 'D9' 'D10' 'D11' 'D12' 'D13' 'D14' 'D15' 'D16' 'D17' 'D18' 'D19' 'D20' 'D21' 'D22' 'D23' 'D24' 'D25' 'D26' 'D27' 'D28' 'D29' 'D30' 'D31' 'D32' 'EXG1' 'EXG2' 'EXG3' 'EXG4' 'EXG5' 'EXG6' 'EXG7' 'EXG8' 'Status'};
labelnew = { 'P9' 'PPO9h' 'PO7' 'PPO5h' 'PPO3h' 'PO5h' 'POO9h' 'PO9' 'I1' 'OI1h' 'O1' 'POO1' 'PO3h' 'PPO1h' 'PPO2h' 'POz' 'Oz' 'Iz' 'I2' 'OI2h' 'O2' 'POO2' 'PO4h' 'PPO4h' 'PO6h' 'POO10h' 'PO10' 'PO8' 'PPO6h' 'PPO10h' 'P10' 'P8' 'TPP9h' 'TP7' 'TTP7h' 'CP5' 'TPP7h' 'P7' 'P5' 'CPP5h' 'CCP5h' 'CP3' 'P3' 'CPP3h' 'CCP3h' 'CP1' 'P1' 'Pz' 'CPP1h' 'CPz' 'CPP2h' 'P2' 'CPP4h' 'CP2' 'CCP4h' 'CP4' 'P4' 'P6' 'CPP6h' 'CCP6h' 'CP6' 'TPP8h' 'TP8' 'TPP10h' 'T7' 'FTT7h' 'FT7' 'FC5' 'FCC5h' 'C5' 'C3' 'FCC3h' 'FC3' 'FC1' 'C1' 'CCP1h' 'Cz' 'FCC1h' 'FCz' 'FFC1h' 'Fz' 'FFC2h' 'FC2' 'FCC2h' 'CCP2h' 'C2' 'C4' 'FCC4h' 'FC4' 'FC6' 'FCC6h' 'C6' 'TTP8h' 'T8' 'FTT8h' 'FT8' 'FT9' 'FFT9h' 'F7' 'FFT7h' 'FFC5h' 'F5' 'AFF7h' 'AF7' 'AF5h' 'AFF5h' 'F3' 'FFC3h' 'F1' 'AF3h' 'Fp1' 'Fpz' 'Fp2' 'AFz' 'AF4h' 'F2' 'FFC4h' 'F4' 'AFF6h' 'AF6h' 'AF8' 'AFF8h' 'F6' 'FFC6h' 'FFT8h' 'F8' 'FFT10h' 'FT10'};
% rename the channel labels
for i=1:length(labelnew)
chan = strcmp(labelold(i), hdr.label);
hdr.label(chan) = labelnew(chan);
end
end
case {'biosemi_old'}
% this uses the openbdf and readbdf functions that were copied from EEGLAB
orig = openbdf(filename);
if any(orig.Head.SampleRate~=orig.Head.SampleRate(1))
ft_error('channels with different sampling rate not supported');
end
hdr.Fs = orig.Head.SampleRate(1);
hdr.nChans = orig.Head.NS;
hdr.label = cellstr(orig.Head.Label);
% it is continuous data, therefore append all records in one trial
hdr.nSamples = orig.Head.NRec * orig.Head.Dur * orig.Head.SampleRate(1);
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.orig = orig;
% close the file between separate read operations
fclose(orig.Head.FILE.FID);
case 'blackrock_nev'
% read header for nsX file associated with NEV file
% use ft_read_event to read event information in .nev file
ft_hastoolbox('NPMK', 1);
% ensure that the filename contains a full path specification,
% otherwise the low-level function fails
[p,n] = fileparts(filename);
if isempty(p)
filename = which(filename);
[p,n] = fileparts(filename);
end
NEV = openNEV(filename,'noread','nosave');
% searching for associated nsX file in same folder
files=dir(strcat(fullfile(p,n),'.ns*'));
if isempty(files)
ft_error('no .ns* file associated to %s in %s',n,p);
end
%searching for nsX file with same sampling freq that NEV
for i=1:numel(files)
nsX_hdr = ft_read_header(fullfile(p,files(i).name),'chantype',chantype);
if nsX_hdr.Fs == NEV.MetaTags.SampleRes
hdr = nsX_hdr;
break
end
end
if isempty(hdr)
ft_error('no .ns* file with same sampling frequency as %s (%i)',n,NEV.MetaTags.SampleRes);
end
case 'blackrock_nsx'
ft_hastoolbox('NPMK', 1);
% ensure that the filename contains a full path specification,
% otherwise the low-level function fails
p = fileparts(filename);
if isempty(p)
filename = which(filename);
end
orig = openNSx(filename, 'noread');
channelstype=regexp({orig.ElectrodesInfo.Label},'[A-Za-z]+','match','once');
chaninfo=table({orig.ElectrodesInfo.ElectrodeID}',...
transpose(deblank({orig.ElectrodesInfo.Label})),[channelstype]',...
{orig.ElectrodesInfo.ConnectorBank}',{orig.ElectrodesInfo.ConnectorPin}',...
transpose(deblank({orig.ElectrodesInfo.AnalogUnits})),...
'VariableNames',{'id' 'label' 'chantype' 'bank' 'pin' 'unit'});
if isempty(chantype)
chantype = unique(channelstype,'stable');
end
% selecting channel according to chantype
orig_label=deblank({orig.ElectrodesInfo.Label});
orig_unit=deblank({orig.ElectrodesInfo.AnalogUnits});
channels={}; channelstype={}; channelsunit={}; skipfactor=[];
for c=1:length(chantype)
chantype_split=strsplit(chantype{c},':');
if numel(chantype_split) == 2
chantype{c}=chantype_split{1};
skipfactor=[skipfactor,str2double(chantype_split{2})];
elseif numel(chantype_split) > 2
ft_error('Use : to specify skipfactor, e.g. analog:10')
end
chan_sel=~cellfun(@isempty,regexp(orig_label,chantype{c}));
if sum(chan_sel)==0
if ~strcmp(chantype{c},'chaninfo')
ft_error('unknown chantype %s, available channels are %s',chantype{c},strjoin(orig_label));
end
else
channels=[channels, orig_label(chan_sel)];
channelsunit=[channelsunit, orig_unit(chan_sel)];
channelstype=[channelstype, repmat(chantype(c), 1, sum(chan_sel))];
end
end
skipfactor=unique(skipfactor);
if isempty(skipfactor)
skipfactor=1;
elseif length(skipfactor)>1
ft_error('inconsistent skip factors across channels');
end
% If no channel selected issue error specifying available chantypes
if isempty(channels)
ft_error('No channel selected. Availabe chantypes are: %s', strjoin(unique(chaninfo.chantype)));
end
hdr.Fs = orig.MetaTags.SamplingFreq/skipfactor;
hdr.nChans = length(channels);
hdr.nSamples = floor(orig.MetaTags.DataPoints/skipfactor);
hdr.nSamplesPre = 0;
hdr.nTrials = 1; %?
hdr.label = deblank(channels)';
hdr.chantype = channelstype;
hdr.chanunit = channelsunit;
hdr.orig = orig;
hdr.orig.chaninfo = chaninfo;
hdr.orig.skipfactor = skipfactor;
case {'brainvision_vhdr', 'brainvision_seg', 'brainvision_eeg', 'brainvision_dat'}
orig = read_brainvision_vhdr(filename);
hdr.Fs = orig.Fs;
hdr.nChans = orig.NumberOfChannels;
hdr.label = orig.label;
hdr.nSamples = orig.nSamples;
hdr.nSamplesPre = orig.nSamplesPre;
hdr.nTrials = orig.nTrials;
hdr.orig = orig;
% assign the channel type and units for the known channels
hdr.chantype = repmat({'eeg'}, size(hdr.label));
hdr.chanunit = repmat({'uV'}, size(hdr.label));
case 'bucn_nirs'
orig = read_bucn_nirshdr(filename);
hdr = rmfield(orig, 'time');
hdr.orig = orig;
case 'ced_son'
% check that the required low-level toolbox is available
ft_hastoolbox('neuroshare', 1);
% use the reading function supplied by Gijs van Elswijk
orig = read_ced_son(filename,'readevents','no','readdata','no');
orig = orig.header;
% In Spike2, channels can have different sampling rates, units, length
% etc. etc. Here, channels need to have to same properties.
if length(unique([orig.samplerate]))>1,
ft_error('channels with different sampling rates are not supported');
else
hdr.Fs = orig(1).samplerate;
end
hdr.nChans = length(orig);
% nsamples of the channel with least samples
hdr.nSamples = min([orig.nsamples]);
hdr.nSamplesPre = 0;
% only continuous data supported
if sum(strcmpi({orig.mode},'continuous')) < hdr.nChans,
ft_error('not all channels contain continuous data');
else
hdr.nTrials = 1;
end
hdr.label = {orig.label};
case 'combined_ds'
hdr = read_combined_ds(filename);
case {'ctf_ds', 'ctf_meg4', 'ctf_res4'}
% check the presence of the required low-level toolbox
ft_hastoolbox('ctf', 1);
orig = readCTFds(filename);
if isempty(orig)
% this is to deal with data from the 64 channel system and the error
% readCTFds: .meg4 file header=MEG4CPT Valid header options: MEG41CP MEG42CP
ft_error('could not read CTF with this implementation, please try again with the ''ctf_old'' file format');
end
hdr.Fs = orig.res4.sample_rate;
hdr.nChans = orig.res4.no_channels;
hdr.nSamples = orig.res4.no_samples;
hdr.nSamplesPre = orig.res4.preTrigPts;
hdr.nTrials = orig.res4.no_trials;
hdr.label = cellstr(orig.res4.chanNames);
for i=1:numel(hdr.label)
% remove the site-specific numbers from each channel name, e.g. 'MZC01-1706' becomes 'MZC01'
hdr.label{i} = strtok(hdr.label{i}, '-');
end
% read the balance coefficients, these are used to compute the synthetic gradients
coeftype = cellstr(char(orig.res4.scrr(:).coefType));
try
[alphaMEG,MEGlist,Refindex] = getCTFBalanceCoefs(orig,'NONE', 'T');
orig.BalanceCoefs.none.alphaMEG = alphaMEG;
orig.BalanceCoefs.none.MEGlist = MEGlist;
orig.BalanceCoefs.none.Refindex = Refindex;
catch
ft_warning('cannot read balancing coefficients for NONE');
end
if any(~cellfun(@isempty,strfind(coeftype, 'G1BR')))
try
[alphaMEG,MEGlist,Refindex] = getCTFBalanceCoefs(orig,'G1BR', 'T');
orig.BalanceCoefs.G1BR.alphaMEG = alphaMEG;
orig.BalanceCoefs.G1BR.MEGlist = MEGlist;
orig.BalanceCoefs.G1BR.Refindex = Refindex;
catch
ft_warning('cannot read balancing coefficients for G1BR');
end
end
if any(~cellfun(@isempty,strfind(coeftype, 'G2BR')))
try
[alphaMEG,MEGlist,Refindex] = getCTFBalanceCoefs(orig, 'G2BR', 'T');
orig.BalanceCoefs.G2BR.alphaMEG = alphaMEG;
orig.BalanceCoefs.G2BR.MEGlist = MEGlist;
orig.BalanceCoefs.G2BR.Refindex = Refindex;
catch
ft_warning('cannot read balancing coefficients for G2BR');
end
end
if any(~cellfun(@isempty,strfind(coeftype, 'G3BR')))
try
[alphaMEG,MEGlist,Refindex] = getCTFBalanceCoefs(orig, 'G3BR', 'T');
orig.BalanceCoefs.G3BR.alphaMEG = alphaMEG;
orig.BalanceCoefs.G3BR.MEGlist = MEGlist;
orig.BalanceCoefs.G3BR.Refindex = Refindex;
catch
ft_warning('cannot read balancing coefficients for G3BR');
end
end
if any(~cellfun(@isempty,strfind(coeftype, 'G3AR')))
try
[alphaMEG,MEGlist,Refindex] = getCTFBalanceCoefs(orig, 'G3AR', 'T');
orig.BalanceCoefs.G3AR.alphaMEG = alphaMEG;
orig.BalanceCoefs.G3AR.MEGlist = MEGlist;
orig.BalanceCoefs.G3AR.Refindex = Refindex;
catch
% May not want a warning here if these are not commonly used.
% Already get a (fprintf) warning from getCTFBalanceCoefs.m
% ft_warning('cannot read balancing coefficients for G3AR');
end
end
% add a gradiometer structure for forward and inverse modelling
try
[grad, elec] = ctf2grad(orig, strcmp(coordsys, 'dewar'), coilaccuracy);
if ~isempty(grad)
hdr.grad = grad;
end
if ~isempty(elec)
hdr.elec = elec;
end
catch
% this fails if the res4 file is not correctly closed, e.g. during realtime processing
tmp = lasterror;
disp(tmp.message);
ft_warning('could not construct gradiometer definition from the header');
end
% for realtime analysis EOF chasing the res4 does not correctly
% estimate the number of samples, so we compute it on the fly from the
% meg4 file sizes.
sz = 0;
files = dir([filename '/*.*meg4']);
for j=1:numel(files)
sz = sz + files(j).bytes;
end
hdr.nTrials = floor((sz - 8) / (hdr.nChans*4) / hdr.nSamples);
% add the original header details
hdr.orig = orig;
case {'ctf_old', 'read_ctf_res4'}
% read it using the open-source MATLAB code that originates from CTF and that was modified by the FCDC
orig = read_ctf_res4(headerfile);
hdr.Fs = orig.Fs;
hdr.nChans = orig.nChans;
hdr.nSamples = orig.nSamples;
hdr.nSamplesPre = orig.nSamplesPre;
hdr.nTrials = orig.nTrials;
hdr.label = orig.label;
% add a gradiometer structure for forward and inverse modelling
try
hdr.grad = ctf2grad(orig);
catch
% this fails if the res4 file is not correctly closed, e.g. during realtime processing
tmp = lasterror;
disp(tmp.message);
ft_warning('could not construct gradiometer definition from the header');
end
% add the original header details
hdr.orig = orig;
case 'ctf_read_res4'
% check that the required low-level toolbos ix available
ft_hastoolbox('eegsf', 1);
% read it using the CTF importer from the NIH and Daren Weber
orig = ctf_read_res4(fileparts(headerfile), 0);
% convert the header into a structure that FieldTrip understands
hdr = [];
hdr.Fs = orig.setup.sample_rate;
hdr.nChans = length(orig.sensor.info);
hdr.nSamples = orig.setup.number_samples;
hdr.nSamplesPre = orig.setup.pretrigger_samples;
hdr.nTrials = orig.setup.number_trials;
for i=1:length(orig.sensor.info)
hdr.label{i} = orig.sensor.info(i).label;
end
hdr.label = hdr.label(:);
% add a gradiometer structure for forward and inverse modelling
try
hdr.grad = ctf2grad(orig);
catch
% this fails if the res4 file is not correctly closed, e.g. during realtime processing
tmp = lasterror;
disp(tmp.message);
ft_warning('could not construct gradiometer definition from the header');
end
% add the original header details
hdr.orig = orig;
case 'ctf_shm'
% read the header information from shared memory
hdr = read_shm_header(filename);
case {'curry_dat', 'curry_cdt'}
orig = load_curry_data_file(filename);
hdr = [];
hdr.Fs = orig.fFrequency;
hdr.nChans = orig.nChannels;
hdr.nSamples = orig.nSamples;
hdr.nSamplesPre = sum(orig.time<0);
hdr.nTrials = orig.nTrials;
hdr.label = orig.labels(:);
hdr.orig = orig;
case 'dataq_wdq'
orig = read_wdq_header(filename);
hdr = [];
hdr.Fs = orig.fsample;
hdr.nChans = orig.nchan;
hdr.nSamples = orig.nbytesdat/(2*hdr.nChans);
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
for k = 1:hdr.nChans
if isfield(orig.chanhdr(k), 'annot') && ~isempty(orig.chanhdr(k).annot)
hdr.label{k,1} = orig.chanhdr(k).annot;
else
hdr.label{k,1} = orig.chanhdr(k).label;
end
end
% add the original header details
hdr.orig = orig;
case {'deymed_ini' 'deymed_dat'}
% the header is stored in a *.ini file
orig = read_deymed_ini(headerfile);
hdr = [];
hdr.Fs = orig.Fs;
hdr.nChans = orig.nChans;
hdr.nSamples = orig.nSamples;
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.label = orig.label(:);
hdr.orig = orig; % remember the original details
case 'edf'
% this reader is largely similar to the bdf reader
if isempty(chanindx)
hdr = read_edf(filename);
else
% this will keep the non-selected channels hidden from the user
hdr = read_edf(filename, [], chanindx);
end
case 'eep_avr'
% check that the required low-level toolbox is available
ft_hastoolbox('eeprobe', 1);
% read the whole average and keep only header info (it is a bit silly, but the easiest to do here)
hdr = read_eep_avr(filename);
hdr.Fs = hdr.rate;
hdr.nChans = size(hdr.data,1);
hdr.nSamples = size(hdr.data,2);
hdr.nSamplesPre = hdr.xmin*hdr.rate/1000;
hdr.nTrials = 1; % it can always be interpreted as continuous data
% remove the data and variance if present
hdr = removefields(hdr, {'data', 'variance'});
case 'eep_cnt'
% check that the required low-level toolbox is available
ft_hastoolbox('eeprobe', 1);
% read the first sample from the continuous data, this will also return the header
orig = read_eep_cnt(filename, 1, 1);
hdr.Fs = orig.rate;
hdr.nSamples = orig.nsample;
hdr.nSamplesPre = 0;
hdr.label = orig.label;
hdr.nChans = orig.nchan;
hdr.nTrials = 1; % it can always be interpreted as continuous data
hdr.orig = orig; % remember the original details
case 'eeglab_set'
hdr = read_eeglabheader(filename);
case 'eeglab_erp'
hdr = read_erplabheader(filename);
case 'emotiv_mat'
% This is a MATLAB *.mat file that is created using the Emotiv MATLAB
% example code. It contains a 25xNsamples matrix and some other stuff.
orig = load(filename);
hdr.Fs = 128;
hdr.nChans = 25;
hdr.nSamples = size(orig.data_eeg,1);
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.label = {'ED_COUNTER','ED_INTERPOLATED','ED_RAW_CQ','ED_AF3','ED_F7','ED_F3','ED_FC5','ED_T7','ED_P7','ED_O1','ED_O2','ED_P8','ED_T8','ED_FC6','ED_F4','ED_F8','ED_AF4','ED_GYROX','ED_GYROY','ED_TIMESTAMP','ED_ES_TIMESTAMP','ED_FUNC_ID','ED_FUNC_VALUE','ED_MARKER','ED_SYNC_SIGNAL'};
% store the complete information in hdr.orig
% ft_read_data and ft_read_event will get it from there
hdr.orig = orig;
case 'eyelink_asc'
asc = read_eyelink_asc(filename);
hdr.nChans = size(asc.dat,1);
hdr.nSamples = size(asc.dat,2);
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.FirstTimeStamp = asc.dat(1,1);
hdr.TimeStampPerSample = mean(diff(asc.dat(1,:)));
hdr.Fs = 1000/hdr.TimeStampPerSample; % these timestamps are in miliseconds
% give this warning only once
ft_warning('creating fake channel names');
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
% remember the original header details
hdr.orig.header = asc.header;
% remember all header and data details upon request
if cache
hdr.orig = asc;
end
case 'spmeeg_mat'
hdr = read_spmeeg_header(filename);
case 'ced_spike6mat'
hdr = read_spike6mat_header(filename);
case 'egi_egia'
[fhdr,chdr,ename,cnames,fcom,ftext] = read_egis_header(filename);
[p, f, x] = fileparts(filename);
if any(chdr(:,4)-chdr(1,4))
ft_error('Sample rate not the same for all cells.');
end
hdr.Fs = chdr(1,4); %making assumption that sample rate is same for all cells
hdr.nChans = fhdr(19);
for i = 1:hdr.nChans
% this should be consistent with ft_senslabel
hdr.label{i,1} = ['E' num2str(i)];
end
%since NetStation does not properly set the fhdr(11) field, use the number of subjects from the chdr instead
hdr.nTrials = chdr(1,2)*fhdr(18); %number of trials is numSubjects * numCells
hdr.nSamplesPre = ceil(fhdr(14)/(1000/hdr.Fs));
if any(chdr(:,3)-chdr(1,3))
ft_error('Number of samples not the same for all cells.');
end
hdr.nSamples = chdr(1,3); %making assumption that number of samples is same for all cells
% remember the original header details
hdr.orig.fhdr = fhdr;
hdr.orig.chdr = chdr;
hdr.orig.ename = ename;
hdr.orig.cnames = cnames;
hdr.orig.fcom = fcom;
hdr.orig.ftext = ftext;
case 'egi_egis'
[fhdr,chdr,ename,cnames,fcom,ftext] = read_egis_header(filename);
[p, f, x] = fileparts(filename);
if any(chdr(:,4)-chdr(1,4))
ft_error('Sample rate not the same for all cells.');
end
hdr.Fs = chdr(1,4); %making assumption that sample rate is same for all cells
hdr.nChans = fhdr(19);
for i = 1:hdr.nChans
% this should be consistent with ft_senslabel
hdr.label{i,1} = ['E' num2str(i)];
end
hdr.nTrials = sum(chdr(:,2));
hdr.nSamplesPre = ceil(fhdr(14)/(1000/hdr.Fs));
% assuming that a utility was used to insert the correct baseline
% duration into the header since it is normally absent. This slot is
% actually allocated to the age of the subject, although NetStation
% does not use it when generating an EGIS session file.
if any(chdr(:,3)-chdr(1,3))
ft_error('Number of samples not the same for all cells.');
end
hdr.nSamples = chdr(1,3); %making assumption that number of samples is same for all cells
% remember the original header details
hdr.orig.fhdr = fhdr;
hdr.orig.chdr = chdr;
hdr.orig.ename = ename;
hdr.orig.cnames = cnames;
hdr.orig.fcom = fcom;
hdr.orig.ftext = ftext;
case 'egi_sbin'
[header_array, CateNames, CatLengths, preBaseline] = read_sbin_header(filename);
[p, f, x] = fileparts(filename);
hdr.Fs = header_array(9);
hdr.nChans = header_array(10);
for i = 1:hdr.nChans
% this should be consistent with ft_senslabel
hdr.label{i,1} = ['E' num2str(i)];
end
hdr.nTrials = header_array(15);
hdr.nSamplesPre = preBaseline;
hdr.nSamples = header_array(16); % making assumption that number of samples is same for all cells
% remember the original header details
hdr.orig.header_array = header_array;
hdr.orig.CateNames = CateNames;
hdr.orig.CatLengths = CatLengths;
case 'egi_mff_v1'
% The following represents the code that was written by Ingrid, Robert
% and Giovanni to get started with the EGI mff dataset format. It might
% not support all details of the file formats.
%
% An alternative implementation has been provided by EGI, this is
% released as fieldtrip/external/egi_mff and referred further down in
% this function as 'egi_mff_v2'.
%
% An more recent implementation has been provided by EGI and Arno Delorme, this
% is released as https://github.com/arnodelorme/mffmatlabio and referred further
% down in this function as 'egi_mff_v3'.
if ~usejava('jvm')
ft_error('the xml2struct requires MATLAB to be running with the Java virtual machine (JVM)');
% an alternative implementation which does not require the JVM but runs much slower is
% available from http://www.mathworks.com/matlabcentral/fileexchange/6268-xml4mat-v2-0
end
% get header info from .bin files
binfiles = dir(fullfile(filename, 'signal*.bin'));
if isempty(binfiles)
ft_error('could not find any signal.bin in mff directory')
end
orig = [];
for iSig = 1:length(binfiles)
signalname = binfiles(iSig).name;
fullsignalname = fullfile(filename, signalname);
orig.signal(iSig).blockhdr = read_mff_bin(fullsignalname);
end
% get hdr info from xml files
ws = ft_warning('off', 'MATLAB:REGEXP:deprecated'); % due to some small code xml2struct
xmlfiles = dir( fullfile(filename, '*.xml'));
disp('reading xml files to obtain header info...')
for i = 1:numel(xmlfiles)
if strcmpi(xmlfiles(i).name(1:2), '._') % Mac sometimes creates this useless files, don't use them
elseif strcmpi(xmlfiles(i).name(1:6), 'Events') % don't read in events here, can take a lot of time, and we can do that in ft_read_event
else
fieldname = xmlfiles(i).name(1:end-4);
filename_xml = fullfile(filename, xmlfiles(i).name);
orig.xml.(fieldname) = xml2struct(filename_xml);
end
end
ft_warning(ws); % revert the warning state
% epochs.xml seems the most common version, but epoch.xml might also
% occur, so use only one name
if isfield(orig.xml, 'epoch')
orig.xml.epochs = orig.xml.epoch;
orig.xml = rmfield(orig.xml, 'epoch');
end
% make hdr according to FieldTrip rules
hdr = [];
Fs = zeros(length(orig.signal),1);
nChans = zeros(length(orig.signal),1);
nSamples = zeros(length(orig.signal),1);
for iSig = 1:length(orig.signal)
Fs(iSig) = orig.signal(iSig).blockhdr(1).fsample(1);
nChans(iSig) = orig.signal(iSig).blockhdr(1).nsignals;
% the number of samples per block can be different
nSamples_Block = zeros(length(orig.signal(iSig).blockhdr),1);
for iBlock = 1:length(orig.signal(iSig).blockhdr)
nSamples_Block(iBlock) = orig.signal(iSig).blockhdr(iBlock).nsamples(1);
end
nSamples(iSig) = sum(nSamples_Block);
end
if length(unique(Fs)) > 1 || length(unique(nSamples)) > 1
ft_error('Fs and nSamples should be the same in all signals')
end
hdr.Fs = Fs(1);
hdr.nChans = sum(nChans);
hdr.nSamplesPre = 0;
hdr.nSamples = nSamples(1);
hdr.nTrials = 1;
% get channel labels for signal 1 (main net), otherwise create them
if isfield(orig.xml, 'sensorLayout') % assuming that signal1 is hdEEG sensornet, and channels are in xml file sensorLayout
for iSens = 1:numel(orig.xml.sensorLayout.sensors)
if ~isempty(orig.xml.sensorLayout.sensors(iSens).sensor.name) && ~(isstruct(orig.xml.sensorLayout.sensors(iSens).sensor.name) && numel(fieldnames(orig.xml.sensorLayout.sensors(iSens).sensor.name))==0)
%only get name when channel is EEG (type 0), or REF (type 1),
%rest are non interesting channels like place holders and COM and should not be added.
if strcmp(orig.xml.sensorLayout.sensors(iSens).sensor.type, '0') || strcmp(orig.xml.sensorLayout.sensors(iSens).sensor.type, '1')
% get the sensor name from the datafile
hdr.label{iSens} = orig.xml.sensorLayout.sensors(iSens).sensor.name;
end
elseif strcmp(orig.xml.sensorLayout.sensors(iSens).sensor.type, '0') % EEG chan
% this should be consistent with ft_senslabel
hdr.label{iSens} = ['E' num2str(orig.xml.sensorLayout.sensors(iSens).sensor.number)];
elseif strcmp(orig.xml.sensorLayout.sensors(iSens).sensor.type, '1') % REF chan
% ingnie: I now choose REF as name for REF channel since our discussion see bug 1407. Arbitrary choice...
hdr.label{iSens} = ['REF' num2str(iSens)];
else
% non interesting channels like place holders and COM
end
end
% check if the amount of lables corresponds with nChannels in signal 1
if length(hdr.label) == nChans(1)
% good
elseif length(hdr.label) > orig.signal(1).blockhdr(1).nsignals
ft_warning('found more lables in xml.sensorLayout than channels in signal 1, thus can not use info in sensorLayout, creating labels on the fly')
for iSens = 1:orig.signal(1).blockhdr(1).nsignals
% this should be consistent with ft_senslabel
hdr.label{iSens} = ['E' num2str(iSens)];
end
else
ft_warning('found less lables in xml.sensorLayout than channels in signal 1, thus can not use info in sensorLayout, creating labels on the fly')
for iSens = 1:orig.signal(1).blockhdr(1).nsignals
% this should be consistent with ft_senslabel
hdr.label{iSens} = ['E' num2str(iSens)];
end
end
% get lables for other signals
if length(orig.signal) == 2
if isfield(orig.xml, 'pnsSet') % signal2 is PIB box, and lables are in xml file pnsSet
nbEEGchan = length(hdr.label);
for iSens = 1:numel(orig.xml.pnsSet.sensors)
hdr.label{nbEEGchan+iSens} = num2str(orig.xml.pnsSet.sensors(iSens).sensor.name);
end
if length(hdr.label) == orig.signal(1).blockhdr(1).nsignals + orig.signal(2).blockhdr(1).nsignals
% good
elseif length(hdr.label) < orig.signal(1).blockhdr(1).nsignals + orig.signal(2).blockhdr(1).nsignals
ft_warning('found less lables in xml.pnsSet than channels in signal 2, labeling with s2_unknownN instead')
for iSens = length(hdr.label)+1 : orig.signal(1).blockhdr(1).nsignals + orig.signal(2).blockhdr(1).nsignals
hdr.label{iSens} = ['s2_unknown', num2str(iSens)];
end
else
ft_warning('found more lables in xml.pnsSet than channels in signal 2, thus can not use info in pnsSet, and labeling with s2_eN instead')
for iSens = orig.signal(1).blockhdr(1).nsignals+1 : orig.signal(1).blockhdr(1).nsignals + orig.signal(2).blockhdr(1).nsignals
hdr.label{iSens} = ['s2_E', num2str(iSens)];
end
end
else % signal2 is not PIBbox
ft_warning('creating channel labels for signal 2 on the fly')
for iSens = 1:orig.signal(2).blockhdr(1).nsignals
hdr.label{end+1} = ['s2_E', num2str(iSens)];
end
end
elseif length(orig.signal) > 2
% loop over signals and label channels accordingly
ft_warning('creating channel labels for signal 2 to signal N on the fly')
for iSig = 2:length(orig.signal)
for iSens = 1:orig.signal(iSig).blockhdr(1).nsignals
if iSig == 1 && iSens == 1
hdr.label{1} = ['s',num2str(iSig),'_E', num2str(iSens)];
else
hdr.label{end+1} = ['s',num2str(iSig),'_E', num2str(iSens)];
end
end
end
end
else % no xml.sensorLayout present
ft_warning('no sensorLayout found in xml files, creating channel labels on the fly')
for iSig = 1:length(orig.signal)
for iSens = 1:orig.signal(iSig).blockhdr(1).nsignals
if iSig == 1 && iSens == 1
hdr.label{1} = ['s',num2str(iSig),'_E', num2str(iSens)];
else
hdr.label{end+1} = ['s',num2str(iSig),'_E', num2str(iSens)];
end
end
end
end
% check if multiple epochs are present
if isfield(orig.xml,'epochs')
% add info to header about which sample correspond to which epochs, becasue this is quite hard for user to get...
epochdef = zeros(length(orig.xml.epochs),3);
for iEpoch = 1:length(orig.xml.epochs)
if iEpoch == 1
epochdef(iEpoch,1) = round(str2double(orig.xml.epochs(iEpoch).epoch.beginTime)./(1000000./hdr.Fs))+1;
epochdef(iEpoch,2) = round(str2double(orig.xml.epochs(iEpoch).epoch.endTime )./(1000000./hdr.Fs));
epochdef(iEpoch,3) = round(str2double(orig.xml.epochs(iEpoch).epoch.beginTime)./(1000000./hdr.Fs)); % offset corresponds to timing
else
NbSampEpoch = round(str2double(orig.xml.epochs(iEpoch).epoch.endTime)./(1000000./hdr.Fs) - str2double(orig.xml.epochs(iEpoch).epoch.beginTime)./(1000000./hdr.Fs));
epochdef(iEpoch,1) = epochdef(iEpoch-1,2) + 1;
epochdef(iEpoch,2) = epochdef(iEpoch-1,2) + NbSampEpoch;
epochdef(iEpoch,3) = round(str2double(orig.xml.epochs(iEpoch).epoch.beginTime)./(1000000./hdr.Fs)); % offset corresponds to timing
end
end
if epochdef(end,2) ~= hdr.nSamples
% check for NS 4.5.4 picosecond timing
if (epochdef(end,2)/1000) == hdr.nSamples
for iEpoch=1:size(epochdef,1)
epochdef(iEpoch,1) = ((epochdef(iEpoch,1)-1)/1000)+1;
epochdef(iEpoch,2) = epochdef(iEpoch,2)/1000;
epochdef(iEpoch,3) = epochdef(iEpoch,3)/1000;
end
ft_warning('mff apparently generated by NetStation 4.5.4. Adjusting time scale to microseconds from nanoseconds.');
else
ft_error('number of samples in all epochs do not add up to total number of samples')
end
end
epochLengths = epochdef(:,2)-epochdef(:,1)+1;
if ~any(diff(epochLengths))
hdr.nSamples = epochLengths(1);
hdr.nTrials = length(epochLengths);
else
ft_warning('the data contains multiple epochs with variable length, possibly causing discontinuities in the data')
% sanity check
if epochdef(end,2) ~= hdr.nSamples
% check for NS 4.5.4 picosecond timing
if (epochdef(end,2)/1000) == hdr.nSamples
for iEpoch=1:size(epochdef,1)
epochdef(iEpoch,1)=((epochdef(iEpoch,1)-1)/1000)+1;
epochdef(iEpoch,2)=epochdef(iEpoch,2)/1000;
epochdef(iEpoch,3)=epochdef(iEpoch,3)/1000;
end
disp('mff apparently generated by NetStation 4.5.4. Adjusting time scale to microseconds from nanoseconds.');
else
ft_error('number of samples in all epochs do not add up to total number of samples')
end
end
end
orig.epochdef = epochdef;
end
hdr.orig = orig;
case 'egi_mff_v2'
% ensure that the EGI_MFF_V2 toolbox is on the path
ft_hastoolbox('egi_mff_v2', 1);
%%%%%%%%%%%%%%%%%%%%%%
%workaround for MATLAB bug resulting in global variables being cleared
globalTemp=cell(0);
globalList=whos('global');
varList=whos;
for i=1:length(globalList)
eval(['global ' globalList(i).name ';']);
eval(['globalTemp{end+1}=' globalList(i).name ';']);
end
%%%%%%%%%%%%%%%%%%%%%%
% ensure that the JVM is running and the jar file is on the path
mff_setup;
%%%%%%%%%%%%%%%%%%%%%%
%workaround for MATLAB bug resulting in global variables being cleared
varNames={varList.name};
for i=1:length(globalList)
eval(['global ' globalList(i).name ';']);
eval([globalList(i).name '=globalTemp{i};']);
if ~any(strcmp(globalList(i).name,varNames)) %was global variable originally out of scope?
eval(['clear ' globalList(i).name ';']); %clears link to global variable without affecting it
end
end
clear globalTemp globalList varNames varList;
%%%%%%%%%%%%%%%%%%%%%%
if isunix && filename(1)~=filesep
% add the full path to the dataset directory
filename = fullfile(pwd, filename);
elseif ispc && ~any(strcmp(filename(2),{':','\'}))
% add the full path, including drive letter or slashes as needed.
filename = fullfile(pwd, filename);
end
hdr = read_mff_header(filename);
case {'egi_mff_v3' 'egi_mff'} % this is the default
ft_hastoolbox('mffmatlabio', 1);
hdr = mff_fileio_read_header(filename);
case 'fcdc_buffer'
% read from a networked buffer for realtime analysis
[host, port] = filetype_check_uri(filename);
if retry
orig = [];
while isempty(orig)
try
% try reading the header, catch the error and retry
orig = buffer('get_hdr', [], host, port);
catch
ft_warning('could not read header from %s, retrying in 1 second', filename);
pause(1);
end
end % while
else
% try reading the header only once, give error if it fails
orig = buffer('get_hdr', [], host, port);
end % if retry
% construct the standard header elements
hdr.Fs = orig.fsample;
hdr.nChans = orig.nchans;
hdr.nSamples = orig.nsamples;
hdr.nSamplesPre = 0; % since continuous
hdr.nTrials = 1; % since continuous
hdr.orig = []; % this will contain the chunks (if present)
% add the contents of attached NEUROMAG_HEADER chunk after decoding to MATLAB structure
if isfield(orig, 'neuromag_header')
if isempty(cachechunk)
% this only needs to be decoded once
cachechunk = decode_fif(orig);
end
% convert to FieldTrip format header
hdr.label = cachechunk.ch_names(:);
hdr.nChans = cachechunk.nchan;
hdr.Fs = cachechunk.sfreq;
% add a gradiometer structure for forward and inverse modelling
try
[grad, elec] = mne2grad(cachechunk, true, coilaccuracy); % the coordsys is 'dewar'
if ~isempty(grad)
hdr.grad = grad;
end
if ~isempty(elec)
hdr.elec = elec;
end
catch
disp(lasterr);
end
% store the original details
hdr.orig = cachechunk;
end
% add the contents of attached CTF_RES4 chunk after decoding to MATLAB structure
if isfield(orig, 'ctf_res4')
if isempty(cachechunk)
% this only needs to be decoded once
cachechunk = decode_res4(orig.ctf_res4);
end
% copy the gradiometer details
hdr.grad = cachechunk.grad;
hdr.orig = cachechunk.orig;
if isfield(orig, 'channel_names')
% get the same selection of channels from the two chunks
[selbuf, selres4] = match_str(orig.channel_names, cachechunk.label);
if length(selres4)<length(orig.channel_names)
ft_error('the res4 chunk did not contain all channels')
end
% copy some of the channel details
hdr.label = cachechunk.label(selres4);
hdr.chantype = cachechunk.chantype(selres4);
hdr.chanunit = cachechunk.chanunit(selres4);
% add the channel names chunk as well
hdr.orig.channel_names = orig.channel_names;
end
% add the raw chunk as well
hdr.orig.ctf_res4 = orig.ctf_res4;
end
% add the contents of attached NIFTI_1 chunk after decoding to MATLAB structure
if isfield(orig, 'nifti_1')
hdr.nifti_1 = decode_nifti1(orig.nifti_1);
% add the raw chunk as well
hdr.orig.nifti_1 = orig.nifti_1;
end
% add the contents of attached SiemensAP chunk after decoding to MATLAB structure
if isfield(orig, 'siemensap') && exist('sap2matlab')==3 % only run this if MEX file is present
hdr.siemensap = sap2matlab(orig.siemensap);
% add the raw chunk as well
hdr.orig.siemensap = orig.siemensap;
end
if ~isfield(hdr, 'label')
% prevent overwriting the labels that we might have gotten from a RES4 chunk
if isfield(orig, 'channel_names')
hdr.label = orig.channel_names;
else
hdr.label = cell(hdr.nChans,1);
if hdr.nChans < 2000 % don't do this for fMRI etc.
ft_warning('creating fake channel names'); % give this warning only once
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
else
ft_warning('skipping fake channel names'); % give this warning only once
checkUniqueLabels = false;
end
end
end
if ~isfield(hdr, 'chantype')
% prevent overwriting the chantypes that we might have gotten from a RES4 chunk
hdr.chantype = cell(hdr.nChans,1);
if hdr.nChans < 2000 % don't do this for fMRI etc.
hdr.chantype = repmat({'unknown'}, 1, hdr.nChans);
end
end
if ~isfield(hdr, 'chanunit')
% prevent overwriting the chanunits that we might have gotten from a RES4 chunk
hdr.chanunit = cell(hdr.nChans,1);
if hdr.nChans < 2000 % don't do this for fMRI etc.
hdr.chanunit = repmat({'unknown'}, 1, hdr.nChans);
end
end
hdr.orig.bufsize = orig.bufsize;
case 'fcdc_buffer_offline'
[hdr, nameFlag] = read_buffer_offline_header(headerfile);
switch nameFlag
case 0
% no labels generated (fMRI etc)
checkUniqueLabels = false; % no need to check these
case 1
% has generated fake channels
% give this warning only once
ft_warning('creating fake channel names');
checkUniqueLabels = false; % no need to check these
case 2
% got labels from chunk, check those
checkUniqueLabels = true;
end
case 'fcdc_matbin'
% this is multiplexed data in a *.bin file, accompanied by a MATLAB file containing the header
load(headerfile, 'hdr');
case 'fcdc_mysql'
% check that the required low-level toolbox is available
ft_hastoolbox('mysql', 1);
% read from a MySQL server listening somewhere else on the network
db_open(filename);
if db_blob
hdr = db_select_blob('fieldtrip.header', 'msg', 1);
else
hdr = db_select('fieldtrip.header', {'nChans', 'nSamples', 'nSamplesPre', 'Fs', 'label'}, 1);
hdr.label = mxDeserialize(hdr.label);
end
case 'gtec_hdf5'
% check that the required low-level toolbox is available
ft_hastoolbox('gtec', 1);
% there is only a precompiled *.p reader that reads the whole file at once
orig = ghdf5read(filename);
for i=1:numel(orig.RawData.AcquisitionTaskDescription.ChannelProperties.ChannelProperties)
lab = orig.RawData.AcquisitionTaskDescription.ChannelProperties.ChannelProperties(i).ChannelName;
typ = orig.RawData.AcquisitionTaskDescription.ChannelProperties.ChannelProperties(1).ChannelType;
if isnumeric(lab)
hdr.label{i} = num2str(lab);
else
hdr.label{i} = lab;
end
if ischar(typ)
hdr.chantype{i} = lower(typ);
else
hdr.chantype{i} = 'unknown';
end
end
hdr.Fs = orig.RawData.AcquisitionTaskDescription.SamplingFrequency;
hdr.nChans = size(orig.RawData.Samples, 1);
hdr.nSamples = size(orig.RawData.Samples, 2);
hdr.nSamplesPre = 0;
hdr.nTrials = 1; % assume continuous data, not epoched
assert(orig.RawData.AcquisitionTaskDescription.NumberOfAcquiredChannels==hdr.nChans, 'inconsistent number of channels');
% remember the complete data upon request
if cache
hdr.orig = orig;
end
case 'gtec_mat'
% this is a simple MATLAB format, it contains a log and a names variable
tmp = load(headerfile);
log = tmp.log;
names = tmp.names;
hdr.label = cellstr(names);
hdr.nChans = size(log,1);
hdr.nSamples = size(log,2);
hdr.nSamplesPre = 0;
hdr.nTrials = 1; % assume continuous data, not epoched
% compute the sampling frequency from the time channel
sel = strcmp(hdr.label, 'Time');
time = log(sel,:);
hdr.Fs = 1./(time(2)-time(1));
% also remember the complete data upon request
if cache
hdr.orig.log = log;
hdr.orig.names = names;
end
case 'gdf'
% this requires the biosig toolbox
ft_hastoolbox('BIOSIG', 1);
% In the case that the gdf files are written by one of the FieldTrip
% realtime applications, such as biosig2ft, the gdf recording can be
% split over multiple 1GB files. The sequence of files is then
% filename.gdf <- this is the one that should be specified as the filename/dataset
% filename_1.gdf
% filename_2.gdf
% ...
[p, f, x] = fileparts(filename);
if exist(sprintf('%s_%d%s', fullfile(p, f), 1, x), 'file')
% there are multiple files, count the number of additional files (excluding the first one)
count = 0;
while exist(sprintf('%s_%d%s', fullfile(p, f), count+1, x), 'file')
count = count+1;
end
hdr = read_biosig_header(filename);
for i=1:count
hdr(i+1) = read_biosig_header(sprintf('%s_%d%s', fullfile(p, f), i, x));
% do some sanity checks
if hdr(i+1).nChans~=hdr(1).nChans
ft_error('multiple GDF files detected that should be appended, but the channel count is inconsistent');
elseif hdr(i+1).Fs~=hdr(1).Fs
ft_error('multiple GDF files detected that should be appended, but the sampling frequency is inconsistent');
elseif ~isequal(hdr(i+1).label, hdr(1).label)
ft_error('multiple GDF files detected that should be appended, but the channel names are inconsistent');
end
end % for count
% combine all headers into one
combinedhdr = [];
combinedhdr.Fs = hdr(1).Fs;
combinedhdr.nChans = hdr(1).nChans;
combinedhdr.nSamples = sum([hdr.nSamples].*[hdr.nTrials]);
combinedhdr.nSamplesPre = 0;
combinedhdr.nTrials = 1;
combinedhdr.label = hdr(1).label;
combinedhdr.orig = hdr; % include all individual file details
hdr = combinedhdr;
else
% there is only a single file
hdr = read_biosig_header(filename);
% the GDF format is always continuous
hdr.nSamples = hdr.nSamples * hdr.nTrials;
hdr.nTrials = 1;
hdr.nSamplesPre = 0;
end % if single or multiple gdf files
case {'homer_nirs'}
% Homer files are MATLAB files in disguise
% see https://www.nitrc.org/plugins/mwiki/index.php/homer2:Homer_Input_Files#NIRS_data_file_format
nirs = load(filename, '-mat');
% convert it to a raw data structure according to FT_DATATYPE_RAW
data = homer2fieldtrip(nirs);
% get the header information as structure
hdr = ft_fetch_header(data);
case {'itab_raw' 'itab_mhd'}
% read the full header information frtom the binary header structure
header_info = read_itab_mhd(headerfile);
% these are the channels that are visible to FieldTrip
chansel = 1:header_info.nchan;
% convert the header information into a FieldTrip compatible format
hdr.nChans = length(chansel);
hdr.label = {header_info.ch(chansel).label};
hdr.label = hdr.label(:); % should be column vector
hdr.Fs = header_info.smpfq;
% it will always be continuous data
hdr.nSamples = header_info.ntpdata;
hdr.nSamplesPre = 0; % it is a single continuous trial
hdr.nTrials = 1; % it is a single continuous trial
% keep the original details AND the list of channels as used by FieldTrip
hdr.orig = header_info;
hdr.orig.chansel = chansel;
% add the gradiometer definition
hdr.grad = itab2grad(header_info);
case 'jaga16'
% this is hard-coded for the Jinga-Hi JAGA16 system with 16 channels
packetsize = (4*2 + 6*2 + 16*43*2); % in bytes
% read the first packet
fid = fopen_or_error(filename, 'r');
buf = fread(fid, packetsize/2, 'uint16');
fclose(fid);
if buf(1)==0
% it does not have timestamps, i.e. it is the raw UDP stream
packetsize = packetsize - 8; % in bytes
packet = jaga16_packet(buf(1:(packetsize/2)), false);
else
% each packet starts with a timestamp
packet = jaga16_packet(buf, true);
end
% determine the number of packets from the file size
info = dir(filename);
npackets = floor((info.bytes)/packetsize/2);
hdr = [];
hdr.Fs = packet.fsample;
hdr.nChans = packet.nchan;
hdr.nSamples = 43;
hdr.nSamplesPre = 0;
hdr.nTrials = npackets;
hdr.label = cell(hdr.nChans,1);
hdr.chantype = cell(hdr.nChans,1);
hdr.chanunit = cell(hdr.nChans,1);
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
hdr.chantype{i} = 'eeg';
hdr.chanunit{i} = 'uV';
end
% store some low-level details
hdr.orig.offset = 0;
hdr.orig.packetsize = packetsize;
hdr.orig.packet = packet;
hdr.orig.info = info;
case {'manscan_mbi', 'manscan_mb2'}
orig = in_fopen_manscan(filename);
hdr.Fs = orig.prop.sfreq;
hdr.nChans = numel(orig.channelmat.Channel);
hdr.nTrials = 1;
if isfield(orig, 'epochs') && ~isempty(orig.epochs)
hdr.nSamples = 0;
for i = 1:numel(orig.epochs)
hdr.nSamples = hdr.nSamples + diff(orig.epochs(i).samples) + 1;
end
else
hdr.nSamples = diff(orig.prop.samples) + 1;
end
if orig.prop.times(1) < 0
hdr.nSamplesPre = round(orig.prop.times(1)/hdr.Fs);
else
hdr.nSamplesPre = 0;
end
for i=1:hdr.nChans
hdr.label{i,1} = orig.channelmat.Channel(i).Name;
hdr.chantype{i,1} = lower(orig.channelmat.Channel(i).Type);
if isequal(hdr.chantype{i,1}, 'eeg')
hdr.chanunit{i, 1} = 'uV';
else
hdr.chanunit{i, 1} = 'unknown';
end
end
hdr.orig = orig;
case 'matlab'
% read the header structure from a MATLAB file
% it should either contain a "hdr" structure, or a FieldTrip data structure according to FT_DATATYPE_RAW
w = whos(matfile(filename));
if any(strcmp({w.name}, 'hdr'))
hdr = loadvar(filename, 'hdr');
elseif any(strcmp({w.name}, 'data')) || length(w)==1
data = loadvar(filename, 'data');
hdr = ft_fetch_header(data);
end
case 'mayo_mef30'
ft_hastoolbox('mayo_mef', 1); % make sure mayo_mef exists
hdr = read_mayo_mef30(filename, password);
case 'mayo_mef21'
ft_hastoolbox('mayo_mef', 1); % make sure mayo_mef exists
hdr = read_mayo_mef21(filename, password);
case 'mega_neurone'
% ensure that this external toolbox is on the path
ft_hastoolbox('neurone', 1);
if filename(end)~=filesep
% it should end with a slash
filename = [filename filesep];
end
% this is like the EEGLAB data structure
EEG = readneurone(filename);
hdr.Fs = EEG.srate;
hdr.nChans = EEG.nbchan;
hdr.nSamples = EEG.pnts;
hdr.nSamplesPre = -EEG.xmin*EEG.srate;
hdr.nTrials = EEG.trials;
try
hdr.label = { EEG.chanlocs.labels }';
catch
ft_warning('creating default channel names');
for i=1:hdr.nChans
hdr.label{i} = sprintf('chan%03d', i);
end
end
ind = 1;
for i = 1:length( EEG.chanlocs )
if isfield(EEG.chanlocs(i), 'X') && ~isempty(EEG.chanlocs(i).X)
hdr.elec.label{ind, 1} = EEG.chanlocs(i).labels;
% this channel has a position
hdr.elec.elecpos(ind,1) = EEG.chanlocs(i).X;
hdr.elec.elecpos(ind,2) = EEG.chanlocs(i).Y;
hdr.elec.elecpos(ind,3) = EEG.chanlocs(i).Z;
ind = ind+1;
end
end
if cache
% also remember the data and events
hdr.orig = EEG;
else
% remember only the header details
hdr.orig = removefields(EEG, {'data', 'event'});
end
case 'micromed_trc'
orig = read_micromed_trc(filename);
hdr = [];
hdr.Fs = orig.Rate_Min; % FIXME is this correct?
hdr.nChans = orig.Num_Chan;
hdr.nSamples = orig.Num_Samples;
hdr.nSamplesPre = 0; % continuous
hdr.nTrials = 1; % continuous
hdr.label = cell(1,hdr.nChans);
% give this warning only once
hdr.label = {orig.elec.Name};
hdr.chanunit = {orig.elec.Unit};
hdr.subjectname = orig.name;
%warning('using a modified read_micromed_trc() function');
% this should be a column vector
hdr.label = hdr.label(:);
% remember the original header details
hdr.orig = orig;
case {'mpi_ds', 'mpi_dap'}
hdr = read_mpi_ds(filename);
case 'netmeg'
ft_hastoolbox('netcdf', 1);
% this will read all NetCDF data from the file and subsequently convert
% each of the three elements into a more easy to parse MATLAB structure
s = netcdf(filename);
for i=1:numel(s.AttArray)
fname = fixname(s.AttArray(i).Str);
fval = s.AttArray(i).Val;
if ischar(fval)
fval = fval(fval~=0); % remove the \0 characters
fval = strtrim(fval); % remove insignificant whitespace
end
Att.(fname) = fval;
end
for i=1:numel(s.VarArray)
fname = fixname(s.VarArray(i).Str);
fval = s.VarArray(i).Data;
if ischar(fval)
fval = fval(fval~=0); % remove the \0 characters
fval = strtrim(fval); % remove insignificant whitespace
end
Var.(fname) = fval;
end
for i=1:numel(s.DimArray)
fname = fixname(s.DimArray(i).Str);
fval = s.DimArray(i).Dim;
if ischar(fval)
fval = fval(fval~=0); % remove the \0 characters
fval = strtrim(fval); % remove insignificant whitespace
end
Dim.(fname) = fval;
end
% convert the relevant fields into the default header structure
hdr.Fs = 1000/Var.samplinginterval;
hdr.nChans = length(Var.channelstatus);
hdr.nSamples = Var.numsamples;
hdr.nSamplesPre = 0;
hdr.nTrials = size(Var.waveforms, 1);
hdr.chanunit = cellstr(reshape(Var.channelunits, hdr.nChans, 2));
hdr.chantype = cellstr(reshape(lower(Var.channeltypes), hdr.nChans, 3));
ft_warning('creating fake channel names');
hdr.label = cell(hdr.nChans, 1);
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
% remember the original details of the file
% note that this also includes the data
% this is large, but can be reused elsewhere
hdr.orig.Att = Att;
hdr.orig.Var = Var;
hdr.orig.Dim = Dim;
% construct the gradiometer structure from the complete header information
hdr.grad = netmeg2grad(hdr);
case 'nervus_eeg'
hdr = read_nervus_header(filename);
checkUniqueLabels = false;
case 'neuralynx_dma'
hdr = read_neuralynx_dma(filename);
case 'neuralynx_sdma'
hdr = read_neuralynx_sdma(filename);
case 'neuralynx_ncs'
ncs = read_neuralynx_ncs(filename, 1, 0);
[p, f, x] = fileparts(filename);
hdr.Fs = ncs.hdr.SamplingFrequency;
hdr.label = {f};
hdr.nChans = 1;
hdr.nTrials = 1;
hdr.nSamplesPre = 0;
hdr.nSamples = ncs.NRecords * 512;
hdr.orig = ncs.hdr;
FirstTimeStamp = ncs.hdr.FirstTimeStamp; % this is the first timestamp of the first block
LastTimeStamp = ncs.hdr.LastTimeStamp; % this is the first timestamp of the last block, i.e. not the timestamp of the last sample
hdr.TimeStampPerSample = double(LastTimeStamp - FirstTimeStamp) ./ ((ncs.NRecords-1)*512);
hdr.FirstTimeStamp = FirstTimeStamp;
case 'neuralynx_nse'
nse = read_neuralynx_nse(filename, 1, 0);
[p, f, x] = fileparts(filename);
hdr.Fs = nse.hdr.SamplingFrequency;
hdr.label = {f};
hdr.nChans = 1;
hdr.nTrials = nse.NRecords; % each record contains one waveform
hdr.nSamples = 32; % there are 32 samples in each waveform
hdr.nSamplesPre = 0;
hdr.orig = nse.hdr;
% FIXME add hdr.FirstTimeStamp and hdr.TimeStampPerSample
case {'neuralynx_ttl', 'neuralynx_tsl', 'neuralynx_tsh'}
% these are hardcoded, they contain an 8-byte header and int32 values for a single channel
% FIXME this should be done similar as neuralynx_bin, i.e. move the hdr into the function
hdr = [];
hdr.Fs = 32556;
hdr.nChans = 1;
hdr.nSamples = (filesize(filename)-8)/4;
hdr.nSamplesPre = 1;
hdr.nTrials = 1;
hdr.label = {headerformat((end-3):end)};
case 'neuralynx_bin'
hdr = read_neuralynx_bin(filename);
case 'neuralynx_ds'
hdr = read_neuralynx_ds(filename);
case 'neuralynx_cds'
hdr = read_neuralynx_cds(filename);
case 'nexstim_nxe'
hdr = read_nexstim_nxe(filename);
case 'neuromag_headpos'
% neuromag headposition file created with maxfilter, the position varies over time
orig = read_neuromag_headpos(filename);
hdr.Fs = 1/(orig.data(1,2)-orig.data(1,1));
hdr.nChans = size(orig.data,1);
hdr.nSamples = size(orig.data,2);
hdr.nSamplesPre = 0; % convert from ms to samples
hdr.nTrials = 1;
hdr.label = orig.colheaders;
case 'neuromag_maxfilterlog'
% neuromag log file created with maxfilter
log = read_neuromag_maxfilterlog(filename);
hdr = [];
hdr.label = {'t' 'e' 'g' 'v' 'r' 'd'};
for i=1:numel(log.hpi)
for j=1:11
hdr.label{end+1} = sprintf('hpi%d_%02d', i, j);
end
end
hdr.nChans = length(hdr.label);
hdr.nSamples = length(log.t);
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.Fs = 1 / median(diff(log.t));
hdr.orig = log;
case {'neuromag_fif' 'neuromag_mne'}
% check that the required low-level toolbox is available
ft_hastoolbox('mne', 1);
info = fiff_read_meas_info(filename);
% convert to FieldTrip format header
hdr.label = info.ch_names(:);
hdr.nChans = info.nchan;
hdr.Fs = info.sfreq;
% add a gradiometer structure for forward and inverse modelling
try
[grad, elec] = mne2grad(info, strcmp(coordsys, 'dewar'), coilaccuracy);
if ~isempty(grad)
hdr.grad = grad;
end
if ~isempty(elec)
hdr.elec = elec;
end
catch
disp(lasterr);
end
iscontinuous = 0;
isepoched = 0;
isaverage = 0;
if isempty(fiff_find_evoked(filename)) % true if file contains no evoked responses
try
epochs = fiff_read_epochs(filename);
isepoched = 1;
catch
% the "catch me" syntax is broken on MATLAB74, this fixes it
me = lasterror;
if strcmp(me.identifier, 'MNE:fiff_read_events')
iscontinuous = 1;
else
rethrow(me)
end
end
else
isaverage = 1;
end
if iscontinuous
try
% we only use 1 input argument here to allow backward
% compatibility up to MNE 2.6.x:
raw = fiff_setup_read_raw(filename);
catch
% the "catch me" syntax is broken on MATLAB74, this fixes it
me = lasterror;
% there is an error - we try to use MNE 2.7.x (if present) to
% determine if the cause is maxshielding:
try
allow_maxshield = true;
raw = fiff_setup_read_raw(filename,allow_maxshield);
catch
% unknown problem, or MNE version 2.6.x or less:
rethrow(me);
end
% no error message from fiff_setup_read_raw? Then maxshield
% was applied, but maxfilter wasn't, so return this error:
if istrue(checkmaxfilter)
ft_error('Maxshield data should be corrected using Maxfilter prior to importing in FieldTrip.');
else
ft_warning('Maxshield data should be corrected using Maxfilter prior to importing in FieldTrip.');
end
end
hdr.nSamples = raw.last_samp - raw.first_samp + 1; % number of samples per trial
hdr.nSamplesPre = 0;
% otherwise conflicts will occur in read_data
hdr.nTrials = 1;
info.raw = raw; % keep all the details
elseif isepoched
hdr.nSamples = length(epochs.times);
hdr.nSamplesPre = sum(epochs.times < 0);
hdr.nTrials = size(epochs.data, 1);
info.epochs = epochs; % this is used by read_data to get the actual data, i.e. to prevent re-reading
elseif isaverage
try
evoked_data = fiff_read_evoked_all(filename);
vartriallength = any(diff([evoked_data.evoked.first])) || any(diff([evoked_data.evoked.last]));
if vartriallength
% there are trials averages with variable durations in the file
ft_warning('EVOKED FILE with VARIABLE TRIAL LENGTH! - check data have been processed accurately');
hdr.nSamples = 0;
for i=1:length(evoked_data.evoked)
hdr.nSamples = hdr.nSamples + size(evoked_data.evoked(i).epochs, 2);
end
% represent it as a continuous file with a single trial
% all trial average details will be available through read_event
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
info.evoked = evoked_data.evoked; % this is used by read_data to get the actual data, i.e. to prevent re-reading
info.info = evoked_data.info; % keep all the details
info.vartriallength = 1;
else
% represent it as a file with multiple trials, each trial has the same length
% all trial average details will be available through read_event
hdr.nSamples = evoked_data.evoked(1).last - evoked_data.evoked(1).first + 1;
hdr.nSamplesPre = -evoked_data.evoked(1).first; % represented as negative number in fif file
hdr.nTrials = length(evoked_data.evoked);
info.evoked = evoked_data.evoked; % this is used by read_data to get the actual data, i.e. to prevent re-reading
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
% output variable "data"
ft_warning('%s does not contain data', filename);
hdr.nSamples = 0;
hdr.nSamplesPre = 0;
hdr.nTrials = 0;
end
end
% remember the original header details
hdr.orig = info;
% these are useful to know in ft_read_event and ft_read_data
hdr.orig.isaverage = isaverage;
hdr.orig.iscontinuous = iscontinuous;
hdr.orig.isepoched = isepoched;
case 'neuromag_mex'
% check that the required low-level toolbox is available
ft_hastoolbox('meg-pd', 1);
rawdata('any',filename);
rawdata('goto', 0);
megmodel('head',[0 0 0],filename);
% get the available information from the fif file
[orig.rawdata.range,orig.rawdata.calib] = rawdata('range');
[orig.rawdata.sf] = rawdata('sf');
[orig.rawdata.samples] = rawdata('samples');
[orig.chaninfo.N,orig.chaninfo.S,orig.chaninfo.T] = chaninfo; % Numbers, names & places
[orig.chaninfo.TY,orig.chaninfo.NA] = chaninfo('type'); % Coil type
[orig.chaninfo.NO] = chaninfo('noise'); % Default noise level
[orig.channames.NA,orig.channames.KI,orig.channames.NU] = channames(filename); % names, kind, logical numbers
% read a single trial to determine the data size
[buf, status] = rawdata('next');
rawdata('close');
% This is to solve a problem reported by Doug Davidson: The problem
% is that rawdata('samples') is not returning the number of samples
% correctly. It appears that the example script rawchannels in meg-pd
% might work, however, so I want to use rawchannels to read in one
% channel of data in order to get the number of samples in the file:
if orig.rawdata.samples<0
tmpchannel = 1;
tmpvar = rawchannels(filename,tmpchannel);
[orig.rawdata.samples] = size(tmpvar,2);
clear tmpvar tmpchannel;
end
% convert to FieldTrip format header
hdr.label = orig.channames.NA;
hdr.Fs = orig.rawdata.sf;
hdr.nSamplesPre = 0; % I don't know how to get this out of the file
hdr.nChans = size(buf,1);
hdr.nSamples = size(buf,2); % number of samples per trial
hdr.nTrials = orig.rawdata.samples ./ hdr.nSamples;
% add a gradiometer structure for forward and inverse modelling
hdr.grad = fif2grad(filename);
% remember the original header details
hdr.orig = orig;
case 'neuroomega_mat'
% These are MATLAB *.mat files created by the software 'Map File
% Converter' from the proprietary .mpx files recorded by NeuroOmega
%defining default if chantype is missing
if isempty(chantype) || strcmpi(chantype{1},'chaninfo') || strcmpi(chantype{1},'info')
chantype={'micro'};
end
chantype_dict={'micro','macro', 'analog', 'micro_lfp','macro_lfp','micro_hp','add_analog','emg', 'eeg';...
'CRAW', 'CMacro_RAW','CANALOG', 'CLFP', 'CMacro_LFP', 'CSPK' ,'CADD_ANALOG','CEMG', 'CEEG'};
neuroomega_param={'_KHz','_KHz_Orig','_Gain','_BitResolution','_TimeBegin','_TimeEnd'};
%identifying channels to be loaded
orig = matfile(filename);
fields_orig=who(orig);
%is_param=endsWith(fields_orig,neuroomega_param); %Matlab 2017a
is_param=zeros(length(fields_orig),1);
for i=1:length(neuroomega_param)
is_param = is_param | ~cellfun('isempty',regexp(fields_orig,strcat(neuroomega_param(i),'$'),'start'));
end
%creating channel info table
channels_all=fields_orig(contains(fields_orig,chantype_dict(2,:)) & ~is_param);
%Matching channels to chantypes
M=cell2mat(cellfun(@(x) contains(channels_all,x),chantype_dict(2,:),'UniformOutput',false));
chantype_ix = sum( cumprod(M == 0, 2), 2) + 1;
Fs=cellfun(@(x) orig.([x '_KHz'])*1000,channels_all);
chaninfo=table(channels_all,chantype_dict(1,chantype_ix)',Fs,'VariableNames',{'channel' 'chantype' 'Fs'});
channels={}; channelstype={};
for c = 1:length(chantype)
chantype_dict_sel=strcmpi(chantype_dict(1,:),chantype{c});
if sum(chantype_dict_sel)>0
chanbasename=chantype_dict{2, chantype_dict_sel};
sel_chan=fields_orig(strncmpi(fields_orig,chanbasename,length(chanbasename)) & ~is_param);
if isempty(sel_chan)
ft_warning(strjoin({'chantype ',chantype{c},' selected but no ',chanbasename,' found'}))
else
channels=[channels;sel_chan];
channelstype=[channelstype;repmat(chantype(c), size(sel_chan))];
end
else
ft_warning(strjoin({'unknown chantype ',chantype{c}}));
end
end
if ~isempty(channels)
chan_t=table;
for i=1:length(channels)
ch=channels{i};
ch_whos=whos(orig,ch);
chan_t=[chan_t;{ch,orig.([ch,'_KHz'])*1000, orig.([ch,'_TimeBegin']), ch_whos.size(2)}];
end
chan_t.Properties.VariableNames={'channel' 'Fs' 'T0' 'nSamples'};
Fs=unique(chan_t.Fs);
T0=unique(chan_t.T0);
nSamples=unique(chan_t.nSamples);
if length(Fs)>1
chan_t %; printing table for user
ft_error('inconsistent channels with different sampling rates for %s',filename);
end
if length(T0)>1
chan_t %; printing table for user
ft_warning('inconsistent channels with different initial times for %s. Selecting minimum time',filename);
T0 = min(T0);
end
if length(nSamples)>1
chan_t %; printing table for user
ft_warning('inconsistent number of samples across channels for %s. Selecting minimun nSample',filename)
nSamples=min(nSamples);
end
else %If no channel selected
Fs=nan; nSamples=nan; channelstype=chaninfo.chantype;
end
% building header
hdr.Fs = Fs;
hdr.nChans = length(channels);
hdr.nSamples = nSamples;
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
hdr.label = deblank(channels);
hdr.chantype = channelstype;
hdr.chanunit = repmat({'uV'}, size(hdr.label));
% store the complete information in hdr.orig
% ft_read_data and ft_read_event will get it from there
hdr.orig = [];
hdr.orig.orig = orig;
hdr.orig.chaninfo = chaninfo;
hdr.orig.fields = fields_orig;
case 'neuroprax_eeg'
orig = np_readfileinfo(filename);
hdr.Fs = orig.fa;
hdr.nChans = orig.K;
hdr.nSamples = orig.N;
hdr.nSamplesPre = 0; % continuous
hdr.nTrials = 1; % continuous
hdr.label = orig.channels(:);
hdr.unit = orig.units(:);
% remember the original header details
hdr.orig = orig;
case 'neuroscope_bin'
[p,f,e] = fileparts(filename);
headerfile = fullfile(p,[f,'.xml']);
hdr = ft_read_header(headerfile, 'headerformat', 'neuroscope_xml');
case 'neuroscope_ds'
listing = dir(filename);
filenames = {listing.name}';
headerfile = filenames{~cellfun('isempty',strfind(filenames,'.xml'))};
hdr = ft_read_header(headerfile, 'headerformat', 'neuroscope_xml');
case 'neuroscope_xml'
ft_hastoolbox('neuroscope', 1);
ft_hastoolbox('gifti', 1);
% this pertains to generic header file, and the other neuroscope
% formats will recurse into this one
[p,f,e] = fileparts(filename);
if isempty(p), p = pwd; end
listing = dir(p);
filenames = {listing.name}';
lfpfile_idx = find(~cellfun('isempty',strfind(filenames,'.eeg')));
rawfile_idx = find(~cellfun('isempty',strfind(filenames,'.dat')));
if ~isempty(lfpfile_idx)
% FIXME this assumes only 1 such file, or at least it only takes the
% first one.
lfpfile = filenames{lfpfile_idx(1)};
else
lfpfile = {};
end
if ~isempty(rawfile_idx)
rawfile = filenames{rawfile_idx(1)};
end
params = LoadParameters(filename);
hdr = [];
hdr.nChans = params.nChannels;
hdr.nTrials = 1; % is it always continuous? FIXME
hdr.nSamplesPre = 0;
if ~isempty(lfpfile)
% use the sampling of the lfp-file to be leading
hdr.Fs = params.rates.lfp;
hdr.nSamples = listing(strcmp(filenames,lfpfile)).bytes./(hdr.nChans*params.nBits/8);
hdr.TimeStampPerSample = params.rates.wideband./params.rates.lfp;
else
% use the sampling of the raw-file to be leading
hdr.Fs = params.rates.wideband;
hdr.nSamples = listing(strcmp(filenames,rawfile)).bytes./(hdr.nChans*params.nBits/8);
hdr.TimeStampPerSample = 1;
end
hdr.orig = params;
hdr.label = cell(hdr.nChans,1);
for k = 1:hdr.nChans
hdr.label{k} = ['chan',num2str(k,'%0.3d')];
end
case 'neurosim_evolution'
hdr = read_neurosim_evolution(filename);
case {'neurosim_ds' 'neurosim_signals'}
hdr = read_neurosim_signals(filename);
case 'neurosim_spikes'
headerOnly = true;
hdr = read_neurosim_spikes(filename, headerOnly);
case 'nihonkohden_m00'
% this is an ASCII file format which is rather inefficient to read
if cache
% read it once and store the data along with the header
[hdr, dat] = read_nihonkohden_m00(filename);
hdr.orig.dat = dat;
else
% read only the header
hdr = read_nihonkohden_m00(filename);
end
case 'nihonkohden_eeg'
ft_hastoolbox('brainstorm', 1);
hdr = read_brainstorm_header(filename);
case 'nimh_cortex'
cortex = read_nimh_cortex(filename, 'epp', 'no', 'eog', 'no');
% look at the first trial to determine whether it contains data in the EPP and EOG channels
trial1 = read_nimh_cortex(filename, 'epp', 'yes', 'eog', 'yes', 'begtrial', 1, 'endtrial', 1);
hasepp = ~isempty(trial1.epp);
haseog = ~isempty(trial1.eog);
if hasepp
ft_warning('EPP channels are not yet supported');
end
% at the moment only the EOG channels are supported here
if haseog
hdr.label = {'EOGx' 'EOGy'};
hdr.nChans = 2;
else
hdr.label = {};
hdr.nChans = 0;
end
hdr.nTrials = length(cortex);
hdr.nSamples = inf;
hdr.nSamplesPre = 0;
hdr.orig.trial = cortex;
hdr.orig.hasepp = hasepp;
hdr.orig.haseog = haseog;
case 'ns_avg'
orig = read_ns_hdr(filename);
% do some reformatting/renaming of the header items
hdr.Fs = orig.rate;
hdr.nSamples = orig.npnt;
hdr.nSamplesPre = round(-orig.rate*orig.xmin/1000);
hdr.nChans = orig.nchan;
hdr.label = orig.label(:);
hdr.nTrials = 1; % the number of trials in this datafile is only one, i.e. the average
% remember the original header details
hdr.orig = orig;
case {'ns_cnt' 'ns_cnt16', 'ns_cnt32'}
ft_hastoolbox('eeglab', 1);
if strcmp(headerformat, 'ns_cnt')
orig = loadcnt(filename); % let loadcnt figure it out
elseif strcmp(headerformat, 'ns_cnt16')
orig = loadcnt(filename, 'dataformat', 'int16');
elseif strcmp(headerformat, 'ns_cnt32')
orig = loadcnt(filename, 'dataformat', 'int32');
end
% do some reformatting/renaming of the header items
hdr.Fs = orig.header.rate;
hdr.nChans = orig.header.nchannels;
hdr.nSamples = orig.ldnsamples;
hdr.nSamplesPre = 0;
hdr.nTrials = 1;
for i=1:hdr.nChans
hdr.label{i} = deblank(orig.electloc(i).lab);
end
% remember the original header details
hdr.orig = orig;
case 'ns_eeg'
orig = read_ns_hdr(filename);
% do some reformatting/renaming of the header items
hdr.label = orig.label;
hdr.Fs = orig.rate;
hdr.nSamples = orig.npnt;
hdr.nSamplesPre = round(-orig.rate*orig.xmin/1000);
hdr.nChans = orig.nchan;
hdr.nTrials = orig.nsweeps;
% remember the original header details
hdr.orig = orig;
case 'nmc_archive_k'
hdr = read_nmc_archive_k_hdr(filename);
case 'neuroshare' % NOTE: still under development
% check that the required neuroshare toolbox is available
ft_hastoolbox('neuroshare', 1);
tmp = read_neuroshare(filename);
hdr.Fs = tmp.hdr.analoginfo(end).SampleRate; % take the sampling freq from the last analog channel (assuming this is the same for all chans)
hdr.nChans = length(tmp.list.analog(tmp.analog.contcount~=0)); % get the analog channels, only the ones that are not empty
hdr.nSamples = max([tmp.hdr.entityinfo(tmp.list.analog).ItemCount]); % take the number of samples from the longest channel
hdr.nSamplesPre = 0; % continuous data
hdr.nTrials = 1; % continuous data
hdr.label = {tmp.hdr.entityinfo(tmp.list.analog(tmp.analog.contcount~=0)).EntityLabel}; %%% contains non-unique chans?
hdr.orig = tmp; % remember the original header
case 'nwb'
ft_hastoolbox('MatNWB', 1); % when I run this locally outside of ft_read_header it does not work for me
try
c = load('namespaces/core.mat');
nwb_version = c.version;
nwb_fileversion = util.getSchemaVersion(filename);
if ~strcmp(nwb_version, nwb_fileversion)
warning(['Installed NWB:N schema version (' nwb_version ') does not match the file''s schema (' nwb_fileversion{1} '). This might result in an error. If so, try to install the matching schema from here: https://github.com/NeurodataWithoutBorders/nwb-schema/releases'])
end
catch
warning('Something might not be alright with your MatNWB path. Will try anyways.')
end
tmp = nwbRead(filename); % is lazy, so should not be too costly
es_key = tmp.searchFor('ElectricalSeries').keys; % find lfp data, which should be an ElectricalSeries object
es_key = es_key(~contains(es_key, 'acquisition'));
if isempty(es_key)
error('Dataset does not contain an LFP signal (i.e., no object of the class ''ElectricalSeries''.')
elseif numel(es_key) > 1 % && isempty(additional_user_input) % TODO: Try to sort this out with the user's help
% Temporary fix: SpikeEventSeries is a daughter of ElectrialSeries but should not be found here (searchFor update on its way)
es_key = es_key(contains(es_key,'lfp','IgnoreCase',true));
end
if numel(es_key) > 1 % in case we weren't able to sort out a single
error('More than one ElectricalSeries present in data. Please specify which signal to use.')
else
eseries = io.resolvePath(tmp, es_key{1});
end
if isa(eseries.data, 'types.untyped.DataStub')
hdr.nSamples = eseries.data.dims(2);
elseif isa(eseries.data, 'types.untyped.DataPipe')
hdr.nSamples = eseries.data.internal.maxSize(2);
else
warning('Cannot determine number of samples in the data.')
hdr.nSamples = [];
end
hdr.Fs = eseries.starting_time_rate;
hdr.nSamplesPre = 0; % for now: hardcoded continuous data
hdr.nTrials = 1; % for now: hardcoded continuous data
hdr.label = {};
tmp_ch = io.resolvePath(tmp, eseries.electrodes.table.path).id.data.load; % electrode names
for iCh=1:numel(tmp_ch) % TODO: does that work if nwb ids are strings?
if isnumeric(tmp_ch(iCh))
hdr.label(iCh,1) = {num2str(tmp_ch(iCh))};
else
hdr.label(iCh,1) = tmp_ch(iCh);
end
end
hdr.nChans = numel(hdr.label);
[hdr.chanunit{1:hdr.nChans,1}] = deal(eseries.data_unit);
hdr.chanunit = strrep(hdr.chanunit, 'volt', 'V');
hdr.chanunit = strrep(hdr.chanunit, 'micro', 'u');
% TODO: hdr.FirstTimeStamp
% TODO: hdr.TimeStampPerSample
% carry over some metadata
hdr.orig = [];
fn = {'general_experimenter', ...
'general_institution', ...
'general_keywords', ...
'general_lab', ...
'general_notes', ...
'general_related_publications', ...
'general_session_id', ...
'identifier', ...
'session_description', ...
'nwb_version', ...
'help'};
for iFn = 1:numel(fn)
if isprop(tmp, fn{iFn}) && ~isempty(tmp.(fn{iFn}))
hdr.orig.(fn{iFn}) = tmp.(fn{iFn});
end
end
case 'artinis_oxy3'
ft_hastoolbox('artinis', 1);
hdr = read_artinis_oxy3(filename);
case 'artinis_oxy4'
ft_hastoolbox('artinis', 1);
hdr = read_artinis_oxy4(filename);
case 'artinis_oxy5'
ft_hastoolbox('artinis', 1);
hdr = read_artinis_oxy5(filename);
case 'artinis_oxyproj'
ft_hastoolbox('artinis', 1);
hdr = read_oxyproj_header(filename);
case 'plexon_ds'
hdr = read_plexon_ds(filename);
case 'plexon_ddt'
orig = read_plexon_ddt(filename);
hdr.nChans = orig.NChannels;
hdr.Fs = orig.Freq;
hdr.nSamples = orig.NSamples;
hdr.nSamplesPre = 0; % continuous
hdr.nTrials = 1; % continuous
hdr.label = cell(1,hdr.nChans);
% give this warning only once
ft_warning('creating fake channel names');
for i=1:hdr.nChans
hdr.label{i} = sprintf('%d', i);
end
% also remember the original header
hdr.orig = orig;
case {'read_nex_data'} % this is an alternative reader for nex files
orig = read_nex_header(filename);
% assign the obligatory items to the output FCDC header
numsmp = cell2mat({orig.varheader.numsmp});
adindx = find(cell2mat({orig.varheader.typ})==5);
if isempty(adindx)
ft_error('file does not contain continuous channels');
end
hdr.nChans = length(orig.varheader);
hdr.Fs = orig.varheader(adindx(1)).wfrequency; % take the sampling frequency from the first A/D channel
hdr.nSamples = max(numsmp(adindx)); % take the number of samples from the longest A/D channel
hdr.nTrials = 1; % it can always be interpreted as continuous data
hdr.nSamplesPre = 0; % and therefore it is not trial based
for i=1:hdr.nChans
hdr.label{i} = deblank(char(orig.varheader(i).nam));
end
hdr.label = hdr.label(:);
% also remember the original header details
hdr.orig = orig;
case {'plexon_nex' 'read_plexon_nex'} % this is the default reader for nex files
orig = read_plexon_nex(filename);
numsmp = cell2mat({orig.VarHeader.NPointsWave});
adindx = find(cell2mat({orig.VarHeader.Type})==5);
if isempty(adindx)