computeLatencies_offset.m 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. function [ latTimes, lsqCurveTimes, lsqFittedCurve, lsqFittedParam, lsqCurveFitErr] = computeLatencies_offset( MuaeData, MuaeTimes, lsqTimeRes )
  2. % COMPUTE LATENCY function written by Demetrio Ferro demetrio.ferro@iit.it
  3. % Based on (Roelfsema, Tolboom, Khayat, Neuron 2007).
  4. % developed for (Ferro, van Kempen, Boyd, Panzeri, Thiele, PNAS 2021)
  5. %
  6. % The function computes Latency indices base don MUAes fitted by cumulative
  7. % gaussian function with 5 (degrees of freedom) parameters (mu, sigma, alpha, c, d).
  8. % The best fit is assigned based on Least Squares (LS) of iterative estimation.
  9. %
  10. % Inputs
  11. % MuaeData [NxT] Multi-channel (N channels) MUAes in time (T points)
  12. % MuaeTimes [1xT] Timestamps of MUAe recordings (T points)
  13. % lsqTimeRes [1x1] Resolution of time series for Latency estimation
  14. %
  15. % Outputs
  16. % latTimes [1xN] Latency indices at t*:={f(t*)=1/3*max(f(t)),t in MuaeTimes}
  17. % lsqCurveTimes [1x(T*lsqTimeRes)] Timestamps of fitted curves
  18. % lsqFittedCurve [Nx(T*lsqTimeRes)] Fitted curves at LS parameters
  19. % lsqFittedParam [Nx5] LS parameters
  20. % lsqFitErr [1xN] LS error of the fit
  21. %MuaeData=yt;
  22. %MuaeTimes=ts*1e3;
  23. %lsqTimeRes=10;
  24. [N,T]=size(MuaeData);
  25. latTimes=zeros(1,N);
  26. lsqCurveTimes=linspace(MuaeTimes(1),MuaeTimes(end),ceil(T*lsqTimeRes));
  27. lsqFittedCurve=zeros(N,ceil(T*lsqTimeRes));
  28. lsqFittedParam=zeros(N,6);
  29. lsqCurveFitErr=zeros(1,N);
  30. % LsqParam0 = [mu, sigma, alpha, c, d, y_off];
  31. % LsqParam0 is the starting point for the fitting routine;
  32. lsqParam0=[0 0 0 0 0 0]; % starts with naive values for V1;
  33. % Ex-Gaussian Function to fit
  34. exGfunToFit = @(p,tdata)(p(5)*exp(p(1)*p(3)+0.5*p(2)^2*p(3)^2-p(3)*tdata)...
  35. .*normcdf(tdata,p(1)+p(2)^2*p(3),p(2))...
  36. +p(4)*normcdf(tdata,p(1),p(2))+p(6));
  37. movMeanResolution=5;
  38. mu0ValuesOrigVec=MuaeTimes;
  39. options = optimoptions('lsqcurvefit','Display','off');
  40. % LOOP OVER CHANNELS
  41. for ch=1:N
  42. MuaeDataMovMean=movmean(MuaeData(ch,:),movMeanResolution);
  43. IntMuaeDataMM=interp1(MuaeTimes,MuaeDataMovMean,mu0ValuesOrigVec);
  44. DiffMuaeDataMM=diff([IntMuaeDataMM IntMuaeDataMM(end)]);
  45. % EMPIRICAL SETTING: TAKE ONLY POSITIVE SLOPES TO SPEED UP
  46. mu0ValuesVec=mu0ValuesOrigVec(DiffMuaeDataMM>=0); %.2*min(DiffMuaeDataMM));
  47. %MuaeDataMovMean(1)
  48. lsqMuErrMovMean=-1*ones(1,length(mu0ValuesVec));
  49. lsqFittedParamMovMean=zeros(length(mu0ValuesVec),6);
  50. % ATTENTION: FIT THE MOVMEAN THEN USE THE MIN MSE PARAMETERS
  51. for muIdx=1:length(mu0ValuesVec)
  52. lsqParam0(1)=mu0ValuesVec(muIdx);
  53. lsqParam0(6)=MuaeDataMovMean(1);
  54. try
  55. lsqFittedParamMovMean(muIdx,:)=lsqcurvefit(exGfunToFit,lsqParam0,MuaeTimes,MuaeDataMovMean,[],[],options);
  56. lsqMuErrMovMean(muIdx)=norm(feval(exGfunToFit,lsqFittedParamMovMean(muIdx,:),MuaeTimes)-MuaeData(ch,:));
  57. catch
  58. warning('lsqcurvefit does not converge for the parameters chosen!');
  59. end
  60. end
  61. % Remove non-tested mu parameters
  62. lsqMuErrMovMean(lsqMuErrMovMean==-1)=[];
  63. lsqFittedParamMovMean(lsqMuErrMovMean==-1)=[];
  64. [~,lsqMinMuErrIdxMovMean]=min(lsqMuErrMovMean);
  65. lsqFittedCurve(ch,:)=feval(exGfunToFit,lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:),lsqCurveTimes);
  66. lsqCurveFitErr(ch)=norm(interp1(lsqCurveTimes,lsqFittedCurve(ch,:),MuaeTimes)-MuaeData(ch,:));
  67. lsqFittedParam(ch,:)=lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:);
  68. latIndex=find(lsqFittedCurve(ch,:)>=(lsqFittedParam(ch,6)+1/3*max(lsqFittedCurve(ch,:)-lsqFittedParam(ch,6))),1,'first');
  69. if ~isempty(latIndex)
  70. latTimes(ch)=lsqCurveTimes(latIndex);
  71. else
  72. latTimes(ch)=NaN;
  73. end
  74. end
  75. end