This repository was archived by the owner on Jun 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAncillaryData.m
More file actions
354 lines (284 loc) · 14.9 KB
/
AncillaryData.m
File metadata and controls
354 lines (284 loc) · 14.9 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
%% AncillaryData Abstract Interface Class
%
% This object provides an abstract interface to multiple kinds of data
% types that might need to be used to process ACS data
classdef (Abstract) AncillaryData < handle
properties (Abstract)
Name
Type
Units
% a timeseries object
DataObject
levelsMap
% preprocessing data
end
properties (Access = private)
L % logger
end
methods
% try to define constructor here
function obj = AncillaryData(nameIn, dataValuesIn, timestampsIn, unitsIn )
obj.L = log4m.getLogger();
obj.L.debug('AncillaryData.AncillaryData()','Created object');
%disp('CALL ANCILLARY DATA CONSTRUCTOR')
if nargin > 0
%disp('AncillaryData, args in')
if ischar( nameIn )
obj.Name = nameIn;
else
error('Name must be a string')
end
% check dataValues and timeStamps are valid first!
obj.DataObject = timeseries(dataValuesIn, timestampsIn, 'name', nameIn);
% assign units
obj.Units = unitsIn;
obj.DataObject.DataInfo.Unit = unitsIn;
else
% implicit call to superclass constructor?
%disp('AncillaryData, no args in')
obj.Name = '';
obj.Type = '';
end % end if nargin > 0
% define Quality codes 'lookup table' by calling defineQualityCode from
% parent class
%disp('set codes in AncillaryData')
defineQualityCode(obj);
% assign an initial quality code to each data record in matrix
assignInitQualityCode(obj);
% set up processing levels map
keySet = {'raw', ... %L1
'preprocessed', ...%L2
'binned', ... %L3
'filtered', ... %L4
'particulate', ... %L5
'unsmoothed', ... %L6
'below750', ... %L7
'matchedWL', ... %L8
'corrected'}; %L9
valueSet = {'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7', 'L8', 'L9'};
obj.levelsMap = containers.Map(keySet, valueSet);
end % end constructor
function defineQualityCode(obj)
%disp('call defineQualityCode() in AncillaryData')
% Set the quality codes for AC Data Processing
obj.DataObject.QualityInfo.Code = [ 1 2 3 4 9 ];
obj.DataObject.QualityInfo.Description = {'Good', 'Unknown', 'Suspect', 'Bad', 'Missing'};
end
function assignInitQualityCode(obj)
%disp('call assignInitQualityCode() in AncillaryData')
% create a vector of ones ('Good') -- assume all data is good?
obj.DataObject.Quality = ones( size(obj.DataObject.Data) );
end
function nRows = getSize(obj)
nRows = obj.DataObject.Data.getdatasamplesize();
end
function obj = addData(obj, nameIn2, dataValuesIn2, timestampsIn2 )
obj.L.debug('AncillaryData.addData', ...
sprintf('timestampsIn 1: %s', datestr(timestampsIn2(1), 'HH:MM:SS:FFF')));
obj.L.debug('AncillaryData.addData', ...
sprintf('timestampsIn end: %s', datestr(timestampsIn2(end), 'HH:MM:SS:FFF')));
obj.L.debug('AncillaryData.addData', ...
sprintf('timestampsIn min: %s', datestr(min(timestampsIn2), 'HH:MM:SS:FFF')));
obj.L.debug('AncillaryData.addData', ...
sprintf('timestampsIn max: %s', datestr(max(timestampsIn2), 'HH:MM:SS:FFF')));
% create new timeseries object from data coming in:
% name is going to get wiped here as soon as we call append()
tsIn = timeseries(dataValuesIn2, timestampsIn2, 'name', nameIn2);
% assign initial quality codes
tsIn.Quality = ones(size(dataValuesIn2));
obj.L.debug('AncillaryData.addData', ...
sprintf('Min Timestamp in new timeseries object: %s', datestr(min(tsIn.Time), 'HH:MM:SS:FFF')));
obj.L.debug('AncillaryData.addData', ...
sprintf('Max Timestamp in new timeseries object: %s', datestr(max(tsIn.Time), 'HH:MM:SS:FFF')));
tsExist = obj.DataObject;
obj.L.debug('AncillaryData.addData', ...
sprintf('First timestamp in existing ts object: %s', datestr(tsExist.Time(1), 'HH:MM:SS:FFF')));
obj.L.debug('AncillaryData.addData', ...
sprintf('Last timestamp in existing ts object: %s', datestr(tsExist.Time(end), 'HH:MM:SS:FFF')));
% append next time series to this one
tsMerged = append(obj.DataObject, tsIn);
% assign metadata from first ts to this new one.
% append() doesn't carry info across unless it matches exactly
% and it doesn't carry .Name even if it DOES match.
tsMerged.Name = obj.DataObject.Name;
tsMerged.QualityInfo = obj.DataObject.QualityInfo;
tsMerged.DataInfo = obj.DataObject.DataInfo;
% reassign this DataObject to be the new ts3
obj.DataObject = tsMerged;
obj.L.debug('AncillaryData.addData', 'end of function');
end
function getInfo(obj)
clear ts;
ts = obj.DataObject;
disp('Min Timestamp: getInfo')
datestr(min(ts.Time))
disp('Max Timestamp:getInfo')
datestr(max(ts.Time))
disp('Number of records:')
length(ts.Time)
disp('DataInfo')
ts.DataInfo
end
function plotData(obj)
obj.L.debug('AncillaryData.plotData', 'Start');
ts = obj.DataObject;
scatter(obj.DataObject.Time, obj.DataObject.Data, '.')
dynamicDateTicks();
end % end plotData
%% ----------------------------------------------------------------
% Getter and Setter for Variables
% -----------------------------------------------------------------
%% setVar
function obj = setVar( obj, varNameIn, levelNameIn, dataNameIn, dataIn )
%#setVar sets any name/data pair into the right level in .var
%#
%# SYNOPSIS obj = setVar( obj, varNameIn, levelNameIn, dataNameIn, dataIn )
%# INPUT obj - the object
%# varNameIn - the var of the name/data pair being set, i.e. "ap"
%# levelNameIn - the name of the level to set the data in, i.e. "corrected"
%# dataNameIn - the name of the actual data, i.e. "timestamps"
%# dataIn - the actual data
%# OUTPUT obj - the object
%#
obj.L.debug('ProcessedData.setVar()', 'in method');
level = obj.levelsMap(levelNameIn);
obj.var.(varNameIn).(level).(dataNameIn) = dataIn;
%find index number for this level
levelInt = level(:,2); % get just the number off
levelInt = str2num(levelInt);
if isempty(obj.levelsFlags)
% if no flags exist at all yet -- create flags and index
obj.L.debug('ProcessedData.setVar()', 'have no flags at all');
levelFlags = 1:9;
levelIndex = zeros(size(levelFlags));
levelIndex = logical(levelIndex);
obj.levelsFlags.(varNameIn).levelFlags = levelFlags;
obj.levelsFlags.(varNameIn).levelIndex = levelIndex;
obj.levelsFlags.(varNameIn).levelIndex(levelInt) = true;
else
obj.L.debug('ProcessedData.setVar()','we do have flags');
if ~isfield(obj.levelsFlags, varNameIn )
% it's not empty - but need to check if we have this level
obj.L.debug('ProcessedData.setVar()','have flags but not for this data');
levelFlags = 1:9;
levelIndex = zeros(size(levelFlags));
levelIndex = logical(levelIndex);
obj.levelsFlags.(varNameIn).levelFlags = levelFlags;
obj.levelsFlags.(varNameIn).levelIndex = levelIndex;
obj.levelsFlags.(varNameIn).levelIndex(levelInt) = true;
else
% just update correct level
obj.L.debug('ProcessedData.setVar()','have flags for this data');
obj.levelsFlags.(varNameIn).levelIndex(levelInt) = true;
end;
end
end; %#setVar
%% getVar
function [varOut] = getVar( obj, varargin )
%#getVar gets any data out of the right level in .var
%#
%# SYNOPSIS [varOut] = getVar( obj, varargin )
%# INPUT obj - the object
%# varNameIn - the var of the name/data pair being set, i.e. "ap"
%# levelNameIn - the name of the level to set the data in, i.e. "corrected"
%# dataNameIn - the name of the actual data, i.e. "timestamps"
%# OUTPUT varOut - the data
%#
obj.L.debug('ProcessedData.getVar()', 'in method');
% check inputs
% check varName is one that exists. Create if it doesn't exist?
varNameIn = '';
levelNameIn = '';
dataNameIn = '';
% loop through name/value pairs
if (~isempty(varargin))
iArg = 1;
while iArg < nargin
% varargin{iArg};
if strcmpi(varargin{iArg}, 'name')
varNameIn = varargin{iArg+1};
elseif strcmpi(varargin{iArg}, 'level')
levelNameIn = varargin{iArg+1};
elseif strcmpi(varargin{iArg}, 'data')
dataNameIn = varargin{iArg+1};
else
obj.L.error('ProcessedData.getVar', 'invalid argument');
end;
iArg = iArg + 2;
end; % while loop
else
obj.L.error('ProcessedData.getVar', 'no argument');
end;
% get variable - by name, level and datatype
if ~isempty(varNameIn) && ~isempty(levelNameIn) && ~isempty(dataNameIn)
% lookup level
level = obj.levelsMap(levelNameIn);
% check this data field exists for this level of data
if isfield(obj.var.(varNameIn).(level), dataNameIn )
varOut = obj.var.(varNameIn).(level).(dataNameIn);
obj.L.debug('ProcessedData.getVar()', ...
sprintf('level: %s', level));
else
obj.L.error('ProcessedData.getVar', 'invalid data name');
end;
elseif ~isempty(varNameIn) && ~isempty(levelNameIn) && isempty(dataNameIn)
obj.L.debug('ProcessedData.getVar()', ...
'have both name and level, don''t need data -- getting specific level');
% lookup level
level = obj.levelsMap(levelNameIn);
if isfield(obj.var.(varNameIn), level )
varOut = obj.var.(varNameIn).(level);
else
obj.L.error('ProcessedData.getVar', 'invalid level');
end;
elseif ~isempty(varNameIn) && isempty(levelNameIn) && isempty(dataNameIn)
obj.L.debug('ProcessedData.getVar()', ...
'have name, don''t need data -- need to find most recent level');
% find most recent level
idx = obj.levelsFlags.(varNameIn).levelIndex;
thisLevels = obj.levelsFlags.(varNameIn).levelFlags(idx);
% if we have data for this var:
if ~isempty(thisLevels)
maxLevel = max(thisLevels);
level = sprintf('L%u', maxLevel);
varOut = obj.var.(varNameIn).(level);
else
obj.L.error('ProcessedData.getVar()', 'no data for this variable');
end;
elseif ~isempty(varNameIn) && isempty(levelNameIn) && ~isempty(dataNameIn)
% if we have a variable name and a data field name, but no
% specific level, get the highest level we have for this
% data field for this variable
idx = obj.levelsFlags.(varNameIn).levelIndex;
thisLevels = obj.levelsFlags.(varNameIn).levelFlags(idx);
% if we have data for this var:
if ~isempty(thisLevels)
minLevel = min(thisLevels);
maxLevel = max(thisLevels);
for iLevel = minLevel:maxLevel
level = sprintf('L%u', iLevel);
levelExists = isfield(obj.var.(varNameIn), level);
if levelExists
dataExists = isfield(obj.var.(varNameIn).(level), dataNameIn );
if dataExists % at this level
varOut = obj.var.(varNameIn).(level).(dataNameIn);
obj.L.debug('ProcessedData.getVar()', ...
sprintf('setting data to level: %s', level));
else
obj.L.debug('ProcessedData.getVar()', ...
'data doesn''t exist at this level');
end;
else
obj.L.debug('ProcessedData.getVar()', ...
'level doesn''t exist');
end;
end; % for loop through levels
else
obj.L.error('ProcessedData.getVar()', 'no data for this variable');
end; %isempty(thisLevels)
else
obj.L.error('ProcessedData.getVar', 'problem');
end; %~isempty(varNameIn) && ~isempty(levelNameIn) && ~isempty(dataNameIn)
end; %#getVar
end
end