TEST remote repository
This commit is contained in:
parent
5491738fff
commit
ba8df083e2
7
AutocorrRegSymmSteps.m
Normal file
7
AutocorrRegSymmSteps.m
Normal file
@ -0,0 +1,7 @@
|
||||
% autocorrelation for step symmetrie
|
||||
function [c, lags] = AutocorrRegSymmSteps(x)
|
||||
[c,lags] = xcov(x,200,'unbiased');
|
||||
c = c/c(lags==0);
|
||||
c = c(lags>=0);
|
||||
lags = lags(lags>=0);
|
||||
|
43
AutocorrStrides.m
Normal file
43
AutocorrStrides.m
Normal file
@ -0,0 +1,43 @@
|
||||
function [ResultStruct] = AutocorrStrides(data,FS, StrideTimeRange,ResultStruct);
|
||||
|
||||
%% Stride-times measures
|
||||
% Stride time and regularity from auto correlation (according to Moe-Nilssen and Helbostad, Estimation of gait cycle characteristics by trunk accelerometry. J Biomech, 2004. 37: 121-6.)
|
||||
RangeStart = round(FS*StrideTimeRange(1));
|
||||
RangeEnd = round(FS*StrideTimeRange(2));
|
||||
[AutocorrResult,Lags]=xcov(data,RangeEnd,'unbiased');
|
||||
AutocorrSum = sum(AutocorrResult(:,[1 5 9]),2); % This sum is independent of sensor re-orientation, as long as axes are kept orthogonal
|
||||
AutocorrResult2= [AutocorrResult(:,[1 5 9]),AutocorrSum];
|
||||
IXRange = (numel(Lags)-(RangeEnd-RangeStart)):numel(Lags);
|
||||
% check that autocorrelations are positive for any direction,
|
||||
|
||||
AutocorrPlusTrans = AutocorrResult+AutocorrResult(:,[1 4 7 2 5 8 3 6 9]);
|
||||
IXRangeNew = IXRange( ...
|
||||
AutocorrPlusTrans(IXRange,1) > 0 ...
|
||||
& prod(AutocorrPlusTrans(IXRange,[1 5]),2) > prod(AutocorrPlusTrans(IXRange,[2 4]),2) ...
|
||||
& prod(AutocorrPlusTrans(IXRange,[1 5 9]),2) + prod(AutocorrPlusTrans(IXRange,[2 6 7]),2) + prod(AutocorrPlusTrans(IXRange,[3 4 8]),2) ...
|
||||
> prod(AutocorrPlusTrans(IXRange,[1 6 8]),2) + prod(AutocorrPlusTrans(IXRange,[2 4 9]),2) + prod(AutocorrPlusTrans(IXRange,[3 5 7]),2) ...
|
||||
);
|
||||
if isempty(IXRangeNew)
|
||||
StrideTimeSamples = Lags(IXRange(AutocorrSum(IXRange)==max(AutocorrSum(IXRange)))); % to be used in other estimations
|
||||
StrideTimeSeconds = nan;
|
||||
ResultStruct.StrideTimeSamples = StrideTimeSamples;
|
||||
ResultStruct.StrideTimeSeconds = StrideTimeSeconds;
|
||||
|
||||
else
|
||||
StrideTimeSamples = Lags(IXRangeNew(AutocorrSum(IXRangeNew)==max(AutocorrSum(IXRangeNew))));
|
||||
StrideRegularity = AutocorrResult2(Lags==StrideTimeSamples,:)./AutocorrResult2(Lags==0,:); % Moe-Nilssen&Helbostatt,2004
|
||||
RelativeStrideVariability = 1-StrideRegularity;
|
||||
StrideTimeSeconds = StrideTimeSamples/FS;
|
||||
|
||||
ResultStruct.StrideRegularity_V = StrideRegularity(1);
|
||||
ResultStruct.StrideRegularity_ML = StrideRegularity(2);
|
||||
ResultStruct.StrideRegularity_AP = StrideRegularity(3);
|
||||
ResultStruct.StrideRegularity_All = StrideRegularity(4);
|
||||
ResultStruct.RelativeStrideVariability_V = RelativeStrideVariability(1);
|
||||
ResultStruct.RelativeStrideVariability_ML = RelativeStrideVariability(2);
|
||||
ResultStruct.RelativeStrideVariability_AP = RelativeStrideVariability(3);
|
||||
ResultStruct.RelativeStrideVariability_All = RelativeStrideVariability(4);
|
||||
ResultStruct.StrideTimeSamples = StrideTimeSamples;
|
||||
ResultStruct.StrideTimeSeconds = StrideTimeSeconds;
|
||||
|
||||
end
|
118
CalcMaxLyapConvGait.m
Normal file
118
CalcMaxLyapConvGait.m
Normal file
@ -0,0 +1,118 @@
|
||||
function [L_Estimate,ExtraArgsOut] = CalcMaxLyapConvGait(ThisTimeSeries,FS,ExtraArgsIn)
|
||||
if nargin > 2
|
||||
if isfield(ExtraArgsIn,'J')
|
||||
J=ExtraArgsIn.J;
|
||||
end
|
||||
if isfield(ExtraArgsIn,'m')
|
||||
m=ExtraArgsIn.m;
|
||||
end
|
||||
if isfield(ExtraArgsIn,'FitWinLen')
|
||||
FitWinLen=ExtraArgsIn.FitWinLen;
|
||||
end
|
||||
end
|
||||
|
||||
%% Initialize output args
|
||||
L_Estimate=nan;ExtraArgsOut.Divergence=nan;ExtraArgsOut.J=nan;ExtraArgsOut.m=nan;ExtraArgsOut.FitWinLen=nan;
|
||||
|
||||
%% Some checks
|
||||
% predefined J and m should not be NaN or Inf
|
||||
if (exist('J','var') && ~isempty(J) && ~isfinite(J)) || (exist('m','var') && ~isempty(m) && ~isfinite(m))
|
||||
warning('Predefined J and m cannot be NaN or Inf');
|
||||
return;
|
||||
end
|
||||
% multidimensional time series need predefined J and m
|
||||
if size(ThisTimeSeries,2) > 1 && (~exist('J','var') || ~exist('m','var') || isempty(J) || isempty(m))
|
||||
warning('Multidimensional time series needs predefined J and m, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
%Check that there are no NaN or Inf values in the TimeSeries
|
||||
if any(~isfinite(ThisTimeSeries(:)))
|
||||
warning('Time series contains NaN or Inf, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
%Check that there is variation in the TimeSeries
|
||||
if ~(nanstd(ThisTimeSeries) > 0)
|
||||
warning('Time series is constant, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
|
||||
%% Determine FitWinLen (=cycle time) of ThisTimeSeries
|
||||
if ~exist('FitWinLen','var') || isempty(FitWinLen)
|
||||
if size(ThisTimeSeries,2)>1
|
||||
for dim=1:size(ThisTimeSeries,2),
|
||||
[Pd(:,dim),F] = pwelch(detrend(ThisTimeSeries(:,dim)),[],[],[],FS);
|
||||
end
|
||||
P = sum(Pd,2);
|
||||
else
|
||||
[P,F] = pwelch(detrend(ThisTimeSeries),[],[],[],FS);
|
||||
end
|
||||
MeanF = sum(P.*F)./sum(P);
|
||||
CycleTime = 1/MeanF;
|
||||
FitWinLen = round(CycleTime*FS);
|
||||
else
|
||||
CycleTime = FitWinLen/FS;
|
||||
end
|
||||
ExtraArgsOut.FitWinLen=FitWinLen;
|
||||
|
||||
%% Determine J
|
||||
if ~exist('J','var') || isempty(J)
|
||||
% Calculate mutual information and take first local minimum Tau as J
|
||||
bV = min(40,floor(sqrt(size(ThisTimeSeries,1))));
|
||||
tauVmax = FitWinLen;
|
||||
[mutMPro,cummutMPro,minmuttauVPro] = MutualInformationHisPro(ThisTimeSeries,(0:tauVmax),bV,1); % (xV,tauV,bV,flag)
|
||||
if isnan(minmuttauVPro)
|
||||
display(mutMPro);
|
||||
warning('minmuttauVPro is NaN. Consider increasing tauVmax.');
|
||||
return;
|
||||
end
|
||||
J=minmuttauVPro;
|
||||
end
|
||||
ExtraArgsOut.J=J;
|
||||
|
||||
%% Determine m
|
||||
if ~exist('m','var') || isempty(m)
|
||||
escape = 10;
|
||||
max_m = 20;
|
||||
max_fnnM = 0.02;
|
||||
mV = 0;
|
||||
fnnM = 1;
|
||||
for mV = 2:max_m % for m=1, FalseNearestNeighbors is slow and lets matlab close if N>500000
|
||||
fnnM = FalseNearestNeighborsSR(ThisTimeSeries,J,mV,escape,FS); % (xV,tauV,mV,escape,theiler)
|
||||
if fnnM <= max_fnnM || isnan(fnnM)
|
||||
break
|
||||
end
|
||||
end
|
||||
if fnnM <= max_fnnM
|
||||
m = mV;
|
||||
else
|
||||
warning('Too many false nearest neighbours');
|
||||
return;
|
||||
end
|
||||
end
|
||||
ExtraArgsOut.m=m;
|
||||
|
||||
%% Create state space based upon J and m
|
||||
N_ss = size(ThisTimeSeries,1)-(m-1)*J;
|
||||
StateSpace=nan(N_ss,m*size(ThisTimeSeries,2));
|
||||
for dim=1:size(ThisTimeSeries,2),
|
||||
for delay=1:m,
|
||||
StateSpace(:,(dim-1)*m+delay)=ThisTimeSeries((1:N_ss)'+(delay-1)*J,dim);
|
||||
end
|
||||
end
|
||||
|
||||
%% Parameters for Lyapunov
|
||||
WindowLen = floor(min(N_ss/5,10*FitWinLen));
|
||||
if WindowLen < FitWinLen
|
||||
warning('Not enough samples for Lyapunov estimation');
|
||||
return;
|
||||
end
|
||||
WindowLenSec=WindowLen/FS;
|
||||
|
||||
%% Calculate divergence
|
||||
Divergence=div_calc(StateSpace,WindowLenSec,FS,CycleTime,0);
|
||||
ExtraArgsOut.Divergence=Divergence;
|
||||
|
||||
%% Calculate slope of first FitWinLen samples of divergence curve
|
||||
p = polyfit((1:FitWinLen)/FS,Divergence(1:FitWinLen),1);
|
||||
L_Estimate = p(1);
|
||||
|
134
CalcMaxLyapWolfFixedEvolv.m
Normal file
134
CalcMaxLyapWolfFixedEvolv.m
Normal file
@ -0,0 +1,134 @@
|
||||
function [L_Estimate,ExtraArgsOut] = CalcMaxLyapWolfFixedEvolv(ThisTimeSeries,FS,ExtraArgsIn)
|
||||
|
||||
%% Description
|
||||
% This function calculates the maximum Lyapunov exponent from a time
|
||||
% series, based on the method described by Wolf et al. in
|
||||
% Wolf, A., et al., Determining Lyapunov exponents from a time series.
|
||||
% Physica D: 8 Nonlinear Phenomena, 1985. 16(3): p. 285-317.
|
||||
%
|
||||
% Input:
|
||||
% ThisTimeSeries: a vector or matrix with the time series
|
||||
% FS: sample frequency of the ThisTimeSeries
|
||||
% ExtraArgsIn: a struct containing optional input arguments
|
||||
% J (embedding delay)
|
||||
% m (embedding dimension)
|
||||
% Output:
|
||||
% L_Estimate: The Lyapunov estimate
|
||||
% ExtraArgsOut: a struct containing the additional output arguments
|
||||
% J (embedding delay)
|
||||
% m (embedding dimension)
|
||||
|
||||
%% Copyright
|
||||
% COPYRIGHT (c) 2012 Sietse Rispens, VU University Amsterdam
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
%% Author
|
||||
% Sietse Rispens
|
||||
|
||||
%% History
|
||||
% April 2012, initial version of CalcMaxLyapWolf
|
||||
% 23 October 2012, use fixed evolve time instead of adaptable
|
||||
|
||||
if nargin > 2
|
||||
if isfield(ExtraArgsIn,'J')
|
||||
J=ExtraArgsIn.J;
|
||||
end
|
||||
if isfield(ExtraArgsIn,'m')
|
||||
m=ExtraArgsIn.m;
|
||||
end
|
||||
end
|
||||
|
||||
%% Initialize output args
|
||||
L_Estimate=nan;ExtraArgsOut.J=nan;ExtraArgsOut.m=nan;
|
||||
|
||||
%% Some checks
|
||||
% predefined J and m should not be NaN or Inf
|
||||
if (exist('J','var') && ~isempty(J) && ~isfinite(J)) || (exist('m','var') && ~isempty(m) && ~isfinite(m))
|
||||
warning('Predefined J and m cannot be NaN or Inf');
|
||||
return;
|
||||
end
|
||||
% multidimensional time series need predefined J and m
|
||||
if size(ThisTimeSeries,2) > 1 && (~exist('J','var') || ~exist('m','var') || isempty(J) || isempty(m))
|
||||
warning('Multidimensional time series needs predefined J and m, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
%Check that there are no NaN or Inf values in the TimeSeries
|
||||
if any(~isfinite(ThisTimeSeries(:)))
|
||||
warning('Time series contains NaN or Inf, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
%Check that there is variation in the TimeSeries
|
||||
if ~(nanstd(ThisTimeSeries) > 0)
|
||||
warning('Time series is constant, can''t determine Lyapunov');
|
||||
return;
|
||||
end
|
||||
|
||||
%% Determine J
|
||||
if ~exist('J','var') || isempty(J)
|
||||
% Calculate mutual information and take first local minimum Tau as J
|
||||
bV = min(40,floor(sqrt(size(ThisTimeSeries,1))));
|
||||
tauVmax = 70;
|
||||
[mutMPro,cummutMPro,minmuttauVPro] = MutualInformationHisPro(ThisTimeSeries,(0:tauVmax),bV,1); % (xV,tauV,bV,flag)
|
||||
if isnan(minmuttauVPro)
|
||||
display(mutMPro);
|
||||
warning('minmuttauVPro is NaN. Consider increasing tauVmax.');
|
||||
return;
|
||||
end
|
||||
J=minmuttauVPro;
|
||||
end
|
||||
ExtraArgsOut.J=J;
|
||||
|
||||
%% Determine m
|
||||
if ~exist('m','var') || isempty(m)
|
||||
escape = 10;
|
||||
max_m = 20;
|
||||
max_fnnM = 0.02;
|
||||
mV = 0;
|
||||
fnnM = 1;
|
||||
for mV = 2:max_m % for m=1, FalseNearestNeighbors is slow and lets matlab close if N>500000
|
||||
fnnM = FalseNearestNeighborsSR(ThisTimeSeries,J,mV,escape,FS); % (xV,tauV,mV,escape,theiler)
|
||||
if fnnM <= max_fnnM || isnan(fnnM)
|
||||
break
|
||||
end
|
||||
end
|
||||
if fnnM <= max_fnnM
|
||||
m = mV;
|
||||
else
|
||||
warning('Too many false nearest neighbours');
|
||||
return;
|
||||
end
|
||||
end
|
||||
ExtraArgsOut.m=m;
|
||||
|
||||
%% Create state space based upon J and m
|
||||
N_ss = size(ThisTimeSeries,1)-(m-1)*J;
|
||||
StateSpace=nan(N_ss,m*size(ThisTimeSeries,2));
|
||||
for dim=1:size(ThisTimeSeries,2),
|
||||
for delay=1:m,
|
||||
StateSpace(:,(dim-1)*m+delay)=ThisTimeSeries((1:N_ss)'+(delay-1)*J,dim);
|
||||
end
|
||||
end
|
||||
|
||||
%% Parameters for Lyapunov estimation
|
||||
CriticalLen=J*m;
|
||||
max_dist = sqrt(sum(std(StateSpace).^2))/10;
|
||||
max_dist_mult = 5;
|
||||
min_dist = max_dist/2;
|
||||
max_theta = 0.3;
|
||||
evolv = J;
|
||||
|
||||
%% Calculate Lambda
|
||||
[L_Estimate]=div_wolf_fixed_evolv(StateSpace, FS, min_dist, max_dist, max_dist_mult, max_theta, CriticalLen, evolv);
|
||||
|
48
CalculateNonLinearParametersFunc.m
Normal file
48
CalculateNonLinearParametersFunc.m
Normal file
@ -0,0 +1,48 @@
|
||||
function [ResultStruct] = CalculateNonLinearParametersFunc(ResultStruct,dataAccCut,WindowLen,FS,Lyap_m,Lyap_FitWinLen,Sen_m,Sen_r)
|
||||
|
||||
%% Calculation non-linear parameters;
|
||||
|
||||
% cut into windows of size WindowLen
|
||||
N_Windows = floor(size(dataAccCut,1)/WindowLen);
|
||||
N_SkipBegin = ceil((size(dataAccCut,1)-N_Windows*WindowLen)/2);
|
||||
LyapunovWolf = nan(N_Windows,3);
|
||||
LyapunovRosen = nan(N_Windows,3);
|
||||
SE= nan(N_Windows,3);
|
||||
|
||||
for WinNr = 1:N_Windows;
|
||||
AccWin = dataAccCut(N_SkipBegin+(WinNr-1)*WindowLen+(1:WindowLen),:);
|
||||
for j=1:3
|
||||
[LyapunovWolf(WinNr,j),~] = CalcMaxLyapWolfFixedEvolv(AccWin(:,j),FS,struct('m',Lyap_m));
|
||||
[LyapunovRosen(WinNr,j),outpo] = CalcMaxLyapConvGait(AccWin(:,j),FS,struct('m',Lyap_m,'FitWinLen',Lyap_FitWinLen));
|
||||
[SE(WinNr,j)] = funcSampleEntropy(AccWin(:,j), Sen_m, Sen_r);
|
||||
% no correction for FS; SE does increase with higher FS but effect is considered negligible as range is small (98-104HZ). Might consider updating r to account for larger ranges.
|
||||
end
|
||||
end
|
||||
|
||||
LyapunovWolf = nanmean(LyapunovWolf,1);
|
||||
LyapunovRosen = nanmean(LyapunovRosen,1);
|
||||
SampleEntropy = nanmean(SE,1);
|
||||
|
||||
ResultStruct.LyapunovWolf_V = LyapunovWolf(1);
|
||||
ResultStruct.LyapunovWolf_ML = LyapunovWolf(2);
|
||||
ResultStruct.LyapunovWolf_AP = LyapunovWolf(3);
|
||||
ResultStruct.LyapunovRosen_V = LyapunovRosen(1);
|
||||
ResultStruct.LyapunovRosen_ML = LyapunovRosen(2);
|
||||
ResultStruct.LyapunovRosen_AP = LyapunovRosen(3);
|
||||
ResultStruct.SampleEntropy_V = SampleEntropy(1);
|
||||
ResultStruct.SampleEntropy_ML = SampleEntropy(2);
|
||||
ResultStruct.SampleEntropy_AP = SampleEntropy(3);
|
||||
|
||||
if isfield(ResultStruct,'StrideFrequency')
|
||||
LyapunovPerStrideWolf = LyapunovWolf/ResultStruct.StrideFrequency;
|
||||
LyapunovPerStrideRosen = LyapunovRosen/ResultStruct.StrideFrequency;
|
||||
end
|
||||
|
||||
ResultStruct.LyapunovPerStrideWolf_V = LyapunovPerStrideWolf(1);
|
||||
ResultStruct.LyapunovPerStrideWolf_ML = LyapunovPerStrideWolf(2);
|
||||
ResultStruct.LyapunovPerStrideWolf_AP = LyapunovPerStrideWolf(3);
|
||||
ResultStruct.LyapunovPerStrideRosen_V = LyapunovPerStrideRosen(1);
|
||||
ResultStruct.LyapunovPerStrideRosen_ML = LyapunovPerStrideRosen(2);
|
||||
ResultStruct.LyapunovPerStrideRosen_AP = LyapunovPerStrideRosen(3);
|
||||
|
||||
end
|
48
CalculateStrideParametersFunc.m
Normal file
48
CalculateStrideParametersFunc.m
Normal file
@ -0,0 +1,48 @@
|
||||
function [ResultStruct] = CalculateStrideParametersFunc(dataAccCut_filt,FS,ApplyRemoveSteps,dataAcc,dataAcc_filt,StrideTimeRange)
|
||||
|
||||
%% Calculate stride parameters
|
||||
ResultStruct = struct; % create empty struct
|
||||
|
||||
% Run function AutoCorrStrides, Outcomeparameters: StrideRegularity,RelativeStrideVariability,StrideTimeSamples,StrideTime
|
||||
[ResultStruct] = AutocorrStrides(dataAccCut_filt,FS, StrideTimeRange,ResultStruct);
|
||||
StrideTimeSamples = ResultStruct.StrideTimeSamples; % needed as input for other functions
|
||||
|
||||
% Calculate Step symmetry --> method 1
|
||||
ij = 1;
|
||||
dirSymm = [1,3]; % Gait Synmmetry is only informative in AP/V direction: See Tura A, Raggi M, Rocchi L, Cutti AG, Chiari L: Gait symmetry and regularity in transfemoral amputees assessed by trunk accelerations. J Neuroeng Rehabil 2010, 7:4.
|
||||
|
||||
for jk=1:length(dirSymm)
|
||||
[C, lags] = AutocorrRegSymmSteps(dataAccCut_filt(:,dirSymm(jk)));
|
||||
[Ad,p] = findpeaks(C,'MinPeakProminence',0.2, 'MinPeakHeight', 0.2);
|
||||
|
||||
if size(Ad,1) > 1
|
||||
Ad1 = Ad(1);
|
||||
Ad2 = Ad(2);
|
||||
GaitSymm(:,ij) = abs((Ad1-Ad2)/mean([Ad1+Ad2]))*100;
|
||||
else
|
||||
GaitSymm(:,ij) = NaN;
|
||||
end
|
||||
ij = ij +1;
|
||||
end
|
||||
% Save outcome in struct;
|
||||
ResultStruct.GaitSymm_V = GaitSymm(1);
|
||||
ResultStruct.GaitSymm_AP = GaitSymm(2);
|
||||
|
||||
% Calculate Step symmetry --> method 2
|
||||
[PksAndLocsCorrected] = StepcountFunc(dataAccCut_filt,StrideTimeSamples,FS);
|
||||
LocsSteps = PksAndLocsCorrected(2:2:end,2);
|
||||
|
||||
if rem(size(LocsSteps,1),2) == 0; % is number of steps is even
|
||||
LocsSteps2 = LocsSteps(1:2:end);
|
||||
else
|
||||
LocsSteps2 = LocsSteps(3:2:end);
|
||||
end
|
||||
|
||||
LocsSteps1 = LocsSteps(2:2:end);
|
||||
DiffLocs2 = diff(LocsSteps2);
|
||||
DiffLocs1 = diff(LocsSteps1);
|
||||
StepTime2 = DiffLocs2(1:end-1)/FS; % leave last one out because it is higher
|
||||
StepTime1 = DiffLocs1(1:end-1)/FS;
|
||||
SI = abs((2*(StepTime2-StepTime1))./(StepTime2+StepTime1))*100;
|
||||
ResultStruct.GaitSymmIndex = nanmean(SI);
|
||||
end
|
18
FilterandRealignFunc.m
Normal file
18
FilterandRealignFunc.m
Normal file
@ -0,0 +1,18 @@
|
||||
function [dataAcc, dataAcc_filt] = FilterandRealignFunc(inputData,FS,ApplyRealignment)
|
||||
|
||||
%% Filter and Realign Accdata
|
||||
|
||||
% Apply Realignment & Filter data
|
||||
|
||||
if ApplyRealignment % apply relignment as described in Rispens S, Pijnappels M, van Schooten K, Beek PJ, Daffertshofer A, van Die?n JH (2014).
|
||||
data = inputData(:, [3,2,4]); % reorder data to 1 = V; 2= ML, 3 = AP%
|
||||
% Consistency of gait characteristics as determined from acceleration data collected at different trunk locations. Gait Posture 2014;40(1):187-92.
|
||||
[RealignedAcc, ~] = RealignSensorSignalHRAmp(data, FS);
|
||||
dataAcc = RealignedAcc;
|
||||
[B,A] = butter(2,20/(FS/2),'low');
|
||||
dataAcc_filt = filtfilt(B,A,dataAcc);
|
||||
else % we asume tat data is already reorderd to 1 = V; 2= ML, 3 = AP in an earlier stage;
|
||||
[B,A] = butter(2,20/(FS/2),'low');
|
||||
dataAcc = inputData;
|
||||
dataAcc_filt = filtfilt(B,A,dataAcc);
|
||||
end
|
210
GaitOutcomesTrunkAccFuncIH.m
Normal file
210
GaitOutcomesTrunkAccFuncIH.m
Normal file
@ -0,0 +1,210 @@
|
||||
function [ResultStruct] = GaitOutcomesTrunkAccFuncIH(inputData,FS,LegLength,WindowLen,ApplyRealignment,ApplyRemoveSteps)
|
||||
|
||||
% DESCRIPTON: Trunk analysis of Iphone data without the need for step detection
|
||||
% CL Nov 2019
|
||||
% Adapted IH feb-april 2020
|
||||
|
||||
% koloms data of smartphone
|
||||
% 1st column is time data;
|
||||
% 2nd column is X, medio-lateral: + left, - right
|
||||
% 3rd column is Y, vertical: + downwards, - upwards
|
||||
% 4th column is Z, anterior- posterior : + forwards, - backwards
|
||||
|
||||
%% Input Trunk accelerations during locomotion in VT, ML, AP direction
|
||||
% InputData: Acceleration signal with time and accelerations in VT,ML and
|
||||
% AP direction.
|
||||
% FS: sample frequency of the Accdata
|
||||
% LegLength: length of the leg of the participant in m;
|
||||
|
||||
|
||||
%% Output
|
||||
% ResultStruct: structure coninting all outcome measured calculated
|
||||
% Spectral parameters, spatiotemporal gait parameters, non-linear
|
||||
% parameters
|
||||
% fields and subfields: include the multiple measurements of a subject
|
||||
|
||||
%% Literature
|
||||
% Richman & Moorman, 2000; [ sample entropy]
|
||||
% Bisi & Stagni Gait & Posture 2016, 47 (6) 37-42
|
||||
% Kavagnah et al., Eur J Appl Physiol 2005 94: 468?475; Human Movement Science 24(2005) 574?587 [ synchrony]
|
||||
% Moe-Nilsen J Biomech 2004 37, 121-126 [ autorcorrelation step regularity and symmetry
|
||||
% Kobsar et al. Gait & Posture 2014 39, 553?557 [ synchrony ]
|
||||
% Rispen et al; Gait & Posture 2014, 40, 187 - 192 [realignment axes]
|
||||
% Zijlstra & HofGait & Posture 2003 18,2, 1-10 [spatiotemporal gait variables]
|
||||
% Lamoth et al, 2002 [index of harmonicity]
|
||||
% Costa et al. 2003 Physica A 330 (2003) 5360 [ multiscale entropy]
|
||||
% Cignetti F, Decker LM, Stergiou N. Ann Biomed Eng. 2012
|
||||
% May;40(5):1122-30. doi: 10.1007/s10439-011-0474-3. Epub 2011 Nov 25. [
|
||||
% Wofl vs. Rosenstein Lyapunov]
|
||||
|
||||
|
||||
%% Settings
|
||||
Gr = 9.81; % Gravity acceleration, multiplication factor for accelerations
|
||||
StrideFreqEstimate = 1.00; % Used to set search for stride frequency from 0.5*StrideFreqEstimate until 2*StrideFreqEstimate
|
||||
StrideTimeRange = [0.2 4.0]; % Range to search for stride time (seconds)
|
||||
IgnoreMinMaxStrides = 0.10; % Number or percentage of highest&lowest values ignored for improved variability estimation
|
||||
N_Harm = 12; % Number of harmonics used for harmonic ratio, index of harmonicity and phase fluctuation
|
||||
LowFrequentPowerThresholds = ...
|
||||
[0.7 1.4]; % Threshold frequencies for estimation of low-frequent power percentages
|
||||
Lyap_m = 7; % Embedding dimension (used in Lyapunov estimations)
|
||||
Lyap_FitWinLen = round(60/100*FS); % Fitting window length (used in Lyapunov estimations Rosenstein's method)
|
||||
Sen_m = 5; % Dimension, the length of the subseries to be matched (used in sample entropy estimation)
|
||||
Sen_r = 0.3; % Tolerance, the maximum distance between two samples to qualify as match, relative to std of DataIn (used in sample entropy estimation)
|
||||
NStartEnd = [100];
|
||||
M = 5; % maximum template length
|
||||
ResultStruct = struct();
|
||||
|
||||
%% Filter and Realign Accdata
|
||||
|
||||
% Apply Realignment & Filter data
|
||||
|
||||
if ApplyRealignment % apply relignment as described in Rispens S, Pijnappels M, van Schooten K, Beek PJ, Daffertshofer A, van Die?n JH (2014).
|
||||
data = inputData(:, [3,2,4]); % reorder data to 1 = V; 2= ML, 3 = AP%
|
||||
% Consistency of gait characteristics as determined from acceleration data collected at different trunk locations. Gait Posture 2014;40(1):187-92.
|
||||
[RealignedAcc, ~] = RealignSensorSignalHRAmp(data, FS);
|
||||
dataAcc = RealignedAcc;
|
||||
[B,A] = butter(2,20/(FS/2),'low');
|
||||
dataAcc_filt = filtfilt(B,A,dataAcc);
|
||||
else % we asume tat data is already reorderd to 1 = V; 2= ML, 3 = AP in an earlier stage;
|
||||
[B,A] = butter(2,20/(FS/2),'low');
|
||||
dataAcc = inputData;
|
||||
dataAcc_filt = filtfilt(B,A,dataAcc);
|
||||
end
|
||||
|
||||
|
||||
%% Step dectection
|
||||
% Determines the number of steps in the signal so that the first 30 and last 30 steps in the signal can be removed
|
||||
|
||||
if ApplyRemoveSteps
|
||||
|
||||
% In order to run the step detection script we first need to run an autocorrelation function;
|
||||
[ResultStruct] = AutocorrStrides(dataAcc_filt,FS, StrideTimeRange,ResultStruct);
|
||||
|
||||
% StrideTimeSamples is needed as an input for the stepcountFunc;
|
||||
StrideTimeSamples = ResultStruct.StrideTimeSamples;
|
||||
|
||||
% Calculate the number of steps;
|
||||
[PksAndLocsCorrected] = StepcountFunc(dataAcc_filt,StrideTimeSamples,FS);
|
||||
% This function selects steps based on negative and positive values.
|
||||
% However to determine the steps correctly we only need one of these;
|
||||
LocsSteps = PksAndLocsCorrected(1:2:end,2);
|
||||
|
||||
%% Cut data & remove currents results
|
||||
% Remove 20 steps in the beginning and end of data
|
||||
dataAccCut = dataAcc(LocsSteps(31):LocsSteps(end-30),:);
|
||||
dataAccCut_filt = dataAcc_filt(LocsSteps(31):LocsSteps(end-30),:);
|
||||
|
||||
% Clear currently saved results from Autocorrelation Analysis
|
||||
|
||||
clear ResultStruct;
|
||||
clear PksAndLocsCorrected;
|
||||
clear LocsSteps;
|
||||
|
||||
else;
|
||||
dataAccCut = dataAcc;
|
||||
dataAccCut_filt = dataAcc_filt;
|
||||
end
|
||||
|
||||
%% Calculate stride parameters
|
||||
ResultStruct = struct; % create empty struct
|
||||
|
||||
% Run function AutoCorrStrides, Outcomeparameters: StrideRegularity,RelativeStrideVariability,StrideTimeSamples,StrideTime
|
||||
[ResultStruct] = AutocorrStrides(dataAccCut_filt,FS, StrideTimeRange,ResultStruct);
|
||||
StrideTimeSamples = ResultStruct.StrideTimeSamples; % needed as input for other functions
|
||||
|
||||
% Calculate Step symmetry --> method 1
|
||||
ij = 1;
|
||||
dirSymm = [1,3]; % Gait Synmmetry is only informative in AP/V direction: See Tura A, Raggi M, Rocchi L, Cutti AG, Chiari L: Gait symmetry and regularity in transfemoral amputees assessed by trunk accelerations. J Neuroeng Rehabil 2010, 7:4.
|
||||
|
||||
for jk=1:length(dirSymm)
|
||||
[C, lags] = AutocorrRegSymmSteps(dataAccCut_filt(:,dirSymm(jk)));
|
||||
[Ad,p] = findpeaks(C,'MinPeakProminence',0.2, 'MinPeakHeight', 0.2);
|
||||
|
||||
if size(Ad,1) > 1
|
||||
Ad1 = Ad(1);
|
||||
Ad2 = Ad(2);
|
||||
GaitSymm(:,ij) = abs((Ad1-Ad2)/mean([Ad1+Ad2]))*100;
|
||||
else
|
||||
GaitSymm(:,ij) = NaN;
|
||||
end
|
||||
ij = ij +1;
|
||||
end
|
||||
% Save outcome in struct;
|
||||
ResultStruct.GaitSymm_V = GaitSymm(1);
|
||||
ResultStruct.GaitSymm_AP = GaitSymm(2);
|
||||
|
||||
% Calculate Step symmetry --> method 2
|
||||
[PksAndLocsCorrected] = StepcountFunc(dataAccCut_filt,StrideTimeSamples,FS);
|
||||
LocsSteps = PksAndLocsCorrected(2:2:end,2);
|
||||
|
||||
if rem(size(LocsSteps,1),2) == 0; % is number of steps is even
|
||||
LocsSteps2 = LocsSteps(1:2:end);
|
||||
else
|
||||
LocsSteps2 = LocsSteps(3:2:end);
|
||||
end
|
||||
|
||||
LocsSteps1 = LocsSteps(2:2:end);
|
||||
DiffLocs2 = diff(LocsSteps2);
|
||||
DiffLocs1 = diff(LocsSteps1);
|
||||
StepTime2 = DiffLocs2(1:end-1)/FS; % leave last one out because it is higher
|
||||
StepTime1 = DiffLocs1(1:end-1)/FS;
|
||||
SI = abs((2*(StepTime2-StepTime1))./(StepTime2+StepTime1))*100;
|
||||
ResultStruct.GaitSymmIndex = nanmean(SI);
|
||||
|
||||
%% Calculate spatiotemporal stride parameters
|
||||
|
||||
% Measures from height variation by double integration of VT accelerations and high-pass filtering
|
||||
[ResultStruct] = SpatioTemporalGaitParameters(dataAccCut_filt,StrideTimeSamples,ApplyRealignment,LegLength,FS,IgnoreMinMaxStrides,ResultStruct);
|
||||
|
||||
%% Measures derived from spectral analysis
|
||||
|
||||
AccVectorLen = sqrt(sum(dataAccCut_filt(:,1:3).^2,2));
|
||||
[ResultStruct] = SpectralAnalysisGaitfunc(dataAccCut_filt,WindowLen,FS,N_Harm,LowFrequentPowerThresholds,AccVectorLen,ResultStruct);
|
||||
|
||||
|
||||
%% Calculation non-linear parameters;
|
||||
|
||||
% cut into windows of size WindowLen
|
||||
N_Windows = floor(size(dataAccCut,1)/WindowLen);
|
||||
N_SkipBegin = ceil((size(dataAccCut,1)-N_Windows*WindowLen)/2);
|
||||
LyapunovWolf = nan(N_Windows,3);
|
||||
LyapunovRosen = nan(N_Windows,3);
|
||||
SE= nan(N_Windows,3);
|
||||
|
||||
for WinNr = 1:N_Windows;
|
||||
AccWin = dataAccCut(N_SkipBegin+(WinNr-1)*WindowLen+(1:WindowLen),:);
|
||||
for j=1:3
|
||||
[LyapunovWolf(WinNr,j),~] = CalcMaxLyapWolfFixedEvolv(AccWin(:,j),FS,struct('m',Lyap_m));
|
||||
[LyapunovRosen(WinNr,j),outpo] = CalcMaxLyapConvGait(AccWin(:,j),FS,struct('m',Lyap_m,'FitWinLen',Lyap_FitWinLen));
|
||||
[SE(WinNr,j)] = funcSampleEntropy(AccWin(:,j), Sen_m, Sen_r);
|
||||
% no correction for FS; SE does increase with higher FS but effect is considered negligible as range is small (98-104HZ). Might consider updating r to account for larger ranges.
|
||||
end
|
||||
end
|
||||
|
||||
LyapunovWolf = nanmean(LyapunovWolf,1);
|
||||
LyapunovRosen = nanmean(LyapunovRosen,1);
|
||||
SampleEntropy = nanmean(SE,1);
|
||||
|
||||
ResultStruct.LyapunovWolf_V = LyapunovWolf(1);
|
||||
ResultStruct.LyapunovWolf_ML = LyapunovWolf(2);
|
||||
ResultStruct.LyapunovWolf_AP = LyapunovWolf(3);
|
||||
ResultStruct.LyapunovRosen_V = LyapunovRosen(1);
|
||||
ResultStruct.LyapunovRosen_ML = LyapunovRosen(2);
|
||||
ResultStruct.LyapunovRosen_AP = LyapunovRosen(3);
|
||||
ResultStruct.SampleEntropy_V = SampleEntropy(1);
|
||||
ResultStruct.SampleEntropy_ML = SampleEntropy(2);
|
||||
ResultStruct.SampleEntropy_AP = SampleEntropy(3);
|
||||
|
||||
if isfield(ResultStruct,'StrideFrequency')
|
||||
LyapunovPerStrideWolf = LyapunovWolf/ResultStruct.StrideFrequency;
|
||||
LyapunovPerStrideRosen = LyapunovRosen/ResultStruct.StrideFrequency;
|
||||
end
|
||||
|
||||
ResultStruct.LyapunovPerStrideWolf_V = LyapunovPerStrideWolf(1);
|
||||
ResultStruct.LyapunovPerStrideWolf_ML = LyapunovPerStrideWolf(2);
|
||||
ResultStruct.LyapunovPerStrideWolf_AP = LyapunovPerStrideWolf(3);
|
||||
ResultStruct.LyapunovPerStrideRosen_V = LyapunovPerStrideRosen(1);
|
||||
ResultStruct.LyapunovPerStrideRosen_ML = LyapunovPerStrideRosen(2);
|
||||
ResultStruct.LyapunovPerStrideRosen_AP = LyapunovPerStrideRosen(3);
|
||||
|
||||
end
|
74
GaitVariabilityAnalysisIH.m
Normal file
74
GaitVariabilityAnalysisIH.m
Normal file
@ -0,0 +1,74 @@
|
||||
% Gait Variability Analysis
|
||||
% Script created for BAP students 2020
|
||||
% Iris Hagoort
|
||||
% April 2020
|
||||
|
||||
% Input: needs mat file which contains all raw accelerometer data
|
||||
% Input: needs excel file containing the participant information including
|
||||
% leg length.
|
||||
|
||||
%% Clear and close;
|
||||
clear;
|
||||
close all;
|
||||
|
||||
%% Load data
|
||||
load('Phyphoxdata.mat'); % loads accelerometer data, is stored in struct with name AccData
|
||||
load('ExcelInfo.mat');
|
||||
Participants = fields(AccData);
|
||||
|
||||
%% Settings
|
||||
FS = 100; % sample frequency
|
||||
LegLengths = excel.data.GeneralInformation(:,5); % leglength info is in 5th column
|
||||
LegLengthsM = LegLengths/100; % convert to m
|
||||
|
||||
%% Calculate parameters;
|
||||
for i = 1: length(Participants);
|
||||
tic;
|
||||
LegLength = LegLengthsM(i);
|
||||
WalkingConditions = fields(AccData.([char(Participants(i))]));
|
||||
|
||||
for j = 1: length(WalkingConditions);
|
||||
|
||||
if strcmp(char(WalkingConditions(j)),'Treadmill')
|
||||
|
||||
SubConditions = fieldnames(AccData.([char(Participants(i))]).([char(WalkingConditions(j))]));
|
||||
|
||||
for k = 1: length(SubConditions);
|
||||
inputData = AccData.([char(Participants(i))]).([char(WalkingConditions(j))]).([char(SubConditions(k))]);
|
||||
WindowLength = FS*10;
|
||||
ApplyRealignment = true;
|
||||
ApplyRemoveSteps = true;
|
||||
[ResultStruct] = GaitOutcomesTrunkAccFuncIH(inputData,FS,LegLength,WindowLen,ApplyRealignment,ApplyRemoveSteps);
|
||||
OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]).([char(SubConditions(k))]) = ResultStruct;
|
||||
end
|
||||
|
||||
elseif strcmp(char(WalkingConditions(j)),'Balance') || strcmp(char(WalkingConditions(j)),'TwoMWT')
|
||||
disp('Files are not used for current analysis');
|
||||
|
||||
elseif strcmp(char(WalkingConditions(j)),'InsideStraight')
|
||||
inputData = AccData.([char(Participants(i))]).([char(WalkingConditions(j))]);
|
||||
ApplyRealignment = true;
|
||||
ApplyRemoveSteps = false; % don't remove steps for the straight conditions
|
||||
% function specific for the walking conditions containing a lot
|
||||
% of turns
|
||||
[ResultStruct] = GaitVariabilityAnalysisIH_WithoutTurns(inputData,FS,LegLength,ApplyRealignment,ApplyRemoveSteps);
|
||||
OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]) = ResultStruct;
|
||||
|
||||
else
|
||||
inputData = AccData.([char(Participants(i))]).([char(WalkingConditions(j))]);
|
||||
ApplyRealignment = true;
|
||||
ApplyRemoveSteps = true;
|
||||
WindowLen = FS*10;
|
||||
[ResultStruct] = GaitOutcomesTrunkAccFuncIH(inputData,FS,LegLength,WindowLen,ApplyRealignment,ApplyRemoveSteps)
|
||||
OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]) = ResultStruct;
|
||||
end
|
||||
end
|
||||
|
||||
toc;
|
||||
end
|
||||
|
||||
% Save struct as .mat file
|
||||
save('GaitVarOutcomes30pril.mat', 'OutcomesAcc');
|
||||
|
||||
|
||||
|
108
GaitVariabilityAnalysisIH_WithoutTurns.m
Normal file
108
GaitVariabilityAnalysisIH_WithoutTurns.m
Normal file
@ -0,0 +1,108 @@
|
||||
function [ResultStruct] = GaitVariabilityAnalysisIH_WithoutTurns(inputData,FS,LegLength,ApplyRealignment,ApplyRemoveSteps);
|
||||
|
||||
|
||||
% SCRIPT FOR ANAlysis straight parts
|
||||
% NOG GOEDE BESCHRIJVING TOEVOEGEN.
|
||||
|
||||
%% Realign data
|
||||
data = inputData(:, [3,2,4]); % reorder data to 1 = V; 2= ML, 3 = AP
|
||||
|
||||
%Realign sensor data to VT-ML-AP frame
|
||||
if ApplyRealignment % apply relignment as described in Rispens S, Pijnappels M, van Schooten K, Beek PJ, Daffertshofer A, van Die?n JH (2014).
|
||||
% Consistency of gait characteristics as determined from acceleration data collected at different trunk locations. Gait Posture 2014;40(1):187-92.
|
||||
[RealignedAcc, ~] = RealignSensorSignalHRAmp(data, FS);
|
||||
dataAcc = RealignedAcc;
|
||||
end
|
||||
|
||||
%% Filter data strongly & Determine location of steps
|
||||
|
||||
% Filter data
|
||||
[B,A] = butter(2,3/(FS/2),'low'); % Filters data very strongly which is needed to determine turns correctly
|
||||
dataStepDetection = filtfilt(B,A,dataAcc);
|
||||
|
||||
% Determine steps;
|
||||
|
||||
%%%%%%% HIER MISSCHIEN ALTERNATIEF VOOR VAN RISPENS %%%%%%%%%%%%%
|
||||
|
||||
% Explanation of method: https://nl.mathworks.com/help/supportpkg/beagleboneblue/ref/counting-steps-using-beagleboneblue-hardware-example.html
|
||||
% From website: To convert the XYZ acceleration vectors at each point in time into scalar values,
|
||||
% calculate the magnitude of each vector. This way, you can detect large changes in overall acceleration,
|
||||
% such as steps taken while walking, regardless of device orientation.
|
||||
|
||||
magfilt = sqrt(sum((dataStepDetection(:,1).^2) + (dataStepDetection(:,2).^2) + (dataStepDetection(:,3).^2), 2));
|
||||
magNoGfilt = magfilt - mean(magfilt);
|
||||
minPeakHeight2 = std(magNoGfilt);
|
||||
[pks, locs] = findpeaks(magNoGfilt, 'MINPEAKHEIGHT', minPeakHeight2); % for step detection
|
||||
numStepsOption2_filt = numel(pks); % counts number of steps;
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%% TOT HIER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% Determine locations of turns;
|
||||
|
||||
diffLocs = diff(locs); % calculates difference in step location
|
||||
avg_diffLocs = mean(diffLocs); % average distance between steps
|
||||
std_diffLocs = std(diffLocs); % standard deviation of distance between steps
|
||||
|
||||
figure;
|
||||
findpeaks(diffLocs, 'MINPEAKHEIGHT', avg_diffLocs, 'MINPEAKDISTANCE',5); % these values have been chosen based on visual inspection of the signal
|
||||
line([1 length(diffLocs)],[avg_diffLocs avg_diffLocs])
|
||||
[pks_diffLocs, locs_diffLocs] = findpeaks(diffLocs, 'MINPEAKHEIGHT', avg_diffLocs,'MINPEAKDISTANCE',5);
|
||||
locsTurns = [locs(locs_diffLocs), locs(locs_diffLocs+1)];
|
||||
|
||||
%% Visualizing turns
|
||||
|
||||
% Duplying signal + visualing
|
||||
% to make second signal with the locations of the turns filled with NaN, so
|
||||
% that both signals can be plotted above each other in a different colour
|
||||
|
||||
magNoGfilt_copy = magNoGfilt;
|
||||
for k = 1: size(locsTurns,1);
|
||||
magNoGfilt_copy(locsTurns(k,1):locsTurns(k,2)) = NaN;
|
||||
end
|
||||
|
||||
|
||||
% visualising signal;
|
||||
figure;
|
||||
subplot(2,1,1)
|
||||
hold on;
|
||||
plot(magNoGfilt,'b')
|
||||
plot(magNoGfilt_copy, 'r');
|
||||
title('Inside Straight: Filtered data with turns highlighted in blue')
|
||||
hold off;
|
||||
|
||||
%% Calculation
|
||||
% VRAAG LAURENS zie blauwe blaadje
|
||||
|
||||
startPos = 1;
|
||||
for i = 1: size(locsTurns,1);
|
||||
endPos = locsTurns(i,1)-1;
|
||||
|
||||
inputData = dataAcc(startPos:endPos,:);
|
||||
WindowLen = size(inputData,1);
|
||||
ApplyRealignment = false;
|
||||
[ResultStruct] = GaitOutcomesTrunkAccFuncIH(inputData,FS,LegLength,WindowLen,ApplyRealignment,ApplyRemoveSteps); % Naam van deze moet nog aangepast.
|
||||
|
||||
if i ==1 % only the firs time
|
||||
Parameters = fieldnames(ResultStruct);
|
||||
NrParameters = length(Parameters);
|
||||
end
|
||||
|
||||
for j = 1:NrParameters % only works if for every bin we get the same outcomes (which is the case in this script)
|
||||
DataStraight.([char(Parameters(j))])(i) = ResultStruct.([char(Parameters(j))]);
|
||||
end
|
||||
startPos = locsTurns(i,2)+1;
|
||||
|
||||
end
|
||||
|
||||
clear ResultStruct;
|
||||
|
||||
% Calculate mean over the bins without turns to get 1 outcome value per parameter for inside
|
||||
% straight;
|
||||
|
||||
for j = 1:NrParameters;
|
||||
ResultStruct.([char(Parameters(j))]) = nanmean(DataStraight.([char(Parameters(j))]))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
1709
GaitVariabilityAnalysisLD.html
Normal file
1709
GaitVariabilityAnalysisLD.html
Normal file
File diff suppressed because one or more lines are too long
BIN
GaitVariabilityAnalysisLD.mlx
Normal file
BIN
GaitVariabilityAnalysisLD.mlx
Normal file
Binary file not shown.
1944
GaitVariabilityAnalysisLD.tex
Normal file
1944
GaitVariabilityAnalysisLD.tex
Normal file
File diff suppressed because it is too large
Load Diff
103
GaitVariabilityAnalysisLD_v1.m
Normal file
103
GaitVariabilityAnalysisLD_v1.m
Normal file
@ -0,0 +1,103 @@
|
||||
%% Gait Variability Analysis CLBP
|
||||
|
||||
% Gait Variability Analysis
|
||||
% Script created for MAP 2020-2021
|
||||
% adapted from Claudine Lamoth and Iris Hagoort
|
||||
% version1 October 2020
|
||||
|
||||
% Input: needs mat file which contains all raw accelerometer data
|
||||
% Input: needs excel file containing the participant information including
|
||||
% leg length.
|
||||
%% Clear and close;
|
||||
|
||||
clear;
|
||||
close all;
|
||||
%% Load data;
|
||||
% Select 1 trial.
|
||||
% For loop to import all data will be used at a later stage
|
||||
|
||||
[FNaam,FilePad] = uigetfile('*.xls','Load phyphox data...');
|
||||
filename =[FilePad FNaam];
|
||||
PhyphoxData = xlsread(filename)
|
||||
|
||||
%load('Phyphoxdata.mat'); % loads accelerometer data, is stored in struct with name AccData
|
||||
%load('ExcelInfo.mat');
|
||||
%Participants = fields(AccData);
|
||||
%% Settings;
|
||||
%adapted from GaitOutcomesTrunkAccFuncIH
|
||||
|
||||
LegLength = 98; % LegLength info not available!
|
||||
FS = 100; % Sample frequency
|
||||
|
||||
Gr = 9.81; % Gravity acceleration, multiplication factor for accelerations
|
||||
StrideFreqEstimate = 1.00; % Used to set search for stride frequency from 0.5*StrideFreqEstimate until 2*StrideFreqEstimate
|
||||
StrideTimeRange = [0.2 4.0]; % Range to search for stride time (seconds)
|
||||
IgnoreMinMaxStrides = 0.10; % Number or percentage of highest&lowest values ignored for improved variability estimation
|
||||
N_Harm = 12; % Number of harmonics used for harmonic ratio, index of harmonicity and phase fluctuation
|
||||
LowFrequentPowerThresholds = ...
|
||||
[0.7 1.4]; % Threshold frequencies for estimation of low-frequent power percentages
|
||||
Lyap_m = 7; % Embedding dimension (used in Lyapunov estimations)
|
||||
Lyap_FitWinLen = round(60/100*FS); % Fitting window length (used in Lyapunov estimations Rosenstein's method)
|
||||
Sen_m = 5; % Dimension, the length of the subseries to be matched (used in sample entropy estimation)
|
||||
Sen_r = 0.3; % Tolerance, the maximum distance between two samples to qualify as match, relative to std of DataIn (used in sample entropy estimation)
|
||||
NStartEnd = [100];
|
||||
M = 5; % Maximum template length
|
||||
ResultStruct = struct(); % Empty struct
|
||||
|
||||
|
||||
inputData = (PhyphoxData(:,[1 2 3 4])); % matrix with accelerometer data
|
||||
ApplyRealignment = true;
|
||||
ApplyRemoveSteps = true;
|
||||
WindowLen = FS*10;
|
||||
|
||||
|
||||
%% Filter and Realign Accdata
|
||||
% dataAcc depends on ApplyRealignment = true/false
|
||||
% dataAcc_filt (low pass Butterworth Filter + zerophase filtering
|
||||
|
||||
[dataAcc, dataAcc_filt] = FilterandRealignFunc(inputData,FS,ApplyRealignment);
|
||||
|
||||
%% Step dectection
|
||||
% Determines the number of steps in the signal so that the first 30 and last 30 steps in the signal can be removed
|
||||
% StrideTimeSamples is needed for calculation stride parameters!
|
||||
|
||||
[dataAccCut,dataAccCut_filt,StrideTimeSamples] = StepDetectionFunc(FS,ApplyRemoveSteps,dataAcc,dataAcc_filt,StrideTimeRange);
|
||||
|
||||
%% Calculate Stride Parameters
|
||||
% Outcomeparameters: StrideRegularity,RelativeStrideVariability,StrideTimeSamples,StrideTime
|
||||
|
||||
[ResultStruct] = CalculateStrideParametersFunc(dataAccCut_filt,FS,ApplyRemoveSteps,dataAcc,dataAcc_filt,StrideTimeRange);
|
||||
|
||||
%% Calculate spatiotemporal stride parameters
|
||||
% Measures from height variation by double integration of VT accelerations and high-pass filtering
|
||||
% StepLengthMean; Distance; WalkingSpeedMean; StrideTimeVariability; StrideSpeedVariability;
|
||||
% StrideLengthVariability; StrideTimeVariabilityOmitOutlier; StrideSpeedVariabilityOmitOutlier; StrideLengthVariabilityOmitOutlier;
|
||||
|
||||
[ResultStruct] = SpatioTemporalGaitParameters(dataAccCut_filt,StrideTimeSamples,ApplyRealignment,LegLength,FS,IgnoreMinMaxStrides,ResultStruct);
|
||||
|
||||
%% Measures derived from spectral analysis
|
||||
% IndexHarmonicity_V/ML/AP/ALL ; HarmonicRatio_V/ML/AP ; HarmonicRatioP_V/ML/AP ; FrequencyVariability_V/ML/AP; Stride Frequency
|
||||
|
||||
AccVectorLen = sqrt(sum(dataAccCut_filt(:,1:3).^2,2));
|
||||
[ResultStruct] = SpectralAnalysisGaitfunc(dataAccCut_filt,WindowLen,FS,N_Harm,LowFrequentPowerThresholds,AccVectorLen,ResultStruct);
|
||||
|
||||
%% calculate non-linear parameters
|
||||
% Outcomeparameters: Sample Entropy, Lyapunov exponents
|
||||
|
||||
[ResultStruct] = CalculateNonLinearParametersFunc(ResultStruct,dataAccCut,WindowLen,FS,Lyap_m,Lyap_FitWinLen,Sen_m,Sen_r);
|
||||
|
||||
% Save struct as .mat file
|
||||
% save('GaitVarOutcomesParticipantX.mat', 'OutcomesAcc');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%% AggregateFunction (seperate analysis per minute);
|
||||
% see AggregateEpisodeValues.m
|
||||
%
|
||||
%
|
||||
|
||||
|
95
GaitVariabilityStructToExcelIH.m
Normal file
95
GaitVariabilityStructToExcelIH.m
Normal file
@ -0,0 +1,95 @@
|
||||
%% Description file
|
||||
|
||||
%% Clear and close
|
||||
clear;
|
||||
close all;
|
||||
|
||||
%% Load data;
|
||||
load('GaitVarOutcomes30april.mat')
|
||||
Participants = fields(OutcomesAcc);
|
||||
|
||||
%% Settings;
|
||||
counter = 1;
|
||||
run = 1;
|
||||
ParticipantNr = 0;
|
||||
ConditionNames = {'TwoMWT','InsidePath','InsideStraight','Outside','Treadmill_Comfortable','Treadmill_Condition1','Treadmill_Condition2','Treadmill_Condition3','Treadmill_Condition4','Treadmill_Condition5','Treadmill_Condition6','Treadmill_Condition7','Treadmill_Condition8'};
|
||||
|
||||
%% Reorder data;
|
||||
for i = 1: length(Participants);
|
||||
% Participant Nr;
|
||||
ParticipantNr = ParticipantNr + 1;
|
||||
|
||||
% Group Nr;
|
||||
if contains(Participants(i),'Y');
|
||||
Group = 1;
|
||||
else
|
||||
Group = 2;
|
||||
end
|
||||
|
||||
WalkingConditions = fields(OutcomesAcc.([char(Participants(i))]));
|
||||
|
||||
for j = 1: length(WalkingConditions);
|
||||
|
||||
if strcmp(char(WalkingConditions(j)),'Treadmill')
|
||||
|
||||
SubConditions = fieldnames(OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]));
|
||||
|
||||
for k = 1: length(SubConditions);
|
||||
|
||||
NameTreadmill = [char(WalkingConditions(j)),'_',char(SubConditions(k))];
|
||||
ConditionNr = find(strcmp(ConditionNames, NameTreadmill));
|
||||
Parameters = fieldnames(OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]).([char(SubConditions(k))]));
|
||||
|
||||
for l = 1: length(Parameters);
|
||||
|
||||
Data(counter+1,1) = {([char(Participants(i))])};
|
||||
Data(counter+1,2) = {ParticipantNr};
|
||||
Data(counter+1,3) = {Group};
|
||||
Data(counter+1,4) = {([char(NameTreadmill)])};
|
||||
Data(counter+1,5) = {ConditionNr};
|
||||
Data(counter+1,l+5) = {OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]).([char(SubConditions(k))]).([char(Parameters(l))])(1)};
|
||||
|
||||
end
|
||||
counter = counter+1;
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
ConditionNr = find(strcmp(ConditionNames, WalkingConditions(j)));
|
||||
Parameters = fieldnames(OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]));
|
||||
|
||||
for l = 1: length(Parameters);
|
||||
|
||||
Data(counter+1,1) = {([char(Participants(i))])};
|
||||
Data(counter+1,2) = {ParticipantNr};
|
||||
Data(counter+1,3) = {Group};
|
||||
Data(counter+1,4) = {([char(WalkingConditions(j))])};
|
||||
Data(counter+1,5) = {ConditionNr};
|
||||
Data(counter+1,l+5) = {OutcomesAcc.([char(Participants(i))]).([char(WalkingConditions(j))]).([char(Parameters(l))])(1)};
|
||||
|
||||
end
|
||||
|
||||
counter = counter+1;
|
||||
|
||||
end
|
||||
if run == 1;
|
||||
Data(1,1) = {'ParticipantCode'};
|
||||
Data(1,2) = {'ParticipantNr'};
|
||||
Data(1,3) = {'Group'};
|
||||
Data(1,4) = {'ConditionName'};
|
||||
Data(1,5) = {'ConditionNr'};
|
||||
Data(1,6:(size(Parameters,1)+5)) = Parameters'; % First row with variable names;
|
||||
run = run+1;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if ispc;
|
||||
xlswrite('GaitVariabilityOutcomes.xls', 'Data');
|
||||
elseif ismac;
|
||||
filename = 'GaitVariabilityOutcomes.xlsx';
|
||||
writecell(Data,filename);
|
||||
else
|
||||
end
|
83
HarmonicityFrequency.m
Normal file
83
HarmonicityFrequency.m
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
function [ResultStruct] = HarmonicityFrequency(dataAccCut_filt,P,F, StrideFrequency,dF,LowFrequentPowerThresholds,N_Harm,FS,AccVectorLen,ResultStruct)
|
||||
|
||||
% Add sum of power spectra (as a rotation-invariant spectrum)
|
||||
P = [P,sum(P,2)];
|
||||
PS = sqrt(P);
|
||||
|
||||
% Calculate the measures for the power per separate dimension
|
||||
for i=1:size(P,2);
|
||||
% Relative cumulative power and frequencies that correspond to these cumulative powers
|
||||
PCumRel = cumsum(P(:,i))/sum(P(:,i));
|
||||
PSCumRel = cumsum(PS(:,i))/sum(PS(:,i));
|
||||
FCumRel = F+0.5*dF;
|
||||
|
||||
% Derive relative cumulative power for threshold frequencies
|
||||
Nfreqs = size(LowFrequentPowerThresholds,2);
|
||||
LowFrequentPercentage(i,1:Nfreqs) = interp1(FCumRel,PCumRel,LowFrequentPowerThresholds)*100;
|
||||
|
||||
% Calculate relative power of first 10 harmonics, taking the power of each harmonic with a band of + and - 10% of the first
|
||||
% harmonic around it
|
||||
PHarm = zeros(N_Harm,1);
|
||||
PSHarm = zeros(N_Harm,1);
|
||||
for Harm = 1:N_Harm,
|
||||
FHarmRange = (Harm+[-0.1 0.1])*StrideFrequency;
|
||||
PHarm(Harm) = diff(interp1(FCumRel,PCumRel,FHarmRange));
|
||||
PSHarm(Harm) = diff(interp1(FCumRel,PSCumRel,FHarmRange));
|
||||
end
|
||||
|
||||
% Derive index of harmonicity
|
||||
if i == 2 % for ML we expect odd instead of even harmonics
|
||||
IndexHarmonicity(i) = PHarm(1)/sum(PHarm(1:2:12));
|
||||
elseif i == 4
|
||||
IndexHarmonicity(i) = sum(PHarm(1:2))/sum(PHarm(1:12));
|
||||
else
|
||||
IndexHarmonicity(i) = PHarm(2)/sum(PHarm(2:2:12));
|
||||
end
|
||||
|
||||
% Calculate the phase speed fluctuations
|
||||
StrideFreqFluctuation = nan(N_Harm,1);
|
||||
StrSamples = round(FS/StrideFrequency);
|
||||
for h=1:N_Harm,
|
||||
CutOffs = [StrideFrequency*(h-(1/3)) , StrideFrequency*(h+(1/3))]/(FS/2);
|
||||
if all(CutOffs<1) % for Stride frequencies above FS/20/2, the highest harmonics are not represented in the power spectrum
|
||||
[b,a] = butter(2,CutOffs);
|
||||
if i==4 % Take the vector length as a rotation-invariant signal
|
||||
AccFilt = filtfilt(b,a,AccVectorLen);
|
||||
else
|
||||
AccFilt = filtfilt(b,a,dataAccCut_filt(:,i));
|
||||
end
|
||||
Phase = unwrap(angle(hilbert(AccFilt)));
|
||||
SmoothPhase = sgolayfilt(Phase,1,2*(floor(FS/StrideFrequency/2))+1); % This is in fact identical to a boxcar filter with linear extrapolation at the edges
|
||||
StrideFreqFluctuation(h) = std(Phase(1+StrSamples:end,1)-Phase(1:end-StrSamples,1));
|
||||
end
|
||||
end
|
||||
FrequencyVariability(i) = nansum(StrideFreqFluctuation./(1:N_Harm)'.*PHarm)/nansum(PHarm);
|
||||
|
||||
if i<4,
|
||||
% Derive harmonic ratio (two variants)
|
||||
if i == 2 % for ML we expect odd instead of even harmonics
|
||||
HarmonicRatio(i) = sum(PSHarm(1:2:end-1))/sum(PSHarm(2:2:end)); % relative to summed 3d spectrum
|
||||
HarmonicRatioP(i) = sum(PHarm(1:2:end-1))/sum(PHarm(2:2:end)); % relative to own spectrum
|
||||
else
|
||||
HarmonicRatio(i) = sum(PSHarm(2:2:end))/sum(PSHarm(1:2:end-1));
|
||||
HarmonicRatioP(i) = sum(PHarm(2:2:end))/sum(PHarm(1:2:end-1));
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ResultStruct.IndexHarmonicity_V = IndexHarmonicity(1); % higher smoother more regular patter
|
||||
ResultStruct.IndexHarmonicity_ML = IndexHarmonicity(2); % higher smoother more regular patter
|
||||
ResultStruct.IndexHarmonicity_AP = IndexHarmonicity(3); % higher smoother more regular patter
|
||||
ResultStruct.IndexHarmonicity_All = IndexHarmonicity(4);
|
||||
ResultStruct.HarmonicRatio_V = HarmonicRatio(1);
|
||||
ResultStruct.HarmonicRatio_ML = HarmonicRatio(2);
|
||||
ResultStruct.HarmonicRatio_AP = HarmonicRatio(3);
|
||||
ResultStruct.HarmonicRatioP_V = HarmonicRatioP(1);
|
||||
ResultStruct.HarmonicRatioP_ML = HarmonicRatioP(2);
|
||||
ResultStruct.HarmonicRatioP_AP = HarmonicRatioP(3);
|
||||
ResultStruct.FrequencyVariability_V = FrequencyVariability(1);
|
||||
ResultStruct.FrequencyVariability_ML = FrequencyVariability(2);
|
||||
ResultStruct.FrequencyVariability_AP = FrequencyVariability(3);
|
||||
ResultStruct.StrideFrequency = StrideFrequency;
|
146
MutualInformationHisPro.m
Normal file
146
MutualInformationHisPro.m
Normal file
@ -0,0 +1,146 @@
|
||||
function [mutM,cummutM,minmuttauV] = MutualInformationHisPro(xV,tauV,bV,flag)
|
||||
% [mutM,cummutM,minmuttauV] = MutualInformationHisPro(xV,tauV,bV,flag)
|
||||
% MUTUALINFORMATIONHISPRO computes the mutual information on the time
|
||||
% series 'xV' for given delays in 'tauV'. The estimation of mutual
|
||||
% information is based on 'b' partitions of equal probability at each dimension.
|
||||
% A number of different 'b' can be given in the input vector 'bV'.
|
||||
% According to a given flag, it can also compute the cumulative mutual
|
||||
% information for each given lag, as well as the time of the first minimum
|
||||
% of the mutual information.
|
||||
% INPUT
|
||||
% - xV : a vector for the time series
|
||||
% - tauV : a vector of the delays to be evaluated for
|
||||
% - bV : a vector of the number of partitions of the histogram-based
|
||||
% estimate.
|
||||
% - flag : if 0-> compute only mutual information,
|
||||
% : if 1-> compute the mutual information, the first minimum of
|
||||
% mutual information and the cumulative mutual information.
|
||||
% if 2-> compute (also) the cumulative mutual information
|
||||
% if 3-> compute (also) the first minimum of mutual information
|
||||
% OUTPUT
|
||||
% - mutM : the vector of the mutual information values s for the given
|
||||
% delays.
|
||||
% - cummutM : the vector of the cumulative mutual information values for
|
||||
% the given delays
|
||||
% - minmuttauV : the time of the first minimum of the mutual information.
|
||||
%========================================================================
|
||||
% <MutualInformationHisPro.m>, v 1.0 2010/02/11 22:09:14 Kugiumtzis & Tsimpiris
|
||||
% This is part of the MATS-Toolkit http://eeganalysis.web.auth.gr/
|
||||
|
||||
%========================================================================
|
||||
% Copyright (C) 2010 by Dimitris Kugiumtzis and Alkiviadis Tsimpiris
|
||||
% <dkugiu@gen.auth.gr>
|
||||
|
||||
%========================================================================
|
||||
% Version: 1.0
|
||||
|
||||
% LICENSE:
|
||||
% This program is free software; you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation; either version 3 of the License, or
|
||||
% any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see http://www.gnu.org/licenses/>.
|
||||
|
||||
%=========================================================================
|
||||
% Reference : D. Kugiumtzis and A. Tsimpiris, "Measures of Analysis of Time Series (MATS):
|
||||
% A Matlab Toolkit for Computation of Multiple Measures on Time Series Data Bases",
|
||||
% Journal of Statistical Software, in press, 2010
|
||||
|
||||
% Link : http://eeganalysis.web.auth.gr/
|
||||
%=========================================================================
|
||||
nsam = 1;
|
||||
n = length(xV);
|
||||
if nargin==3
|
||||
flag = 1;
|
||||
elseif nargin==2
|
||||
flag = 1;
|
||||
bV = round(sqrt(n/5));
|
||||
end
|
||||
if isempty(bV)
|
||||
bV = round(sqrt(n/5));
|
||||
end
|
||||
bV(bV==0)=round(sqrt(n/5));
|
||||
tauV = sort(tauV);
|
||||
ntau = length(tauV);
|
||||
taumax = tauV(end);
|
||||
nb = length(bV);
|
||||
[oxV,ixV]=sort(xV);
|
||||
[tmpV,ioxV]=sort(ixV);
|
||||
switch flag
|
||||
case 0
|
||||
% Compute only the mutual information for the given lags
|
||||
mutM = NaN*ones(ntau,nb);
|
||||
for ib=1:nb
|
||||
b = bV(ib);
|
||||
if n<2*b
|
||||
break;
|
||||
end
|
||||
mutM(:,ib)=mutinfHisPro(xV,tauV,b,ioxV,ixV);
|
||||
end % for ib
|
||||
cummutM=[];
|
||||
minmuttauV=[];
|
||||
case 1
|
||||
% Compute the mutual information for all lags up to the
|
||||
% largest given lag, then compute the lag of the first minimum of
|
||||
% mutual information and the cumulative mutual information for the
|
||||
% given lags.
|
||||
mutM = NaN*ones(ntau,nb);
|
||||
cummutM = NaN*ones(ntau,nb);
|
||||
minmuttauV = NaN*ones(nb,1);
|
||||
miM = NaN*ones(taumax+1,nb);
|
||||
for ib=1:nb
|
||||
b = bV(ib);
|
||||
if n<2*b
|
||||
break;
|
||||
end
|
||||
miM(:,ib)=mutinfHisPro(xV,[0:taumax]',b,ioxV,ixV);
|
||||
mutM(:,ib) = miM(tauV+1,ib);
|
||||
minmuttauV(ib) = findminMutInf(miM(:,ib),nsam);
|
||||
% Compute the cumulative mutual information for the given delays
|
||||
for i=1:ntau
|
||||
cummutM(i,ib) = sum(miM(1:tauV(i)+1,ib));
|
||||
end
|
||||
end % for ib
|
||||
case 2
|
||||
% Compute the mutual information for all lags up to the largest
|
||||
% given lag and then sum up to get the cumulative mutual information
|
||||
% for the given lags.
|
||||
cummutM = NaN*ones(ntau,nb);
|
||||
miM = NaN*ones(taumax+1,nb);
|
||||
for ib=1:nb
|
||||
b = bV(ib);
|
||||
if n<2*b
|
||||
break;
|
||||
end
|
||||
miM(:,ib)=mutinfHisPro(xV,[0:taumax]',b,ioxV,ixV);
|
||||
% Compute the cumulative mutual information for the given delays
|
||||
for i=1:ntau
|
||||
cummutM(i,ib) = sum(miM(1:tauV(i)+1,ib));
|
||||
end
|
||||
end % for ib
|
||||
mutM = [];
|
||||
minmuttauV=[];
|
||||
case 3
|
||||
% Compute the mutual information for all lags up to the largest
|
||||
% given lag and then compute the lag of the first minimum of the
|
||||
% mutual information.
|
||||
minmuttauV = NaN*ones(nb,1);
|
||||
miM = NaN*ones(taumax+1,nb);
|
||||
for ib=1:nb
|
||||
b = bV(ib);
|
||||
if n<2*b
|
||||
break;
|
||||
end
|
||||
miM(:,ib)=mutinfHisPro(xV,[0:taumax]',b,ioxV,ixV);
|
||||
minmuttauV(ib) = findminMutInf(miM(:,ib),nsam);
|
||||
end % for ib
|
||||
mutM = [];
|
||||
cummutM=[];
|
||||
end
|
BIN
Phyphoxdata.mat
Normal file
BIN
Phyphoxdata.mat
Normal file
Binary file not shown.
275
RealignSensorSignalHRAmp.m
Normal file
275
RealignSensorSignalHRAmp.m
Normal file
@ -0,0 +1,275 @@
|
||||
function [RealignedAcc,RotationMatrixT,Flags] = RealignSensorSignalHRAmp(Acc, FS)
|
||||
|
||||
% History
|
||||
% 2013/05/31 SR solve problem of ML-AP rotation of appr. 90 degrees ("Maximum number of function evaluations has been exceeded")
|
||||
% 2013/08/15 SR use literature HR (i.e. amplitude not power, and harmonic frequencies not sin^4 windows)
|
||||
% 2013/08/16 SR correct: use abs(X) instead of sqrt(X.*X)
|
||||
% 2013/09/11 SR use only relevant F (containing odd or even harmonics) and speed up determination of harmonic frequencies
|
||||
|
||||
%% Define VT direction (RVT) as direction of mean acceleration
|
||||
MeanAcc = mean(Acc);
|
||||
RVT = MeanAcc'/norm(MeanAcc);
|
||||
RMLGuess = cross([0;0;1],RVT); RMLGuess = RMLGuess/norm(RMLGuess);
|
||||
RAPGuess = cross(RVT,RMLGuess); RAPGuess = RAPGuess/norm(RAPGuess);
|
||||
|
||||
%% Estimate stride frequency
|
||||
StrideFrequency = StrideFrequencyFrom3dAcc(Acc, FS);
|
||||
AccDetrend = detrend(Acc,'constant');
|
||||
|
||||
N = size(AccDetrend,1);
|
||||
|
||||
%% calculate complex DFT
|
||||
F = FS*(0:(N-1))'/N;
|
||||
|
||||
dF = FS/N;
|
||||
FHarmRange = [(1:20)'-0.1, (1:20)'+0.1]*StrideFrequency;
|
||||
|
||||
EvenRanges = FHarmRange(2:2:end,:);
|
||||
OddRanges = FHarmRange(1:2:end,:);
|
||||
|
||||
EvenHarmonics = zeros(size(F));
|
||||
for j=1:size(EvenRanges,1)
|
||||
IXRange = [EvenRanges(j,1)/dF, EvenRanges(j,2)/dF]+1;
|
||||
IXRangeRound = min(N,round(IXRange));
|
||||
EvenHarmonics(IXRangeRound(1):IXRangeRound(2)) = EvenHarmonics(IXRangeRound(1):IXRangeRound(2)) + 1;
|
||||
EvenHarmonics(IXRangeRound(1)) = EvenHarmonics(IXRangeRound(1)) - (IXRange(1)-IXRangeRound(1)+0.5);
|
||||
EvenHarmonics(IXRangeRound(2)) = EvenHarmonics(IXRangeRound(2)) - (IXRangeRound(2)-IXRange(2)+0.5);
|
||||
end
|
||||
|
||||
OddHarmonics = zeros(size(F));
|
||||
for j=1:size(OddRanges,1)
|
||||
IXRange = [OddRanges(j,1)/dF, OddRanges(j,2)/dF]+1;
|
||||
IXRangeRound = min(N,round(IXRange));
|
||||
OddHarmonics(IXRangeRound(1):IXRangeRound(2)) = OddHarmonics(IXRangeRound(1):IXRangeRound(2)) + 1;
|
||||
OddHarmonics(IXRangeRound(1)) = OddHarmonics(IXRangeRound(1)) - (IXRange(1)-IXRangeRound(1)+0.5);
|
||||
OddHarmonics(IXRangeRound(2)) = OddHarmonics(IXRangeRound(2)) - (IXRangeRound(2)-IXRange(2)+0.5);
|
||||
end
|
||||
|
||||
%% select only frequencies that contain odd or even harmonics
|
||||
RelevantF = OddHarmonics~=0 | EvenHarmonics~=0;
|
||||
OddHarmonics = OddHarmonics(RelevantF);
|
||||
EvenHarmonics = EvenHarmonics(RelevantF);
|
||||
DFT = fft(AccDetrend,N,1);
|
||||
DFT = DFT(RelevantF,:);
|
||||
DFTMLG = DFT*RMLGuess;
|
||||
DFTAPG = DFT*RAPGuess;
|
||||
|
||||
%% Initial estimation of ML-AP realignment (educated guess)
|
||||
% We start of by finding the directions ML = MLguess + x*APguess for which
|
||||
% the 'harmonic ratio' for the ML directions is maximal or minimal, and we
|
||||
% do this by varying x in still the power variant of HR instead of amplitude:
|
||||
HRML = @(x) real(((OddHarmonics'.*(DFTMLG'+x*DFTAPG'))*(DFTMLG+x*DFTAPG))...
|
||||
/((EvenHarmonics'.*(DFTMLG'+x*DFTAPG'))*(DFTMLG+x*DFTAPG)));
|
||||
% or by substititutions
|
||||
a = real((OddHarmonics.*DFTAPG)'*DFTAPG);
|
||||
b = real((OddHarmonics.*DFTMLG)'*DFTAPG + (OddHarmonics.*DFTAPG)'*DFTMLG);
|
||||
c = real((OddHarmonics.*DFTMLG)'*DFTMLG);
|
||||
d = real((EvenHarmonics.*DFTAPG)'*DFTAPG);
|
||||
e = real((EvenHarmonics.*DFTMLG)'*DFTAPG + (EvenHarmonics.*DFTAPG)'*DFTMLG);
|
||||
f = real((EvenHarmonics.*DFTMLG)'*DFTMLG);
|
||||
% and by setting d/dx(HRML(x))==0 we get the quadratic formula
|
||||
aq = a*e-b*d;
|
||||
bq = 2*a*f-2*c*d;
|
||||
cq = b*f-c*e;
|
||||
% DT = bq^2-4*aq*cq = 4*(a*f-*c*d)*(a*f-c*d) - 4*(a*e-b*d)*(b*f-c*e)
|
||||
% = 4aaff +4ccdd -8acdf -4abef -4bcde +4acee + 4bbdf
|
||||
% = 4aaff +4ccdd -4abef -4bcde +4ac(ee-df) +4(bb-ac)df
|
||||
x = (-bq +[1 -1]*sqrt(bq.^2-4*aq*cq))/(2*aq);
|
||||
% with hopefully two solutions for x.
|
||||
|
||||
% from here on use amplitude HR:
|
||||
HRML = @(x) real((OddHarmonics'*abs(DFTMLG+x*DFTAPG))...
|
||||
/(EvenHarmonics'*abs(DFTMLG+x*DFTAPG)));
|
||||
% now we take the x for which the HR is maximal, and the x rotated 90
|
||||
% degrees fo which it is minimal, and take the mean angle between them
|
||||
HR1 = HRML(x(1));
|
||||
HR2 = HRML(x(2));
|
||||
if HR1 > HR2
|
||||
thetas = atan([x(1),-1/x(2)]);
|
||||
else
|
||||
thetas = atan([-1/x(1),x(2)]);
|
||||
end
|
||||
theta = mean(thetas);
|
||||
xeg = tan(theta);
|
||||
if abs(diff(thetas)) > pi/2 % thetas are more than 90 degrees apart, and the 'mean of the two axes' should be rotated 90 degrees
|
||||
xeg = -1/xeg;
|
||||
end
|
||||
|
||||
%% Numerical search for best harmonic ratios product
|
||||
% Now we want to improve the solution, since the two above directions are
|
||||
% not necessarily orthogonal. Thus we want to find the orthogonal
|
||||
% directions ML = MLguess + x*APguess, and AP = APguess - x*MLguess,
|
||||
% for which the product of 'harmonic ratios' for the ML and AP directions
|
||||
% is maximal, and we do this by varying x, starting with the educated guess
|
||||
% above using power instead of amplitude:
|
||||
% HRML = ((OddHarmonics'.*(DFTMLG'+x*DFTAPG'))*(DFTMLG+x*DFTAPG))...
|
||||
% /((EvenHarmonics'.*(DFTMLG'+x*DFTAPG'))*(DFTMLG+x*DFTAPG));
|
||||
% HRAP = ((EvenHarmonics'.*(-x*DFTMLG'+DFTAPG'))*(-x*DFTMLG+DFTAPG))...
|
||||
% /((OddHarmonics'.*(-x*DFTMLG'+DFTAPG'))*(-x*DFTMLG+DFTAPG));
|
||||
% and
|
||||
% HR_Prod = HRML*HRAP = ...
|
||||
|
||||
% Now use amplitude
|
||||
Minus_HR_Prod = @(x) -real(...
|
||||
(OddHarmonics'*abs(DFTMLG+x*DFTAPG))...
|
||||
/(EvenHarmonics'*abs(DFTMLG+x*DFTAPG))...
|
||||
*(EvenHarmonics'*abs(-x*DFTMLG+DFTAPG))...
|
||||
/(OddHarmonics'*abs(-x*DFTMLG+DFTAPG)));
|
||||
[xopt1,Minus_HR_Prod_opt1,exitflag1]=fminsearch(Minus_HR_Prod,xeg);
|
||||
if exitflag1 == 0 && abs(xopt1) > 100 % optimum for theta beyond +- pi/2
|
||||
[xopt1a,Minus_HR_Prod_opt1a,exitflag1a]=fminsearch(Minus_HR_Prod,-xopt1);
|
||||
if ~(exitflag1a == 0 && abs(xopt1a) > 100)
|
||||
xopt1org = xopt1;
|
||||
xopt1 = xopt1a;
|
||||
Minus_HR_Prod_opt1 = Minus_HR_Prod_opt1a;
|
||||
end
|
||||
end
|
||||
options = optimset('LargeScale','off','GradObj','off','Display','notify');
|
||||
[xopt2,Minus_HR_Prod_opt2,exitflag2]=fminunc(Minus_HR_Prod,xopt1,options);
|
||||
if exitflag2 == 0 && abs(xopt2) > 100 % optimum for theta beyond +- pi/2
|
||||
[xopt2a,Minus_HR_Prod_opt2a,exitflag2a]=fminsearch(Minus_HR_Prod,-xopt2);
|
||||
if ~(exitflag2a == 0 && abs(xopt2a) > 100)
|
||||
xopt2 = xopt2a;
|
||||
Minus_HR_Prod_opt2 = Minus_HR_Prod_opt2a;
|
||||
end
|
||||
end
|
||||
|
||||
RML = (RMLGuess+xopt2*RAPGuess)/sqrt(1+xopt2^2);
|
||||
RAP = cross(RVT,RML); RAP = RAP/norm(RAP);
|
||||
|
||||
RT = [RVT,RML,RAP];
|
||||
if nargout > 1
|
||||
RotationMatrixT = RT;
|
||||
end
|
||||
RealignedAcc = Acc*RT;
|
||||
|
||||
if nargout > 2
|
||||
Flags = [exitflag1,exitflag2];
|
||||
end
|
||||
|
||||
if nargin > 2 && plotje
|
||||
Thetas = pi*(-0.49:0.01:0.49)';
|
||||
Xs = tan(Thetas);
|
||||
HR_Prod = nan(size(Xs));
|
||||
HRMLs = nan(size(Xs));
|
||||
for i=1:numel(Xs),
|
||||
HR_Prod(i,1) = -Minus_HR_Prod(Xs(i));
|
||||
HRMLs(i,1) = HRML(Xs(i));
|
||||
end
|
||||
figure();
|
||||
semilogy(Thetas,[HR_Prod,HRMLs.^2]);
|
||||
hold on;
|
||||
semilogy(atan(x),[HR1,HR2].^2,'r.');
|
||||
semilogy(atan([xopt1;xopt2]),-[Minus_HR_Prod_opt1;Minus_HR_Prod_opt2],'g.');
|
||||
semilogy(atan(xeg),-Minus_HR_Prod(xeg),'kx');
|
||||
end
|
||||
|
||||
%% attempt to solve it analytically:
|
||||
% % We can rewrite HR_Prod as:
|
||||
% % HR_Prod =
|
||||
% % ( (x.^2)*((OddHarmonics.*DFTAPG)'*DFTAPG) ...
|
||||
% % + x*((OddHarmonics.*DFTMLG)'*DFTAPG + (OddHarmonics.*DFTAPG)'*DFTMLG)...
|
||||
% % + ((OddHarmonics.*DFTMLG)'*DFTMLG) )...
|
||||
% % /
|
||||
% % ( (x.^2)*((EvenHarmonics.*DFTAPG)'*DFTAPG) ...
|
||||
% % + x*((EvenHarmonics.*DFTMLG)'*DFTAPG + (EvenHarmonics.*DFTAPG)'*DFTMLG)...
|
||||
% % + ((EvenHarmonics.*DFTMLG)'*DFTMLG) )
|
||||
% % *
|
||||
% % ( (x.^2)*((EvenHarmonics.*DFTMLG)'*DFTMLG) ...
|
||||
% % - x*((EvenHarmonics.*DFTMLG)'*DFTAPG + (EvenHarmonics.*DFTAPG)'*DFTMLG)...
|
||||
% % + ((EvenHarmonics.*DFTAPG)'*DFTAPG) )...
|
||||
% % /
|
||||
% % ( (x.^2)*((OddHarmonics.*DFTMLG)'*DFTMLG) ...
|
||||
% % - x*((OddHarmonics.*DFTMLG)'*DFTAPG + (OddHarmonics.*DFTAPG)'*DFTMLG)...
|
||||
% % + ((OddHarmonics.*DFTAPG)'*DFTAPG) )
|
||||
% %
|
||||
% % or by making these substitutions:
|
||||
% a2 = (OddHarmonics.*DFTAPG)'*DFTAPG;
|
||||
% a1 = (OddHarmonics.*DFTMLG)'*DFTAPG + (OddHarmonics.*DFTAPG)'*DFTMLG;
|
||||
% a0 = (OddHarmonics.*DFTMLG)'*DFTMLG;
|
||||
% c2 = (EvenHarmonics.*DFTAPG)'*DFTAPG;
|
||||
% c1 = (EvenHarmonics.*DFTMLG)'*DFTAPG + (EvenHarmonics.*DFTAPG)'*DFTMLG;
|
||||
% c0 = (EvenHarmonics.*DFTMLG)'*DFTMLG;
|
||||
% b2 = c0;
|
||||
% b1 = c1;
|
||||
% b0 = c2;
|
||||
% d2 = a0;
|
||||
% d1 = a1;
|
||||
% d0 = a2;
|
||||
% % we get:
|
||||
% % HR_Prod = (x.^2*a2 + x*a1 + a0) * (x.^2*b2 + x*b1 + b0) / (x.^2*c2 + x*c1 + c0) * (x.^2*d2 + x*d1 + d0)
|
||||
% % or:
|
||||
% % HR_Prod = N/D
|
||||
% % with
|
||||
% % N = ( x.^4*(a2*b2)...
|
||||
% % +x.^3*(a2*b1+a1*b2)...
|
||||
% % +x.^2*(a2*b0+a1*b1+a0*b2)...
|
||||
% % +x *(a1*b0+a0*b1)...
|
||||
% % + a0*b0 )...
|
||||
% % and with
|
||||
% % D = ( x.^4*(c2*d2)...
|
||||
% % +x.^3*(c2*d1+c1*d2)...
|
||||
% % +x.^2*(c2*d0+c1*d1+c0*d2)...
|
||||
% % +x *(c1*d0+c0*d1)...
|
||||
% % + c0*d0 )
|
||||
% % or D = ( x.^4*(a0*b0)...
|
||||
% % +x.^3*(a1*b0+a0*b1)...
|
||||
% % +x.^2*(a2*b0+a1*b1+a0*b2)...
|
||||
% % +x *(a2*b1+a1*b2)...
|
||||
% % + a2*b2 )
|
||||
% % or by substition of
|
||||
% n4 = a2*b2;
|
||||
% n3 = a2*b1+a1*b2;
|
||||
% n2 = a2*b0+a1*b1+a0*b2;
|
||||
% n1 = a1*b0+a0*b1;
|
||||
% n0 = a0*b0;
|
||||
% % d4 = n0;
|
||||
% % d3 = n1;
|
||||
% % d2 = n2;
|
||||
% % d1 = n3;
|
||||
% % d0 = n4;
|
||||
% % we get
|
||||
% % N = sum(ni*x^i) and D=sum(di*x^i)
|
||||
% %
|
||||
% % Then HR_Prod reaches a maximum or minimum when d/dx (N/D) = 0
|
||||
% % or (dN/dx)/D - N/(D^2)*(dD/dx) = 0
|
||||
% % or (dN/dx)*D - (dD/dx)*N = 0
|
||||
% % or
|
||||
% % x^7*(4*n4*d4 - 4*n4*d4)
|
||||
% % + x^6*(4*n4*d3 - 4*n3*d4 + 3*n3*d4 - 3*n4*d3)
|
||||
% % + x^5*(4*n4*d2 - 4*n2*d4 + 3*n3*d3 - 3*n3*d3 + 2*n2*d4 - 2*n4*d2)
|
||||
% % + x^4*(4*n4*d1 - 4*n1*d4 + 3*n3*d2 - 3*n2*d3 + 2*n2*d3 - 2*n3*d2 + 1*n1*d4 - 1*n4*d1)
|
||||
% % + x^3*(4*n4*d0 - 4*n0*d4 + 3*n3*d1 - 3*n1*d3 + 2*n2*d2 - 2*n2*d2 + 1*n1*d3 - 1*n3*d1)
|
||||
% % + x^2*( 3*n3*d0 - 3*n0*d3 + 2*n2*d1 - 2*n1*d2 + 1*n1*d2 - 1*n2*d1)
|
||||
% % + x^1*( 2*n2*d0 - 2*n0*d2 + 1*n1*d1 - 1*n1*d1)
|
||||
% % + x^0*( 1*n1*d0 - 1*n0*d1)
|
||||
% % = 0
|
||||
% % or
|
||||
% % x^6*(1*n4*d3 - 1*n3*d4)
|
||||
% % + x^5*(2*n4*d2 - 2*n2*d4)
|
||||
% % + x^4*(3*n4*d1 - 3*n1*d4 + 1*n3*d2 - 1*n2*d3)
|
||||
% % + x^3*(4*n4*d0 - 4*n0*d4 + 2*n3*d1 - 2*n1*d3)
|
||||
% % + x^2*( 3*n3*d0 - 3*n0*d3 + 1*n2*d1 - 1*n1*d2)
|
||||
% % + x^1*( 2*n2*d0 - 2*n0*d2)
|
||||
% % + x^0*( 1*n1*d0 - 1*n0*d1)
|
||||
% % = 0
|
||||
% % or
|
||||
% % (x^6+x^0) * (1*n4*n1 - 1*n3*n0)
|
||||
% % + (x^5+x^1) * (2*n4*n2 - 2*n2*n0)
|
||||
% % + (x^4+x^2) * (3*n4*n3 - 3*n1*n0 + 1*n3*n2 - 1*n2*n1)
|
||||
% % + x^3 * (4*n4*n4 - 4*n0*n0 + 2*n3*n3 - 2*n1*n1)
|
||||
% % = 0
|
||||
% % or
|
||||
% % (x^6+x^0) * (1*(a2*b2)*(a1*b0+a0*b1) - 1*(a2*b1+a1*b2)*(a0*b0))
|
||||
% % + (x^5+x^1) * (2*(a2*b2)*(a2*b0+a1*b1+a0*b2) - 2*(a2*b0+a1*b1+a0*b2)*(a0*b0))
|
||||
% % + (x^4+x^2) * (3*(a2*b2)*(a2*b1+a1*b2) - 3*(a1*b0+a0*b1)*(a0*b0) + 1*(a2*b1+a1*b2)*(a2*b0+a1*b1+a0*b2) - 1*(a2*b0+a1*b1+a0*b2)*(a1*b0+a0*b1))
|
||||
% % + x^3 * (4*(a2*b2)*(a2*b2) - 4*(a0*b0)*(a0*b0) + 2*(a2*b1+a1*b2)*(a2*b1+a1*b2) - 2*(a1*b0+a0*b1)*(a1*b0+a0*b1))
|
||||
% % = 0
|
||||
% % or
|
||||
% % (x^6+x^0) * (a2*b2*a1*b0 + a2*b2*b1*a0 - a2*b1*a0*b0 - b2*a1*a0*b0)
|
||||
% % + (x^5+x^1) * (2*(a2*a2*b2*b0+a2*b2*b2*a0+a2*b2*a1*b1) - 2*(a2*a0*b0*b0+b2*a0*a0*b0+a1*b1*a0*b0))
|
||||
% % + (x^4+x^2) * (3*a2*a2*b2*b1 +a2*a2*b1*b0 +3*a2*b2*b2*a1 +a2*b2*a1*b0 +a2*b2*b1*a0 +a2*a1*b1*b1 -a2*a1*b0*b0 -a2*a0*b1*b0 +b2*b2*a1*a0 +b2*a1*a1*b1 -b2*a1*a0*b0 -b2*b1*a0*a0 -a1*a1*b1*b0 -a1*b1*b1*a0 -3*a1*a0*b0*b0 -3*b1*a0*a0*b0)
|
||||
% % + x^3 * (4*a2*a2*b2*b2 +2*a2*a2*b1*b1 +4*a2*b2*a1*b1 +2*b2*b2*a1*a1 -2*a1*a1*b0*b0 -4*a1*b1*a0*b0 -2*b1*b1*a0*a0 -4*a0*a0*b0*b0)
|
||||
% % = 0
|
||||
% % which can probably not be solved analytically?.
|
||||
|
118
SpatioTemporalGaitParameters.m
Normal file
118
SpatioTemporalGaitParameters.m
Normal file
@ -0,0 +1,118 @@
|
||||
function [ResultStruct] = SpatioTemporalGaitParameters(dataAccCut_filt,StrideTimeSamples,ApplyRealignment,LegLength,FS,IgnoreMinMaxStrides,ResultStruct);
|
||||
|
||||
Cutoff = 0.1;
|
||||
MinDist = floor(0.7*0.5*StrideTimeSamples); % Use StrideTimeSamples estimated above
|
||||
DatalFilt = dataAccCut_filt;
|
||||
|
||||
% From acceleration to velocity
|
||||
Vel = cumsum(detrend(DatalFilt,'constant'))/FS;
|
||||
[B,A] = butter(2,Cutoff/(FS/2),'high');
|
||||
Pos = cumsum(filtfilt(B,A,Vel))/FS;
|
||||
PosFilt = filtfilt(B,A,Pos);
|
||||
PosFiltVT = PosFilt(:,1);
|
||||
|
||||
% Find minima and maxima in vertical position
|
||||
|
||||
% if ~ApplyRealignment % Signals were not realigned, so it has to be done here
|
||||
% MeanAcc = mean(AccLoco);
|
||||
% VT = MeanAcc'/norm(MeanAcc);
|
||||
% PosFiltVT = PosFilt*VT;
|
||||
% end
|
||||
|
||||
% Find minima and maxima in vertical position
|
||||
[PosPks,PosLocs] = findpeaks(PosFiltVT(:,1),'minpeakdistance',MinDist);
|
||||
[NegPks,NegLocs] = findpeaks(-PosFiltVT(:,1),'minpeakdistance',MinDist);
|
||||
NegPks = -NegPks;
|
||||
if isempty(PosPks) && isempty(NegPks)
|
||||
PksAndLocs = zeros(0,3);
|
||||
else
|
||||
PksAndLocs = sortrows([PosPks,PosLocs,ones(size(PosPks)) ; NegPks,NegLocs,-ones(size(NegPks))], 2);
|
||||
end
|
||||
% Correct events for two consecutive maxima or two consecutive minima
|
||||
Events = PksAndLocs(:,2);
|
||||
NewEvents = PksAndLocs(:,2);
|
||||
Signs = PksAndLocs(:,3);
|
||||
FalseEventsIX = find(diff(Signs)==0);
|
||||
PksAndLocsToAdd = zeros(0,3);
|
||||
PksAndLocsToAddNr = 0;
|
||||
for i=1:numel(FalseEventsIX),
|
||||
FIX = FalseEventsIX(i);
|
||||
if FIX <= 2
|
||||
% remove the event
|
||||
NewEvents(FIX) = nan;
|
||||
elseif FIX >= numel(Events)-2
|
||||
% remove the next event
|
||||
NewEvents(FIX+1) = nan;
|
||||
else
|
||||
StrideTimesWhenAdding = [Events(FIX+1)-Events(FIX-2),Events(FIX+3)-Events(FIX)];
|
||||
StrideTimesWhenRemoving = Events(FIX+3)-Events(FIX-2);
|
||||
if max(abs(StrideTimesWhenAdding-StrideTimeSamples)) < abs(StrideTimesWhenRemoving-StrideTimeSamples)
|
||||
% add an event
|
||||
[M,IX] = min(Signs(FIX)*PosFiltVT((Events(FIX)+1):(Events(FIX+1)-1)));
|
||||
PksAndLocsToAddNr = PksAndLocsToAddNr+1;
|
||||
PksAndLocsToAdd(PksAndLocsToAddNr,:) = [M,Events(FIX)+IX,-Signs(FIX)];
|
||||
else
|
||||
% remove an event
|
||||
if FIX >= 5 && FIX <= numel(Events)-5
|
||||
ExpectedEvent = (Events(FIX-4)+Events(FIX+5))/2;
|
||||
else
|
||||
ExpectedEvent = (Events(FIX-2)+Events(FIX+3))/2;
|
||||
end
|
||||
if abs(Events(FIX)-ExpectedEvent) > abs(Events(FIX+1)-ExpectedEvent)
|
||||
NewEvents(FIX) = nan;
|
||||
else
|
||||
NewEvents(FIX+1) = nan;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
PksAndLocsCorrected = sortrows([PksAndLocs(~isnan(NewEvents),:);PksAndLocsToAdd],2);
|
||||
% Find delta height and delta time
|
||||
DH = abs(diff(PksAndLocsCorrected(:,1),1,1));
|
||||
DT = diff(PksAndLocsCorrected(:,2),1,1);
|
||||
% Correct outliers in delta h
|
||||
MaxDH = min(median(DH)+3*mad(DH,1),LegLength/2);
|
||||
DH(DH>MaxDH) = MaxDH;
|
||||
% Estimate total length and speed
|
||||
% (Use delta h and delta t to calculate walking speed: use formula from
|
||||
% Z&H, but divide by 2 (skip factor 2)since we get the difference twice
|
||||
% each step, and multiply by 1.25 which is the factor suggested by Z&H)
|
||||
HalfStepLen = 1.25*sqrt(2*LegLength*DH-DH.^2);
|
||||
Distance = sum(HalfStepLen);
|
||||
WalkingSpeedMean = Distance/(sum(DT)/FS);
|
||||
% Estimate variabilities between strides
|
||||
StrideLengths = HalfStepLen(1:end-3) + HalfStepLen(2:end-2) + HalfStepLen(3:end-1) + HalfStepLen(4:end);
|
||||
StrideTimes = PksAndLocsCorrected(5:end,2)-PksAndLocsCorrected(1:end-4,2);
|
||||
StrideSpeeds = StrideLengths./(StrideTimes/FS);
|
||||
WSS = nan(1,4);
|
||||
STS = nan(1,4);
|
||||
for i=1:4,
|
||||
STS(i) = std(StrideTimes(i:4:end))/FS;
|
||||
WSS(i) = std(StrideSpeeds(i:4:end));
|
||||
end
|
||||
|
||||
StepLengthMean=mean(StrideLengths);
|
||||
|
||||
StrideTimeVariability = min(STS);
|
||||
StrideSpeedVariability = min(WSS);
|
||||
StrideLengthVariability = std(StrideLengths);
|
||||
% Estimate Stride time variability and stride speed variability by removing highest and lowest part
|
||||
if ~isinteger(IgnoreMinMaxStrides)
|
||||
IgnoreMinMaxStrides = ceil(IgnoreMinMaxStrides*size(StrideTimes,1));
|
||||
end
|
||||
StrideTimesSorted = sort(StrideTimes);
|
||||
StrideTimeVariabilityOmitOutlier = std(StrideTimesSorted(1+IgnoreMinMaxStrides:end-IgnoreMinMaxStrides));
|
||||
StrideSpeedSorted = sort(StrideSpeeds);
|
||||
StrideSpeedVariabilityOmitOutlier = std(StrideSpeedSorted(1+IgnoreMinMaxStrides:end-IgnoreMinMaxStrides));
|
||||
StrideLengthsSorted = sort(StrideLengths);
|
||||
StrideLengthVariabilityOmitOutlier = std(StrideLengthsSorted(1+IgnoreMinMaxStrides:end-IgnoreMinMaxStrides));
|
||||
|
||||
ResultStruct.StepLengthMean = StepLengthMean;
|
||||
ResultStruct.Distance = Distance;
|
||||
ResultStruct.WalkingSpeedMean = WalkingSpeedMean;
|
||||
ResultStruct.StrideTimeVariability = StrideTimeVariability;
|
||||
ResultStruct.StrideSpeedVariability = StrideSpeedVariability;
|
||||
ResultStruct.StrideLengthVariability = StrideLengthVariability;
|
||||
ResultStruct.StrideTimeVariabilityOmitOutlier = StrideTimeVariabilityOmitOutlier;
|
||||
ResultStruct.StrideSpeedVariabilityOmitOutlier = StrideSpeedVariabilityOmitOutlier;
|
||||
ResultStruct.StrideLengthVariabilityOmitOutlier = StrideLengthVariabilityOmitOutlier;
|
15
SpectralAnalysisGaitfunc.m
Normal file
15
SpectralAnalysisGaitfunc.m
Normal file
@ -0,0 +1,15 @@
|
||||
function [ResultStruct] = SpectralAnalysisGaitfunc(dataAccCut_filt,WindowLen,FS,N_Harm,LowFrequentPowerThresholds,AccVectorLen,ResultStruct)
|
||||
|
||||
P=zeros(0,size(dataAccCut_filt,2));
|
||||
|
||||
for i=1:size(dataAccCut_filt,2)
|
||||
[P1,~] = pwelch(dataAccCut_filt(:,i),hamming(WindowLen),[],WindowLen,FS);
|
||||
[P2,F] = pwelch(dataAccCut_filt(end:-1:1,i),hamming(WindowLen),[],WindowLen,FS);
|
||||
P(1:numel(P1),i) = (P1+P2)/2;
|
||||
end
|
||||
dF = F(2)-F(1);
|
||||
|
||||
% Calculate stride frequency and peak widths
|
||||
[StrideFrequency, ~, PeakWidth, MeanNormalizedPeakWidth] = StrideFrequencyRispen(P,F);
|
||||
[ResultStruct] = HarmonicityFrequency(dataAccCut_filt, P,F, StrideFrequency,dF,LowFrequentPowerThresholds,N_Harm,FS,AccVectorLen,ResultStruct);
|
||||
end
|
35
StepDetectionFunc.m
Normal file
35
StepDetectionFunc.m
Normal file
@ -0,0 +1,35 @@
|
||||
function [dataAccCut,dataAccCut_filt,StrideTimeSamples] = StepDetectionFunc(FS,ApplyRemoveSteps,dataAcc,dataAcc_filt,StrideTimeRange)
|
||||
%% Step detecection
|
||||
% Determines the number of steps in the signal so that the first 30 and last 30 steps in the signal can be removed
|
||||
|
||||
ResultStruct = struct();
|
||||
|
||||
if ApplyRemoveSteps
|
||||
|
||||
% In order to run the step detection script we first need to run an autocorrelation function;
|
||||
[ResultStruct] = AutocorrStrides(dataAcc_filt,FS,StrideTimeRange,ResultStruct);
|
||||
|
||||
% StrideTimeSamples is needed as an input for the stepcountFunc;
|
||||
StrideTimeSamples = ResultStruct.StrideTimeSamples;
|
||||
|
||||
% Calculate the number of steps;
|
||||
[PksAndLocsCorrected] = StepcountFunc(dataAcc_filt,StrideTimeSamples,FS);
|
||||
% This function selects steps based on negative and positive values.
|
||||
% However to determine the steps correctly we only need one of these;
|
||||
LocsSteps = PksAndLocsCorrected(1:2:end,2);
|
||||
|
||||
%% Cut data & remove currents results
|
||||
% Remove 20 steps in the beginning and end of data
|
||||
dataAccCut = dataAcc(LocsSteps(31):LocsSteps(end-30),:);
|
||||
dataAccCut_filt = dataAcc_filt(LocsSteps(31):LocsSteps(end-30),:);
|
||||
|
||||
% Clear currently saved results from Autocorrelation Analysis
|
||||
|
||||
clear ResultStruct;
|
||||
clear PksAndLocsCorrected;
|
||||
clear LocsSteps;
|
||||
|
||||
else;
|
||||
dataAccCut = dataAcc;
|
||||
dataAccCut_filt = dataAcc_filt;
|
||||
end
|
65
StepcountFunc.m
Normal file
65
StepcountFunc.m
Normal file
@ -0,0 +1,65 @@
|
||||
function [PksAndLocsCorrected] = StepcountFunc(dataAcc_filt,StrideTimeSamples,FS);
|
||||
|
||||
% Step count funciton extracted from other function called:
|
||||
|
||||
Cutoff = 0.1;
|
||||
MinDist = floor(0.7*0.5*StrideTimeSamples); % Use StrideTimeSamples estimated above
|
||||
DataLFilt = dataAcc_filt;
|
||||
|
||||
% From acceleration to
|
||||
Vel = cumsum(detrend(DataLFilt,'constant'))/FS;
|
||||
[B,A] = butter(2,Cutoff/(FS/2),'high');
|
||||
Pos = cumsum(filtfilt(B,A,Vel))/FS;
|
||||
PosFilt = filtfilt(B,A,Pos);
|
||||
PosFiltVT = PosFilt(:,1);
|
||||
|
||||
% Find minima and maxima in vertical position
|
||||
[PosPks,PosLocs] = findpeaks(PosFiltVT(:,1),'minpeakdistance',MinDist);
|
||||
[NegPks,NegLocs] = findpeaks(-PosFiltVT(:,1),'minpeakdistance',MinDist);
|
||||
NegPks = -NegPks;
|
||||
if isempty(PosPks) && isempty(NegPks)
|
||||
PksAndLocs = zeros(0,3);
|
||||
else
|
||||
PksAndLocs = sortrows([PosPks,PosLocs,ones(size(PosPks)) ; NegPks,NegLocs,-ones(size(NegPks))], 2);
|
||||
end
|
||||
% Correct events for two consecutive maxima or two consecutive minima
|
||||
Events = PksAndLocs(:,2);
|
||||
NewEvents = PksAndLocs(:,2);
|
||||
Signs = PksAndLocs(:,3);
|
||||
FalseEventsIX = find(diff(Signs)==0);
|
||||
PksAndLocsToAdd = zeros(0,3);
|
||||
PksAndLocsToAddNr = 0;
|
||||
for i=1:numel(FalseEventsIX),
|
||||
FIX = FalseEventsIX(i);
|
||||
if FIX <= 2
|
||||
% remove the event
|
||||
NewEvents(FIX) = nan;
|
||||
elseif FIX >= numel(Events)-2
|
||||
% remove the next event
|
||||
NewEvents(FIX+1) = nan;
|
||||
else
|
||||
StrideTimesWhenAdding = [Events(FIX+1)-Events(FIX-2),Events(FIX+3)-Events(FIX)];
|
||||
StrideTimesWhenRemoving = Events(FIX+3)-Events(FIX-2);
|
||||
if max(abs(StrideTimesWhenAdding-StrideTimeSamples)) < abs(StrideTimesWhenRemoving-StrideTimeSamples)
|
||||
% add an event
|
||||
[M,IX] = min(Signs(FIX)*PosFiltVT((Events(FIX)+1):(Events(FIX+1)-1)));
|
||||
PksAndLocsToAddNr = PksAndLocsToAddNr+1;
|
||||
PksAndLocsToAdd(PksAndLocsToAddNr,:) = [M,Events(FIX)+IX,-Signs(FIX)];
|
||||
else
|
||||
% remove an event
|
||||
if FIX >= 5 && FIX <= numel(Events)-5
|
||||
ExpectedEvent = (Events(FIX-4)+Events(FIX+5))/2;
|
||||
else
|
||||
ExpectedEvent = (Events(FIX-2)+Events(FIX+3))/2;
|
||||
end
|
||||
if abs(Events(FIX)-ExpectedEvent) > abs(Events(FIX+1)-ExpectedEvent)
|
||||
NewEvents(FIX) = nan;
|
||||
else
|
||||
NewEvents(FIX+1) = nan;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
PksAndLocsCorrected = sortrows([PksAndLocs(~isnan(NewEvents),:);PksAndLocsToAdd],2);
|
||||
|
||||
end
|
150
StrideFrequencyFrom3dAcc.m
Normal file
150
StrideFrequencyFrom3dAcc.m
Normal file
@ -0,0 +1,150 @@
|
||||
function [StrideFrequency, QualityInd, PeakWidth, MeanNormalizedPeakWidth] = StrideFrequencyFrom3dAcc(AccXYZ, F)
|
||||
|
||||
%% Description
|
||||
% Estimate stride frequency in 3d accelerometer data, using multi-taper and
|
||||
% pwelch spectral densities
|
||||
%
|
||||
% Input:
|
||||
% AccXYZ: a three-dimensional time series with trunk accelerations
|
||||
% FS: the sample frequency of the time series
|
||||
% StrideFreqGuess: a first guess of the stride frequency
|
||||
%
|
||||
% Output:
|
||||
% StrideFrequency: the estimated stride frequency
|
||||
% QualityInd: a number (0-1, 0=no confidence, 1=fully confident) indicating how much confidence we have in the
|
||||
% estimated stride frequency
|
||||
|
||||
%% Copyright
|
||||
% COPYRIGHT (c) 2012 Sietse Rispens, VU University Amsterdam
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
%% Author
|
||||
% Sietse Rispens
|
||||
|
||||
%% History
|
||||
% February 2013, version 1.1, adapted from StrideFrequencyFrom3dAcc
|
||||
|
||||
%% Check input
|
||||
if size(AccXYZ,2) ~= 3
|
||||
error('AccXYZ must be 3-d time series, i.e. contain 3 columns');
|
||||
elseif size(AccXYZ,1) < 10*F
|
||||
error('AccXYZ must be at least ten seconds long');
|
||||
end
|
||||
|
||||
|
||||
%% Get PSD
|
||||
if numel(F) == 1, % Calculate the PSD from time series AccXYZ, F is the sample frequency
|
||||
AccFilt = detrend(AccXYZ,'constant'); % Detrend data to get rid of DC component in most of the specific windows
|
||||
LenPSD = 10*F;
|
||||
for i=1:3,
|
||||
[P1,Fwf] = pwelch(AccFilt(:,i),hamming(LenPSD),[],LenPSD,F);
|
||||
[P2,Fwf] = pwelch(AccFilt(end:-1:1,i),hamming(LenPSD),[],LenPSD,F);
|
||||
Pwf(:,i) = (P1+P2)/2;
|
||||
end
|
||||
elseif numel(F)==size(AccXYZ,1), % F are the frequencies of the power spectrum AccXYZ
|
||||
Fwf = F;
|
||||
Pwf = AccXYZ;
|
||||
end
|
||||
Pwf(:,4) = sum(Pwf,2);
|
||||
|
||||
|
||||
%% Estimate stride frequency
|
||||
% set parameters
|
||||
HarmNr = [2 1 2];
|
||||
CommonRange = [0.6 1.2];
|
||||
% Get modal frequencies and the 'mean freq. of the peak'
|
||||
for i=1:4,
|
||||
MF1I = find([zeros(5,1);Pwf(6:end,i)]==max([zeros(5,1);Pwf(6:end,i)]),1);
|
||||
MF1 = Fwf(MF1I,1);
|
||||
IndAround = Fwf>=MF1*0.5 & Fwf<=MF1*1.5;
|
||||
MeanAround = mean(Pwf(IndAround,i));
|
||||
PeakBeginI = find(IndAround & Fwf<MF1 & Pwf(:,i) < mean([MeanAround,Pwf(MF1I,i)]),1,'last');
|
||||
PeakEndI = find(IndAround & Fwf>MF1 & Pwf(:,i) < mean([MeanAround,Pwf(MF1I,i)]),1,'first');
|
||||
if isempty(PeakBeginI), PeakBeginI = find(IndAround,1,'first'); end
|
||||
if isempty(PeakEndI), PeakEndI = find(IndAround,1,'last'); end
|
||||
ModalF(i) = sum(Fwf(PeakBeginI:PeakEndI,1).*Pwf(PeakBeginI:PeakEndI,i))/sum(Pwf(PeakBeginI:PeakEndI,i));
|
||||
if i==4
|
||||
HarmNr(4) = HarmNr(find(Pwf(MF1I,1:3)==max(Pwf(MF1I,1:3)),1));
|
||||
end
|
||||
end
|
||||
% Get stride frequency and quality indicator from modal frequencies
|
||||
StrFreqFirstGuesses = ModalF./HarmNr;
|
||||
StdOverMean = std(StrFreqFirstGuesses)/mean(StrFreqFirstGuesses);
|
||||
StrideFrequency1 = median(StrFreqFirstGuesses(1:3));
|
||||
if StrideFrequency1 > CommonRange(2) && min(StrFreqFirstGuesses(1:3)) < CommonRange(2) && min(StrFreqFirstGuesses(1:3)) > CommonRange(1)
|
||||
StrideFrequency1 = min(StrFreqFirstGuesses(1:3));
|
||||
end
|
||||
if StrideFrequency1 < CommonRange(1) && max(StrFreqFirstGuesses(1:3)) > CommonRange(1) && max(StrFreqFirstGuesses(1:3)) < CommonRange(2)
|
||||
StrideFrequency1 = min(StrFreqFirstGuesses(1:3));
|
||||
end
|
||||
HarmGuess = ModalF/StrideFrequency1;
|
||||
StdHarmGuessRoundErr = std(HarmGuess - round(HarmGuess));
|
||||
if StdOverMean < 0.1
|
||||
QI1 = 1;
|
||||
StrideFrequency = mean(StrFreqFirstGuesses);
|
||||
else
|
||||
if StdHarmGuessRoundErr < 0.1 && all(round(HarmGuess) >= 1)
|
||||
QI1 = 0.5;
|
||||
StrideFrequency = mean(ModalF./round(HarmGuess));
|
||||
else
|
||||
QI1 = 0;
|
||||
StrideFrequency = StrideFrequency1;
|
||||
end
|
||||
end
|
||||
if nargout >= 2
|
||||
QualityInd = QI1;
|
||||
end
|
||||
|
||||
if nargout >= 3
|
||||
N_Harm = 20;
|
||||
PeakWidth = nan(1,3);
|
||||
if nargout >= 4
|
||||
MeanNormalizedPeakWidth = nan(1,3);
|
||||
end
|
||||
%% Get (mean) widths of harmonic peaks
|
||||
for i=1:3,
|
||||
WidthHarm = nan(1,N_Harm);
|
||||
PowerHarm = nan(1,N_Harm);
|
||||
for HarmonicNr = 1:N_Harm,
|
||||
FreqRangeIndices = ...
|
||||
Fwf >= StrideFrequency*(HarmonicNr-0.5) ...
|
||||
& Fwf <= StrideFrequency*(HarmonicNr+0.5);
|
||||
PeakPower = sum(Pwf(FreqRangeIndices,i));
|
||||
PeakMean = sum(Pwf(FreqRangeIndices,i).*Fwf(FreqRangeIndices))/PeakPower;
|
||||
PeakMeanSquare = sum(Pwf(FreqRangeIndices,i).*Fwf(FreqRangeIndices).^2)/PeakPower;
|
||||
WidthHarm(HarmonicNr) = sqrt(PeakMeanSquare-PeakMean.^2);
|
||||
PowerHarm(HarmonicNr) = PeakPower;
|
||||
end
|
||||
PeakWidth(i) = WidthHarm(HarmNr(i)); % Take the 1st or 2nd harmonic width as original measure
|
||||
if nargout >= 4
|
||||
MeanNormalizedPeakWidth(i) = sum(WidthHarm./(1:N_Harm).*PowerHarm)/sum(PowerHarm);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if nargout == 0
|
||||
IXplotw = Fwf<10;
|
||||
figure();
|
||||
for i=1:3,
|
||||
subplot(2,2,i);
|
||||
plot(Fwf(IXplotw,1),Pwf(IXplotw,i));
|
||||
end
|
||||
subplot(2,2,4);
|
||||
plot(Fwf(IXplotw,1),Pwf(IXplotw,1:3));
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
150
StrideFrequencyRispen.m
Normal file
150
StrideFrequencyRispen.m
Normal file
@ -0,0 +1,150 @@
|
||||
function [StrideFrequency, QualityInd, PeakWidth, MeanNormalizedPeakWidth] = StrideFrequencyRispen(AccXYZ, F)
|
||||
|
||||
%% Description
|
||||
% Estimate stride frequency in 3d accelerometer data, using multi-taper and
|
||||
% pwelch spectral densities
|
||||
%
|
||||
% Input:
|
||||
% AccXYZ: a three-dimensional time series with trunk accelerations
|
||||
% FS: the sample frequency of the time series
|
||||
% StrideFreqGuess: a first guess of the stride frequency
|
||||
%
|
||||
% Output:
|
||||
% StrideFrequency: the estimated stride frequency
|
||||
% QualityInd: a number (0-1, 0=no confidence, 1=fully confident) indicating how much confidence we have in the
|
||||
% estimated stride frequency
|
||||
|
||||
%% Copyright
|
||||
% COPYRIGHT (c) 2012 Sietse Rispens, VU University Amsterdam
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
%% Author
|
||||
% Sietse Rispens
|
||||
|
||||
%% History
|
||||
% February 2013, version 1.1, adapted from StrideFrequencyFrom3dAcc
|
||||
|
||||
%% Check input
|
||||
if size(AccXYZ,2) ~= 3
|
||||
error('AccXYZ must be 3-d time series, i.e. contain 3 columns');
|
||||
elseif size(AccXYZ,1) < 10*F
|
||||
error('AccXYZ must be at least ten seconds long');
|
||||
end
|
||||
|
||||
|
||||
%% Get PSD
|
||||
if numel(F) == 1, % Calculate the PSD from time series AccXYZ, F is the sample frequency
|
||||
AccFilt = detrend(AccXYZ,'constant'); % Detrend data to get rid of DC component in most of the specific windows
|
||||
LenPSD = 10*F;
|
||||
for i=1:3,
|
||||
[P1,Fwf] = pwelch(AccFilt(:,i),hamming(LenPSD),[],LenPSD,F);
|
||||
[P2,Fwf] = pwelch(AccFilt(end:-1:1,i),hamming(LenPSD),[],LenPSD,F);
|
||||
Pwf(:,i) = (P1+P2)/2;
|
||||
end
|
||||
elseif numel(F)==size(AccXYZ,1), % F are the frequencies of the power spectrum AccXYZ
|
||||
Fwf = F;
|
||||
Pwf = AccXYZ;
|
||||
end
|
||||
Pwf(:,4) = sum(Pwf,2);
|
||||
|
||||
|
||||
%% Estimate stride frequency
|
||||
% set parameters
|
||||
HarmNr = [2 1 2];
|
||||
CommonRange = [0.6 1.2];
|
||||
% Get modal frequencies and the 'mean freq. of the peak'
|
||||
for i=1:4,
|
||||
MF1I = find([zeros(5,1);Pwf(6:end,i)]==max([zeros(5,1);Pwf(6:end,i)]),1);
|
||||
MF1 = Fwf(MF1I,1);
|
||||
IndAround = Fwf>=MF1*0.5 & Fwf<=MF1*1.5;
|
||||
MeanAround = mean(Pwf(IndAround,i));
|
||||
PeakBeginI = find(IndAround & Fwf<MF1 & Pwf(:,i) < mean([MeanAround,Pwf(MF1I,i)]),1,'last');
|
||||
PeakEndI = find(IndAround & Fwf>MF1 & Pwf(:,i) < mean([MeanAround,Pwf(MF1I,i)]),1,'first');
|
||||
if isempty(PeakBeginI), PeakBeginI = find(IndAround,1,'first'); end
|
||||
if isempty(PeakEndI), PeakEndI = find(IndAround,1,'last'); end
|
||||
ModalF(i) = sum(Fwf(PeakBeginI:PeakEndI,1).*Pwf(PeakBeginI:PeakEndI,i))/sum(Pwf(PeakBeginI:PeakEndI,i));
|
||||
if i==4
|
||||
HarmNr(4) = HarmNr(find(Pwf(MF1I,1:3)==max(Pwf(MF1I,1:3)),1));
|
||||
end
|
||||
end
|
||||
% Get stride frequency and quality indicator from modal frequencies
|
||||
StrFreqFirstGuesses = ModalF./HarmNr;
|
||||
StdOverMean = std(StrFreqFirstGuesses)/mean(StrFreqFirstGuesses);
|
||||
StrideFrequency1 = median(StrFreqFirstGuesses(1:3));
|
||||
if StrideFrequency1 > CommonRange(2) && min(StrFreqFirstGuesses(1:3)) < CommonRange(2) && min(StrFreqFirstGuesses(1:3)) > CommonRange(1)
|
||||
StrideFrequency1 = min(StrFreqFirstGuesses(1:3));
|
||||
end
|
||||
if StrideFrequency1 < CommonRange(1) && max(StrFreqFirstGuesses(1:3)) > CommonRange(1) && max(StrFreqFirstGuesses(1:3)) < CommonRange(2)
|
||||
StrideFrequency1 = min(StrFreqFirstGuesses(1:3));
|
||||
end
|
||||
HarmGuess = ModalF/StrideFrequency1;
|
||||
StdHarmGuessRoundErr = std(HarmGuess - round(HarmGuess));
|
||||
if StdOverMean < 0.1
|
||||
QI1 = 1;
|
||||
StrideFrequency = mean(StrFreqFirstGuesses);
|
||||
else
|
||||
if StdHarmGuessRoundErr < 0.1 && all(round(HarmGuess) >= 1)
|
||||
QI1 = 0.5;
|
||||
StrideFrequency = mean(ModalF./round(HarmGuess));
|
||||
else
|
||||
QI1 = 0;
|
||||
StrideFrequency = StrideFrequency1;
|
||||
end
|
||||
end
|
||||
if nargout >= 2
|
||||
QualityInd = QI1;
|
||||
end
|
||||
|
||||
if nargout >= 3
|
||||
N_Harm = 20;
|
||||
PeakWidth = nan(1,3);
|
||||
if nargout >= 4
|
||||
MeanNormalizedPeakWidth = nan(1,3);
|
||||
end
|
||||
%% Get (mean) widths of harmonic peaks
|
||||
for i=1:3,
|
||||
WidthHarm = nan(1,N_Harm);
|
||||
PowerHarm = nan(1,N_Harm);
|
||||
for HarmonicNr = 1:N_Harm,
|
||||
FreqRangeIndices = ...
|
||||
Fwf >= StrideFrequency*(HarmonicNr-0.5) ...
|
||||
& Fwf <= StrideFrequency*(HarmonicNr+0.5);
|
||||
PeakPower = sum(Pwf(FreqRangeIndices,i));
|
||||
PeakMean = sum(Pwf(FreqRangeIndices,i).*Fwf(FreqRangeIndices))/PeakPower;
|
||||
PeakMeanSquare = sum(Pwf(FreqRangeIndices,i).*Fwf(FreqRangeIndices).^2)/PeakPower;
|
||||
WidthHarm(HarmonicNr) = sqrt(PeakMeanSquare-PeakMean.^2);
|
||||
PowerHarm(HarmonicNr) = PeakPower;
|
||||
end
|
||||
PeakWidth(i) = WidthHarm(HarmNr(i)); % Take the 1st or 2nd harmonic width as original measure
|
||||
if nargout >= 4
|
||||
MeanNormalizedPeakWidth(i) = sum(WidthHarm./(1:N_Harm).*PowerHarm)/sum(PowerHarm);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if nargout == 0
|
||||
IXplotw = Fwf<10;
|
||||
figure();
|
||||
for i=1:3,
|
||||
subplot(2,2,i);
|
||||
plot(Fwf(IXplotw,1),Pwf(IXplotw,i));
|
||||
end
|
||||
subplot(2,2,4);
|
||||
plot(Fwf(IXplotw,1),Pwf(IXplotw,1:3));
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
1
div_calc.m
Normal file
1
div_calc.m
Normal file
@ -0,0 +1 @@
|
||||
function Divergence=div_calc(state,ws_sec,fs,period_sec,progress)
% calculate divergence curve (needed for max lyapunov exponent).
% Input:
% state: state space
% ws_sec: window size over which divergence will be calculated(in seconds)
% fs: sample frequency
% period_sec: indicates period of time-wise near samples to exclude in
% nearest neighbour search
% progress: show progress or not
%
% Output:
% Divergence: the divergence curve
%
% History:
% August 2011: v1, Sietse Rispens based on version KvS en SMB
% 1 November 2012, Sietse Rispens: Use div_calc_shorttimeseries for short
% time series
if size(state,1) <= 10000
Divergence=div_calc_shorttimeseries(state,ws_sec,fs,period_sec,progress);
return;
end
[N,D]=size(state);
ws=round(ws_sec*fs);
Period=round(period_sec*fs);
if N<ws, error('ws shall not be larger than N'); end
NCompleteWindow = N-ws+1;
Divergence_sum=zeros(1,ws);
Divergence_count=zeros(1,ws);
SumStd = sqrt(sum(std(state,1).^2));
DistLimStart = SumStd*nthroot(1/NCompleteWindow,D);
DistLim = DistLimStart;
TreeRoot=kdtree_build(state(1:NCompleteWindow,:));
k0 = 2; % The initial number of nearest neighbors to look for
kmax = min(2*Period + 2, NCompleteWindow); % The maximum number of nearest neighbors to look for
for i = 1:NCompleteWindow
StateI = state(i,:);
if ~isnan(StateI)
DistLim = DistLim*nthroot(1/5,D);
DistLim = min(DistLim,DistLimStart);
Index = [];
k=k0;
kmaxreached = 0;
while isempty(Index) && ~kmaxreached
[Idx, Dist] = kdtree_k_nearest_neighbors(TreeRoot,StateI,k);
Dist(abs(i-Idx) < Period)=nan;
Index = Idx(find(Dist==min(Dist),1,'first'));
if k >= kmax
kmaxreached = 1;
elseif isempty(Index)
k = min(kmax,k*2);
end
end
if ~isempty(Index)
if i+ws>N || Index+ws>N
% do not use these data
else
if ~isempty(Index)
DistCurve = log(sqrt(sum((state(i:i+ws-1,:)-state(Index:Index+ws-1,:)).^2,2)));
NotNan = ~isnan(DistCurve);
Divergence_sum(NotNan) = Divergence_sum(NotNan) + DistCurve(NotNan)';
Divergence_count(NotNan) = Divergence_count(NotNan) + 1;
end
end
end
end
if progress > 0
if mod(i,round(progress))==0
if i>round(progress)
fprintf('\b\b\b\b\b\b\b\b\b\b');
end
fprintf('i=%8d', i);
end
end
end
kdtree_delete(TreeRoot); % Free the pointer to k-d-tree
if progress > 0
fprintf('\n');
end
Divergence= (Divergence_sum./Divergence_count);
|
1
div_calc_shorttimeseries.m
Normal file
1
div_calc_shorttimeseries.m
Normal file
@ -0,0 +1 @@
|
||||
function divergence=div_calc_shorttimeseries(state,ws_sec,fs,period_sec,progress)
% calculate local dynamic stability (max lyapunov exponent).
% Input:
% state: appropriate state space
% ws: window size over which divergence should be calculated(in seconds)
% fs: sample frequency
% period: ..... (can be dominant period in the signal(in seconds))
% plotje: show a graph.
%
% Note that in this version ws should be larger than 4*period, as the long
% term divergence is calculated from 4*period to ws*fs.
%
% Output:
% divergence: the divergence curve
% lds: the 2 estimates of the maximum lyapunov exponents (short term and long term)
%
% Earlier versions by KvS en SMB. Made the routine faster, 14/01/2011, Sietse Rispens
% Use less memory to prevent memory error, 01/02/2011, Sietse Rispens
% Take mean of the log of divergence instead of log of the mean of divergence, 26/05/2011, Sietse Rispens
% Allow non-integer ws_sec and period_sec and change fitting-range, 01/08/2011, Sietse Rispens
% Do not try to find neighbours that need to be followed beyond end of time series, 01/11/2012, Sietse Rispens
% Exclude neighbours closer than Period in time (instead of period/2), 01/11/2012, Sietse Rispens
[m,n]=size(state);
ws=round(ws_sec*fs);
period=round(period_sec*fs);
mcompletewindow = m-ws+1;
statecw = state(1:mcompletewindow,:);
divergence_sum=zeros(1,ws);
divergence_count=zeros(1,ws);
diff_state = statecw*0;
diff_state_sqr = diff_state;
diff_total = zeros(size(diff_state,1),1);
for i = 1:mcompletewindow
if ~isnan(state(i,:))
start=round(max([1,i-period+1]));
stop=round(min([mcompletewindow,i+period-1]));
for j=1:n,
diff_state(:,j) = statecw(:,j)-statecw(i,j);
diff_state_sqr(:,j)=diff_state(:,j).^2;
end
diff_total(:,1)=sum(diff_state_sqr,2);
diff_total(start:stop,1)=NaN;
[mini,index]=min(diff_total);
if i+ws>m || index+ws>m
% do not use these data
else
divergence_sum = divergence_sum + log(sqrt(sum((state(i:i+ws-1,:)-state(index:index+ws-1,:)).^2,2)))';
divergence_count = divergence_count + 1;
end
end
if progress > 0
if mod(i,round(progress))==0
if i>round(progress)
fprintf('\b\b\b\b\b\b\b\b\b\b');
end
fprintf('i=%8d', i);
end
end
end
if progress > 0
fprintf('\n');
end
divergence= (divergence_sum./divergence_count);
|
1
div_wolf_fixed_evolv.m
Normal file
1
div_wolf_fixed_evolv.m
Normal file
File diff suppressed because one or more lines are too long
55
findminMutInf.m
Normal file
55
findminMutInf.m
Normal file
@ -0,0 +1,55 @@
|
||||
function minmuttau = findminMutInf(miV,nsam)
|
||||
% minmuttau = findminMutInf(miV,nsam)
|
||||
% findminMutInf finds the lag tau of the first local minimum of mutual
|
||||
% information 'miV' (given from lag 0 and up to a maximum lag 'taumax')
|
||||
% and using a sliding window of length 2*nsam+1.
|
||||
%========================================================================
|
||||
% <findminMutInf.m>, v 1.0 2010/02/11 22:09:14 Kugiumtzis & Tsimpiris
|
||||
% This is part of the MATS-Toolkit http://eeganalysis.web.auth.gr/
|
||||
|
||||
%========================================================================
|
||||
% Copyright (C) 2010 by Dimitris Kugiumtzis and Alkiviadis Tsimpiris
|
||||
% <dkugiu@gen.auth.gr>
|
||||
|
||||
%========================================================================
|
||||
% Version: 1.0
|
||||
|
||||
% LICENSE:
|
||||
% This program is free software; you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation; either version 3 of the License, or
|
||||
% any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see http://www.gnu.org/licenses/>.
|
||||
|
||||
%=========================================================================
|
||||
% Reference : D. Kugiumtzis and A. Tsimpiris, "Measures of Analysis of Time Series (MATS):
|
||||
% A Matlab Toolkit for Computation of Multiple Measures on Time Series Data Bases",
|
||||
% Journal of Statistical Software, in press, 2010
|
||||
|
||||
% Link : http://eeganalysis.web.auth.gr/
|
||||
%=========================================================================
|
||||
taumax = length(miV)-1;
|
||||
minmuttau = NaN;
|
||||
if taumax>2*nsam
|
||||
i=nsam+1;
|
||||
found = 0;
|
||||
while i<taumax+1-nsam & found==0
|
||||
winx = miV([i-nsam:i-1 i+1:i+nsam]);
|
||||
check = find(miV(i) < winx);
|
||||
if length(check) == length(winx)
|
||||
found=1;
|
||||
else
|
||||
i=i+1;
|
||||
end
|
||||
end
|
||||
if found
|
||||
minmuttau = i-1;
|
||||
end
|
||||
end
|
79
funcSampleEntropy.m
Normal file
79
funcSampleEntropy.m
Normal file
@ -0,0 +1,79 @@
|
||||
function [SE] = funcSampleEntropy(DataIn, m, r)
|
||||
|
||||
%% Description
|
||||
% Calculate the sample entropy as described in
|
||||
% Richman JS, Moorman JR (2000)
|
||||
% "Physiological time-series analysis using approximate entropy and sample entropy"
|
||||
% American Journal of Physiology. Heart and Circulatory Physiology [2000, 278(6):H2039-49]
|
||||
%
|
||||
% The sample entropy is calculated as the natural logarithm of the
|
||||
% probability that two samples of length m that are within a distance of r,
|
||||
% remain within a distance of r when adding one additional sample. Note
|
||||
% that distance is considered as the maximum of the distances for the
|
||||
% individual dimensions 1 to m, and that the input data is normalised.
|
||||
%
|
||||
% Input:
|
||||
% DataIn: a one-dimensional time series
|
||||
% m: the dimension of the vectors to be used. The vectors consist of m
|
||||
% consecutive samples
|
||||
% r: the maximum distance between two samples to qualify as a
|
||||
% mathch, relative to the std of DataIn
|
||||
%
|
||||
% Output:
|
||||
% SE: the calculated sample entropy
|
||||
%
|
||||
|
||||
%% Copyright
|
||||
% COPYRIGHT (c) 2012 Sietse Rispens, VU University Amsterdam
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
%% Author
|
||||
% Sietse Rispens
|
||||
|
||||
%% History
|
||||
% 7 May 2012, version 1.0
|
||||
|
||||
%% Check input
|
||||
if size(DataIn,1) ~= 1 && size(DataIn,2) ~= 1
|
||||
error('DataIn must be a vector');
|
||||
end
|
||||
DataIn = DataIn(:)/std(DataIn(:));
|
||||
N = size(DataIn,1);
|
||||
if N-m <= 0
|
||||
error('m must be smaller than the length of the time series DataIn');
|
||||
end
|
||||
|
||||
%% Create the vectors Xm to be compared
|
||||
Xm = zeros(N-m,m);
|
||||
for i = 1:m,
|
||||
Xm(:,i) = DataIn(i:end-1-m+i,1);
|
||||
end
|
||||
|
||||
%% Count the numbers of matches for Xm and Xmplusone
|
||||
CountXm = 0;
|
||||
CountXmplusone = 0;
|
||||
XmDist = nan(size(Xm));
|
||||
for i = 1:N-m,
|
||||
for j=1:m,
|
||||
XmDist(:,j)=abs(Xm(:,j)-Xm(i,j));
|
||||
end
|
||||
IdXmi = find(max(XmDist,[],2)<=r);
|
||||
CountXm = CountXm + length(IdXmi) - 1;
|
||||
CountXmplusone = CountXmplusone + sum(abs(DataIn(IdXmi+m)-DataIn(i+m))<=r) - 1;
|
||||
end
|
||||
|
||||
%% Return sample entropy
|
||||
SE = -log(CountXmplusone/CountXm);
|
||||
|
104
matlab.sty
Normal file
104
matlab.sty
Normal file
@ -0,0 +1,104 @@
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{matlab}
|
||||
|
||||
\RequirePackage{verbatim}
|
||||
\RequirePackage{fancyvrb}
|
||||
\RequirePackage{alltt}
|
||||
\RequirePackage{upquote}
|
||||
\RequirePackage[framemethod=tikz]{mdframed}
|
||||
\RequirePackage{hyperref}
|
||||
\RequirePackage{color}
|
||||
|
||||
|
||||
\newcommand{\maxwidth}[1]{\ifdim\linewidth>#1 #1\else\linewidth\fi}
|
||||
\newcommand{\mlcell}[1]{{\color{output}\verbatim@font#1}}
|
||||
|
||||
\definecolor{output}{gray}{0.4}
|
||||
|
||||
% Unicode character conversions
|
||||
\DeclareUnicodeCharacter{B0}{\ensuremath{^\circ}}
|
||||
\DeclareUnicodeCharacter{21B5}{\ensuremath{\hookleftarrow}}
|
||||
|
||||
% Paragraph indentation
|
||||
\setlength{\parindent}{0pt}
|
||||
|
||||
% Hyperlink style
|
||||
\hypersetup{
|
||||
colorlinks=true,
|
||||
linkcolor=blue,
|
||||
urlcolor=blue
|
||||
}
|
||||
|
||||
|
||||
% environment styles for MATLAB code and output
|
||||
\mdfdefinestyle{matlabcode}{%
|
||||
outerlinewidth=.5pt,
|
||||
linecolor=gray!20!white,
|
||||
roundcorner=2pt,
|
||||
innertopmargin=.5\baselineskip,
|
||||
innerbottommargin=.5\baselineskip,
|
||||
innerleftmargin=1em,
|
||||
backgroundcolor=gray!10!white
|
||||
}
|
||||
|
||||
\newenvironment{matlabcode}{\verbatim}{\endverbatim}
|
||||
\surroundwithmdframed[style=matlabcode]{matlabcode}
|
||||
|
||||
\newenvironment{matlaboutput}{%
|
||||
\Verbatim[xleftmargin=1.25em, formatcom=\color{output}]%
|
||||
}{\endVerbatim}
|
||||
|
||||
\newenvironment{matlabsymbolicoutput}{%
|
||||
\list{}{\leftmargin=1.25em\relax}%
|
||||
\item\relax%
|
||||
\color{output}\verbatim@font%
|
||||
}{\endlist}
|
||||
|
||||
\newenvironment{matlabtableoutput}[1]{%
|
||||
{\color{output}%
|
||||
\hspace*{1.25em}#1}%
|
||||
}{}
|
||||
|
||||
|
||||
% Table of Contents style
|
||||
\newcounter{multititle}
|
||||
\newcommand{\matlabmultipletitles}{\setcounter{multititle}{1}}
|
||||
|
||||
\newcounter{hastoc}
|
||||
\newcommand{\matlabhastoc}{\setcounter{hastoc}{1}}
|
||||
|
||||
\newcommand{\matlabtitle}[1]{
|
||||
\ifnum\value{multititle}>0
|
||||
\ifnum\value{hastoc}>0
|
||||
\addcontentsline{toc}{section}{#1}
|
||||
\fi
|
||||
\fi
|
||||
\section*{#1}
|
||||
}
|
||||
|
||||
\newcommand{\matlabheading}[1]{
|
||||
\ifnum\value{hastoc}>0
|
||||
\addcontentsline{toc}{subsection}{#1}
|
||||
\fi
|
||||
\subsection*{#1}
|
||||
}
|
||||
|
||||
\newcommand{\matlabheadingtwo}[1]{
|
||||
\ifnum\value{hastoc}>0
|
||||
\addcontentsline{toc}{subsubsection}{#1}
|
||||
\fi
|
||||
\subsubsection*{#1}
|
||||
}
|
||||
|
||||
\newcommand{\matlabheadingthree}[1]{
|
||||
\ifnum\value{hastoc}>0
|
||||
\addcontentsline{toc}{paragraph}{#1}
|
||||
\fi
|
||||
\paragraph*{#1}
|
||||
}
|
||||
|
||||
\newcommand{\matlabtableofcontents}[1]{
|
||||
\renewcommand{\contentsname}{#1}
|
||||
\tableofcontents
|
||||
}
|
||||
|
71
mutinfHisPro.m
Normal file
71
mutinfHisPro.m
Normal file
@ -0,0 +1,71 @@
|
||||
function mutV=mutinfHisPro(xV,tauV,b,ioxV,ixV)
|
||||
% mutV=mutinfHisPro(xV,tauV,b,ioxV,ixV)
|
||||
% mutinfHisPro computes the mutual information on the time series 'xV'
|
||||
% for given delays in 'tauV'. The estimation of mutual information is
|
||||
% based on 'b' partitions of equal probability at each dimension.
|
||||
% The last two input parameters are the ordered time series and the
|
||||
% corresponding indices that will be used in the equiprobable binning
|
||||
% (they both have been computed before and therefore they are passed
|
||||
% here rather than computing it again).
|
||||
%========================================================================
|
||||
% <mutinfHisPro.m>, v 1.0 2010/02/11 22:09:14 Kugiumtzis & Tsimpiris
|
||||
% This is part of the MATS-Toolkit http://eeganalysis.web.auth.gr/
|
||||
|
||||
%========================================================================
|
||||
% Copyright (C) 2010 by Dimitris Kugiumtzis and Alkiviadis Tsimpiris
|
||||
% <dkugiu@gen.auth.gr>
|
||||
|
||||
%========================================================================
|
||||
% Version: 1.0
|
||||
|
||||
% LICENSE:
|
||||
% This program is free software; you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation; either version 3 of the License, or
|
||||
% any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see http://www.gnu.org/licenses/>.
|
||||
|
||||
%=========================================================================
|
||||
% Reference : D. Kugiumtzis and A. Tsimpiris, "Measures of Analysis of Time Series (MATS):
|
||||
% A Matlab Toolkit for Computation of Multiple Measures on Time Series Data Bases",
|
||||
% Journal of Statistical Software, in press, 2010
|
||||
|
||||
% Link : http://eeganalysis.web.auth.gr/
|
||||
%=========================================================================
|
||||
n = length(xV);
|
||||
ntau = length(tauV);
|
||||
mutV = NaN*ones(ntau,1);
|
||||
hM = NaN*ones(b,b);
|
||||
cumhM = zeros(b,b+1);
|
||||
cpxV = [1/b:1/b:1]';
|
||||
for itau=1:ntau
|
||||
tau = tauV(itau);
|
||||
ntotal = n-tau;
|
||||
rxV = [0;round(cpxV*ntotal)];
|
||||
ix1V = ixV;
|
||||
ix1V(ioxV(end-tau+1:end)) = [];
|
||||
x2prV = prctile(xV(ix1V+tau),cpxV*100);
|
||||
for i = 1:b
|
||||
for j = 1:b
|
||||
cumhM(i,j+1) = length(find(xV(ix1V(rxV(i)+1:rxV(i+1))+tau)<=x2prV(j)));
|
||||
end
|
||||
hM(i,:) = diff(cumhM(i,:));
|
||||
end
|
||||
% The use of formula H(x)=1, when log_b is used.
|
||||
mutS = 2;
|
||||
for j=1:b
|
||||
for i=1:b
|
||||
if hM(i,j) > 0
|
||||
mutS=mutS+(hM(i,j)/ntotal)*log(hM(i,j)/ntotal)/log(b);
|
||||
end
|
||||
end
|
||||
end
|
||||
mutV(itau) = mutS;
|
||||
end
|
Loading…
Reference in New Issue
Block a user