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