## Copyright (c) 2012 Juan Pablo Carbajal
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{h} =} srl_plot ()
## @deftypefnx {Function File} {@var{h} =} srl_plot (@var{property},@var{value})
## Plots data read from the serial port.
##
## It takes the following properties from the command line.
## @strong{Properties}
## @table @asis
## @item "Figure"
## Handle to the figure to plot into. Default @code{gcf()}.
## @item "QeueSize"
## Size of the array to be plotted. Default 1e3.
## @item "BufferSize"
## Size of the array to be read form the serial port. Default 1e2.
## @item "Baudrate"
## Baud rate of the communication. Default 9600
## @item "Timeout"
## Time out for reading from the serial port. Default 1e3 ms.
## @item "Axis"
## What to do with the y-axis after the plot is updated. It can be a 1-by-2 array
## [miny maxy] indicating the limits of the axis. Default @asis{"tight"}.
## @item "PlotMethod"
## Indicates the method used ot update the plot. Either via "source" using callback
## or directly updating the "data" of the plot. Default @asis{"data"}.
## @item "Deserializer"
## Handle to a function executed exactly after the data gas been read from the serial
## port. It must take a vector input (the data) and return an array of double.
## Default @code{"@double"}.
## @end table
##
## @seealso{srl_read, srl_write}
## @end deftypefn
## Author: Juan Pablo Carbajal
function h = srl_plot (varargin)
% Parse argumnets %%%%%%%%%%%%
parser = inputParser ();
parser.FunctionName = "srl_plot";
parser = addParamValue (parser, "Figure", gcf (), @isindex);
parser = addParamValue (parser, "QeueSize", 5e2, @(x) x > 0 );
parser = addParamValue (parser, "BufferSize", 1e2, @(x) x > 0 );
parser = addParamValue (parser, "Baudrate", 9600, @isbaud);
parser = addParamValue (parser, "Timeout", 1e3, @(x) x > 0 ); % in ms
parser = addParamValue (parser, "Axis", "tight", @isaxis);
parser = addParamValue (parser, "PlotMethod", "data");
parser = addParamValue (parser, "Deserializer", @double, @is_function_handle);
parser = parse (parser, varargin{:});
fig = parser.Results.Figure;
qsize = parser.Results.QeueSize;
bsize = parser.Results.BufferSize;
baud = parser.Results.Baudrate;
timeo = parser.Results.Timeout/1e3*10; %to ds
axistyp = parser.Results.Axis;
if isnumeric (axistyp)
axistyp = [1 qsize axistyp(:)'];
endif
plotmet = parser.Results.PlotMethod;
desrl = parser.Results.Deserializer;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Open serial port
s = serial ();
srl_baudrate (s,baud);
srl_timeout (s,timeo);
srl_flush (s);
% Test if desrl changes sizes
y = uint8 (randi(255,qsize,1));
y = desrl (y);
if any (!isfloat (y))
error ("Octave:invalid-input-arg", ["Deserializer function handle must " ...
"return double."])
end
% Allocate vectors
x = (1:qsize)';
y = nan (qsize,1);
% Prepare plot
figure(fig);
clf;
h = plot (x,y,'.g');
t_txt = @(x) sprintf("Time between updates: %.2f seconds",x);
ht = title (t_txt(0));
axis (axistyp);
% Initate transmission
srl_write (s, "\r");
% Get data
idx = 0;
switch plotmet
case "source"
set (h,"ydatasource","y");
while true
tic
[data n] = srl_read (s, bsize);
data = desrl (data);
n = length (data);
[y idx] = updatedata (y,data,idx,n,qsize);
refreshdata (fig, "caller");
set (ht, "string", t_txt(toc));
sleep (0.008);
endwhile
case "data"
while (true)
tic
[data n] = srl_read (s, bsize);
data = desrl (data);
n = length (data);
[y idx] = updatedata (y,data,idx,n,qsize);
set (h, "ydata", y);
axis (axistyp);
set (ht, "string", t_txt(toc));
sleep (0.008)
endwhile
endswitch
% Close serial port
srl_close (s);
endfunction
function [y idxnew scrap] = updatedata (y, data, idx, n, qsize)
persistent qeue_full
if isempty(qeue_full)
% While queue not full, increase the position in the queue
idxnew = idx + n;
else
% Once is full just return how much data was pushed in.
idxnew = n;
end
if qeue_full
% The qeue is full push the n entries
scrap = y(1:n);
y(1:qsize-n) = y(n+1:qsize);
y(qsize-n+1:qsize) = data;
elseif idxnew > qsize
% The qeue will be full next iteration
% insert the elements that fit, push the rest.
in = n - (qsize - idx);
out = idx - in;
scrap = y(1:out);
y(1:out) = y(in+1:idx);
y(out+1:qsize) = data;
qeue_full = true;
else
% The qeue is still not full, insert elements.
y(idx+1:idxnew) = data;
scrap = [];
endif
endfunction
% Validator functions
function v = isbaud (x)
v = any(x == [0, 50, 75, 110, 134, 150, 200, 300, 600, ...
1200, 1800, 2400, 4800, 9600 19200, 38400, ...
57600, 115200, 230400]);
endfunction
function v = isaxis (x)
v = ischar(x) || all(size (x) == [1 2]) || all(size (x) == [2 1]);
endfunction