function hpol = dirplot(theta,rho,line_style,params)
% DIRPLOT Polar directivity plot.
% A modification of The Mathworks POLAR function, DIRPLOT generates
% directivity plots in the style commonly used in acoustic and RF work.
% Features include:
% 1. Plots -90 to +90 or -180 to +180 degrees based on range of input
% THETA, with 0 degrees at top center.
% 2. Produces semicircular plots when plot range is -90 to +90 degrees.
% 3. RHO is assumed to be in decibels and may include negative
% values.
% 4. Default automatic rho-axis scaling in "scope knob" factors.
% 5. Optional PARAMS argument allows manual setting of rho-axis
% scaling.
%
% DIRPLOT(THETA, RHO) makes a plot using polar coordinates of the
% angle THETA versus the radius RHO. THETA must be in degrees, and
% must be within the range -180 to +180 degrees. If THETA is within
% the range -90 to +90 degrees, the plot will be semicircular. RHO is
% assumed to be in decibels and the values may be positive or negative or
% both. By default, with no PARAMS argument, rho-axis scaling will be determined
% automatically using scope knob factors of 1-2-5. By default, 10
% ticks will be plotted. Note: Like POLAR, DIRPLOT does not rescale the
% axes when a new plot is added to a held graph.
%
% DIRPLOT(THETA, RHO, LINE_STYLE, PARAMS) makes a plot as described above
% using the linestyle specified in string LINE_STYLE, and using the rho-axis
% scaling specified in vector PARAMS. Either of these optional arguments may be
% used alone. Vector PARAMS is a 3-element row vector defined as
% [RHOMAX RHOMIN RHOTICKS]. String LINE_STYLE is the standard MATLAB linestyle
% string. See PLOT for a description.
%
% HPOL = DIRPLOT(...) returns a handle to the LINE object generated by the PLOT
% function that actually generates the plot in DIRPLOT.
%
% See also POLAR, PLOT, LOGLOG, SEMILOGX, SEMILOGY.
%
% Rev 1.0, 17 January 2002
% Tested in MATLAB v. 6.0
%
% Adapted from The MathWorks POLAR function by
% Steve Rickman
% sar@surewest.net
if nargin <= 1
error('Requires 2, 3, or 4 input arguments.')
elseif nargin == 2
line_style = 'auto';
elseif nargin == 3
if isnumeric(line_style)
params = line_style;
line_style = 'auto';
end
end
if exist('params')
if length(params) ~= 3
error('Argument PARAMS must be a 3-element vector: [RHOMAX RHOMIN RHOTICKS].')
end
if params(1) <= params(2)
error('Error in PARAMS argument. RHOMAX must be greater than RHOMIN.')
end
if params(3) <= 0
params(3) = 1;
warning('Error in PARAMS argument. RTICKS set to 1.')
end
end
if isstr(theta) | isstr(rho)
error('THETA and RHO must be numeric.');
end
if ~isequal(size(theta),size(rho))
error('THETA and RHO must be the same size.');
end
if (max(theta) - min(theta)) < 6.3
warning('THETA must be in degrees');
end
if min(theta) >= 0
warning('Plot is -90 to +90 or -180 to +180 degrees');
end
if max(abs(theta)) > 180
error('Plot is -90 to +90 or -180 to +180 degrees');
end
% Get range of theta and set flag for full or half plot.
if (max(theta)-min(theta)) > 180 | max(theta) > 90
fullplot = 1;
else
fullplot = 0;
end
% Translate theta degrees to radians
theta = theta*pi/180;
cax = newplot;
next = lower(get(cax,'NextPlot'));
hold_state = ishold;
if hold_state & exist('params')
warning('Plot is held. New plot parameters ignored')
end
% get x-axis text color so grid is in same color
tc = get(cax,'xcolor');
ls = get(cax,'gridlinestyle');
% Hold on to current Text defaults, reset them to the
% Axes' font attributes so tick marks use them.
fAngle = get(cax, 'DefaultTextFontAngle');
fName = get(cax, 'DefaultTextFontName');
fSize = get(cax, 'DefaultTextFontSize');
fWeight = get(cax, 'DefaultTextFontWeight');
fUnits = get(cax, 'DefaultTextUnits');
set(cax, 'DefaultTextFontAngle', get(cax, 'FontAngle'), ...
'DefaultTextFontName', get(cax, 'FontName'), ...
'DefaultTextFontSize', get(cax, 'FontSize'), ...
'DefaultTextFontWeight', get(cax, 'FontWeight'), ...
'DefaultTextUnits','data')
% only do grids if hold is off
if ~hold_state
% make a radial grid
hold on;
if ~exist('params')
rticks = 10; % default ticks
lims = findscale(rho,rticks); % get click, rmax, rmin
click = lims(1); rmax = lims(2); rmin = lims(3);
rngdisp = rmax - rmin;
else
rmax = params(1); rmin = params(2); rticks = params(3);
rngdisp = rmax - rmin;
click = rngdisp/rticks;
end
set(cax,'userdata',[rngdisp rmax rmin]); % save variables for added plots
% define a circle
th = 0:pi/50:2*pi;
xunit = cos(th);
yunit = sin(th);
% now really force points on x/y axes to lie on them exactly
inds = 1:(length(th)-1)/4:length(th);
xunit(inds(2:2:4)) = zeros(2,1);
yunit(inds(1:2:5)) = zeros(3,1);
% plot background if necessary
if ~isstr(get(cax,'color')),
patch('xdata',xunit*rngdisp,'ydata',yunit*rngdisp, ...
'edgecolor',tc,'facecolor',get(gca,'color'),...
'handlevisibility','off');
end
% draw radial circles
% angles for text labels
c88 = cos(88*pi/180);
s88 = sin(88*pi/180);
c92 = -cos(92*pi/180);
s92 = -sin(92*pi/180);
for i=click:click:rngdisp
tickt = i+rmin;
if abs(tickt) < .001
tickt = 0;
end
ticktext = ['' num2str(tickt)];
hhh = plot(xunit*i,yunit*i,ls,'color',tc,'linewidth',1,...
'handlevisibility','off');
if i < rngdisp
text(i*c88,i*s88, ...
ticktext,'verticalalignment','bottom',...
'handlevisibility','off','fontsize',8)
else
text(i*c88,i*s88, ...
[ticktext,' dB'],'verticalalignment','bottom',...
'handlevisibility','off','fontsize',8)
end
if fullplot
if i < rngdisp
text(i*c92,i*s92, ...
ticktext,'verticalalignment','bottom',...
'handlevisibility','off','fontsize',8)
else
text(i*c92,i*s92, ...
[ticktext,' dB'],'verticalalignment','bottom',...
'handlevisibility','off','fontsize',8)
end
end
end
set(hhh,'linestyle','-') % Make outer circle solid
% plot spokes at 10 degree intervals
th = (0:18)*2*pi/36;
cst = cos(th); snt = sin(th);
cs = [-cst; cst];
sn = [-snt; snt];
plot(rngdisp*cs,rngdisp*sn,ls,'color',tc,'linewidth',1,...
'handlevisibility','off')
% label spokes in 30 degree intervals
rt = 1.1*rngdisp;
for i = 1:3:19
text(rt*cst(i),rt*snt(i),[int2str(90-(i-1)*10),'^o'],...
'horizontalalignment','center',...
'handlevisibility','off');
end
if fullplot
for i = 3:3:6
text(-rt*cst(i+1),-rt*snt(i+1),[int2str(-90-i*10),'^o'],...
'horizontalalignment','center',...
'handlevisibility','off');
end
for i = 9:3:15
text(-rt*cst(i+1),-rt*snt(i+1),[int2str(270-i*10),'^o'],...
'horizontalalignment','center',...
'handlevisibility','off');
end
end
% set view to 2-D
view(2);
% set axis limits
if fullplot
axis(rngdisp*[-1 1 -1.15 1.15]);
else
axis(rngdisp*[-1 1 0 1.15]);
end
end
if hold_state
v = get(cax,'userdata');
rngdisp = v(1);
rmax = v(2);
rmin = v(3);
end
% Reset defaults.
set(cax, 'DefaultTextFontAngle', fAngle , ...
'DefaultTextFontName', fName , ...
'DefaultTextFontSize', fSize, ...
'DefaultTextFontWeight', fWeight, ...
'DefaultTextUnits',fUnits );
% transform data to Cartesian coordinates.
% Rotate by pi/2 to get 0 degrees at top. Use negative
% theta to have negative degrees on left.
xx = (rho+rngdisp-rmax).*cos(-theta+pi/2);
yy = (rho+rngdisp-rmax).*sin(-theta+pi/2);
% plot data on top of grid
if strcmp(line_style,'auto')
q = plot(xx,yy);
else
q = plot(xx,yy,line_style);
end
if nargout > 0
hpol = q;
end
set(gca,'dataaspectratio',[1 1 1]), axis off; set(cax,'NextPlot',next);
set(get(gca,'xlabel'),'visible','on')
set(get(gca,'ylabel'),'visible','on')
% Subfunction finds optimal scaling using "scope knob"
% factors of 1, 2, 5. Range is limited to practical
% decibel values.
function lims = findscale(rho, rticks)
clicks = [.001 .002 .005 .01 .02 .05 .1 ...
.2 .5 1 2 5 10 20 50 100 200 500 1000];
lenclicks = length(clicks);
rhi = max(rho);
rlo = min(rho);
rrng = rhi - rlo;
rawclick = rrng/rticks;
n = 1;
while clicks(n) < rawclick
n = n + 1;
if n > lenclicks
close;
error('Cannot autoscale; unrealistic decibel range.');
end
end
click = clicks(n);
m = floor(rhi/click);
rmax = click * m;
if rhi - rmax ~= 0
rmax = rmax + click;
end
rmin = rmax - click * rticks;
% Check that minimum rho value is at least one tick
% above rmin. If not, increase click value and
% rescale.
if rlo < rmin + click
if n < lenclicks
click = clicks(n+1);
else
error('Cannot autoscale; unrealistic decibel range.');
end
m = floor(rhi/click);
rmax = click * m;
if rhi - rmax ~= 0
rmax = rmax + click;
end
rmin = rmax - click * rticks;
end
lims = [click rmax rmin];