Browse Source

TEST remote repository

master
Lars 2 years ago
parent
commit
ba8df083e2
  1. 7
      AutocorrRegSymmSteps.m
  2. 43
      AutocorrStrides.m
  3. 118
      CalcMaxLyapConvGait.m
  4. 134
      CalcMaxLyapWolfFixedEvolv.m
  5. 48
      CalculateNonLinearParametersFunc.m
  6. 48
      CalculateStrideParametersFunc.m
  7. 18
      FilterandRealignFunc.m
  8. 210
      GaitOutcomesTrunkAccFuncIH.m
  9. 74
      GaitVariabilityAnalysisIH.m
  10. 108
      GaitVariabilityAnalysisIH_WithoutTurns.m
  11. 1709
      GaitVariabilityAnalysisLD.html
  12. BIN
      GaitVariabilityAnalysisLD.mlx
  13. 1944
      GaitVariabilityAnalysisLD.tex
  14. 103
      GaitVariabilityAnalysisLD_v1.m
  15. 95
      GaitVariabilityStructToExcelIH.m
  16. 83
      HarmonicityFrequency.m
  17. 146
      MutualInformationHisPro.m
  18. BIN
      Phyphoxdata.mat
  19. 275
      RealignSensorSignalHRAmp.m
  20. 118
      SpatioTemporalGaitParameters.m
  21. 15
      SpectralAnalysisGaitfunc.m
  22. 35
      StepDetectionFunc.m
  23. 65
      StepcountFunc.m
  24. 150
      StrideFrequencyFrom3dAcc.m
  25. 150
      StrideFrequencyRispen.m
  26. 1
      div_calc.m
  27. 1
      div_calc_shorttimeseries.m
  28. 1
      div_wolf_fixed_evolv.m
  29. 55
      findminMutInf.m
  30. 79
      funcSampleEntropy.m
  31. 104
      matlab.sty
  32. 71
      mutinfHisPro.m

7
AutocorrRegSymmSteps.m

@ -0,0 +1,7 @@ @@ -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

@ -0,0 +1,43 @@ @@ -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

@ -0,0 +1,118 @@ @@ -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

@ -0,0 +1,134 @@ @@ -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

@ -0,0 +1,48 @@ @@ -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

@ -0,0 +1,48 @@ @@ -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

@ -0,0 +1,18 @@ @@ -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

@ -0,0 +1,210 @@ @@ -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) 53–60 [ 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

@ -0,0 +1,74 @@ @@ -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

@ -0,0 +1,108 @@ @@ -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

File diff suppressed because one or more lines are too long

BIN
GaitVariabilityAnalysisLD.mlx

Binary file not shown.

1944
GaitVariabilityAnalysisLD.tex

File diff suppressed because it is too large Load Diff

103
GaitVariabilityAnalysisLD_v1.m

@ -0,0 +1,103 @@ @@ -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

@ -0,0 +1,95 @@ @@ -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

@ -0,0 +1,83 @@ @@ -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

@ -0,0 +1,146 @@ @@ -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

Binary file not shown.

275
RealignSensorSignalHRAmp.m

@ -0,0 +1,275 @@ @@ -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;