Skip to content

Commit

Permalink
added bbci_recordSignals and required functions
Browse files Browse the repository at this point in the history
fixed some functions that were adapted to the new toolbox before
  • Loading branch information
BenjaminBlankertz committed Jan 29, 2014
1 parent e7260cc commit 6c561ee
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 23 deletions.
91 changes: 91 additions & 0 deletions fileio/file_writeBVheader.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
function file_writeBVheader(file, varargin)
% FILE_WRITEBVHEADER - Write Header in BrainVision Format
%
% Synopsis:
% file_writeBVheader(FILE, 'Property1', Value1, ...)
%
% Arguments:
% FILE: string containing filename to save in.
%
% Properties:
% 'Fs': sampling interval of raw data, required
% 'CLab': cell array, channel labels, required
% 'Scale': scaling factors for each channel, required
% 'DataPoints': number of datapoints, required
% 'Precision': precision (default 'int16')
% 'DataFile': name of corresponding .eeg file (default FILE)
% 'MarkerFile': name of corresponding .mrk file (default FILE)
% 'Impedances': for each channel
%
% See also: file_*


global BBCI

props= {'Folder' BBCI.TmpDir 'CHAR'};
opt= opt_proplistToStruct(varargin{:});
opt= opt_setDefaults(opt, props);

if fileutil_isAbsolutePath(file),
fullName= file;
else
fullName= fullfile(opt.export_dir, file);
end

[pathstr, fileName]= fileparts(fullName);
props= {'Precision' 'int16' 'CHAR(int16 int32 single double float32 float64)'
'DataFile' fileName 'CHAR'
'MarkerFile' fileName 'CHAR'
'Impedances' [] 'DOUBLE'};
opt= opt_setDefaults(opt, props);

if ~ischar(opt.DataPoints), %% not sure, why DataPoints is a string
opt.DataPoints= sprintf('%d', opt.DataPoints);
end

fid= fopen([fullName '.vhdr'], 'w','b');
if fid==-1, error(sprintf('cannot write to %s.vhdr', fullName)); end
fprintf(fid, ['Brain Vision Data Exchange Header File Version 1.0' 13 10]);
fprintf(fid, ['; Data exported from BBCI Matlab Toolbox' 13 10]);
fprintf(fid, [13 10 '[Common Infos]' 13 10]);
fprintf(fid, ['DataFile=%s.eeg' 13 10], opt.DataFile);
fprintf(fid, ['MarkerFile=%s.vmrk' 13 10], opt.MarkerFile);
fprintf(fid, ['DataFormat=BINARY' 13 10]);
fprintf(fid, ['DataOrientation=MULTIPLEXED' 13 10]);
fprintf(fid, ['NumberOfChannels=%d' 13 10], length(opt.CLab));
fprintf(fid, ['DataPoints=%s' 13 10], opt.DataPoints);
fprintf(fid, ['SamplingInterval=%g' 13 10], 1000000/opt.Fs);
fprintf(fid, [13 10 '[Binary Infos]' 13 10]);
switch(lower(opt.Precision)),
case 'int16',
fprintf(fid, ['BinaryFormat=INT_16' 13 10]);
fprintf(fid, ['UseBigEndianOrder=NO' 13 10]);
case 'int32',
fprintf(fid, ['BinaryFormat=INT_32' 13 10]);
fprintf(fid, ['UseBigEndianOrder=NO' 13 10]);
case {'float32','single','float'},
fprintf(fid, ['BinaryFormat=IEEE_FLOAT_32' 13 10]);
case {'float64','double'},
fprintf(fid, ['BinaryFormat=IEEE_FLOAT_64' 13 10]);
otherwise,
error(['Unknown precision, not implemented yet: ' opt.Precision]);
end
fprintf(fid, [13 10 '[Channel Infos]' 13 10]);
for ic= 1:length(opt.CLab),
fprintf(fid, ['Ch%d=%s,,%g' 13 10], ic, opt.CLab{ic}, ...
opt.Scale(min(ic,end)));
end
fprintf(fid, ['' 13 10]);

if ~isempty(opt.Impedances),
fprintf(fid, ['Impedance [kOhm].' 13 10]);
for ic= 1:length(opt.CLab)
if isinf(opt.Impedances(ic))
fprintf(fid, [opt.CLab{ic} ': Out of Range!' 13 10]);
else
fprintf(fid, [opt.CLab{ic} ': ' num2str(opt.Impedances(ic)) 13 10]);
end
end
end

fclose(fid);
19 changes: 11 additions & 8 deletions online/apply_functions/bbci_apply_recordSignals.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
% return;
% end
DS_record= struct('recording', 1);
DS_record.opt= propertylist2struct(BS.record_param{:});
DS_record.opt= set_defaults(DS_record.opt, ...
'internal', 0, ...
'checkimpedances', 0, ...
'folder', BBCI.Tp.Dir);
DS_record.opt= opt_proplistToStruct(BS.record_param{:});
props= {'Internal' 0 'BOOL'
'CheckImpedances' 0 'BOOL'
'Folder', BBCI.Tp.Dir 'CHAR'};
DS_record.opt= opt_setDefaults(DS_record.opt, props);
filebase= BS.record_basename;
if ~fileutil_isAbsolutePath(filebase),
filebase= fullfile(DS_record.opt.folder, filebase);
Expand All @@ -41,7 +41,7 @@
end
DS_record.filename= filename;

if DS_record.opt.internal,
if DS_record.opt.Internal,
DS_record.fcn= 'internal';
else
list_external= {'bv'};
Expand All @@ -60,9 +60,12 @@
bvr_sendcommand('startrecording', [filename '.eeg']);
end
case 'internal',
opt= copy_fields(DS_record.opt, DS, {'clab','fs'});
opt= DS_record.opt;
opt.CLab= DS.clab;
opt.Fs= DS.fs;
%opt= struct_copyFields(DS_record.opt, DS, {'clab','fs'});
state= bbciutil_recordSignals('init', filename, opt);
DS_record= copy_fields(DS_record, state);
DS_record= struct_copyFields(DS_record, state);
otherwise,
% This should not happen (see 'list_external' above)
error('Implementation for opening ''%s'' is missing', DS_record.fcn);
Expand Down
2 changes: 1 addition & 1 deletion online/calibration/bbci_calibrate_NIRS.m
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@
if nComb > 1,
% [dummy, bi]= min(mean_loss + 0.1*std_loss);
bi=2;
bbci= copy_fields(bbci, bbci_all(bi), cfy_fields);
bbci= struct_copyFields(bbci, bbci_all(bi), cfy_fields);
% bbci.feature.param = { {ival_cfy{ci}} };
end

Expand Down
4 changes: 2 additions & 2 deletions online/calibration/bbci_calibrate_cspPlusLap.m
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
end
else
result_flds= {'rejected_trials', 'rejected_clab', 'clab'};
BC_result= copy_fields(BC_result, previous, result_flds);
BC_result= struct_copyFields(BC_result, previous, result_flds);
end

if isequal(opt.classes, 'auto'),
Expand Down Expand Up @@ -506,7 +506,7 @@
if nComb > 1,
data.all_results= data.result;
[dmy, bi]= min(mean_loss + 0.1*std_loss);
bbci= copy_fields(bbci, bbci_all(bi), cfy_fields);
bbci= struct_copyFields(bbci, bbci_all(bi), cfy_fields);
data.result= data.all_results(bi);
data.result.class_selection_loss= [mean_loss; std_loss];
bbci_log_write(data, sprintf('\nCombination <%s> vs <%s> chosen.\n', ...
Expand Down
10 changes: 5 additions & 5 deletions online/feedback/utils/bbci_fbutil_replay.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
function bbci_fbutil_replay(logfile, varargin)

opt= propertylist2struct(varargin{:});
opt= set_defaults(opt, ...
'realtime',1, ...
'fbopt', struct);
opt= opt_proplistToStruct(varargin{:});
opt= opt_setDefaults(opt, ...
'realtime',1, ...
'fbopt', struct);

logline= textread(logfile, '%s', 'delimiter','');

Expand All @@ -15,7 +15,7 @@ function bbci_fbutil_replay(logfile, varargin)
for ii= idx',
eval(['fb' logline{ii}(2:end) ';']);
end
fbopt= copy_fields(fbopt, opt.fbopt);
fbopt= struct_copyFields(fbopt, opt.fbopt);

HH= feval([fcn '_init'], fbopt);
[handles, H]= bbciutil_handleStruct2Vector(HH);
Expand Down
4 changes: 2 additions & 2 deletions online/feedback/utils/bbci_fbutil_set.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
switch(num),
case 'init',
DF= DFE;
DFE= copy_fields(DF.opt, {'trigger_fcn', 'trigger_param'});
DFE= copy_fields(DFE, DF, {'log'});
DFE= struct_copyFields(DF.opt, {'trigger_fcn', 'trigger_param'});
DFE= struct_copyFields(DFE, DF, {'log'});
DFE.handles= varargin{1};
DFE.log_str= '';
bbci_log_write(DFE, '# Settings:');
Expand Down
45 changes: 45 additions & 0 deletions online/utils/bbci_recordSignals.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
function data= bbci_recordSignals(bbci_in, filename)
%BBCI_RECORDSIGNALS - Record acquired brain signals
%
%Synopsis:
% DATA= bbci_recordSignals(BBCI, BASENAME)
%
%Arguments:
% BBCI: Type 'help bbci_apply_structures' to get a description of the
% structure 'bbci'. This function only uses the fields
% 'source': while defines the data acquisition, and
% 'quit_condition'
% BASENAME: Basename of the files. Nummers are appended in order to avoid
% overwriting.

if nargin<2,
filename= '/tmp/bbci_recording';
end

bbci= struct_copyFields(bbci_in, {'source','quit_condition'});
bbci.feature.ival= [-100 0];

%default_param= {'internal',1, 'precision','double'};
default_param= {'internal',1};
props= {'record_signals' 1 'BOOL',
'record_basename' filename 'CHAR',
'record_param' default_param 'PROPLIST'};
bbci.source= opt_setDefaults(bbci.source, props);

fprintf('Recording started.\n');

bbci= bbci_apply_setDefaults(bbci);
[data, bbci]= bbci_apply_initData(bbci);
run= true;
while run,
[data.source, data.marker]= ...
bbci_apply_acquireData(data.source, bbci.source, data.marker);
if ~data.source.state.running,
break;
end
data.marker.current_time= data.source.time;
run= bbci_apply_evalQuitCondition(data.marker, bbci);
end
bbci_apply_close(bbci, data);

fprintf('Recording finished -> %s\n', data.source.record.filename);
10 changes: 5 additions & 5 deletions online/utils/bbciutil_recordSignals.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

% Write Header file
opt= varargin{3};
opt_hdr= set_defaults(opt, ...
'scale', 0.1, ...
'precision', 'int16');
props= {'Scale' 0.1 'DOUBLE'
'Precision' 'int16' 'CHAR'};
opt_hdr= opt_setDefaults(opt, props);
opt_hdr.DataPoints= 0;
file_writeBVheader(filename, opt_hdr);
state= struct('precision', opt_hdr.precision, ...
'factor', diag(1./opt_hdr.scale));
state= struct('precision', opt_hdr.Precision, ...
'factor', diag(1./opt_hdr.Scale));

% Open EEG file for writing
state.fid_eeg= fopen([filename '.eeg'], 'w');
Expand Down
29 changes: 29 additions & 0 deletions online/utils/selftesting/test_recording.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
C= 2;
T= 10000;
cnt= struct('fs', 100);
cnt.x= 50*randn(T, C);
cnt.clab= cprintf('Ch%d', 1:C);

M= 100;
mrk= struct; %('fs', cnt.fs);
mrk.time= round(linspace(0, T/cnt.fs*1000, M+2));
mrk.time([1 end])= [];
%mrk.desc= cprintf('S%3d', ceil(rand(1,M)*10))';
mrk.desc= ceil(rand(1,M)*10);


% --- Setup a very simple system for (simulated) online processing
bbci= struct;
bbci.source.acquire_fcn= @bbci_acquire_offline;
bbci.source.acquire_param= {cnt, mrk};
bbci.source.log.output= 'screen';
data= bbci_recordSignals(bbci, '/tmp/rec_test');

[cnt_re, mrk_re]= file_readBV(data.source.record.filename);

isequal(cnt.clab, cnt_re.clab)
max(abs(cnt.x(:)-cnt_re.x(:)))

isequal(mrk.pos, mrk_re.pos(2:end))
isequal(mrk.desc, mrk_re.desc(2:end))

0 comments on commit 6c561ee

Please sign in to comment.