User Manual Sig Viewer.m
User%20manual%20sigViewer.m
User Manual:
Open the PDF directly: View PDF .
Page Count: 6
Download | |
Open PDF In Browser | View PDF |
User manual sigViewer Necessary functions and information for raw data extraction. The raw data is gotten from the buffer.m through the function updateData: %Updates the data: Waits for samples from buffer.m, and tracks at which %sample it's currently at, how many updates it has gone through, and %whether the loop in which it is called (for ~endTraining) should %terminate. %rawdat: Row for each electrode,column for each sample. The oldest data is % deleted, and the new samples are inserted at the end. 1260 samples are % remembered at a time. %cursamp: Which sample it's currently at. %nUpdate: Amount of updates it's done. %loopBool: Whether the loop for ~endTraining should terminate. Only one %that relies on the fig input. function[rawdat, cursamp, nUpdate, loopBool] = updateData(buffhost, buffport,opts, fig, cursamp,updateSamp, rawdat, nUpdate, blkIdx, fs, iseeg) %-----------------------------------------------------------------------% wait for new data, % N.B. wait_data returns based on Number of samples, get_data uses sample index = #samples-1 loopBool = true; status=buffer('wait_dat',[cursamp+updateSamp+1 inf opts.timeOut_ms],buffhost,buffport); %wait for either enough data or timeout if( status.nSamples < cursamp+updateSamp ) %In case the timeout has passed. fprintf('Buffer stall detected...\n'); pause(1); cursamp=status.nSamples; %Which sample it's currently at. if ( ~ishandle(fig) ); loopBool = false; return; end; elseif ( status.nSamples > cursamp+1*fs) % missed a 1 seconds of data fprintf('Warning: Cant keep up with the data!\n%d Dropped samples...\n',status.nSamples-updateSamp-1-cursamp); cursamp=status.nSamples - updateSamp-1; % jump to the current time end; % Get the samples inbetween the current point and current+amount of samples added per update. dat =buffer('get_dat',[cursamp cursamp+updateSamp-1],buffhost,buffport); %Struct with one field: .buf, which is 63x4. cursamp=cursamp+updateSamp; %Update at which sample it's at for the next loop. nUpdate=nUpdate+1; % shift and insert into the data buffer rawdat(:, 1 : blkIdx) =rawdat(:,updateSamp+1:end); %The data is shifted one update to the left rawdat(:,blkIdx+1 : end) =dat.buf(iseeg,:); % The new data is inserted into rawdat %if ( cursamp-hdr.nSamples > hdr.fSample*30 ) keyboard; end; if ( opts.verb>0 ); fprintf('.'); end; if ( ~ishandle(fig) ); loopBool = false; end; end The wait_dat function (17) from buffer returns how many samples it delivers, and the number of events. When the number of samples is sufficient, the get_dat function (31) gets the actual data. this data consists of a row for each electrode and a column for each datapoint (sample). the get_dat buffer command gets 63 datapoints per electrode at a time. this data is added to dat.buf (technically only the ones that conform to the iseeg boolean, but this should not be relevant for us). rawdat contains a running history of the 1260 most recent datapoints. it removes the oldest points and then proceeds to add the data of dat.buf at every execution of updateData. finally, the r awdat is preprocessed and stored in p pdat my assumption is that the 63 samples is dictated by fs/updatefreq, rounded upwards. the updatefreq is set to 4, and fs to 250, giving 62.5, which rounded up matches with the number of samples. to actually use any data we need to know how to interact with buffer. there are a few commands of note: get_hdr returns a struct-type header that denotes the characteristics of the data. this header consists of information regarding the number of channels, samples, events, the sample frequency, labels and datatype. this information is stored twice for compatibility reasons. get_dat takes in a 4 column by 63 row of data through bufClient, each column an electrode, each row a datapoint. this is transposed to the 63 column by 4 row format used throughout and passed on. get_evt returns events. this is not used in sigViewer so can be skipped for now. wait_dat in case there have not been enough new samples for a full update wait_dat waits until there either are enough samples or until a pre-set amount of time has passed. It returns the amount of samples it currently has. put_hdr, put_dat, put_evt sends (modifies) the header/data/event on the server, there are further commands, but they are not relevant for us at this point. buffer connects with the server through reconnect. It creates a javaObject, which is an encapsulation of a java class within Matlab. The server/buffer construction appears to be made in java, which is good news for us. nl.fcdonders.fieldtrip.bufferclient.BufferClientClock keeps Matlab and java synced and connected. Fieldtrip (the toolbox upon which everything is built) has a reference implementation available. This reference implementation is not found in the described subdirectories in the buffer_bci we are working from. There is a very similar implementation in \ dataAcq\buffer\java\nl\fcdonders\fieldtrip\bufferclient, however. We might want to abandon starting from buffer_bci and try to base our work on the fieldtrip source, or at least give it due attention. It can be downloaded here. The frequencies for updating, sampling and other information needed for updateData are initialized in the function freqSamp: %fs: sampling frequency. (250) %times: [1x1260], gradual increasing values from ~(-5) to 0. %blkIdx: Index where to insert new data. (max amount of data - amount of % new samples per update) %opts: trialLengthSamp gets updated (amount of data to plot for each channel). %freqInd: Filter of frequencies that fit into the frequency bands in opts. %noiseNormInd: Filter for noisefrac (not yet closely examined) %updateSamp: How many samples are updated per round, in integer (63 in % testing). %noiseInd: Filter of frequencies that fit into the noise bands in opts % (around 50 hz). Used for preprocessing %freqs: list of all frequencies that can have data (0-125 in quickstart), %used in the frequency domain. %trialLengthSamp: part of opts, nr of samples that are retained in memory (1260) function [fs, times, blkIdx, opts,freqInd, noiseNormInd,updateSamp,noiseInd,freqs] = freqSamp(header,opts) %Header may contain duplicate fields with(out) camel case.. if ( isfield(header,'fSample') ); fs=header.fSample; else fs=header.fsample; end; %Initialize trialLengthSamp in case it hasn't. if ( isempty(opts.trialLengthSamp) && ~isempty(opts.trlen_ms) ); opts.trialLengthSamp=round(opts.trlen_ms*fs/1000); end; updateSamp=ceil(fs/opts.updateFreq); opts.trialLengthSamp=ceil(opts.trialLengthSamp/updateSamp)*updateSamp; fprintf('tr_samp = %d updateSamp = %d\n',opts.trialLengthSamp,updateSamp); blkIdx = opts.trialLengthSamp-updateSamp; % index for where new data gets inserted if ( isempty(opts.downsample) || opts.downsample>fs ) times=(-opts.trialLengthSamp+1:0)./fs; else times=(-ceil((opts.trialLengthSamp+1)*opts.downsample/fs):0)/opts.downsample; end %Filter for the frequency domain. freqs=0:1000/opts.welch_width_ms:fs/2; % 0 to (fs/2), with step size 1000/welch_width_ms freqInd =getfreqInd(freqs,opts.freqbands); if ( ~iscell(opts.noisebands) ) opts.noisebands={opts.noisebands}; end; %Filters of frequency for the 50hz and noisefrac filters. noiseInd =false(numel(freqInd),numel(opts.noisebands)); noiseNormInd=false(size(noiseInd)); %array with zeroes with the same size as noiseInd. for ni=1:numel(opts.noisebands); % find the noise ind for each of the noise bands. noiseInd(:,ni)=getfreqInd(freqs,opts.noisebands{ni}); nWidth=ceil(sum(noiseInd(:,ni))/2); nIdx=[find(noiseInd(:,ni)>0,1,'first') find(noiseInd(:,ni)>0,1,'last')]; % start/end of the noise band noiseNormInd( max(1, nIdx(1)-(1:nWidth))) =true; % nWidth bins before noiseNormInd( min(end, nIdx(2)+(1:nWidth))) =true; % nWidth bins after end end In order to make updateData work properly, the following variables created here are necessary: fs, updateSamp and blkIdx. All variables with -Ind at the end are used for visualization and/or filtering in other modalities than a standard time domain plot. Freqs is used in the frequency domain. This function makes a lot of computations that shouldn’t be too hard to replicate, but a deep understanding of the workings apart from what the output won’t be necessary. The ‘header’ that’s needed as input for freqSamp is gotten through the buffer.m file as well, through the function chanInfo: %Reads the header from the buffer and checks whether the head should be %drawn. %The header contains: number of channels, number of samples, number of %events, the sampling frequency, the data type and the names of the %channels. function [header, opts] = chanInfo(buffhost, buffport, opts) header=[]; header=buffer('get_hdr',[],buffhost,buffport); %Read data from the buffer server. while ( isempty(header) || ~isstruct(header) || (header.nchans==0) ) % wait for the buffer to contain valid data try header=buffer('get_hdr',[],buffhost,buffport); catch header=[]; fprintf('Invalid header info... waiting.\n'); end; pause(1); end; if ( isempty(opts.drawHead) ) opts.drawHead=true; end; end This function is quite straightforward: it attempts to receive the header from buffer from r7-r17. It has to wait for this data, hence the loop. The last part at r19 is concerning whether a head should be drawn behind the plots. The struct opts is used everywhere in the code, and in the functions above as well. it’s initialized in the beginning with these values: function[opts] = initializeOpts() %Find buffer.m in dataAcq. If it's not found, initPaths is called, which adds the required folders to the path. wb=which('buffer'); %find location of the buffer.m file if ( isempty(wb) || isempty(strfind('dataAcq',wb)) ); fprintf('Running %s\n',fullfile(fileparts(mfilename('fullpath')),'../utilities/initPaths.m')); run(fullfile(fileparts(mfilename('fullpath')),'../utilities/initPaths.m')); end; %Makes a structure array. The structure is as such: name-field. Fields can %be called with opts.name. General storage for important variables. opts=struct('endType','end.training','verb',1,'timeOut_ms',1000,... 'trlen_ms',5000,'trialLengthSamp',[],'updateFreq',4,... 'detrend',1,'fftfilter',[.1 .3 45 47],'freqbands',[],'downsample',[],'spatfilt','car',... 'adapthalflife_s',15,... 'adaptspatialfiltFn','','whiten',0,'rmemg',0,... 'rmartch',0,'artChBands',[.5 1 45 48],... 'artCh',{{'EOG' 'AFz' 'EMG' 'AF3' 'FP1' 'FPz' 'FP2' 'AF4' '1'}},... 'useradaptspatfiltFn','',... 'badchrm',0,'badchthresh',3,'capFile',[],'overridechnms',0,... 'welch_width_ms',1000,'spect_width_ms',500,'spectBaseline',1,... 'noisebands',{{[23 24 26 27] [45 47 53 55] [97 98 102 103]}},'noiseBins',[],'noisefracBins',[.5 10],...%[0 1.75],... 'sigProcOptsGui',1,'dataStd',2.5,'drawHead',1); end The variables in this struct are explained at the start of sigViewer. The first part searches for the buffer.m file, which is used to extract information from the bufferClient. The only variables that are used as input but are not explained in the functions above, are fig and rawdat. Rawdat is initialized before the loop in which updateData is called: rawdat = zeros(sum(iseeg),outputSize(1)); Which uses outputSize, computed after freqSamp is called: outputSize=[opts.trialLengthSamp opts.trialLengthSamp]; %output size; amount of data to plot for each channel x if(~isempty(opts.downsample)); outputSize(2)=min(outputSize(2), round(opts.trialLengthSamp * opts.downsample /fs)); end; This should be a sufficient amount of building blocks for the visualization of the raw data.
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.5 Linearized : Yes Producer : Skia/PDF m72 Page Count : 6EXIF Metadata provided by EXIF.tools