0001 function [slave_delta_t, max_corr, master, slave] = bml_timealign(cfg, master, slave)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046 if nargin == 2
0047 slave = master;
0048 master = cfg;
0049 cfg = [];
0050 elseif nargin ~=3
0051 error('incorrect number of arguments in call');
0052 end
0053
0054 resample_freq = bml_getopt(cfg,'resample_freq', 10000);
0055 scan = bml_getopt(cfg, 'scan');
0056 freqreltol = bml_getopt(cfg, 'freqreltol', 1e-5);
0057 method = string(bml_getopt(cfg, 'method', 'envelope'));
0058 env_freq = bml_getopt(cfg,'env_freq', 100);
0059 lpf_freq = bml_getopt(cfg,'lpf_freq', 4000);
0060 penalty_tau = bml_getopt(cfg,'penalty_tau');
0061 penalty_n = bml_getopt(cfg,'penalty_n', 2);
0062 ft_feedback = bml_getopt(cfg,'ft_feedback','no');
0063 ft_feedback = ft_feedback{1};
0064
0065
0066 if numel(master.trial) > 1; error('master should be single trial raw'); end
0067 if numel(slave.trial) > 1; error('slave should be single trial raw'); end
0068 if numel(master.label) > 1; error('master should be single channel raw'); end
0069 if numel(slave.label) > 1; error('slave should be single channel raw'); end
0070
0071
0072 mc=[]; sc=[];
0073 mc.s1=1; mc.s2=length(master.time{1});
0074 mc.t1=master.time{1}(1); mc.t2=master.time{1}(end);
0075 sc.s1=1; sc.s2=length(slave.time{1});
0076 sc.t1=slave.time{1}(1); sc.t2=slave.time{1}(end);
0077 max_scan_range = [mc.t1 - sc.t2, mc.t2 - sc.t1];
0078 if prod(max_scan_range) > 0
0079 slave_delta_t=nan;
0080 max_corr=nan;
0081 return
0082 end
0083 if isempty(scan)
0084 scan = max_scan_range;
0085 elseif length(scan)==1
0086 scan=[max(-scan,max_scan_range(1)), min(scan,max_scan_range(2))];
0087 elseif length(scan)==2
0088 scan=[max(scan(1),max_scan_range(1)), min(scan(2),max_scan_range(2))];
0089 else
0090 error('invalid use of cfg.scan argument');
0091 end
0092
0093
0094 master_median = median(master.trial{1});
0095 slave_median = median(slave.trial{1});
0096 master_std = robust_std(master.trial{1});
0097 slave_std = robust_std(slave.trial{1});
0098 if master_std==0; error('master can''t be correlated'); end
0099 if slave_std==0; error('slave can''t be correlated'); end
0100
0101
0102 ctw = [sc.t1+scan(1), sc.t2+scan(2)];
0103 master = bml_pad(master, ctw(1), ctw(2), 0);
0104 slave = bml_pad(slave, master.time{1}(1), master.time{1}(end), 0);
0105
0106
0107 cfg=[]; cfg.feedback=ft_feedback;
0108 cfg.resamplefs=resample_freq;
0109 master = ft_resampledata(cfg, master);
0110 cfg=[]; cfg.feedback=ft_feedback;
0111 cfg.time=master.time; cfg.method='linear';
0112 slave = ft_resampledata(cfg, slave);
0113
0114
0115 if abs(slave.fsample/master.fsample-1) < freqreltol
0116 slave.fsample = master.fsample;
0117 else
0118 error('failed to resample slave to master''s time');
0119 end
0120 is_nan=isnan(slave.trial{1});
0121 if sum(is_nan)>0
0122 master.trial{1} = master.trial{1}(:,~is_nan);
0123 master.time{1} = master.time{1}(:,~is_nan);
0124 slave.trial{1} = slave.trial{1}(:,~is_nan);
0125 slave.time{1} = slave.time{1}(:,~is_nan);
0126 end
0127
0128
0129 if ismember(lower(method),{'env','envelope'})
0130 cfg=[]; cfg.freq = env_freq;
0131 master = bml_envelope_binabs(cfg,master);
0132 slave = bml_envelope_binabs(cfg,slave);
0133 try_polarity=false;
0134
0135 elseif ismember(lower(method),{'lpf','low-pass-filter'})
0136 master.trial{1} = ft_preproc_lowpassfilter(master.trial{1},...
0137 master.fsample, lpf_freq, 4, 'but', 'twopass');
0138 slave.trial{1} = ft_preproc_lowpassfilter(slave.trial{1},...
0139 slave.fsample, lpf_freq, 4, 'but', 'twopass');
0140 try_polarity=true;
0141
0142 else
0143 error('unknown method');
0144 end
0145
0146
0147 master.trial{1} = (master.trial{1} - master_median) / master_std;
0148 slave.trial{1} = (slave.trial{1} - slave_median) / slave_std;
0149
0150
0151 [corr,lag]=xcorr(master.trial{1}(1,:), slave.trial{1}(1,:),...
0152 floor(max(abs(scan))*master.fsample),'coeff');
0153 [max_corr_idx,max_corr] = find_delta_corr(corr,lag,penalty_tau,penalty_n);
0154
0155 if try_polarity
0156 [corr_m,lag_m]=xcorr(master.trial{1}(1,:), (-1).* slave.trial{1}(1,:),...
0157 floor(max(abs(scan))*master.fsample),'coeff');
0158 [max_corr_idx_m,max_corr_m] = find_delta_corr(corr_m,lag_m,penalty_tau,penalty_n);
0159 if max_corr_m > max_corr
0160 slave.trial{1}=(-1).* slave.trial{1};
0161 max_corr_idx = max_corr_idx_m;
0162 max_corr = max_corr_m;
0163 lag=lag_m;
0164 end
0165 end
0166
0167 slave_delta_t = lag(max_corr_idx)/master.fsample;
0168 slave.time{1} = slave.time{1} + slave_delta_t;
0169
0170 end
0171
0172
0173 function [max_corr_idx,max_corr] = find_delta_corr(corr,lag,penalty_tau,penalty_n)
0174 if ~isempty(penalty_tau)
0175 [~,max_corr_idx]=max(corr./((penalty_tau*master.fsample)^penalty_n + abs(lag).^penalty_n));
0176 max_corr=corr(max_corr_idx);
0177 else
0178 [max_corr,max_corr_idx]=max(corr);
0179 end
0180
0181 end