MAP_Gait_Dynamics/StrideFrequencyFrom3dAcc.m

151 lines
5.4 KiB
Matlab

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