Max indices in for loop not changing after initial run (index exceeds the number or array elements)

1 view (last 30 days)
I am getting the error
Warning: Error occurred while executing the listener callback for event ElementsAvailable defined for class daq.Buffer:
Index exceeds the number of array elements. Index must not exceed 3.
this error is occuring on my set function line in the code below. After pressing the "Start Button" on my UI(as seen below) and run it the first time after opening it it runs smoothly. after checking off additional channels it leads to errors. After the daq is done collecting and the user presses stop it writes the values to the workspace and I can see that it is still collecting those values even though they are not being displayed.
Any help is greatly appreciated!
function updateLivePlot(app)
if isempty(app.DataFIFOBufferch1) || isempty(app.SelectedChannels)
return
end
% Disable interactivity
disableDefaultInteractivity(app.LiveAxes);
% Keep the colors the same after each new data point
app.LiveAxes.ColorOrderIndex = 1;
if isempty(app.LivePlotLine)
% First-Time Setup
app.LivePlotLine = plot(app.LiveAxes, app.TimestampsFIFOBuffer, app.DataFIFOBufferch1(:,app.SelectedChannels));
else
% Update existing plot
for j = 1:numel(app.SelectedChannels)
disp(j)
% Check if the line needs to be extended
if any(j <= numel(app.LivePlotLine)) && isempty(app.LivePlotLine(j))
% Use existing axes to plot
app.LivePlotLine(j) = plot(app.LiveAxes, app.TimestampsFIFOBuffer, app.DataFIFOBufferch1(:, j));
else
% Update existing line
set(app.LivePlotLine(j), 'XData', app.TimestampsFIFOBuffer, 'YData', app.DataFIFOBufferch1(:, j));
end
end
end
if numel(app.TimestampsFIFOBuffer) > 1
xlim(app.LiveAxes, [app.TimestampsFIFOBuffer(1), app.TimestampsFIFOBuffer(end)]);
end
end

Accepted Answer

Voss
Voss on 21 Mar 2024
Looks like you already found an apparent solution, but I'll go ahead and point out the things I noticed, since they may still be useful for future debugging.
First, this condition is always false:
% Check if the line needs to be extended
if any(j <= numel(app.LivePlotLine)) && isempty(app.LivePlotLine(j))
The any is unnecessary because you are comparing two scalars, but also isempty(app.LivePlotLine(j)) is always false for the following reason:
app.LivePlotLine is a vector of line objects, so if j <= numel(app.LivePlotLine) then app.LivePlotLine(j) is a scalar, (i.e., a single line object). Since a scalar is not empty by definition, that condition is always false and the if branch there (i.e., the "Use existing axes to plot" line) is never executed.
So, ignoring that line and focusing on the "First-Time Setup" line and the "Update existing line" line, the relation between app.LivePlotLine and app.SelectedChannels seems inconsistent between the two.
When the first lines are created, you create as many lines as there are selected channels:
% First-Time Setup
app.LivePlotLine = plot(app.LiveAxes, app.TimestampsFIFOBuffer, app.DataFIFOBufferch1(:,app.SelectedChannels));
so that app.LivePlotLine(1) corresponds to app.SelectedChannels(1), app.LivePlotLine(2) corresponds to app.SelectedChannels(2), and so on. For example, if app.SelectedChannels is [2,5,9], then LivePlotLine(1) has data from channel 2, LivePlotLine(2) is channel 5, and LivePlotLine(3) is channel 9. No problem (yet).
However, later, when updating those same lines (suppose app.SelectedChannels is still [2,5,9]) in the "Update existing plot" branch, j runs from 1 to numel(app.SelectedChannels), so that this:
% Update existing line
set(app.LivePlotLine(j), 'XData', app.TimestampsFIFOBuffer, 'YData', app.DataFIFOBufferch1(:, j));
would be updating app.LivePlotLine(1) from channel 1 (not channel 2), app.LivePlotLine(2) from channel 2 (not channel 5), and app.LivePlotLine(3) from channel 3 (not channel 9).
Seems like it should be the following, which would be consistent with how the lines were created in the First-Time Setup:
% Update existing line
set(app.LivePlotLine(j), 'XData', app.TimestampsFIFOBuffer, 'YData', app.DataFIFOBufferch1(:,app.SelectedChannels(j)));
More generally, I think it's worth considering a simpler code design if possible, where you create as many lines as you have channels (all channels, not selected channels) when the app starts, giving you a nice 1:1 correspondence between channels and lines. Initially the lines can be invisible and have no data. When a channel is selected, the line becomes visible; when a channel is deselected, the line becomes invisible. When you update the lines' data, you only need to update the selected channels' lines, whose index in app.LivePlotLine is the same as app.SelectedChannels, because they are 1:1 now.
  6 Comments
Connor
Connor on 25 Mar 2024
yeah, Okay I do start with plot and then update with set so I will continue with that. While trying to go through and debugg the situation where if I select channels 1 and 3 rather ie channels with gaps inbetween them I noticed that it doesnt even show up as channels 1 and 3 selected, rather it shows up as 1,2 for both app.SelectedChannel & app.SelectedCal. I am using a find function to search throught he UItable and it doesnt seem to be working in the way I expected it to be.
This is the function that I use to find the T/F values of the UItable
function UITableCellEdit(app, event)
% Update the Legends on the Axes based on the channel names
app.legnam = table2array(app.UITable.Data(:,1));
% Update SelectedChannels based on ofchan
app.SelectedChannels = find(table2array(app.UITable.Data(:,2)));
% Update SelectedCal based on IndvCal
app.SelectedCal = find(table2array(app.UITable.Data(:,3)));
% Call the function that uses the selected channels (e.g., data calibration)
updateChannelMeasurementComponents(app)
end
the following is what I use for calibration.
function calibratedData = calibrateData(app, data, slopes, intercepts)
% Initialize calibrated data array
calibratedData = data;
% We want to make sure all channels selected are included to prevent
% any errors from uccuring
for jj = app.SelectedChannels
calibratedData(:,jj) = (data(:,jj));
end
for j = app.SelectedCal
disp(j)
% Apply calibration
calibratedData(:, j) = (data(:, j) .* slopes(:,j)) + intercepts(:,j);
end
end
I am not use if their is a better way to do this but I hadnt even thought of the "if" someone is using it
thanks again for the help!
Connor
Connor on 25 Mar 2024
I was able to figure it out, the logic in the find function was fine I was just debugging impoperly. I think that I had to change back to
app.DataFIFOBufferch1(:, j)
%from
app.DataFIFOBufferch1(:,(app.SelectedChannels(i)))
becuase I was already going from 1:numel(app.SelectedChannels) in my for loop for the plotting and the calibrated data setup outside of the function had not been thoughly checked.
if app.calibrate == true
app.calibratedData = calibrateData(app,app.data, app.sl, app.int);
else
for jj = app.SelectedChannels
app.calibratedData(:,jj) = (app.data(:,jj));
end
end
is now what I am using vs what was
if app.calibrate == true
app.calibratedData = calibrateData(app,app.data, app.sl, app.int);
else
app.calibratedData = app.data
end
and inside the function my intial line
app.calibratedData = app.data
that I was using for preallocation was creating 8 channels worth of column which would affect the calibration function and the plotting function later on in the code.

Sign in to comment.

More Answers (1)

Connor
Connor on 21 Mar 2024
I fixed this by isomgpreaalocating of the app using
goobjects(size(app.SelectedChannels))
to give app.liveplotline the correct size

Categories

Find more on Data Acquisition Toolbox Supported Hardware in Help Center and File Exchange

Products


Release

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!