% POP_BIOSIG - import data files into EEGLAB using BIOSIG toolbox
%
% Usage:
% >> OUTEEG = pop_biosig; % pop up window
% >> OUTEEG = pop_biosig( filename, channels, type);
%
% Inputs:
% filename - [string] file name
%
% Optional inputs:
% 'channels' - [integer array] list of channel indices
% 'blockrange' - [min max] integer range of data blocks to import, in seconds.
% Entering [0 3] will import the first three blocks of data.
% Default is empty -> import all data blocks.
% 'ref' - [integer] channel index or index(s) for the reference.
% Reference channels are not removed from the data,
% allowing easy re-referencing. If more than one
% channel, data are referenced to the average of the
% indexed channels. WARNING! Biosemi Active II data
% are recorded reference-free, but LOSE 40 dB of SNR
% if no reference is used!. If you do not know which
% channel to use, pick one and then re-reference after
% the channel locations are read in. {default: none}
% 'rmeventchan' - ['on'|'off'] remove event channel after event
% extraction. Default is 'on'.
%
% Outputs:
% OUTEEG - EEGLAB data structure
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, Oct. 29, 2003-
%
% Note: BIOSIG toolbox must be installed. Download BIOSIG at
% http://biosig.sourceforge.net
% Contact a.schloegl@ieee.org for troubleshooting using BIOSIG.
% Copyright (C) 2003 Arnaud Delorme, SCCN, INC, UCSD, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.
function [EEG, command] = my_pop_biosig(filename, varargin);
EEG = [];
command = '';
if nargin < 1
% ask user
[filename, filepath] = uigetfile('*.*', 'Choose an BDF file -- pop_biosig()'); %%% this is incorrect in original version!!!!!!!!!!!!!!
drawnow;
if filename == 0
return;
end
filename = [filepath filename];
% open file to get infos
% ----------------------
disp('Reading data file header...');
dat = sopen(filename);
% special BIOSEMI
% ---------------
if strcmpi(dat.TYPE, 'BDF')
disp('We highly recommend that you choose a reference channel IF these are Biosemi data');
disp('(e.g., a mastoid or other channel). Otherwise the data will lose 40 dB of SNR!');
end
uilist = { { 'style' 'text' 'String' 'Channel list (default all):' } ...
{ 'style' 'edit' 'string' '' } ...
{ 'style' 'text' 'String' [ 'Data range (in seconds) to read (default all [0 ' int2str(dat.NRec) '])' ] } ...
{ 'style' 'edit' 'string' '' } ...
{ 'style' 'text' 'String' 'Extract event - cannot be unset (set=yes)' } ...
{ 'style' 'checkbox' 'string' '' 'value' 1 'enable' 'on' } {} ...
{ 'style' 'text' 'String' 'Import continuous data (set=yes)' 'value' 1} ...
{ 'style' 'checkbox' 'string' '' 'value' 0 } {} ...
{ 'style' 'text' 'String' 'Reference chan(s) indices - required for BIOSEMI' } ...
{ 'style' 'edit' 'string' '' } };
geom = { [3 1] [3 1] [3 0.35 0.5] [3 0.35 0.5] [3 1] };
result = inputgui( geom, uilist, 'pophelp(''pop_biosig'')', ...
'Load data using BIOSIG -- pop_biosig()');
if isempty(result)
return;
end
% decode GUI params
% -----------------
options = {};
if ~isempty(result{1}), options = { options{:} 'channels' eval( [ '[' result{1} ']' ] ) }; end
if ~isempty(result{2}), options = { options{:} 'blockrange' eval( [ '[' result{2} ']' ] ) }; end
if length(result) > 2
if ~isempty(result{4}), options = { options{:} 'ref' eval( [ '[' result{4} ']' ] ) }; end
if ~result{3}, options = { options{:} 'rmeventchan' 'off' }; end
end
else
options = varargin;
end
% decode input parameters
% -----------------------
g = finputcheck( options, { 'blockrange' 'integer' [0 Inf] [];
'channels' 'integer' [0 Inf] [];
'ref' 'integer' [0 Inf] [];
'rmeventchan' 'string' { 'on';'off' } 'on' }, 'pop_biosig');
if ischar(g), error(g); end
% import data
% -----------
EEG = eeg_emptyset;
if ~isempty(g.channels)
dat = sopen(filename, 'r', g.channels,'OVERFLOWDETECTION:OFF');
else dat = sopen(filename, 'r', 0,'OVERFLOWDETECTION:OFF');
end
fprintf('Reading data in %s format...\n', dat.TYPE);
if ~isempty(g.blockrange)
newblockrange = g.blockrange;
newblockrange(2) = min(newblockrange(2), dat.NRec);
newblockrange = newblockrange*dat.Dur;
DAT=sread(dat, newblockrange(2)-newblockrange(1), newblockrange(1))';
else
DAT=sread(dat, Inf)';% this isn't transposed in original!!!!!!!!
newblockrange = [];
end
dat = sclose(dat);
% convert to seconds for sread
% ----------------------------
EEG.nbchan = size(DAT,1);
EEG.srate = dat.SampleRate(1);
EEG.data = DAT;
clear DAT;
EEG.setname = sprintf('%s file', dat.TYPE);
EEG.comments = [ 'Original file: ' filename ];
EEG.xmin = 0;
if strcmpi(dat.TYPE, 'BDF') || strcmpi(dat.TYPE, 'EDF')
EEG.trials = 1;
EEG.pnts = size(EEG.data,2);
else
EEG.trials = dat.NRec;
EEG.pnts = size(EEG.data,2)/dat.NRec;
end
if isfield(dat, 'Label') && ~isempty(dat.Label)
EEG.chanlocs = struct('labels', cellstr(char(dat.Label)));
end
EEG = eeg_checkset(EEG);
% extract events % this part I totally revamped to work... JO
% --------------
disp('Extracting events from last EEG channel...');
EEG.event = [];
% $$$ startval = mode(EEG.data(end,:)); % my code
% $$$ for p = 2:size(EEG.data,2)-1
% $$$ [codeout] = code(EEG.data(end,p));
% $$$ if EEG.data(end,p) > EEG.data(end,p-1) & EEG.data(end,p) >= EEG.data(end,p+1)
% $$$ EEG.event(end+1).latency = p;
% $$$ EEG.event(end).type = bitand(double(EEG.data(end,p)-startval),255);
% $$$ end
% $$$ end
% lastout = mod(EEG.data(end,1),256);newevs = []; % andrey's code 8 bits
% codeout = mod(EEG.data(end,2),256);
% for p = 2:size(EEG.data,2)-1
% nextcode = mod(EEG.data(end,p+1),256);
% if codeout > lastout & codeout >= nextcode
% newevs = [newevs codeout];
% EEG.event(end+1).latency = p;
% EEG.event(end).type = codeout;
% end
% lastout = codeout;
% codeout = nextcode;
% end
%lastout = mod(EEG.data(end,1),256*256);newevs = []; % andrey's code 16 bits
%codeout = mod(EEG.data(end,2),256*256);
%for p = 2:size(EEG.data,2)-1
% nextcode = mod(EEG.data(end,p+1),256*256);
% if (codeout > lastout) & (codeout >= nextcode)
% newevs = [newevs codeout];
% EEG.event(end+1).latency = p;
% EEG.event(end).type = codeout;
% end
% lastout = codeout;
% codeout = nextcode;
%end
% Modifieded by Andrey (Aug.5,2008) to detect all non-zero codes:
thiscode = 0;lastcode=0;
for p = 1:size(EEG.data,2)-1
prevcode = thiscode;
thiscode = mod(EEG.data(end,p),256*256); % andrey's code - 16 bits
if (thiscode ~= 0) && (thiscode~=prevcode) && (thiscode~=lastcode) % fix to avoid repeated codes (per Ying's demand)
EEG.event(end+1).latency = p;
EEG.event(end).type = thiscode;
lastcode = thiscode;
end
end
if strcmpi(g.rmeventchan, 'on')
EEG.data(dat.BDF.Status.Channel,:) = [];
EEG.nbchan = size(EEG.data,1);
if ~isempty(EEG.chanlocs)
EEG.chanlocs(dat.BDF.Status.Channel,:) = [];
end
end
EEG = eeg_checkset(EEG, 'eventconsistency');
% $$$ if ~isempty(dat.EVENT)
% $$$ if isfield(dat, 'out') % Alois fix for event interval does not work
% $$$ if isfield(dat.out, 'EVENT')
% $$$ dat.EVENT = dat.out.EVENT;
% $$$ end
% $$$ end
% $$$ if ~isempty(newblockrange)
% $$$ interval(1) = newblockrange(1) * dat.SampleRate(1) + 1;
% $$$ interval(2) = newblockrange(2) * dat.SampleRate(1);
% $$$ else interval = [];
% $$$ end
% $$$ EEG.event = biosig2eeglabevent(dat.EVENT, interval); % Toby's fix
% $$$ if strcmpi(g.rmeventchan, 'on') & strcmpi(dat.TYPE, 'BDF') & isfield(dat, 'BDF')
% $$$ disp('Removing event channel...');
% $$$ EEG.data(dat.BDF.Status.Channel,:) = [];
% $$$ EEG.nbchan = size(EEG.data,1);
% $$$ if ~isempty(EEG.chanlocs)
% $$$ EEG.chanlocs(dat.BDF.Status.Channel,:) = [];
% $$$ end
% $$$ end
% $$$ EEG = eeg_checkset(EEG, 'eventconsistency');
% $$$ else
% $$$ disp('Warning: no event found. Events might be embedded in a data channel.');
% $$$ disp(' To extract events, use menu File > Import Event Info > From data channel');
% $$$ end
% rerefencing
% -----------
if ~isempty(g.ref)
disp('Re-referencing...');
EEG.data = EEG.data - repmat(mean(EEG.data(g.ref,:),1), [size(EEG.data,1) 1]);
if length(g.ref) == size(EEG.data,1)
EEG.ref = 'average';
end
if length(g.ref) == 1
disp([ 'Warning: channel ' int2str(g.ref) ' is now zeroed (but still present in the data)' ]);
else
disp([ 'Warning: data matrix rank has decreased through re-referencing' ]);
end
end
% convert data to single if necessary
% -----------------------------------
EEG = eeg_checkset(EEG,'makeur'); % Make EEG.urevent field
EEG = eeg_checkset(EEG);
% history
% -------
if isempty(options)
command = sprintf('EEG = my_pop_biosig(''%s'');', filename);
else
command = sprintf('EEG = my_pop_biosig(''%s'', %s);', filename, vararg2str(options));
end