rmeddis@38
|
1 function [LP_SACF, BFlist, SACF, ACFboundaryValue] = ...
|
rmeddis@38
|
2 filteredSACF(inputSignalMatrix, dt, BFlist, params)
|
rmeddis@38
|
3 % UTIL_filteredSACF computes within-channel, running autocorrelations (ACFs)
|
rmeddis@38
|
4 % and finds the running sum across channels (SACF).
|
rmeddis@38
|
5 % The SACF is smoothed to give the 'p function' (LP_SACF).
|
rmeddis@38
|
6 % (Balaguer-Ballestera, E. Denham, S.L. and Meddis, R. (2008))
|
rmeddis@38
|
7 %
|
rmeddis@0
|
8 %
|
rmeddis@0
|
9 % INPUT
|
rmeddis@0
|
10 % inputSignalMatrix: a matrix (channel x time) of AN activity
|
rmeddis@38
|
11 % dt: the signal sampling interval in seconds
|
rmeddis@38
|
12 % BFlist: channel BFs
|
rmeddis@38
|
13 %
|
rmeddis@0
|
14 % params: a list of parmeters to guide the computation:
|
rmeddis@38
|
15 % params.lags: an array of lags to be computed (seconds)
|
rmeddis@38
|
16 % params.acfTau: time constant (sec) of the running ACF
|
rmeddis@38
|
17 % if acfTau>1 it is assumed that Wiegrebe'SACF method
|
rmeddis@0
|
18 % for calculating tau is to be used (see below)
|
rmeddis@38
|
19 % params.Lambda: smoothing factor for the SACF
|
rmeddis@38
|
20 % params.lagsProcedure: strategies for omitting some lags.
|
rmeddis@38
|
21 % (Options: 'useAllLags' or 'omitShortLags')
|
rmeddis@38
|
22 % params.usePressnitzer applies lower weights longer lags
|
rmeddis@38
|
23 % params.plotACFs (=1) creates movie of ACF matrix (optional)
|
rmeddis@0
|
24 %
|
rmeddis@0
|
25 % OUTPUT
|
rmeddis@38
|
26 % LP_SACF: LP_SACF function (time x lags), a low pass filtered version of SACF.
|
rmeddis@38
|
27 % method: updated version of input 'method' (to include lags used)
|
rmeddis@38
|
28 % SACF: (time x lags)
|
rmeddis@38
|
29 %
|
rmeddis@38
|
30 % Notes:
|
rmeddis@38
|
31 % ACFboundaryValue refers to segmented evaluation and is currently not
|
rmeddis@38
|
32 % supported. However the code may be useful later when this function
|
rmeddis@38
|
33 % is incorporated into MAP1_14.
|
rmeddis@0
|
34
|
rmeddis@0
|
35 %%
|
rmeddis@38
|
36 global savedInputSignal
|
rmeddis@38
|
37
|
rmeddis@38
|
38 ACFboundaryValue=[];
|
rmeddis@38
|
39
|
rmeddis@0
|
40 [nChannels inputLength]= size(inputSignalMatrix);
|
rmeddis@0
|
41
|
rmeddis@38
|
42 % create ACF movie
|
rmeddis@0
|
43 if isfield(params, 'plotACFs') && params.plotACFs==1
|
rmeddis@0
|
44 plotACF=1;
|
rmeddis@38
|
45 signalTime=dt:dt:dt*length(savedInputSignal);
|
rmeddis@0
|
46 else
|
rmeddis@0
|
47 plotACF=0; % default
|
rmeddis@0
|
48 end
|
rmeddis@38
|
49 params.plotACFsInterval=round(params.plotACFsInterval/dt);
|
rmeddis@0
|
50
|
rmeddis@0
|
51 if isfield(params,'lags')
|
rmeddis@0
|
52 lags=params.lags;
|
rmeddis@0
|
53 else
|
rmeddis@0
|
54 lags=params.minLag:params.lagStep:params.maxLag;
|
rmeddis@0
|
55 end
|
rmeddis@0
|
56 nLags=length(lags);
|
rmeddis@0
|
57
|
rmeddis@0
|
58 % Establish lag weightsaccording to params.lagsProcedure
|
rmeddis@0
|
59 % lagWeights allow some lag computations to be ignored
|
rmeddis@0
|
60 % lagWeights is a (channel x lag) matrix;
|
rmeddis@0
|
61 if isfield(params, 'lagsProcedure')
|
rmeddis@0
|
62 lagsProcedure=params.lagsProcedure;
|
rmeddis@0
|
63 else
|
rmeddis@0
|
64 lagsProcedure='useAllLags'; % default
|
rmeddis@0
|
65 end
|
rmeddis@38
|
66
|
rmeddis@0
|
67 lagWeights=ones(nChannels,nLags);
|
rmeddis@0
|
68 switch lagsProcedure
|
rmeddis@0
|
69 case 'useAllLags'
|
rmeddis@38
|
70 % no action required lagWeights set above
|
rmeddis@0
|
71 case 'omitShortLags'
|
rmeddis@0
|
72 % remove lags that are short relative to CF
|
rmeddis@0
|
73 allLags=repmat(lags,nChannels,1);
|
rmeddis@0
|
74 allCFs=repmat(BFlist',1,nLags);
|
rmeddis@0
|
75 criterionForOmittingLags=1./(params.criterionForOmittingLags*allCFs);
|
rmeddis@0
|
76 idx= allLags < criterionForOmittingLags; % ignore these lags
|
rmeddis@0
|
77 lagWeights(idx)=0;
|
rmeddis@0
|
78 otherwise
|
rmeddis@38
|
79 error ('ACF: params.lagProcedure not recognised')
|
rmeddis@0
|
80 end
|
rmeddis@0
|
81
|
rmeddis@0
|
82
|
rmeddis@38
|
83 % Establish matrix of lag time constants
|
rmeddis@0
|
84 % these are all the same if tau<1
|
rmeddis@0
|
85 % if acfTau>1, it is assumed that we are using the Wiegrebe method
|
rmeddis@0
|
86 % and a different decay factor is applied to each lag
|
rmeddis@0
|
87 % ( i.e., longer lags have longer time constants)
|
rmeddis@0
|
88 acfTau=params.acfTau;
|
rmeddis@0
|
89 if acfTau<1 % all taus are the same
|
rmeddis@0
|
90 acfTaus=repmat(acfTau, 1, nLags);
|
rmeddis@0
|
91 acfDecayFactors=ones(size(lags)).*exp(-dt/acfTau);
|
rmeddis@38
|
92 else % use Wiegrebe method: tau= 2*lag (for example)
|
rmeddis@0
|
93 WiegrebeFactor=acfTau;
|
rmeddis@0
|
94 acfTaus=WiegrebeFactor*lags;
|
rmeddis@0
|
95 idx= acfTaus<0.0025; acfTaus(idx)=0.0025;
|
rmeddis@0
|
96 acfDecayFactors= exp(-dt./(acfTaus));
|
rmeddis@0
|
97 end
|
rmeddis@0
|
98 % make acfDecayFactors into a (channels x lags) matrix for speedy computation
|
rmeddis@0
|
99 acfDecayFactors=repmat(acfDecayFactors,nChannels, 1);
|
rmeddis@0
|
100
|
rmeddis@38
|
101 % LP_SACF function lowpass filter decay (only one value needed)
|
rmeddis@0
|
102 pDecayFactor=exp(-dt/params.lambda);
|
rmeddis@0
|
103
|
rmeddis@0
|
104 % ACF
|
rmeddis@0
|
105 % lagPointers is a list of pointers relative to 'time now'
|
rmeddis@0
|
106 lagPointers=round(lags/dt);
|
rmeddis@0
|
107 if max(lagPointers)+1>inputLength
|
rmeddis@0
|
108 error([' filteredSACF: not enough signal to evaluate ACF. Max(lag)= ' num2str(max(lags))])
|
rmeddis@0
|
109 end
|
rmeddis@0
|
110
|
rmeddis@0
|
111
|
rmeddis@38
|
112 LP_SACF=zeros(nLags,inputLength+1); % LP_SACF must match segment length +1
|
rmeddis@38
|
113 SACF=zeros(nLags,inputLength);
|
rmeddis@38
|
114 method=[]; % legacy programming
|
rmeddis@0
|
115 if ~isfield(method,'segmentNumber') || method.segmentNumber==1
|
rmeddis@38
|
116 ACF=zeros(nChannels,nLags);
|
rmeddis@0
|
117 % create a runup buffer of signal
|
rmeddis@0
|
118 buffer= zeros(nChannels, max(lagPointers));
|
rmeddis@0
|
119 else
|
rmeddis@38
|
120 % ACFboundaryValue picks up from a previous calculation
|
rmeddis@38
|
121 ACF=params.ACFboundaryValue{1};
|
rmeddis@38
|
122 % NB first value is last value of previous segment
|
rmeddis@38
|
123 LP_SACF(: , 1)=params.ACFboundaryValue{2};
|
rmeddis@38
|
124 buffer=params.ACFboundaryValue{3};
|
rmeddis@0
|
125 end
|
rmeddis@0
|
126 inputSignalMatrix=[buffer inputSignalMatrix];
|
rmeddis@0
|
127 [nChannels inputLength]= size(inputSignalMatrix);
|
rmeddis@0
|
128
|
rmeddis@0
|
129 timeCounter=0; biggestSACF=0;
|
rmeddis@0
|
130 for timePointer= max(lagPointers)+1:inputLength
|
rmeddis@38
|
131 % ACF is a continuously changing channels x lags matrix
|
rmeddis@0
|
132 % Only the current value is stored
|
rmeddis@38
|
133 % SACF is the vertical sum of ACFs; all values are kept and returned
|
rmeddis@38
|
134 % LP_SACF is the smoothed version of SACF:all values are kept and returned
|
rmeddis@0
|
135 % lagWeights emphasise some BF/lag combinations and ignore others
|
rmeddis@0
|
136 % NB time now begins at the longest lag.
|
rmeddis@38
|
137 % E.g. if max(lags) is .04 then this is when the ACf will begin (?).
|
rmeddis@38
|
138
|
rmeddis@0
|
139 % This is the ACF calculation
|
rmeddis@0
|
140 timeCounter=timeCounter+1;
|
rmeddis@38
|
141 ACF= (repmat(inputSignalMatrix(:, timePointer), 1, nLags) .* ...
|
rmeddis@38
|
142 inputSignalMatrix(:, timePointer-lagPointers)).*...
|
rmeddis@38
|
143 lagWeights *dt + ACF.* acfDecayFactors;
|
rmeddis@38
|
144 x=(mean(ACF,1)./acfTaus)';
|
rmeddis@38
|
145 SACF(:,timeCounter)=x;
|
rmeddis@0
|
146
|
rmeddis@38
|
147 % smoothed version of SACF
|
rmeddis@38
|
148 LP_SACF(:,timeCounter+1)=SACF(:,timeCounter)* (1-pDecayFactor) + ...
|
rmeddis@38
|
149 LP_SACF(:,timeCounter)* pDecayFactor;
|
rmeddis@38
|
150
|
rmeddis@38
|
151 % plot ACF at intervals if requested to do so
|
rmeddis@38
|
152 if plotACF && ~mod(timePointer,params.plotACFsInterval) && ...
|
rmeddis@38
|
153 timePointer*dt>3*max(lags)
|
rmeddis@38
|
154 figure(89), clf
|
rmeddis@38
|
155 % plot ACFs one per channel
|
rmeddis@38
|
156 subplot(2,1,1)
|
rmeddis@38
|
157 UTIL_cascadePlot(ACF, lags)
|
rmeddis@38
|
158 title(['running ACF at ' num2str(dt*timePointer,'%5.3f') ' s'])
|
rmeddis@38
|
159 ylabel('channel BF'), xlabel('period (lag, ms)')
|
rmeddis@38
|
160 set(gca,'ytickLabel',[])
|
rmeddis@38
|
161
|
rmeddis@38
|
162 % plot SACF
|
rmeddis@38
|
163 subplot(4,1,3), cla
|
rmeddis@38
|
164 plotSACF=SACF(:,timeCounter)-min(SACF(:,timeCounter));
|
rmeddis@38
|
165 plot(lags*1000, plotSACF, 'k')
|
rmeddis@38
|
166 biggestSACF=max(biggestSACF, max(plotSACF));
|
rmeddis@38
|
167 if biggestSACF>0, ylim([0 biggestSACF]), else ylim([0 1]), end
|
rmeddis@38
|
168 ylabel('SACF'), set(gca,'ytickLabel',[])
|
rmeddis@38
|
169 % xlim([min(lags*1000) max(lags*1000)])
|
rmeddis@38
|
170
|
rmeddis@38
|
171 % plot signal
|
rmeddis@38
|
172 subplot(4,1,4)
|
rmeddis@38
|
173 plot(signalTime, savedInputSignal, 'k'), hold on
|
rmeddis@38
|
174 xlim([0 max(signalTime)])
|
rmeddis@38
|
175 a= ylim;
|
rmeddis@38
|
176 % mark cursor on chart to indicate progress
|
rmeddis@0
|
177 time=timePointer*dt;
|
rmeddis@38
|
178 plot([time time], [a(1) a(1)+(a(2)-a(1))/4], 'r', 'linewidth', 5)
|
rmeddis@0
|
179 pause(params.plotMoviePauses)
|
rmeddis@0
|
180 end
|
rmeddis@0
|
181 end
|
rmeddis@38
|
182 LP_SACF=LP_SACF(:,1:end-1); % correction for speed up above
|
rmeddis@0
|
183
|
rmeddis@0
|
184 % Pressnitzer weights
|
rmeddis@0
|
185 if ~isfield(params, 'usePressnitzer'), params.usePressnitzer=0; end
|
rmeddis@0
|
186 if params.usePressnitzer
|
rmeddis@38
|
187 [a nTimePoints]=size(LP_SACF);
|
rmeddis@0
|
188 % higher pitches get higher weights
|
rmeddis@0
|
189 % PressnitzerWeights=repmat(min(lags)./lags,nTimePoints,1);
|
rmeddis@0
|
190 % weaker weighting
|
rmeddis@0
|
191 PressnitzerWeights=repmat((min(lags)./lags).^0.5, nTimePoints,1);
|
rmeddis@38
|
192 LP_SACF=LP_SACF.*PressnitzerWeights';
|
rmeddis@38
|
193 SACF=SACF.*PressnitzerWeights';
|
rmeddis@0
|
194 end
|
rmeddis@0
|
195
|
rmeddis@0
|
196 % wrap up
|
rmeddis@38
|
197 % BoundaryValue is legacy programming used in segmented model (keep)
|
rmeddis@38
|
198 ACFboundaryValue{1}=ACF(:,end-nLags+1:end);
|
rmeddis@38
|
199 ACFboundaryValue{2}=LP_SACF(:,end);
|
rmeddis@0
|
200 % save signal buffer for next segment
|
rmeddis@38
|
201 ACFboundaryValue{3} = inputSignalMatrix(:, end-max(lagPointers)+1 : end);
|