2

I have written an m-function to capture and plot a 50Hz serial data stream. This works as expected, bar the fact that there is a large (approximately 10 second) delay before any changes in the data stream are reflected in the plot.

The m-function uses the serial data stream design pattern, and the plot is configured to resemble a strip chart. The MATLAB recommendations for optimizing graphics performance have been followed, with YData being updated using get/set methods rather than data being replotted.

Now, either there is something wrong with my code, or I have reached the limit of what MATLAB is capable of doing without using the real-time windows target. Can anyone spot what I am doing wrong? I am considering implementing a USB interface on the embedded system that is streaming the data that MATLAB is plotting, but I am not convinced this will fix the problem.

function [] = tiny_stream(file)
% TINY_STREAM Plot and record a live pulse oximeter data stream.
%   [] = TINY_STREAM(file) plots a live pulse oximeter data stream, and also
%   records it to file. The data stream must contain ten columns of data.
expected_column_count = 10;

%% Declare variables.
% The sample rate is currently set to 50Hz by the pulse oximeter firmware, and
% only the last 10 cycles are plotted. Declaring plot data beforehand makes it
% easier to configure the plot.
global MAIN_LOOP;
MAIN_LOOP = true;

sample_rate = 50;
cycle_count = 10;

red_lowpass_data = NaN(sample_rate * cycle_count,1);

%% Create and configure plot.
% The 'plot' command is not used to update the plot, as it is far too slow to
% handle data streaming at 50Hz. Instead, the existing plot data is updated.
close all;
strip_chart = figure('Renderer','painters');
set(strip_chart,'DoubleBuffer','on');
set(strip_chart,'KeyPressFcn',@stop_stream);

time = 1:(sample_rate * cycle_count);

% Configure red chart.
red_subplot = subplot(2,1,1);
red_lowpass_trace = plot(time,red_lowpass_data,'b');

set(red_subplot,'YTickLabel',...
    {'-10,000','0','10,000','20,000','30,000','40,000'},...
    'YTick',[-10000 0 10000 20000 30000 40000],...
    'YGrid','on');
ylim(red_subplot,[-10000 40000]);
ylabel('count');

set(red_subplot, 'XTickLabel',[],...
    'XTick',1:cycle_count,...
    'XGrid','on');
xlim(red_subplot,[1 cycle_count]);
xlabel('1 sec / div');

set(red_subplot,'OuterPosition',[0 0.5 1 0.5]);
box(red_subplot,'on');
title('Red Data');

set(red_subplot,'ALimMode','manual',...
    'CameraPositionMode','manual',...
    'CameraTargetMode','manual',...
    'CameraUpVectorMode','manual',...
    'CLimMode','manual',...
    'TickDirMode','manual',...
    'XLimMode','manual','YLimMode','manual','ZLimMode','manual',...
    'XTickMode','manual','YTickMode','manual','ZTickMode','manual',...
    'XTickLabelMode','manual','YTickLabelMode','manual',...
    'ZTickLabelMode','manual');

drawnow;

%% Create and configure the serial port object.
serial_object = serial('COM2');

serial_object.BaudRate = 57600;
serial_object.DataBits = 8;
serial_object.FlowControl = 'none';
serial_object.StopBits = 1;

%% Configure the data stream.
% Note the use of the callback function 'transfer_data'. This is called by
% MATLAB whenever it detects the specified terminator in the serial object
% buffer.
serial_object.Terminator = 'CR/LF';
serial_object.InputBufferSize = 2^18;
serial_object.BytesAvailableFcnMode = 'terminator';
serial_object.BytesAvailableFcn = {@transfer_data};

serial_object.UserData.string_data = [];
serial_object.UserData.is_new = false;

%% Open the serial port.
if strcmp(serial_object.Status,'closed')
    fopen(serial_object);
end

%% Open the file.
data_file = fopen(file,'w');

%% Main program loop.
% There may be more than one row of source data in the serial input buffer at
% the start of the main program loop. Any of these rows may be incomplete, so
% the first thing the main program does is to check that the data contains the
% expected number of entries. If it does not, then the entire data chunk is
% discarded.
while MAIN_LOOP == true
    if serial_object.UserData.is_new == true
        chunk_string = serial_object.UserData.string_data;
        serial_object.UserData.is_new = false;

        chunk_numeric = sscanf(chunk_string,'%d');
        chunk_length = length(chunk_numeric);

        if mod(chunk_length, expected_column_count) == 0
            data_column_count = chunk_length / expected_column_count;

            data = reshape(chunk_numeric,expected_column_count,...
                data_column_count);

            fprintf(data_file,...
                '%6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\r\n',data);

            % Update red subplot.
            red_lowpass_data = get(red_lowpass_trace,'YData');

            red_lowpass_data(1,1:end - data_column_count) =...
                red_lowpass_data(1,data_column_count + 1:end);

            red_lowpass_data(1,end - data_column_count + 1:end) =...
                data(4,:);

            set(red_lowpass_trace,'YData',red_lowpass_data);

            drawnow;
        end
    end

    pause(0.001);
end

fclose(data_file);
fclose(serial_object);
delete(serial_object);
clear serial_object;

return

%% Loop control.
function [] = stop_stream(source, event)
% STOP_STREAM Stop the pulse oximeter serial stream.
%   STOP_STREAM(source, event) sets the MAIN_LOOP global variable to false
%   whenever a key is pressed while plot has focus.
global MAIN_LOOP;

MAIN_LOOP = false;

return

%% Data transfer.
function [] = transfer_data(object, event)
% TRANSFER_DATA Transfer data between buffers.
%   TRANSFER_DATA(object, event) transfers data between the serial object
%   input buffer and the user data area of the serial object.
string_data = fgets(object);

if object.UserData.is_new == false
    object.UserData.string_data = string_data;
    object.UserData.is_new = true;
else
    object.UserData.string_data = [object.UserData.string_data string_data];
end

return
2
  • Is the 10s lag between changing something about the streaming data and seeing it in Matlab pretty constant or does it get worse with time? It's possible that rapidly growing red_lowpass_data in your main loop is the culprit. Commented Oct 22, 2013 at 12:25
  • The delay is constant during a particular sampling run. Changing the amount of data that is displayed on the plot does change the length of the delay however - display 10s worth of data and the delay is 10s. Display 20s worth of data and the delay is 20s. This is not being caused by red_lowpass_data growing in size - all that is happening there is that existing data is being left-shifted to make room for the new data. Commented Oct 22, 2013 at 19:01

2 Answers 2

2

I posed the original question some time ago, and neglected to post the answer when I found it - sorry about that. For those who are interested, it turns out that the 10 second delay was being caused by an error in the way that I was setting the XLim and Xtick properties of my axes. Replacing the following code fragment:

set(red_subplot, 'XTickLabel',[],...
    'XTick',1:cycle_count,...
    'XGrid','on');
xlim(red_subplot,[1 cycle_count]);
xlabel('1 sec / div');

With this one:

set(red_subplot,'XTickLabel',[],...
    'XTick',(0:sample_rate:(sample_rate * cycle_count))',...
    'XGrid','on');
xlim(red_subplot,[0 sample_rate * cycle_count]);
xlabel('1 sec / div');

eliminated the 10 second delay. As is usually this case, solving one problem exposed another, more fundamental one. It appears that MATLAB is simply incapable of recording a 57600 serial data stream without dropping samples. This behavior was verified with code similar to that posted above (ie GUI code), as well as a cut-down, command-line version with no GUI at all. The number of samples dropped varied from run-to-run, but sat around 7 per minute, ie 7 per 3000 samples. I then wrote some LabVIEW code to do the same thing, and it did not drop a single sample, no matter how many hours I left it running for. The lesson I draw from all of this is that MATLAB is great for analyzing data, but is not so good for acquiring it. That is a task best left to LabVIEW, or dSPACE.

Sign up to request clarification or add additional context in comments.

Comments

0

I had similar problems. For me it was delay introduced by serial connection overflow. Try to lower the sample rate and rise the baude rate. See:

https://arduino.stackexchange.com/questions/3039/binary-serial-transmission-order-of-data

and

https://stackoverflow.com/questions/24368670/matlab-plot-serial-data-continously

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.