This commit is contained in:
2023-11-14 17:10:27 +08:00
commit d5a822831c
423 changed files with 5909 additions and 0 deletions

View File

@ -0,0 +1,15 @@
function alpha=base_order(G)
% base_order - find the base order of an FOTF object
%
% alpha=base_order(G)
%
% G - an FOTF object
% alpha - the base order of the system
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); a=[];
for i=1:n, for j=1:m, g=G(i,j); a=[a,g.num.na,g.den.na]; end, end
alpha=double(gcd(sym(a)));
end

17
FOTF Toolbox/@fotf/bode.m Normal file
View File

@ -0,0 +1,17 @@
function H=bode(G,w)
% bode - draw Bode diagram for an FOTF object
%
% bode(G,w)
% H=bode(G,w)
%
% G - the FOTF object
% w - the frequency vector
% H - the frequency response data in FRD format
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
arguments, G, w=logspace(-4,4); end
H1=freqresp(1j*w,G); H1=frd(H1,w);
if nargout==0, subplot(111), bode(H1); else, H=H1; end
end

18
FOTF Toolbox/@fotf/diag.m Normal file
View File

@ -0,0 +1,18 @@
function G=diag(G1)
% diag - diagonal matrix manipulation of an FOTF object
%
% G=diag(G1)
%
% G - the FOTF object
% G1 - if G is a vector, configurate a matrix G1, otherwise extract its
% diagonal elements to form G1
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G1); nm=max(n,m); nm1=min(n,m);
if m==1 || n==1
G=fotf(zeros(nm,nm)); for i=1:nm, G(i,i)=G1(i); end
else
G=fotf(zeros(nm1,1)); for i=1:nm1, G(i)=G1(i,i); end
end, end

25
FOTF Toolbox/@fotf/disp.m Normal file
View File

@ -0,0 +1,25 @@
function str=disp(G)
% display - display an FOTF. This function will be called automatically
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); key=0; if nargout==1, key=1; end
for i=1:m, if m>1, disp([' From input ' int2str(i) ' to output...']), end
for j=1:n, if n>1, disp([' ' int2str(j) ':']), end
str=fotfdisp(G(j,i),key); if nargout==0, disp(' '); end
end, end, end
% display a SISO FOTF object
function str=fotfdisp(G,key)
strN=disp(G.num); str=strN;
strD=disp(G.den); nn=length(strN);
if nn==1 && strN=='0', if key==0, disp(strN), end
else, nd=length(strD); nm=max([nn,nd]);
if key==0, disp([char(' '*ones(1,floor((nm-nn)/2))) strN]), end
ss=[]; T=G.ioDelay; if T>0, ss=['exp(-' num2str(T) '*s)']; end
if T>0, str=['(' str ')*' ss '/(' strD ')'];
else, str=['(' str ')/(' strD ')']; end
str=strrep(strrep(str,'{',''),'}','');
if key==0, disp([char('-'*ones(1,nm)), ss]);
disp([char(' '*ones(1,floor((nm-nd)/2))) strD])
end, end, end

View File

@ -0,0 +1,10 @@
function a=double(G)
%double - convert FOTF to double, if possible
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 May, 2022
if G.num.na==0 && G.den.na==0
a=G.num.a./G.den.a;
else, error('G contains dynamic terms, cannot be converted into double')
end
end

13
FOTF Toolbox/@fotf/eig.m Normal file
View File

@ -0,0 +1,13 @@
function p=eig(G)
% eig - finding all the pseudo poles of an FOTF object
%
% p=eig(G)
%
% G - the FOTF object
% p - all the pseudo-poles of G
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
p=eig(foss(G));
end

19
FOTF Toolbox/@fotf/eq.m Normal file
View File

@ -0,0 +1,19 @@
function key=eq(G1,G2)
% eq - test whether two FOTF objects are equal or not
%
% key=G1==G2
%
% G1, G2 - the two FOTF objects
% key = 1 for equal, otherwise key = 0
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n1,m1]=size(G1); [n2,m2]=size(G2);
if n1~=n2 || m1~=m2, key=0;
else
G=G1-G2; [n,m]=size(G); key=0; kk=0;
for i=1:n, for j=1:m, kk=kk+(G(i,j).num==0);
end, end, end
key=(kk==n1*m1);
end

13
FOTF Toolbox/@fotf/exp.m Normal file
View File

@ -0,0 +1,13 @@
function G=exp(del)
% exp - express delay term exp(-tau*s)
%only works for SISO FOTF model
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 May, 2022
try
T=double(del/fotf('s'));
if T>0, error('Delay must be negative'), else, T=-T; end
G=fotf(1); G.ioDelay=T;
catch, error('Error in extracting delay constant');
end
end

View File

@ -0,0 +1,23 @@
function G=feedback(F,H)
% feedback - find the overall model of two FOTF objects in feedback connection
%
% G=feedback(G1,G2)
%
% G1, G2 - the FOTF objects in the forward and backward paths
% G - the overall model of the closed-loop system
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
F=fotf(F); H=fotf(H); [n1,m1]=size(F); [n2,m2]=size(H);
if n1*m1==1 && n2*m2==1
if F.ioDelay==H.ioDelay
G=fotf(F.den*H.den+F.num*H.num,F.num*H.den);
G=simplify(G); G.ioDelay=F.ioDelay;
else, error('delay in incompatible'), end
elseif n1==m1 && n1==n2 && n2==m2
if maxdelay(F)==0 && maxdelay(H)==0
G=inv(fotf(eye(size(F*H)))+F*H)*F; G=simplify(G);
else, error('cannot handle blocks with delays'); end
else, error('not equal sized square matrices'), end
end

View File

@ -0,0 +1,23 @@
function G1=foss_a(G)
% foss_a - convert an FOTF object into an extended FOSS object
%
% G1=foss_a(G)
%
% G - the FOTF object
% G1 - the equivalent extended FOSS object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); n0=[];
for i=1:n, for j=1:m, g=G(i,j); n0=[n0, g.nn g.nd]; end, end
n0=unique(n0); n1=n0(end:-1:1);
for i=1:n, for j=1:m, g=G(i,j);
num=[]; den=[]; nn=g.nn; nd=g.nd; b=g.num; a=g.den;
for k=1:length(nn), t=find(nn(k)==n1); num(t)=b(k); end
for k=1:length(nd), t=find(nd(k)==n1); den(t)=a(k); end
Gt(i,j)=tf(num,den); T(i,j)=g.ioDelay;
end, end
Gf=ss(Gt); E=Gf.e; [a,b,c,d]=dssdata(Gf);
alpha=-diff(n1); G1=foss(a,b,c,d,alpha,T,E);
end

30
FOTF Toolbox/@fotf/fotf.m Normal file
View File

@ -0,0 +1,30 @@
% foss - class constructor for an FOTF class
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
%Note that, the low-level support of FOTF object is changed to
%ppoly object, pseudo-polynomials
classdef fotf
properties
num, den, ioDelay
end
methods
function G=fotf(a,na,b,nb,T)
if isa(a,'fotf'), G=a;
elseif strcmp(class(a),'sym'), G=sym2fotf(a);
elseif isa(a,'foss'), G=foss2fotf(a);
elseif nargin==1 && (isa(a,'tf') || isa(a,'ss') || isa(a,'double'))
a=tf(a); [n1,m1]=size(a); G=[]; D=a.ioDelay;
for i=1:n1, g=[]; for j=1:m1
[n,d]=tfdata(tf(a(i,j)),'v'); nn=length(n)-1:-1:0;
nd=length(d)-1:-1:0; g=[g fotf(d,nd,n,nn,D(i,j))];
end, G=[G; g]; end
elseif nargin==1 && a=='s', G=fotf(1,0,1,1,0);
elseif isa(a,'ppoly'), G.num=na; G.den=a; G.ioDelay=0;
else, ii=find(abs(a)<eps); a(ii)=[]; na(ii)=[];
ii=find(abs(b)<eps); b(ii)=[]; nb(ii)=[];
if isempty(b), b=0; nb=0; end
if nargin==4, T=0; end
num=ppoly(b,nb); den=ppoly(a,na); G.num=num; G.den=den; G.ioDelay=T;
end, end, end, end

View File

@ -0,0 +1,25 @@
function [G1,alpha]=fotf2cotf(G)
% fotf2cotf - convert an FOTF object into a commensurate-order one
%
% [G1,alpha]=fotf2cotf(G)
%
% G - an FOTF object
% G1 - an equivalent commensurate-order FOTF
% alpha - the base order
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); alpha=base_order(G);
if alpha==0
for i=1:n, for j=1:m
g=G(i,j); a=g.den.a; b=g.num.a; D(i,j)=b(1)/a(1); T(i,j)=g.ioDelay;
end, end, G1=tf(D);
else
for i=1:n, for j=1:m, g=G(i,j); a=[]; b=[];
n0=round(g.den.na/alpha); a(n0+1)=g.den.a; a=a(end:-1:1);
m0=round(g.num.na/alpha); b(m0+1)=g.num.a; b=b(end:-1:1);
g1=tf(b,a); G1(i,j)=g1; T(i,j)=g.ioDelay;
end, end, end
G1.ioDelay=T;
end

View File

@ -0,0 +1,16 @@
function G1=fotf2foss(G)
% fotf2foss - convert an FOTF object to FOSS one
%
% G1=fotf2foss(G)
%
% G - an FOTF object
% G1 - an equivalent FOSS object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G);
[G2,alpha]=fotf2cotf(G); G2=minreal(G2); G2=ss(G2);
for i=1:n, for j=1:m, g=G(i,j); T(i,j)=g.ioDelay; end, end
G1=foss(G2.a,G2.b,G2.c,G2.d,alpha,T,G2.E);
end

View File

@ -0,0 +1,19 @@
function [a,na,b,nb,L]=fotfdata(G)
% fotfdata - extract data from an FOTF object
%
% [a,na,b,nb,L]=fotfdata(G)
%
% G - an FOTF object
% [a,na,b,nb] - the coefficients and orders of denominator and numerator
% L - the delay constant
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G);
if n*m==1, b=G.num.a; a=G.den.a; nb=G.num.na; na=G.den.na; L=G.ioDelay;
else
for i=1:n, for j=1:m
[a0,na0,b0,nb0,L0]=fotfdata(G(i,j));
a{i,j}=a0; b{i,j}=b0; na{i,j}=na0; nb{i,j}=nb0; L(i,j)=L0;
end, end, end, end

View File

@ -0,0 +1,23 @@
function H=freqresp(s,G1)
% freqresp - low-level function to evaluate the frequency response of
% an FOTF object
%
% H=freqresp(s,G)
%
% s - the frequency vector or a vector for s
% G - the FOTF object
% H - frequency response, i.e., G(s) vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G1);
for i=1:n, for j=1:m
[a,na,b,nb,L]=fotfdata(G1(i,j));
for k=1:length(s)
P=b*(s(k).^nb.'); Q=a*(s(k).^na.'); H1(k)=P/Q;
end
if L>0, H1=H1.*exp(-L*s); end, H(i,j,:)=H1;
end, end
if n*m==1, H=H(:).'; end
end

View File

@ -0,0 +1,36 @@
function Ga=high_order(G0,filter,wb,wh,N,key)
% high_order - approximate an FOTF object with high-order TFs
%
% Ga=high_order(G0,filter,wb,wh,N)
%
% G0 - an FOTF object
% filter - can be 'ousta_fod', 'new_fod' and 'matsuda_fod'
% wb, wh, N - the interested frequency interval and order of the filter
% Ga - an equivalent TF object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
arguments
G0, filter='ousta_fod', wb(1,1){mustBeNumeric}=1e-3
wh(1,1) {mustBeNumeric, mustBeGreaterThan(wh,wb)}=1e3
N(1,1) {mustBeInteger, mustBePositive}=5, key=0
end
[n,m]=size(G0); F=filter;
for i=1:n, for j=1:m
if G0(i,j)==fotf(0), Ga(i,j)=tf(0);
else, G=simplify(G0(i,j)); [a,na,b,nb]=fotfdata(G);
G1=pseudo_poly(b,nb,F,wb,wh,N,key)/pseudo_poly(a,na,F,wb,wh,N,key);
Ga(i,j)=minreal(G1);
end, end, end, end
%
function p=pseudo_poly(a,na,filter,wb,wh,N,key)
p=0; s=tf('s');
for i=1:length(a), na0=na(i); n1=floor(na0); gam=na0-n1;
if key==1
g1=eval([filter '(gam,N,wb,wh)']); p=p+a(i)*g1;
else
if gam~=0, g1=eval([filter '(gam,N,wb,wh)']);
else, g1=1; end
p=p+a(i)*s^n1*g1;
end, end, end

View File

@ -0,0 +1,15 @@
function y=impulse(G,t)
% impulse - impulse response evaluation of an FOTF object
%
% impulse(G,t)
%
% G - an FOTF object
% t - the time vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
G1=G*fotf('s');
if nargout==0, step(G1,t,1); title('Impulse Response')
else, y=step(G1,t,1); end
end

28
FOTF Toolbox/@fotf/inv.m Normal file
View File

@ -0,0 +1,28 @@
function G1=inv(G)
% inv - inverse of a multivariable FOTF system
%
% G1=inv(G)
%
% G, G1 - an FOTF object and its inverse system
% not recommended for MIMO FOTFs, use fotf2sym and work the
% model G under symbolic framework
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G);
if n*m==1
if G.ioDelay>0, error('Delay terms are not allowed');
else, G1=fotf(G.num,G.den); end
elseif n~=m
error('Error: non-square matrix, not invertible')
else, G1=fotfinv(G); end
end
function G1=fotfinv(G)
[n,~]=size(G); A1=G; E0=fotf(eye(n)); A3=E0;
for i=1:n, ij=1:n; ij=ij(ij~=i);
E=fotf(eye(n)); a0=inv(A1(i,i));
for k=ij, E(k,i)=-A1(k,i)*a0; end
E0=E*E0; A1=E*A1; A3(i,i)=a0;
end, G1=A3*E0;
end

View File

@ -0,0 +1,7 @@
function key=isfotf(p)
%isfotf check input is an FOTF object or not
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 18 May, 2022
key=isa(p,'fotf');
end

View File

@ -0,0 +1,34 @@
function [K,alpha0,apol,p]=isstable(G,a0)
% isstable - check whether an FOTF object is stable or not
%
% [K,alpha,apol]=isstable(G)
%
% G - an FOTF object
% K- identifier to indicate the stability of G, returns 0, and 1
% alpha - the common order
% apol - all the pseudo poles of the system
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n0,m0]=size(G); K=1; if nargin==1, a0=0.001; end
for i=1:n0, for j=1:m0
g=G(i,j); a=g.den.na; a1=fix(a/a0);
if length(a1)==1 && a1==0
else
[g1,alpha]=fotf2cotf(g); c=g1.den{1};
alpha0(i,j)=alpha; p0=roots(c); kk=[];
for k=1:length(p0)
a=g.den.a; na=g.den.na; pa=p0(k)^(1/alpha);
if norm(a*[pa.^na'])<1e-6, kk=[kk,k]; end
end
p=p0(kk); subplot(n0,m0,(i-1)*m0+j),
plot(real(p),imag(p),'x',0,0), xm=xlim;
if alpha<1, xm(1)=0; else, xm(2)=0; end
apol=min(abs(angle(p))); K=K*(apol>alpha*pi/2);
a1=tan(alpha*pi/2)*xm; a2=tan(alpha*pi)*xm;
line(xm,a1), line(xm,-a1), line(xm,a2), line(xm,-a2)
xlabel('Real Axis'), ylabel('Imaginary Axis')
end, end, end
title('Pole Map')
end

View File

@ -0,0 +1,14 @@
function key=iszero(g)
% iszero - check whether a SISO FOTF object is zero or not
%
% key=iszero(G)
%
% G - a SISO FOTF object
% key - identifier, if G is zero, then key = 1.
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
key=0; [~,~,b,nb]=fotfdata(g);
if isempty(b) || (length(nb)==1 && abs(b(1))<eps), key=1; end
end

View File

@ -0,0 +1,26 @@
function str=latex(G)
% latex - convert an FOTF object into its LaTeX string
%
% str=latex(G)
%
% G - an FOTF object
% str - LaTeX string
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G);
if n*m==1
str=['\frac{' latex(G.num) '}{' latex(G.den) '}']; T= G.ioDelay;
if T~=0, ss=[]; if T~=1, ss=num2str(T); end
str=[str '\mathrm{e}^{-' ss 's}'];
end
else
str='\begin{bmatrix}';
for i=1:n
for j=1:m
str=[str, '\displaystyle ' latex(G(i,j)) '&'];
end, str=[str(1:end-1) '\cr'];
end, str=[str(1:end-3),'\end{bmatrix}'];
end
end

26
FOTF Toolbox/@fotf/lsim.m Normal file
View File

@ -0,0 +1,26 @@
function y=lsim(G,u,t)
% lsim - simulation of an FOTFS object driven by given inputs
%
% lsim(G,u,t)
%
% G - an FOTF object
% u, t- input samples and time vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); t0=t(1); t1=t(end); [nu,mu]=size(u);
if nu==m && mu==length(t), u=u.'; end
if nargout==0, lsim(tf(zeros(n,m)),'w',zeros(size(u)),t); end
for i=1:n, y1=0;
for j=1:m, g=G(i,j); uu=u(:,j);
y2=fode_sol9(g.den.a,g.den.na,g.num.a,g.num.na,uu,t,3);
ii=find(t>=g.ioDelay); lz=zeros(1,ii(1)-1);
y2=[lz, y2(1:end-length(lz))]; y(:,i)=y1+y2;
end, end
if nargout==0, khold=ishold; hold on
h=get(gcf,'child'); h0=h(end:-1:2);
for i=1:n, axes(h0(i));
plot(t,y(:,i),t,u,'--'), xlim([t0,t1])
end, if khold==0, hold off, end, end
end

View File

@ -0,0 +1,14 @@
function [Gm,Pm,Wcg,Wcp]=margin(G)
% margin - gain and phase margins of an FOTF object
%
% [Gm,Pm,Wcg,Wcp]=margin(G)
%
% G - an FOTF object
% Gm, Wcg - gain margin in dBs and the corresponding frequency
% Pm, Wcp - phase margin in degrees and the crossover frequency
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
H=bode(G,logspace(-4,4,1000)); [Gm,Pm,Wcg,Wcp]=margin(H);
end

View File

@ -0,0 +1,14 @@
function T=maxdelay(G)
% maxdelay - extract the maximum delay from an FOTF object
%
% T=maxdelay(G)
%
% G - an FOTF object
% T - the maximum delay of the system
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
T=0; [n,m]=size(G);
for i=1:n, for j=1:m, T=max(T,G(i,j).ioDelay); end, end
end

15
FOTF Toolbox/@fotf/mfrd.m Normal file
View File

@ -0,0 +1,15 @@
function H1=mfrd(G,w)
% mfrd - evaluation of frequency responses of an FOTF object
%
% H=mfrd(G,w)
%
% G - an FOTF object
% w - frequency vector
% H - frequency response of G in MFD format
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
H=bode(G,w); h=H.ResponseData; H1=[];
for i=1:length(w); H1=[H1; h(:,:,i)]; end
end

View File

@ -0,0 +1,10 @@
function G=minus(G1,G2)
% minus - minus operation of two FOTF objects
%
% G=G1-G2
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
G=G1+(-G2);
end

View File

@ -0,0 +1,10 @@
function G=mldivide(G1,G2)
% mldivide - left division of two FOTF objects
%
% G=G1\G2
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
G1=fotf(G1); G2=fotf(G2);
if maxdelay(G)==0 && maxdelay(G2)==0, G=inv(G1)*G2;
else, warning('block with positive delay'); end

View File

@ -0,0 +1,30 @@
function G1=mpower(G,n)
% mpower - power of an FOTF object
%
% G1=G^n
%
% G - an FOTF object
% n - power. If G is s or constant, n can be any real number,
% If G is an FOSS, n should be integers
% G1 - returns G^n, and an FOSS object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n1,m1]=size(G);
if n==fix(n)
if n1==m1
if n>=0, G1=fotf(eye(n1)); for i=1:n, G1=G1*G; end
else, G1=inv(G)^(-n); end
elseif n==1, G1=G;
else, error('G must be a square matrix');
end
elseif n1==1 && m1==1
if length(G.num)==1 && length(G.den)==1
[a,na,b,nb,L]=fotfdata(G);
G1=fotf(a^n,na*n,b^n,nb*n,L*n);
else, error('mpower: power must be an integer.');
end
else, error('mpower: power must be an integer.');
end
end

View File

@ -0,0 +1,12 @@
function G=mrdivide(G1,G2)
% mldivide - right division of two FOTF objects
%
% G=G1/G2
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
G1=fotf(G1); G2=fotf(G2);
if maxdelay(G1)==0 && maxdelay(G2)==0, G=G1*inv(G2);
else, error('block with positive delay'); end
end

View File

@ -0,0 +1,30 @@
function G=mtimes(G1,G2)
% mpower - product of two FOTF objects, series connection
%
% G=G1*G2
%
% G1, G2 - FOTF objects
% G - returns the product of the two FOTF objects
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
G1=fotf(G1); G2=fotf(G2);
if numel(G1)==1 && numel(G2)==1, G=sisofotftimes(G1,G2);
else, [n1,m1]=size(G1); [n2,m2]=size(G2);
if m1==n2, G=fotf(zeros(n1,m2));
for i=1:n1, for j=1:m2
for k=1:m1, G(i,j)=G(i,j)+sisofotftimes(G1(i,k),G2(k,j));
end, end, end
elseif n1*m1==1, G=fotf(zeros(n2,m2)); % if G1 is scalar
for i=1:n2, for j=1:m2, G(i,j)=G1*G2(i,j); end, end
elseif n2*m2==1, G=fotf(zeros(n1,m1)); % if G2 is scalar
for i=1:n1, for j=1:m1, G(i,j)=G2*G1(i,j); end, end
else
error('The two matrices are incompatible for multiplication')
end, end, end
% product of two SISO FOTF objects
function G=sisofotftimes(G1,G2)
G=fotf(G1.den*G2.den,G1.num*G2.num); G=simplify(G);
G.ioDelay=G1.ioDelay+G2.ioDelay;
end

View File

@ -0,0 +1,14 @@
function nichols(G,w)
% nichols - draw the Nichols chart of an FOTF object
%
% nichols(G,w)
%
% G - an FOTF object
% w - the frequency vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
arguments, G, w=logspace(-4,4); end
H=bode(G,w); nichols(H);
end

26
FOTF Toolbox/@fotf/norm.m Normal file
View File

@ -0,0 +1,26 @@
function n=norm(G,eps0)
% norm -= norms of an FOTF object
%
% norm(G), norm(G,inf)
%
% G - an FOTF object
% The 2-norm and infinity-norm of G can be evaluated, respectively
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); if nargin==1, eps0=1e-6; end
for i=1:n, for j=1:m, A(i,j)=snorm(G(i,j),eps0); end, end
n=norm(A);
end
% norm of a SISO FOTF object
function n=snorm(G,eps0)
j=sqrt(-1);
if nargin==2 && ~isfinite(eps0) % H infinity norm, find the maximum value
f=@(w)-abs(freqresp(j*w,G));
w=fminsearch(f,0); n=abs(freqresp(j*w,G));
else % H2 norm, numerical integration
f=@(s)freqresp(s,G).*freqresp(-s,G);
n=integral(f,-inf,inf)/(2*pi*j);
end
end

View File

@ -0,0 +1,12 @@
function n=numel(G)
% numel - the number of FOTF objects in G
%
% n=numel(G)
%
% G - FOTF object
% n - returns the number of FOTF objects in G
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 18 May, 2022
[n,m]=size(G); n=n*m;
end

View File

@ -0,0 +1,14 @@
function nyquist(G,w)
% nyquist - draw the Nyquist plot of an FOTF object
%
% nyquist(G,w)
%
% G - an FOTF object
% w - the frequency vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
arguments, G, w=logspace(-4,4); end
H=bode(G,w); nyquist(H);
end

30
FOTF Toolbox/@fotf/plus.m Normal file
View File

@ -0,0 +1,30 @@
function G=plus(G1,G2)
% plus - evaluate the sum of two FOTF objects, parallel connection
%
% G=G1+G2
%
% G1, G2 - the two FOTF objects in parallel connection
% G - the sum of G1 and G2
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
G1=fotf(G1); G2=fotf(G2);
[n1,m1]=size(G1); [n2,m2]=size(G2);
if n1==n2 && m1==m2, G=G1;
for i=1:n1, for j=1:m1
G(i,j)=sisofotfplus(G1(i,j),G2(i,j));
end, end
elseif n1*m1==1, G1=G1*fotf(ones(n2,m2)); G=G1+G2;
elseif n2*m2==1, G2=G2*fotf(ones(n1,m1)); G=G1+G2;
else
error('Error: the sizes of the two FOTF matrices mismatch');
end
% sum of two SISO FOTF objects
function G=sisofotfplus(G1,G2)
if G1.ioDelay==G2.ioDelay
G=fotf(G1.den*G2.den,G1.num*G2.den+G2.num*G1.den);
G=simplify(G); G.ioDelay=G1.ioDelay;
else, error('Error: cannot handle different delays');
end, end
end

View File

@ -0,0 +1,14 @@
function [alpha,r,p,K]=residue(G)
% residue - partial fraction expansion of a SISO FOTF object
%
% [alpha,r,p,K]=residue(G)
%
% G - a SISO commensurate-order FOTF object
% alpha - base order
% r, p, K - the definitions are the same as the conventional residue function
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[H,alpha]=fotf2cotf(G); [r,p,K]=residue(H.num{1},H.den{1});
end

View File

@ -0,0 +1,19 @@
function rlocus(G)
% rlocus - draw the root locus of a SISO FOTF object
%
% rlocus(G)
%
% G - a SISO FOTF object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
if numel(G)==1
[G1,alpha]=fotf2cotf(G); rlocus(G1), xm=xlim;
if alpha<1, xm(1)=0; else, xm(2)=0; end
line(xm,tan(alpha*pi/2)*xm), line(xm,-tan(alpha*pi/2)*xm)
line(xm,tan(alpha*pi)*xm), line(xm,-tan(alpha*pi)*xm)
else
error('Root locus only applies to SISO systems');
end
end

View File

@ -0,0 +1,20 @@
function sigma(G,w)
% sigma - singular value plots of a MIMO FOTF object
%
% sigma(G,w)
%
% G - a MIMO FOTF object
% w - a frequency vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
if nargin==1, w=logspace(-4,4); end
H=bode(G,w); subplot(111); h1=[]; H1=H;
h=H.ResponseData; [n,m,k]=size(h);
for i=1:k, h1=[h1, svd(h(:,:,i))]; end
for i=1:min([n,m])
h2(1,1,:)=h1(i,:).'; H1.ResponseData=h2; bodemag(H1), hold on
end
hold off
end

View File

@ -0,0 +1,20 @@
function G=simplify(G,eps1)
% simplify - simplification of an FOTF object
%
% G=simplify(G1,eps1)
%
% G1 - a FOTF object
% eps1 - error tolerance
% G - simplified FOTF object
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); if nargin==1, eps1=eps; end
for i=1:n, for j=1:m
g=G(i,j); num=g.num.a; den=g.den.a; nn=g.num.na; nd=g.den.na;
i1=abs(num)>eps1; i2=abs(den)>eps1; num=num(i1); den=den(i2);
nn=nn(i1); nd=nd(i2); n0=min(nn); d0=min(nd);
na=min(n0,d0); if isempty(na), na=0; end
G(i,j)=fotf(den,nd-na,num,nn-na,g.ioDelay);
end, end, end

33
FOTF Toolbox/@fotf/step.m Normal file
View File

@ -0,0 +1,33 @@
function Y=step(G,t,key)
% step - simulation of an FOTF object driven by step inputs
%
% step(G,t)
%
% G - an FOTF object
% t- the time vector
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G); M=tf(zeros(n,m));
if nargin==1, t=(0:0.2:10)'; elseif length(t)==1, t=0:t/100:t; end
if nargout==0, t0=t(1); t1=t(end);
if nargin<=2, step(M,'w'); else, impulse(M,'w');
end, end
for i=1:n, for j=1:m
g=G(i,j); y1=g_step(g,t); y(i,j,:)=y1';
end, end
if nargout==0, khold=ishold; hold on
h=get(gcf,'child'); h0=h(end:-1:2);
for i=1:n, for j=1:m, axes(h0((i-1)*n+j));
yy=y(i,j,:); plot(t,yy(:)), xlim([t0,t1])
end, end
if khold==0, hold off, end
elseif n*m==1, Y=y1; else, Y=y; end
end
%subfunction to evaluate step response of SISO model
function y=g_step(g,t,key)
u=ones(size(t));
[a,na,b,nb,T]=fotfdata(g); y1=fode_sol(a,na,b,nb,u,t);
ii=find(t>=T); lz=zeros(1,ii(1)-1); y=[lz, y1(1:end-length(lz))];
end

View File

@ -0,0 +1,14 @@
function G=uminus(G)
% uminus - unary minus of an FOTFS object
%
% G1=-G
%
% G - an FOT object
% G1- the unary minus of G, i.e., -G
% Copyright (c) Dingyu Xue, Northeastern University, China
% Last modified 28 March, 2017
% Last modified 18 May, 2022
[n,m]=size(G);
for i=1:n, for j=1:m, G(i,j).num=-G(i,j).num; end, end
end