12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- function [ latTimes, lsqCurveTimes, lsqFittedCurve, lsqFittedParam, lsqCurveFitErr] = computeLatencies_offset( MuaeData, MuaeTimes, lsqTimeRes )
- % COMPUTE LATENCY function written by Demetrio Ferro demetrio.ferro@iit.it
- % Based on (Roelfsema, Tolboom, Khayat, Neuron 2007).
- % developed for (Ferro, van Kempen, Boyd, Panzeri, Thiele, PNAS 2021)
- %
- % The function computes Latency indices base don MUAes fitted by cumulative
- % gaussian function with 5 (degrees of freedom) parameters (mu, sigma, alpha, c, d).
- % The best fit is assigned based on Least Squares (LS) of iterative estimation.
- %
- % Inputs
- % MuaeData [NxT] Multi-channel (N channels) MUAes in time (T points)
- % MuaeTimes [1xT] Timestamps of MUAe recordings (T points)
- % lsqTimeRes [1x1] Resolution of time series for Latency estimation
- %
- % Outputs
- % latTimes [1xN] Latency indices at t*:={f(t*)=1/3*max(f(t)),t in MuaeTimes}
- % lsqCurveTimes [1x(T*lsqTimeRes)] Timestamps of fitted curves
- % lsqFittedCurve [Nx(T*lsqTimeRes)] Fitted curves at LS parameters
- % lsqFittedParam [Nx5] LS parameters
- % lsqFitErr [1xN] LS error of the fit
- %MuaeData=yt;
- %MuaeTimes=ts*1e3;
- %lsqTimeRes=10;
- [N,T]=size(MuaeData);
- latTimes=zeros(1,N);
- lsqCurveTimes=linspace(MuaeTimes(1),MuaeTimes(end),ceil(T*lsqTimeRes));
- lsqFittedCurve=zeros(N,ceil(T*lsqTimeRes));
- lsqFittedParam=zeros(N,6);
- lsqCurveFitErr=zeros(1,N);
- % LsqParam0 = [mu, sigma, alpha, c, d, y_off];
- % LsqParam0 is the starting point for the fitting routine;
- lsqParam0=[0 0 0 0 0 0]; % starts with naive values for V1;
- % Ex-Gaussian Function to fit
- exGfunToFit = @(p,tdata)(p(5)*exp(p(1)*p(3)+0.5*p(2)^2*p(3)^2-p(3)*tdata)...
- .*normcdf(tdata,p(1)+p(2)^2*p(3),p(2))...
- +p(4)*normcdf(tdata,p(1),p(2))+p(6));
- movMeanResolution=5;
- mu0ValuesOrigVec=MuaeTimes;
- options = optimoptions('lsqcurvefit','Display','off');
- % LOOP OVER CHANNELS
- for ch=1:N
- MuaeDataMovMean=movmean(MuaeData(ch,:),movMeanResolution);
- IntMuaeDataMM=interp1(MuaeTimes,MuaeDataMovMean,mu0ValuesOrigVec);
- DiffMuaeDataMM=diff([IntMuaeDataMM IntMuaeDataMM(end)]);
- % EMPIRICAL SETTING: TAKE ONLY POSITIVE SLOPES TO SPEED UP
- mu0ValuesVec=mu0ValuesOrigVec(DiffMuaeDataMM>=0); %.2*min(DiffMuaeDataMM));
- %MuaeDataMovMean(1)
-
- lsqMuErrMovMean=-1*ones(1,length(mu0ValuesVec));
- lsqFittedParamMovMean=zeros(length(mu0ValuesVec),6);
-
- % ATTENTION: FIT THE MOVMEAN THEN USE THE MIN MSE PARAMETERS
- for muIdx=1:length(mu0ValuesVec)
- lsqParam0(1)=mu0ValuesVec(muIdx);
- lsqParam0(6)=MuaeDataMovMean(1);
- try
- lsqFittedParamMovMean(muIdx,:)=lsqcurvefit(exGfunToFit,lsqParam0,MuaeTimes,MuaeDataMovMean,[],[],options);
- lsqMuErrMovMean(muIdx)=norm(feval(exGfunToFit,lsqFittedParamMovMean(muIdx,:),MuaeTimes)-MuaeData(ch,:));
- catch
- warning('lsqcurvefit does not converge for the parameters chosen!');
- end
- end
- % Remove non-tested mu parameters
- lsqMuErrMovMean(lsqMuErrMovMean==-1)=[];
- lsqFittedParamMovMean(lsqMuErrMovMean==-1)=[];
-
- [~,lsqMinMuErrIdxMovMean]=min(lsqMuErrMovMean);
-
- lsqFittedCurve(ch,:)=feval(exGfunToFit,lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:),lsqCurveTimes);
- lsqCurveFitErr(ch)=norm(interp1(lsqCurveTimes,lsqFittedCurve(ch,:),MuaeTimes)-MuaeData(ch,:));
- lsqFittedParam(ch,:)=lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:);
-
- latIndex=find(lsqFittedCurve(ch,:)>=(lsqFittedParam(ch,6)+1/3*max(lsqFittedCurve(ch,:)-lsqFittedParam(ch,6))),1,'first');
- if ~isempty(latIndex)
- latTimes(ch)=lsqCurveTimes(latIndex);
- else
- latTimes(ch)=NaN;
- end
-
- end
- end
|