Home > bml > signal > bml_conform_to.m

bml_conform_to

PURPOSE ^

BML_CONFORM_TO conforms a slave ft_datatype_raw to the master's time

SYNOPSIS ^

function conformed = bml_conform_to(master, slave)

DESCRIPTION ^

 BML_CONFORM_TO conforms a slave ft_datatype_raw to the master's time

 Use as
    conformed = bml_conform_to(master, slave)

 master - FT_DATATYPE_RAW
 slave  - FT_DATATYPE_RAW

 returns the data of slave, resampled to the times of master.
 Asumes that master and slave are synchronized to same time origin.

 EXAMPLES:

 % merge slave to master in a single merged FT_DATATYPE_RAW

 conformed = bml_conform_to(master, slave)

 cfg=[];
 cfg.appenddim  = 'chan';
 merged = ft_appenddata(cfg,master,conformed);

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function conformed = bml_conform_to(master, slave)
0002 
0003 % BML_CONFORM_TO conforms a slave ft_datatype_raw to the master's time
0004 %
0005 % Use as
0006 %    conformed = bml_conform_to(master, slave)
0007 %
0008 % master - FT_DATATYPE_RAW
0009 % slave  - FT_DATATYPE_RAW
0010 %
0011 % returns the data of slave, resampled to the times of master.
0012 % Asumes that master and slave are synchronized to same time origin.
0013 %
0014 % EXAMPLES:
0015 %
0016 % % merge slave to master in a single merged FT_DATATYPE_RAW
0017 %
0018 % conformed = bml_conform_to(master, slave)
0019 %
0020 % cfg=[];
0021 % cfg.appenddim  = 'chan';
0022 % merged = ft_appenddata(cfg,master,conformed);
0023 %
0024 
0025 assert(isstruct(master) && all(ismember({'trial','time','label'},fieldnames(master))),"Invalid master");
0026 assert(isstruct(slave) && all(ismember({'trial','time','label'},fieldnames(slave))),"Invalid slave");
0027 
0028 master_N = length(master.trial);
0029 nChans = length(slave.label);
0030 
0031 %calculating mapping between master and slave trials
0032 annot_master = bml_raw2annot(master);
0033 annot_slave  = bml_raw2annot(slave);
0034 master_slave = bml_annot_intersect(annot_master,annot_slave);
0035 assert(~isempty(master_slave), "Can't conform. No master trials correspond to any slave trials.");
0036 master_slave.master_frac = master_slave.duration/master_slave.master_duration;
0037 ms_map = zeros(length(master.trial),1); %slave trial index for each master trial
0038 for i=1:master_N
0039   i_ms=master_slave(master_slave.master_id==i,:);
0040   if height(i_ms)>1
0041     [~,i_ms_max_idx]=max(i_ms.master_frac);
0042     i_ms = i_ms(i_ms_max_idx,:);
0043   end
0044   if height(i_ms)>0
0045     ms_map(i)=i_ms.slave_id(1);
0046   end
0047 end
0048 
0049 %creating subset of slave and master times
0050 sub_slave_N=sum(ms_map>0);
0051 sub_slave=[];
0052 sub_slave.label = slave.label;
0053 sub_slave.trial = cell(1,sub_slave_N);
0054 sub_slave.time = cell(1,sub_slave_N);
0055 sub_master_time = cell(1,sub_slave_N);
0056 starts=zeros(sub_slave_N,1);
0057 ends=zeros(sub_slave_N,1);
0058 sub_ms_map = zeros(master_N,1); %subset slave trial index for each master trial
0059 sub_slave_i=1;
0060 for i=1:master_N
0061   mc = annot_master(i,:);
0062   if ms_map(i)==0
0063     warning("Master trial %i has no corresponding Slave trial. Adding empty trial.",i);
0064   else
0065     starts(sub_slave_i) = mc.t1;
0066     ends(sub_slave_i) = mc.t2;
0067     sub_ms_map(i) = sub_slave_i;
0068     sub_slave.trial{sub_slave_i} = slave.trial{ms_map(i)};
0069     sub_slave.time{sub_slave_i} = slave.time{ms_map(i)};
0070     sub_master_time{sub_slave_i} = master.time{i};
0071     sub_slave_i = sub_slave_i + 1;
0072   end
0073 end
0074 
0075 %cheking if sampling rates of slave and master are similar.
0076 %If fs_slave >> fs_master, then apply lowpass filter to slave before interpolating
0077 if annot_slave.Fs(1) > 1.01 * annot_master.Fs(1) % 1% tolerance factor in freq comparison
0078   fprintf("low-pass filtering slave [%f Hz] to half the master's sampling rate [%f Hz], using 6th order Butterworth\n",annot_slave.Fs(1),annot_master.Fs(1)/2);
0079   cfg=[]; cfg.lpfilter='yes'; cfg.lpfreq=annot_master.Fs(1)/2; %such that new slave will be sampled at Nyquist rate
0080   cfg.lpfilttype = 'but'; cfg.lpfiltord = 6; %sixth order butterworth lowpass filter
0081   cfg.lpfiltdir = 'twopass'; %for zero lag
0082   cfg.lpinstabilityfix = 'no';
0083   cfg.lpfiltwintype = 'hamming';
0084   slave = ft_preprocessing(cfg,slave);
0085 end
0086 
0087 %interpolating subset of slave trials to master times
0088 cfg=[]; cfg.time=sub_master_time; cfg.method='pchip';
0089 cfg.feedback='no';
0090 sub_conformed=ft_resampledata(cfg,bml_crop(sub_slave,starts,ends));
0091 
0092 %adding empty slave trials if necessary
0093 conformed=[];
0094 conformed.label = slave.label;
0095 conformed.trial = cell(1,master_N);
0096 conformed.time = cell(1,master_N);
0097 for i=1:master_N
0098   if sub_ms_map(i)>0
0099       conformed.time{i}=sub_conformed.time{sub_ms_map(i)};
0100     conformed.trial{i}=sub_conformed.trial{sub_ms_map(i)};
0101   else
0102     conformed.time{i}=master.time{i};
0103     conformed.trial{i}=zeros(nChans,length(conformed.time{i}));
0104   end
0105 end
0106 
0107 if ismember('hdr',fields(slave))
0108   conformed.hdr=slave.hdr;
0109 else
0110   conformed.hdr=[];
0111 end
0112 
0113 %creating header
0114 conformed.hdr.nChans = length(conformed.label);
0115 conformed.hdr.Fs=round(1/mean(diff(conformed.time{1})));
0116 conformed.hdr.label=conformed.label;
0117 
0118 if ismember('fsample',fields(master))
0119   conformed.fsample = master.fsample;
0120   conformed.hdr.Fs = master.fsample;
0121 elseif ismember('fsample',fields(conformed))
0122   conformed = rmfield(conformed,'fsample');
0123 end
0124 
0125 
0126 
0127 
0128 
0129 % if ismember('hdr',fieldnames(master))
0130 %   conformed.hdr=master.hdr;
0131 % end

Generated on Tue 25-Sep-2018 10:08:19 by m2html © 2005