I'm plotting real-time data in Matlab (received through a serial port), and I found a curious way to affect the refresh rate: changing the size of the plot. When the plot is "normal" it'll get ~35fps, and as I shrink it, it'll go over 120fps. However, when I use the exact same plot setup, but just plot data generated randomly for each loop, the refresh rate is unaffected by the size of the plot. Unfortunately, profile viewer (2017a) is unable to see inside the plotting, and just attributes all of the wait time to the serial port - which isn't possible, given the variable refresh rate described above (also, I've tested a bare-bones serial call and response, and the roundtrip for Matlab is under 8ms (~120fps)).
The two loop examples are not meant to get the same framerate, they are merely to demonstrate that when one changes the plot size (just manually grab a corner and adjust it, or use the position parameter) that the first example (serial) is hugely affected by the size, and the second example (random) is not.
Addition:
After playing with a modified version of this where the plot is updated by a timer callback, I see that I can either make the serial read loop fast (~170Hz) or update the plot fast (~170Hz). However, the other always suffers. And if I change the timer period iteratively to get the same update rate for both, not surprisingly, it is almost exactly the same rate as I get with loop shown below. It would be nice to put the plot update on a separate thread, without jumping through the hoops of calling a separate Matlab process and communicating between the two.
Plot setup:
figure_h = figure();
set(figure_h, 'KeyPressFcn', @key_press_function);
set(figure_h, 'WindowButtonDownFcn', {@click_callback, obj})
%some random things that drastically speed up the plotting
set(figure_h, 'Position', [100, 100, 350, 350]); %the size has a huge effect on performance
set(figure_h, 'MenuBar', 'none');
set(figure_h, 'GraphicsSmoothing', 'Off');
set(figure_h, 'DockControls', 'Off');
set(figure_h, 'NumberTitle', 'Off');
set(figure_h, 'ToolBar', 'none');
%create plots
scatter_h = plot(number_array(:,1)*scale, number_array(:,2)*scale, 'r.');
set(scatter_h, 'MarkerSize',8);
set(scatter_h, 'Clipping','off');
hold on
axis square
grid on
ylim([min_value, max_value*scale]);
xlim([min_value, max_value*scale]);
set(figure_h.CurrentAxes, 'xtick', [min_value:max_value/10:max_value]); %#ok<NBRAK>
set(figure_h.CurrentAxes, 'ytick', [min_value:max_value/10:max_value]); %#ok<NBRAK>
set(figure_h.CurrentAxes, 'XTickLabel',[]);
set(figure_h.CurrentAxes, 'YTickLabel',[]);
set(figure_h.CurrentAxes, 'ZTickLabel',[]);
set(figure_h.CurrentAxes, 'ZGrid', 'off');
set(figure_h.CurrentAxes, 'XLimMode', 'manual');
set(figure_h.CurrentAxes, 'YLimMode', 'manual');
set(figure_h.CurrentAxes, 'ZLimMode', 'manual');
set(figure_h.CurrentAxes, 'ALimMode', 'manual');
set(figure_h.CurrentAxes, 'CLimMode', 'manual');
set(figure_h.CurrentAxes, 'DataAspectRatioMode', 'manual');
set(figure_h.CurrentAxes, 'CameraPositionMode', 'manual');
set(figure_h.CurrentAxes, 'CameraPosition', [max_value/2,max_value/2,1]);
set(figure_h.CurrentAxes, 'CameraTargetMode', 'manual');
set(figure_h.CurrentAxes, 'CameraTarget', [max_value/2,max_value/2,0]);
set(figure_h.CurrentAxes, 'CameraUpVectorMode', 'manual');
set(figure_h.CurrentAxes, 'CameraUpVector', [0,1,0]);
set(figure_h.CurrentAxes, 'CameraViewAngleMode', 'manual');
set(figure_h.CurrentAxes, 'CameraViewAngle', 180);
set(figure_h.CurrentAxes, 'GridAlphaMode', 'manual');
set(figure_h.CurrentAxes, 'Clipping', 'off');
circle_h = line((radius*cos(theta) + center_x)*scale,(radius*sin(theta) + center_y)*scale);
hold off
Simplified loop for serial:
while keep_running
%must wait for next packet to arrive
bytes = obj.bytes_available;
while keep_running && (bytes == 0)
bytes = obj.bytes_available; //function call actually directly uses the java serial objects for speed
end
%send request for next packet
obj.send_command(obj.cmd_id.cmd_history, true); //sends synchronously, otherwise, with async, an additional wait loop is necessary to prevent an async call if serial is still transmitting (Matlab throws an error otherwise - why they can't be queued, I don't know)
%grab the available buffer
new_buffer = obj.buffer_available(bytes);
%append new characters to running buffer
char_buffer = strcat(char_buffer, new_buffer);
%process buffer for all arrays
while (~isempty(strfind(char_buffer, '[')) && ~isempty(strfind(char_buffer, ']'))) %#ok<STREMP>
%extract a number array
[number_array, char_buffer] = extract_hex_array(char_buffer,3,2);
if ~isempty(number_array)
%remove any duplicate points
number_array = unique(number_array, 'rows', 'stable');
frames = frames + 1;
%update data
set(scatter_h, 'xdata', number_array(:,1)*scale, 'ydata', number_array(:,2)*scale);
%update header information (after stabilization, and every 10 frames)
if frames > 25 && mod(frames, 10) == 0
avg_framerate = frames/toc(total_time);
set(figure_h, 'Name', strcat('rate: ', num2str(avg_framerate, '%.1f'), ' Hz buffer: ',num2str(length(char_buffer), '%05d')));
end
%just a small delay, but not too small the callbacks stop working
pause(0.005);
end
end
end
Dummy random-data loop:
start_tic = tic;
for i=1:200
array1 = 4095*rand(82,2);
set(scatter_h, 'xdata', array1(:,1), 'ydata', array1(:,2));
pause(0.005);
end
end_tic = toc(start_tic);
fprintf("fps: %f\n", i/end_tic);
setfunction, especially when their values are the same:set(figure_h.CurrentAxes, {'XLimMode','YLimMode','ZLimMode',...'ALimMode','CLimMode','DataAspectRatioMode','CameraPositionMode',...'CameraTargetMode','CameraUpVectorMode', 'CameraViewAngleMode',...'GridAlphaMode'},repmat({'manual'},1,11));- Anthony