-
Notifications
You must be signed in to change notification settings - Fork 6
/
PIXstackStats.m
165 lines (132 loc) · 5.41 KB
/
PIXstackStats.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
function stats = PIXstackStats( epoch, fe )
% function stats = PIXstackStats( sampledEpoch, canonicalEpoch )
%
% where 'sampledEpoch' is the epoch times from the stack collection
% of NxM, where N is the number of samples and M is the number
% of stacks.
% 'canonicalEpoch' is the optional 'correct' epoch time of
% Nx1.
%
% and 'stats' is a struct with statistics about the epoch times with
% the following fields:
%
% multiInterval: a 1xM array indicating there was a change of interval.
% 0 if no change, otherwise it will be the detected second interval
% noCanonical: a 0/1 flag indicating whether a canonicalEpoch was provided
% intervalStd: 5% of the reported interval
% longInts5: an Mx5 array of the five longest detected intervals, one
% row per stack.
% longInts5When: an array associated with longInts5 that records when
% the long interval occured.
% shortInts5: same as longInts5, except the five minimum intervals
% shortInts5When: epoch time when those shorts occured
% overrun: how much "too long" was the stack. requires a canonical epoch.
% intervalError: how far away from the true interval (canonical) was the
% interval I calculate.
% numLong: a 1xM array of how many intervals exceeded two standard
% deviations from the calculated interval
% numShort: same, except short by that much.
% interval: 1xM calculated interval for each stack.
% startDelay: assuming the first stack to start is correct, how late did
% each stack start.
% startError: how late did each stack start compared to the canonical epoch
% times. Zero if noCanonical.
% endError: duplicate of overrun. Sigh.
% epochs: copy of the incoming epoch data for later study.
%
%
%
% find "canonical interval", if we can
if nargin == 2
feint = mean(diff(fe));
else
feint = -1; % flag that says we don't have it.
end
% swap epochs into the col/row shape I want
% we have two different versions -- one from loadStack (1xN)
% and one from the .mat files (NxM) where M is the number of stacks
% in the data. M << N in that case, so we can tell
[N,M] = size(epoch);
if N<M
epoch = epoch';
end
% per camera stats first
for ii = 1:length(epoch(1,:))
% per camera interval
[pcint,pcstd] = myFindInterval(epoch(:,ii));
% sigh. another thing to look for: multi-interval stacks
% some at duck changed at 15 minutes into the run
% do the same calc as above, but use only the last 100 epochs
pcint100 = myFindInterval(epoch(end-100:end,ii));
% define change of more than 5% as "multi". Return
% the value of the second interval, or 0 if not.
stats.multiInterval(ii) = 0;
if( abs(1-pcint/pcint100) > .05 )
stats.multiInterval(ii) = pcint100;
end;
% if no fixed epoch int, use per-cam.
if feint < 0
stats.noCanonical = 1;
ceint = pcint;
else
stats.noCanonical = 0;
ceint = feint;
end
% gaps in sampling, return top five
stats.intervalStd(ii) = pcstd;
[gaps,gi] = sort(diff(epoch(:,ii)));
stats.longInts5(ii,:) = gaps(end:-1:end-4);
stats.longInts5When(ii,:) = epoch(gi(end:-1:end-4),ii);
% "catchups", top 5 minimum intervals
stats.shortInts5(ii,:) = gaps(1:5);
stats.shortInts5When(ii,:) = epoch(gi(1:5),ii);
% how long should this record have been, list under/overrun
expLen = ceint*(length(epoch(:,1))-1);
stats.overrun(ii) = epoch(end,ii) - epoch(1,ii) - expLen;
stats.intervalError(ii) = ceint - pcint; % will be zero if noCanonical
stats.numLong(ii) = length(find((diff(epoch(:,ii)) > ceint+pcstd )));
stats.numShort(ii) = length(find((diff(epoch(:,ii)) < ceint-pcstd )));
stats.interval(ii) = ceint; % from last camera, or canonical if present
end
% intercamera
stats.startDelay = epoch(1,:) - min(epoch(1,:));
if stats.noCanonical
stats.startError = stats.startDelay;
else
stats.startError = epoch(1,:) - fe(1);
end
stats.endError = epoch(end,:) - epoch(1,:) - expLen;
% and finally, save the incoming epoch for later use
stats.epochs = epoch;
return;
end
function [pcint, pcstd] = myFindInterval( epoch )
% this code works really great, except when it doesn't.
% I have a duck stack where the cycle time is so wacked
% that this gives a NEGATIVE interval. Sigh.
% de = diff(epoch);
% pcstd = std(de);
% valid = find( abs(de-mean(de)) < 2*pcstd );
% pcint = mean(de(valid));
% but a simple mode works. Sigh again.
pcint = mode(diff(epoch));
pcstd = 0.05 * pcint;
return;
end
%
% Copyright (C) 2017 Coastal Imaging Research Network
% and Oregon State University
% 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, version 3 of the
% License.
% 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/>.
% CIRN: https://coastal-imaging-research-network.github.io/
% CIL: http://cil-www.coas.oregonstate.edu
%