-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLD App code
More file actions
450 lines (362 loc) · 16.7 KB
/
LD App code
File metadata and controls
450 lines (362 loc) · 16.7 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
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
classdef LD_data < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
UIFigure matlab.ui.Figure
load_old_data matlab.ui.control.Button
ConnecttoArduinoButton_2 matlab.ui.control.Button
ExportDataButton matlab.ui.control.Button
SolinoidValveSequenceLabel matlab.ui.control.Label
LifeDetectionDataControlPanelLabel matlab.ui.control.Label
ListBox matlab.ui.control.ListBox
OtherTemperaturesLabel matlab.ui.control.Label
DropDown matlab.ui.control.DropDown
DropDownLabel matlab.ui.control.Label
CloseExportButton matlab.ui.control.Button
Leak_Rate matlab.ui.control.UIAxes
Humidity matlab.ui.control.UIAxes
CO2 matlab.ui.control.UIAxes
Temperature matlab.ui.control.UIAxes
end
properties (Access = private)
s
timerObj
t0
row = 0
maxRows = 5000
dataSet % [Temperature Humidity CO2]
tempLine
co2Line
humLine
timeVec % time stamps for current run (size: maxRows x 1)
lastDataSet % data from previous run
lastTimeVec % time stamps from previous run
selectedIdx % indices of points selected via data tips
Leak_RateTime % time stamps for slope points
Leak_RateData % slope values (Temperature change per second)
Leak_RateLine % line handle for slope plot
end
methods (Access = public)
function readSerial(app)
try
line = readline(app.s);
data = str2double(split(line,","));
if numel(data) ~= 3
return
end
T = data(1);
H = data(3);
C = data(2);
app.row = app.row + 1;
app.dataSet(app.row,:) = [T H C];
tNow = toc(app.t0);
app.timeVec(app.row) = tNow;
% Update full line data from stored arrays
set(app.tempLine, 'XData', app.timeVec(1:app.row), ...
'YData', app.dataSet(1:app.row,1));
set(app.co2Line, 'XData', app.timeVec(1:app.row), ...
'YData', app.dataSet(1:app.row,3));
set(app.humLine, 'XData', app.timeVec(1:app.row), ...
'YData', app.dataSet(1:app.row,2));
% --- Compute slope over the last 10 points for Temperature ---
windowSize = 10;
if app.row >= windowSize
idx = app.row-windowSize+1 : app.row;
t10 = app.timeVec(idx);
y10 = app.dataSet(idx,2); % Temperature
% Linear fit: y = p(1)*t + p(2), slope = p(1)
p = polyfit(t10, y10, 1);
slope = p(1);
else
slope = NaN; % not enough points yet
end
app.Leak_RateTime(app.row) = app.timeVec(app.row);
app.Leak_RateData(app.row) = slope;
% Update slope line
set(app.Leak_RateLine, 'XData', app.Leak_RateTime(1:app.row), ...
'YData', app.Leak_RateData(1:app.row));
drawnow limitrate
catch
end
end
function txt = displayCoordinates(~,info)
x = info.Position(1);
y = info.Position(2);
myDatatipText = "(%s, %s)";
txt = sprintf(myDatatipText, num2str(x), num2str(y));
end
end
methods (Access = private)
function setupDataTips(app, lineHandle)
% Customize labels (optional)
if isprop(lineHandle,'DataTipTemplate')
dtRows = lineHandle.DataTipTemplate.DataTipRows;
if numel(dtRows) >= 2
dtRows(1).Label = 'Time (s)';
dtRows(2).Label = 'Value';
end
lineHandle.DataTipTemplate.DataTipRows = dtRows;
end
% When a new datatip is created, log which index it corresponds to
if isprop(lineHandle,'DataTipCreatedFcn')
% src is unused, so ignore it with ~
lineHandle.DataTipCreatedFcn = @(~,event) app.datatipCreatedCallback(event);
end
end
function datatipCreatedCallback(app, event)
% Position = [time value]
pos = event.DataTip.Position;
t = pos(1);
if app.row > 0
% Find nearest recorded time index
[~, idx] = min(abs(app.timeVec(1:app.row) - t));
app.selectedIdx = unique([app.selectedIdx; idx]); % avoid duplicates
end
end
end
% Callbacks that handle component events
methods (Access = private)
% Button pushed function: ConnecttoArduinoButton_2
function ConnecttoArduinoButton_2Pushed3(app, event)
port = "COM11";
baud = 115200;
% --- Preserve previous run, if any ---
if app.row > 0
app.lastDataSet = app.dataSet(1:app.row,:);
app.lastTimeVec = app.timeVec(1:app.row);
else
app.lastDataSet = [];
app.lastTimeVec = [];
end
app.s = serialport(port, baud);
configureTerminator(app.s,"LF");
pause(2);
flush(app.s);
app.dataSet = zeros(app.maxRows,3);
app.timeVec = zeros(app.maxRows,1);
app.selectedIdx = [];
app.row = 0;
app.Leak_RateTime = zeros(app.maxRows,2);
app.slopeCO2Data = zeros(app.maxRows,2);
cla(app.Temperature)
cla(app.CO2)
cla(app.Humidity)
cla(app.Leak_Rate)
% --- Plot previous trial in gray, if available ---
if ~isempty(app.lastDataSet)
% Temperature vs time
hold(app.Temperature,'on');
plot(app.Temperature, app.lastTimeVec, app.lastDataSet(:,1), ...
'Color',[0.7 0.7 0.7],'LineWidth',1);
hold(app.Temperature,'off');
% Humidity vs time (note your axes naming is swapped)
hold(app.CO2,'on');
plot(app.CO2, app.lastTimeVec, app.lastDataSet(:,2), ...
'Color',[0.7 0.7 0.7],'LineWidth',1);
hold(app.CO2,'off');
% CO2 vs time
hold(app.Humidity,'on');
plot(app.Humidity, app.lastTimeVec, app.lastDataSet(:,3), ...
'Color',[0.7 0.7 0.7],'LineWidth',1);
hold(app.Humidity,'off');
end
% Create static line objects with no data yet
axes(app.Temperature);
app.tempLine = plot(app.Temperature, NaN, NaN, 'r-');
ylabel(app.Temperature,'Temperature °C')
axes(app.CO2);
app.co2Line = plot(app.CO2, NaN, NaN, 'b-');
ylabel(app.CO2,'CO2 PPM')
axes(app.Humidity);
app.humLine = plot(app.Humidity, NaN, NaN, 'g-');
ylabel(app.Humidity,'Humidity %')
xlabel(app.Humidity,'Time (s)')
% Create slope line (Temperature slope)
axes(app.Leak_Rate);
app.Leak_RateLine = plot(app.Leak_Rate, NaN, NaN, 'm-');
xlabel(app.Leak_Rate,'Time (s)')
ylabel(app.Leak_Rate,'dC/dt (CO2/s)')
% Enable interactive data tips on the axes
app.Temperature.Interactions = [dataTipInteraction panInteraction zoomInteraction];
app.CO2.Interactions = [dataTipInteraction panInteraction zoomInteraction];
app.Humidity.Interactions = [dataTipInteraction panInteraction zoomInteraction];
% Attach datatip callbacks to each line
setupDataTips(app, app.tempLine);
setupDataTips(app, app.co2Line);
setupDataTips(app, app.humLine);
app.t0 = tic;
app.timerObj = timer( ...
'ExecutionMode','fixedSpacing', ...
'Period',0.1, ...
'TimerFcn',@(~,~)readSerial(app));
start(app.timerObj)
end
% Button pushed function: ExportDataButton
function ExportDataButtonPushed3(app, event)
resultsFolder = '\Results';
if ~isfolder(resultsFolder)
mkdir(resultsFolder);
end
baseName = 'dataSet';
ext = '.csv';
existingFiles = dir(fullfile(resultsFolder,[baseName,'*.csv']));
if isempty(existingFiles)
newFileName = [baseName ext];
else
numbers = zeros(length(existingFiles),1);
for k = 1:length(existingFiles)
tok = regexp(existingFiles(k).name,['_(\d+)' ext],'tokens');
if ~isempty(tok)
numbers(k) = str2double(tok{1}{1});
end
end
newFileName = sprintf('%s_%d%s',baseName,max(numbers)+1,ext);
end
fullFilePath = fullfile(resultsFolder,newFileName);
% Main dataset export
writetable(array2table(app.dataSet(1:app.row,:), ...
'VariableNames',{'Temperature','CO2', 'Humidity'}),fullFilePath);
% Highlighted points export
if ~isempty(app.selectedIdx)
idx = unique(app.selectedIdx);
highlighted = [app.timeVec(idx), app.dataSet(idx,:)]; % [Time T H C]
highlightedTbl = array2table(highlighted, ...
'VariableNames',{'Time_s','Temperature','CO2','Humidity'});
highlightedFile = fullfile(resultsFolder,'highlighted_data.csv');
writetable(highlightedTbl, highlightedFile);
end
end
% Button pushed function: CloseExportButton
function CloseExportButtonPushed3(app, event)
% Stop timer safely
if ~isempty(app.timerObj) && isvalid(app.timerObj)
stop(app.timerObj);
delete(app.timerObj);
end
% Close serial safely
if ~isempty(app.s)
clear app.s
end
delete(app.UIFigure)
end
% Button pushed function: load_old_data
function load_old_dataButtonPushed(app, event)
end
end
% Component initialization
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create UIFigure and hide until all components are created
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100 100 960 540];
app.UIFigure.Name = 'MATLAB App';
% Create Temperature
app.Temperature = uiaxes(app.UIFigure);
title(app.Temperature, 'Temperature')
xlabel(app.Temperature, 'Time')
ylabel(app.Temperature, 'Temperature °C')
zlabel(app.Temperature, 'Z')
app.Temperature.XGrid = 'on';
app.Temperature.YGrid = 'on';
app.Temperature.Position = [21 260 456 222];
% Create CO2
app.CO2 = uiaxes(app.UIFigure);
title(app.CO2, 'Humidity')
xlabel(app.CO2, 'Time')
ylabel(app.CO2, 'Humidity %')
zlabel(app.CO2, 'Z')
app.CO2.Position = [486 98 257 157];
% Create Humidity
app.Humidity = uiaxes(app.UIFigure);
title(app.Humidity, 'CO2')
xlabel(app.Humidity, 'Time')
ylabel(app.Humidity, 'CO2 PPM')
zlabel(app.Humidity, 'Z')
app.Humidity.XGrid = 'on';
app.Humidity.YGrid = 'on';
app.Humidity.Position = [486 260 455 222];
% Create Leak_Rate
app.Leak_Rate = uiaxes(app.UIFigure);
title(app.Leak_Rate, 'Leak Rate')
xlabel(app.Leak_Rate, 'Time')
ylabel(app.Leak_Rate, 'Leak Rate')
zlabel(app.Leak_Rate, 'Z')
app.Leak_Rate.XGrid = 'on';
app.Leak_Rate.YGrid = 'on';
app.Leak_Rate.Position = [21 18 456 222];
% Create CloseExportButton
app.CloseExportButton = uibutton(app.UIFigure, 'push');
app.CloseExportButton.ButtonPushedFcn = createCallbackFcn(app, @CloseExportButtonPushed3, true);
app.CloseExportButton.FontSize = 14;
app.CloseExportButton.Position = [810 17 135 32];
app.CloseExportButton.Text = 'Close & Export';
% Create DropDownLabel
app.DropDownLabel = uilabel(app.UIFigure);
app.DropDownLabel.HorizontalAlignment = 'right';
app.DropDownLabel.Position = [753 183 66 22];
app.DropDownLabel.Text = 'Drop Down';
% Create DropDown
app.DropDown = uidropdown(app.UIFigure);
app.DropDown.Position = [834 183 100 22];
% Create OtherTemperaturesLabel
app.OtherTemperaturesLabel = uilabel(app.UIFigure);
app.OtherTemperaturesLabel.FontSize = 14;
app.OtherTemperaturesLabel.Position = [528 63 174 36];
app.OtherTemperaturesLabel.Text = 'Other Temperatures';
% Create ListBox
app.ListBox = uilistbox(app.UIFigure);
app.ListBox.Items = {'Thermoresistor', 'Heating element'};
app.ListBox.Multiselect = 'on';
app.ListBox.Position = [528 17 174 47];
app.ListBox.Value = {'Thermoresistor'};
% Create LifeDetectionDataControlPanelLabel
app.LifeDetectionDataControlPanelLabel = uilabel(app.UIFigure);
app.LifeDetectionDataControlPanelLabel.FontSize = 24;
app.LifeDetectionDataControlPanelLabel.Position = [21 495 390 32];
app.LifeDetectionDataControlPanelLabel.Text = 'Life Detection Data & Control Panel';
% Create SolinoidValveSequenceLabel
app.SolinoidValveSequenceLabel = uilabel(app.UIFigure);
app.SolinoidValveSequenceLabel.FontSize = 14;
app.SolinoidValveSequenceLabel.Position = [753 218 159 22];
app.SolinoidValveSequenceLabel.Text = 'Solinoid Valve Sequence';
% Create ExportDataButton
app.ExportDataButton = uibutton(app.UIFigure, 'push');
app.ExportDataButton.ButtonPushedFcn = createCallbackFcn(app, @ExportDataButtonPushed3, true);
app.ExportDataButton.FontSize = 14;
app.ExportDataButton.Position = [809 53 137 32];
app.ExportDataButton.Text = 'Export Data';
% Create ConnecttoArduinoButton_2
app.ConnecttoArduinoButton_2 = uibutton(app.UIFigure, 'push');
app.ConnecttoArduinoButton_2.ButtonPushedFcn = createCallbackFcn(app, @ConnecttoArduinoButton_2Pushed3, true);
app.ConnecttoArduinoButton_2.FontSize = 14;
app.ConnecttoArduinoButton_2.Position = [809 90 137 32];
app.ConnecttoArduinoButton_2.Text = 'Connect to Arduino';
% Create load_old_data
app.load_old_data = uibutton(app.UIFigure, 'push');
app.load_old_data.ButtonPushedFcn = createCallbackFcn(app, @load_old_dataButtonPushed, true);
app.load_old_data.FontSize = 14;
app.load_old_data.Position = [809 127 138 32];
app.load_old_data.Text = 'Load Previous Data';
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = LD_data
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
if nargout == 0
clear app
end
end
% Code that executes before app deletion
function delete(app)
% Delete UIFigure when app is deleted
delete(app.UIFigure)
end
end
end