Skip to content

Commit 84f7833

Browse files
authored
Merge pull request #124 from Ergo-Tools/next-version
Added accelerometer TimeSync code which was missing from the released…
2 parents 53ed549 + f89572b commit 84f7833

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

utils/TimeSyncAcc.m

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
function [AccX,status,warnings] = TimeSyncAcc(AccR,AccX,SF,OtherFile,syncFigName)
2+
% TimeSyncAcc try to time synchronize data multiple devices worn by the same person based on signal cross-covariance
3+
% Even if proper synchronization is failed the returned data is time matched to reference data. Any
4+
% data which falls outside reference time range will be returned as zeros or NaNs
5+
% accelerometer. If the times are mismatched a lot a warning will be produced.
6+
7+
% Input:
8+
% AccR [N,4]: Reference accelerometer data first column is time-axis
9+
% AccX: [N,4] raw-data from other sensor
10+
% SF: Sample frequency (N=SF*n)
11+
% OtherFiles: [1,1] string: filename of the other sensor (for plotting sync results)
12+
% syncFigName: [1,1] String: Figure title of synchronization (if empty no figures will be exported)
13+
%
14+
% Output:
15+
% AccX [N,4] raw-data from other sensor time-matched and if possible after synchronization
16+
% Sit-2, Stand-3, Move-4, Walk-5, Run-6,Stair-7, Cycle-8 and Row-9.
17+
% status [1,1] string: synchronization status OK or NotOK
18+
% warnings [1,1] string: synchronization status OK or NotOK
19+
20+
%
21+
22+
% based on original sensor synchronization Acti4 algorithm
23+
% See original source at:
24+
% https://github.com/motus-nfa/Acti4/blob/main/Version%20July%202020/AutoSynchronization.m
25+
26+
% Copyright (c) 2020, Jørgen Skotte
27+
% Copyright (c) 2021, Pasan Hettiarachchi.
28+
29+
% All rights reserved.
30+
%
31+
% Redistribution and use in source and binary forms, with or without
32+
% modification, are permitted provided that the following conditions are met:
33+
% 1. Redistributions of source code must retain the above copyright notice,
34+
% this list of conditions and the following disclaimer.
35+
% 2. Redistributions in binary form must reproduce the above copyright notice,
36+
% this list of conditions and the following disclaimer in the documentation
37+
% and/or other materials provided with the distribution.
38+
% 3. Neither the name of the copyright holder nor the names of its contributors
39+
% may be used to endorse or promote products derived from this software without
40+
% specific prior written permission.
41+
%
42+
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43+
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44+
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45+
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
46+
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
47+
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
48+
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
49+
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
50+
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51+
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52+
% POSSIBILITY OF SUCH DAMAGE.
53+
54+
55+
if isempty(syncFigName)
56+
graph=false;
57+
else
58+
graph=true;
59+
end
60+
61+
% accumulate all warnings
62+
warnings="";
63+
nsek = 30; % number of seconds to look forward and backward (window size = 2*nsek)
64+
65+
try
66+
67+
if ~isbetween(AccR(1,1),AccX(1,1)-nsek/86400,AccX(end,1)+nsek/86400) ||...
68+
~isbetween(AccR(end,1),AccX(1,1)-nsek/86400,AccX(end,1)+nsek/86400)
69+
warnings=warnings+" time range mismatch";
70+
end
71+
72+
N=size(AccR,1);
73+
74+
% for covarians analysis (transverse axis not used)
75+
Rrms = rms(AccR(:,[2,4]),2);
76+
77+
%number of nsek intervals
78+
numSeg = fix(N/(SF*nsek));
79+
80+
% overlapping range of AccX data with AccR is retained and the rest is substituted by zero
81+
Dintp=zeros(size(AccR));
82+
Dintp(:,2:4) = interp1(AccX(:,1),AccX(:,2:4),AccR(:,1),'pchip',0); %dataCALF.AXES must be double if 'cubic' is selected
83+
Dintp(:,1)=AccR(:,1);
84+
AccX=Dintp; % reasssign interpolated data back to AccX
85+
86+
% find rms of other sensor (using all axes)
87+
Xrms = rms(AccX(:,2:4),2);
88+
89+
% max correlation and lag
90+
Corr = zeros(numSeg,1);
91+
Lag = NaN(numSeg,1);
92+
93+
94+
warning('off','stats:statrobustfit:IterationLimit');
95+
for j=1:numSeg %every nsek
96+
iisync = nsek*SF*(j-1)+1:min(nsek*SF*(j+1),N);% +/- nsek intervals, 50% overlap
97+
if std(Rrms(iisync)) >.05 %some activity must be found
98+
Kryds = xcov(Rrms(iisync),Xrms(iisync),2*nsek*SF,'coeff'); %max 2*nsek sec lag
99+
[Corr(j),indMax] = max(Kryds);
100+
Lag(j) = 2*nsek*SF-indMax+1;
101+
end
102+
end
103+
104+
% time points for correlation tests
105+
t = (1:numSeg)'*nsek/86400;
106+
% linear reference time axis starting with 0
107+
tR = 0:(size(AccR,1)-1);
108+
109+
% set coreelations less than 0.4 to NaN
110+
Lag(Corr<0.4) = NaN;
111+
112+
113+
%disp(OtherFiles{k})
114+
ii = ~isnan(Corr) & ~isnan(Lag);
115+
116+
%warning('off')
117+
if any(ii)
118+
% linear regression coffeficinets A and B of robust fit (Y=Ax+B)
119+
[Fit,stat] = robustfit(t(ii),Lag(ii),'bisquare',1);
120+
%warning('on')
121+
Lfit = polyval(flip(Fit),t);
122+
SFratio = N/(N+Lfit(end)-Lfit(1));
123+
tX = polyval([SFratio,-Lfit(1)],0:(size(AccX,1)-1));
124+
125+
if graph
126+
figSync=figure('Units','Normalized','Position',[.55 .05 0.4 0.3],'Visible','off');
127+
subplot(1,1,1)
128+
plot(t(ii),Lag(ii)/SF,'k.',t,Lfit/SF,'r')
129+
ylim([min(Lfit([1,end]))/SF-2 max(Lfit([1,end]))/SF+2])
130+
title("Sync Thigh Acc against: "+OtherFile,'Interpreter','None')
131+
xlabel('Elapsed Time (days)')
132+
ylabel('Relative Delay (s)')
133+
text(.75,.075,['MAD = ',num2str(stat.mad_s/SF,'%6.3f')],'Units','Normalized')
134+
drawnow
135+
exportgraphics(figSync,(syncFigName+"-Sync-"+OtherFile+".png"));
136+
%export_fig(figSync,(syncFigName+"-Sync-"+OtherFiles{k}),'-png','-p0.05');
137+
close(figSync);
138+
end
139+
140+
if stat.mad_s/SF > 1
141+
warnings=warnings+" Uncertain synchronization "+OtherFile;
142+
status="NotOK";
143+
else
144+
%outside values are set to NaN
145+
AccX(:,2:4) = interp1(tX,AccX(:,2:4),tR,'pchip');
146+
status="OK";
147+
end
148+
else
149+
status="NotOK";
150+
warnings=warnings+" Not enough active data for synchronization";
151+
end
152+
warning('on','stats:statrobustfit:IterationLimit');
153+
154+
catch ME
155+
status="NotOK";
156+
warnings=warnings+ME.message;
157+
warning('on','stats:statrobustfit:IterationLimit');
158+
end

0 commit comments

Comments
 (0)