Plotting a loop in MATLAB - matlab

I am very new to MATLAB and am struggling to plot my data. In the program below I have modelled a relation between water level (m.a.s.l) and volume of water captured in a tank. Now I need to plot it in a way that H (water level) be in the Y-axis and V (Volume of the water) be in x-axis. However I have read a lot and tried a lot but still couldn't figure it out. Can Someone Help me PLS ??
I have tried to use simple Plot (X,Y) but it gives error. And so many other suggestions. I also tried fprintf function to have it a s a table in txt then plot it on Excel but always printed the last two H and V. not all of them.
for H = 410.03:0.01:414.57
L = 30; v = 0; fun1 = #(x) (0.3.^2-x.^2).^0.5;
if (H<410.03)
%For the heights below the tank, section 0
dis('No Water in the tank')
elseif (H>=410.03 && H<=410.33)
%section (1)
h = .3-(H-410.03) ;
a = (0.3.^2-h.^2).^0.5 ;
A = 2*(integral(fun1,0,a)-h*a);
V = A*L ;
disp(['H= ' num2str(H)])
disp(['V= ' num2str(V)])
elseif (H>410.33 && H<=410.38)
% section (1+2)
h = H-410.33 ;
V1 = L*(2*(integral(fun1,0,0.3)-0));
V = V1+(.6*h+22*h.^2)*L ;
disp(['H= ' num2str(H)])
disp(['V= ' num2str(V)])
elseif (H>410.38 && H<=414.57)
h = H - 410.38;
V1 = L*(2*(integral(fun1,0,0.3)-0));
V2 = V1+(0.085)*L;
V = V2 + (2.8*h)* L ;
disp(['H= ' num2str(H)])
disp(['V= ' num2str(V)])
end
end

This might get you started, although what I did here might not be the easiest first step to understand. I made a function H->V out of your code that can take a vector of values H and returns a vector V, suitable for plotting. If you want, you could make a simpler function that looks more like your original (with if-then-else structure) and takes a scalar H and returns a scalar V. Then you could loop over the range of H, creating V step-by-step.
First save a function like this (call the file somefunc.m):
function V = somefunc(H)
% constants
L = 30;
fun1 = #(x) (0.3.^2-x.^2).^0.5;
% set up return value (vector)
V = zeros(size(H));
% Section 1 (pick out the parts of H where it's within the first range)
section1 = find(H>=410.03 & H<=410.33);
h = .3-(H-410.03);
a = (0.3.^2-h.^2).^0.5 ;
% Have to loop here because integral won't take a vector of bounds
for i = section1
A(i) = 2*(integral(fun1,0,a(i))-h(i)*a(i));
end
V(section1) = A(section1)*L ;
% Section 1+2
section_1plus2 = find(H>410.33 & H<=410.38);
h = H-410.33 ;
V1 = L*(2*(integral(fun1,0,0.3)-0)); % scalar
V(section_1plus2) = V1+(.6*h(section_1plus2)+22*h(section_1plus2).^2)*L ;
% Section 3
section3 = find(H>410.38 & H<=414.57);
h = H - 410.38;
V1 = L*(2*(integral(fun1,0,0.3)-0)); % scalar
V2 = V1+(0.085)*L; % scalar
V(section3) = V2 + (2.8*h(section3))* L ;
Then you can run it like this:
H = 410.03 : 0.01 : 414.57;
V = somefunc(H);
plot(H,V);
xlabel('H')
ylabel('V')
I'd avoid having similarly named variables, especially the ones that are distinguished just by upper/lowercase.

Related

How can I measure length of signal in simulink?

I have model with "matlab function block" in which I have recursive least square method. Recursive algorithm needs to know length of incoming signal in order to work correctly. But when I use command N=length(y) it returns me length N= 1. But I think it should give me higher length.
Simulink model
Matlab function block code "rls_iden6"
function [P,N] = fcn(u,y)
%%
N = length(y);
sigma=1;
C = sigma*eye(2); %p
P = ones(2,1);
z= [y; u];
lamda=1;
for n=1:N
sample_out = y(n);
C = (C - ( (C*z*z'*C)/( lamda+(z'*C*z) ) ))/lamda;
P = P + (C*z* (sample_out - (z'*P)));
end
My final code should look like it's shown below, because it works in matlab workspace. Simulink should give me 5 parameters instead of just 2.
load data_cela.mat
u=U; %input
y=Y; %output
%%
input = 3;
output = 2;
system = input + output;
N = length(y);
%initial conditions
sigma = 1;
C = sigma*eye(system);
P = ones(system,1);
lamda = 1; %forgetting factor
for n=3:N
for i=1:2
W(i) = y(n-i); %output
end
for i=1:3
V(i) = u(n-i+1); %input
end
z = [V';W'];
sample_out = y(n);
pom(n)= z' * P;
error(n) = y(n) - pom(n);
C = (C - ( (C*z*z'*C)/( lamda+(z'*C*z) ) ))/lamda;
P = P + (C*z* (sample_out - (z'*P) ) );
change(1:system,n) = P;
end
f_param = [P(1:3);-P(4:5)];
num = [P(1:3,1)];
den = [1;-P(4:5,1)];
num1 = num(3,1);
trasferfunction = tf(num1,den',1)
Result:
0.002879
----------------------
z^2 - 1.883 z + 0.8873
You will need to add a buffer before signal to convert the scalar to matrix. Then after the buffer has been added set the buffer size to the amount of data you want, i.e. by setting it to 2 will make 2 rows and 1 column. This will help you to get the data however, for setting delay properly you will require to set buffer overlap to 1.
Hope this helps.

MATLAB: Entering multiple input values for the x-axis in a graph

The MATLAB program below is a function that references specific input values for S, E, r, sigma, and tau.
function [C, Cdelta, P, Pdelta] = ch08(S,E,r,sigma,tau)
% Input arguments: S = asset price at time t
% E = Exercise price
% r = interest rate
% sigma = volatility
% tau = time to expiry (T-t)
%
% Output arguments: C = call value, Cdelta = delta value of call
% P = Put value, Pdelta = delta value of put
%
% function [C, Cdelta, P, Pdelta] = ch08(S,E,r,sigma,tau)
if tau > 0
d1 = (log(S/E) + (r + 0.5*sigma^2)*(tau))/(sigma*sqrt(tau));
d2 = d1 - sigma*sqrt(tau);
N1 = 0.5*(1+erf(d1/sqrt(2)));
N2 = 0.5*(1+erf(d2/sqrt(2)));
C = S*N1-E*exp(-r*(tau))*N2;
Cdelta = N1;
P = C + E*exp(-r*tau) - S;
Pdelta = Cdelta - 1;
title('Graph of Call Value vs. Time to Expiry')
xlabel('Time to expiry')
ylabel('Call Value')
plot (tau,C)
else
C = max(S-E,0);
Cdelta = 0.5*(sign(S-E) + 1);
P = max(E-S,0);
Pdelta = Cdelta - 1;
title('Graph of Call Value vs. Time to Expiry')
xlabel('Time to expiry')
ylabel('Call Value')
plot (tau,C)
end
After running
ch08(3,2.5,0.03,0.25,1)
The following output is produced
After running the function again,
ch08(3,2.5,0.03,0.25,1)
hold on
ch08(3,2.5,0.03,0.25,0.9)
Two data points are produced
After manually typing decreasing tau values,
ch08(3,2.5,0.03,0.25,1)
hold on
ch08(3,2.5,0.03,0.25,0.9)
hold on
ch08(3,2.5,0.03,0.25,0.8)
hold on
ch08(3,2.5,0.03,0.25,0.7)
hold on
ch08(3,2.5,0.03,0.25,0.6)
hold on
ch08(3,2.5,0.03,0.25,0.5)
hold on
ch08(3,2.5,0.03,0.25,0.4)
hold on
ch08(3,2.5,0.03,0.25,0.3)
hold on
ch08(3,2.5,0.03,0.25,0.2)
hold on
ch08(3,2.5,0.03,0.25,0.1)
The graph will produce a bunch of data points,
Is there a way to automate the tau values entered in ch08(S,E,r,sigma,tau) so that the user doesn't have to type all of the input in?
As I suggested in comments, you need to use a for loop. You can create an array with values of tau that you want to use, and call your function with a different element from that array in each loop iteration:
figure, hold on
tau = 10.^(0:-1:-6);
for ii = 1:length(tau)
ch08(3,2.5,0.03,0.25,tau(ii))
end
However, a better solution would be to not plot within your ch08 function, and return the value C as you did in your first version of your question. Then you can do this:
tau = 10.^(0:-1:-6);
C = zeros(size(tau));
for ii = 1:length(tau)
C(ii) = ch08(3,2.5,0.03,0.25,tau(ii));
end
plot(tau,C,'.');
This would allow you to change your plot as you please, for example drawing a line through your points.
PS: Note that you only need to give hold on once. It sets a flag in the figure window that is not cleared until you do hold off or clf.

How to reduce the time consumed by the for loop?

I am trying to implement a simple pixel level center-surround image enhancement. Center-surround technique makes use of statistics between the center pixel of the window and the surrounding neighborhood as a means to decide what enhancement needs to be done. In the code given below I have compared the center pixel with average of the surrounding information and based on that I switch between two cases to enhance the contrast. The code that I have written is as follows:
im = normalize8(im,1); %to set the range of pixel from 0-255
s1 = floor(K1/2); %K1 is the size of the window for surround
M = 1000; %is a constant value
out1 = padarray(im,[s1,s1],'symmetric');
out1 = CE(out1,s1,M);
out = (out1(s1+1:end-s1,s1+1:end-s1));
out = normalize8(out,0); %to set the range of pixel from 0-1
function [out] = CE(out,s,M)
B = 255;
out1 = out;
for i = s+1 : size(out,1) - s
for j = s+1 : size(out,2) - s
temp = out(i-s:i+s,j-s:j+s);
Yij = out1(i,j);
Sij = (1/(2*s+1)^2)*sum(sum(temp));
if (Yij>=Sij)
Aij = A(Yij-Sij,M);
out1(i,j) = ((B + Aij)*Yij)/(Aij+Yij);
else
Aij = A(Sij-Yij,M);
out1(i,j) = (Aij*Yij)/(Aij+B-Yij);
end
end
end
out = out1;
function [Ax] = A(x,M)
if x == 0
Ax = M;
else
Ax = M/x;
end
The code does the following things:
1) Normalize the image to 0-255 range and pad it with additional elements to perform windowing operation.
2) Calls the function CE.
3) In the function CE obtain the windowed image(temp).
4) Find the average of the window (Sij).
5) Compare the center of the window (Yij) with the average value (Sij).
6) Based on the result of comparison perform one of the two enhancement operation.
7) Finally set the range back to 0-1.
I have to run this for multiple window size (K1,K2,K3, etc.) and the images are of size 1728*2034. When the window size is selected as 100, the time consumed is very high.
Can I use vectorization at some stage to reduce the time for loops?
The profiler result (for window size 21) is as follows:
The profiler result (for window size 100) is as follows:
I have changed the code of my function and have written it without the sub-function. The code is as follows:
function [out] = CE(out,s,M)
B = 255;
Aij = zeros(1,2);
out1 = out;
n_factor = (1/(2*s+1)^2);
for i = s+1 : size(out,1) - s
for j = s+1 : size(out,2) - s
temp = out(i-s:i+s,j-s:j+s);
Yij = out1(i,j);
Sij = n_factor*sum(sum(temp));
if Yij-Sij == 0
Aij(1) = M;
Aij(2) = M;
else
Aij(1) = M/(Yij-Sij);
Aij(2) = M/(Sij-Yij);
end
if (Yij>=Sij)
out1(i,j) = ((B + Aij(1))*Yij)/(Aij(1)+Yij);
else
out1(i,j) = (Aij(2)*Yij)/(Aij(2)+B-Yij);
end
end
end
out = out1;
There is a slight improvement in the speed from 93 sec to 88 sec. Suggestions for any other improvements to my code are welcomed.
I have tried to incorporate the suggestions given to replace sliding window with convolution and then vectorize the rest of it. The code below is my implementation and I'm not getting the result expected.
function [out_im] = CE_conv(im,s,M)
B = 255;
temp = ones(2*s,2*s);
temp = temp ./ numel(temp);
out1 = conv2(im,temp,'same');
out_im = im;
Aij = im-out1; %same as Yij-Sij
Aij1 = out1-im; %same as Sij-Yij
Mij = Aij;
Mij(Aij>0) = M./Aij(Aij>0); % if Yij>Sij Mij = M/Yij-Sij;
Mij(Aij<0) = M./Aij1(Aij<0); % if Yij<Sij Mij = M/Sij-Yij;
Mij(Aij==0) = M; % if Yij-Sij == 0 Mij = M;
out_im(Aij>=0) = ((B + Mij(Aij>=0)).*im(Aij>=0))./(Mij(Aij>=0)+im(Aij>=0));
out_im(Aij<0) = (Mij(Aij<0).*im(Aij<0))./ (Mij(Aij<0)+B-im(Aij<0));
I am not able to figure out where I'm going wrong.
A detailed explanation of what I'm trying to implement is given in the following paper:
Vonikakis, Vassilios, and Ioannis Andreadis. "Multi-scale image contrast enhancement." In Control, Automation, Robotics and Vision, 2008. ICARCV 2008. 10th International Conference on, pp. 856-861. IEEE, 2008.
I've tried to see if I could get those times down by processing with colfiltand nlfilter, since both are usually much faster than for-loops for sliding window image processing.
Both worked fine for relatively small windows. For an image of 2048x2048 pixels and a window of 10x10, the solution with colfilt takes about 5 seconds (on my personal computer). With a window of 21x21 the time jumped to 27 seconds, but that is still a relative improvement on the times displayed on the question. Unfortunately I don't have enough memory to colfilt using windows of 100x100, but the solution with nlfilter works, though taking about 120 seconds.
Here the code
Solution with colfilt:
function outval = enhancematrix(inputmatrix,M,B)
%Inputmatrix is a 2D matrix or column vector, outval is a 1D row vector.
% If inputmatrix is made of integers...
inputmatrix = double(inputmatrix);
%1. Compute S and Y
normFactor = 1 / (size(inputmatrix,1) + 1).^2; %Size of column.
S = normFactor*sum(inputmatrix,1); % Sum over the columns.
Y = inputmatrix(ceil(size(inputmatrix,1)/2),:); % Center row.
% So far we have all S and Y, one value per column.
%2. Compute A(abs(Y-S))
A = Afunc(abs(S-Y),M);
% And all A: one value per column.
%3. The tricky part. If Y(i)-S(i) > 0 do something.
doPositive = (Y > S);
doNegative = ~doPositive;
outval = zeros(1,size(inputmatrix,2));
outval(doPositive) = (B + A(doPositive) .* Y(doPositive)) ./ (A(doPositive) + Y(doPositive));
outval(doNegative) = (A(doNegative) .* Y(doNegative)) ./ (A(doNegative) + B - Y(doNegative));
end
function out = Afunc(x,M)
% Input x is a row vector. Output is another row vector.
out = x;
out(x == 0) = M;
out(x ~= 0) = M./x(x ~= 0);
end
And to call it, simply do:
M = 1000; B = 255; enhancenow = #(x) enhancematrix(x,M,B);
w = 21 % windowsize
result = colfilt(inputImage,[w w],'sliding',enhancenow);
Solution with nlfilter:
function outval = enhanceimagecontrast(neighbourhood,M,B)
%1. Compute S and Y
normFactor = 1 / (length(neighbourhood) + 1).^2;
S = normFactor*sum(neighbourhood(:));
Y = neighbourhood(ceil(size(neighbourhood,1)/2),ceil(size(neighbourhood,2)/2));
%2. Compute A(abs(Y-S))
test = (Y>=S);
A = Afunc(abs(Y-S),M);
%3. Return outval
if test
outval = ((B + A) * Y) / (A + Y);
else
outval = (A * Y) / (A + B - Y);
end
function aval = Afunc(x,M)
if (x == 0)
aval = M;
else
aval = M/x;
end
And to call it, simply do:
M = 1000; B = 255; enhancenow = #(x) enhanceimagecontrast(x,M,B);
w = 21 % windowsize
result = nlfilter(inputImage,[w w], enhancenow);
I didn't spend much time checking that everything is 100% correct, but I did see some nice contrast enhancement (hair looks particularly nice).
This answer is the implementation that was suggested by Peter. I debugged the implementation and presenting the final working version of the fast implementation.
function [out_im] = CE_conv(im,s,M)
B = 255;
im = ( im - min(im(:)) ) ./ ( max(im(:)) - min(im(:)) )*255;
h = ones(s,s)./(s*s);
out1 = imfilter(im,h,'conv');
out_im = im;
Aij = im-out1; %same as Yij-Sij
Aij1 = out1-im; %same as Sij-Yij
Mij = Aij;
Mij(Aij>0) = M./Aij(Aij>0); % if Yij>Sij Mij = M/(Yij-Sij);
Mij(Aij<0) = M./Aij1(Aij<0); % if Yij<Sij Mij = M/(Sij-Yij);
Mij(Aij==0) = M; % if Yij-Sij == 0 Mij = M;
out_im(Aij>=0) = ((B + Mij(Aij>=0)).*im(Aij>=0))./(Mij(Aij>=0)+im(Aij>=0));
out_im(Aij<0) = (Mij(Aij<0).*im(Aij<0))./ (Mij(Aij<0)+B-im(Aij<0));
out_im = ( out_im - min(out_im(:)) ) ./ ( max(out_im(:)) - min(out_im(:)) );
To call this use the following code
I = imread('pout.tif');
w_size = 51;
M = 4000;
output = CE_conv(I(:,:,1),w_size,M);
The output for the 'pout.tif' image is given below
The execution time for Bigger image and with 100*100 block size is around 5 secs with this implementation.

How to plot a function of multiple variables in Matlab?

I'm trying to figure out a way to make a plot of a function in Matlab that accepts k parameters and returns a 3D point. Currently I've got this working for two variables m and n. How can I expand this process to any number of parameters?
K = zeros(360*360, number);
for m = 0:5:359
for n = 1:5:360
K(m*360 + n, 1) = cosd(m)+cosd(m+n);
K(m*360 + n, 2) = sind(m)+sind(m+n);
K(m*360 + n, 3) = cosd(m)+sind(m+n);
end
end
K(all(K==0,2),:)=[];
plot3(K(:,1),K(:,2),K(:,3),'.');
end
The code you see above is for a similar problem but not exactly the same.
Most of the time you can do this in a vectorized manner by using ndgrid.
[M, N] = ndgrid(0:5:359, 1:5:360);
X = cosd(M)+cosd(M+N);
Y = sind(M)+sind(M+N);
Z = cosd(M)+sind(M+N);
allZero = (X==0)&(Y==0)&(Z==0); % This ...
X(allZero) = []; % does not ...
Y(allZero) = []; % do ...
Z(allZero) = []; % anything.
plot3(X,Y,Z,'b.');
A little explanation:
The call [M, N] = ndgrid(0:5:359, 1:5:360); generates all combinations, where M is an element of 0:5:359 and N is an element of 1:5:360. This will be in the form of two matrices M and N. If you want you can reshape these matrices to vectors by using M = M(:); N = N(:);, but this isn't needed here.
If you were to have yet another variable, you would use: [M, N, P] = ndgrid(0:5:359, 1:5:360, 10:5:1000).
By the way: The code part where you delete the entry [0,0,0] doesn't do anything here, because this value doesn't appear. I see you only needed it, because you were allocating a lot more memory than you actually needed. Here are two versions of your original code, that are not as good as the ndgrid version, but preferable to your original one:
m = 0:5:359;
n = 1:5:360;
K = zeros(length(m)*length(n), 3);
for i = 1:length(m)
for j = 1:length(n)
nextRow = (i-1)*length(n) + j;
K(nextRow, 1) = cosd(m(i)) + cosd(m(i)+n(j));
K(nextRow, 2) = sind(m(i)) + sind(m(i)+n(j));
K(nextRow, 3) = cosd(m(i)) + sind(m(i)+n(j));
end
end
Or simpler, but a bit slower:
K = [];
for m = 0:5:359
for n = 1:5:360
K(end+1,1:3) = 0;
K(end, 1) = cosd(m)+cosd(m+n);
K(end, 2) = sind(m)+sind(m+n);
K(end, 3) = cosd(m)+sind(m+n);
end
end

How to call automatically symbolic variables in a loop in Matlab

Is there anyway to use a loop index to call symbolic variables in Matlab? For example, consider the following code whose goal is to store the symbolic expression "x1+x2+x3" in "y".
syms x1 x2 x3
y = 0;
for i = 1:3
y = y + xi;
end
The code does not work because on each iteration Matlab reads "y = y + xi" and returns the error "xi is undefined", instead of reading "y = y + x1", "y = y + x2" and "y = y + x3", is there anyway around this?
Thanks.
I'd suggest this, provided that you can create your numbered symbolic variable slightly differently:
x = sym('x',[1 3]); % or: syms x1 x2 x3; x = [x1 x2 x3];
y = x(1);
for i = 2:numel(x)
y = y+x(i);
end
Of course in this simple example, the entire for loop and everything else can be replaced with:
y = sum(sym('x',[1 3]));
See the documentation on sym for more details.
EDIT: Note that, as #pm89 points out, by allocating the 1-by-3 symbolic vector x, you of course won't have direct access to the symbolic variables x1,x2, and x3 in your workspace, but will have to index them as shown. This is similar to working with arrays or cells and has many of the same benefits as my second vectorized example illustrates.
If your Matlab does not support the matrix declaration of symbolics directly (as sym('x',[3 1])) you can write your own function for that:
function out = Matrix_Sym(name, size) %#ok<STOUT>
rows = size(1);
cols = size(2);
S = '';
for k1 = 1:rows
for k2 = 1:cols
if rows == 1
S = [S name int2str(k2) ' '];
elseif cols == 1
S = [S name int2str(k1) ' '];
else
S = [S name int2str(k1) int2str(k2) ' '];
end
end
end
eval(['syms ' S]);
eval (['out = reshape([' S '], [rows, cols]);']);
Then you could get the same result with:
x = Matrix_Sym('x', [3 1])
...