function [out,Wc,Wo] = hankelsv(SYS)
%HANKELSV Compute Hankel singular values and grammians.
%
% [OUT,Wc,Wo] = HANKELSV(SYS) computes controllability and observability
% grammians Wc, Wo, and the Hankel singular values OUT of an LTI
% model SYS (created with either TF, ZPK, SS, or FRD). The model
% SYS can be either continuous-time or discrete-time. However, only
% in continuous-time case that SYS is allowed to be unstable.
% The computed Hankel singular values are sorted in ascending order.
%
% For unstable continuous-time system, state-space stable/anti-stable
% decomposition is used instead, and OUT=[OUT_stable;OUT_anti-stable].
% In addition, Wc={Wc_stable,Wc_anti-stable} and Wo is either.
% The former version of HKSV employs an obsolete fashion in using the
% Matlab function GRAM, i.e., instead of using GRAM(SYS,'c') and
% GRAM(SYS,'o'), it uses GRAM(A,B) and GRAM(A',C'), respectively.
% This restricts the computation of gramians to continuous-time case,
% only, since GRAM(A,B) and GRAM(A',C') solve LYAP, not DLYAP.
%
% This improved version also correct some other bugs in the former
% version, for example, HKSV does not return gramians when SYS is
% unstable, but not anti-stable.
% Developer: Wathanyoo Khaisongkram
% Date Developed: Oct 20, 2004
% Email: sunboom15@yahoo.com
% Improved version of HKSV by R. Y. Chiang & M. G. Safonov March, 1986
% -----------------------------------------------------------------------
SYS=ss(SYS); % convert to state-space model
% Discrete-time LTI model
if get(SYS,'Ts')~=0 % discrete-time LTI model
if max(abs(pole(SYS))) > 1 % unstable discrete system
error('For discrete LTI model, system must be stable')
end
Wc = gram(SYS,'c'); % controllability matrix
Wo = gram(SYS,'o'); % observability matrix
out = sqrt(eig(Wc*Wo)); % Hankel singular values
[no_use,index] = sort(out); % index for sorting
out = out(index); % sorted singular values
return % end function
end
% Continuous-time LTI model
[A,B,C,D]=ssdata(SYS); % extract the system matrices
mode = eig(A); % the eigen values of 'a'
nrow = size(A,1); % the number of rows in 'a'
nsta = length(find(real(mode) < 0)); % the number of unstable modes
if isequal(nsta,nrow), % completely stable
Wc = gram(SYS,'c'); % controllability matrix
Wo = gram(SYS,'o'); % observability matrix
out = sqrt(eig(Wc*Wo)); % Hankel singular values
[no_use,index] = sort(out); % index for sorting
out = out(index); % sorted singular values
elseif isequal(nsta,0), % completely unstable
SYS=ss(-A,-B,C,D,SYS); % change the dynamic and the input matrices to '-a' and '-b',
% respectively with all LTI properties inherited from
% original SYS, e.g., discrete or continuous domain.
Wc = gram(SYS,'c'); % controllability matrix
Wo = gram(SYS,'o'); % observability matrix
out = sqrt(eig(Wc*Wo)); % Hankel singular values
[no_use,index] = sort(out); % index for sorting
out = out(index); % sorted singular values
else, % 0 < nsta < nrow
[SYSs,SYSu] = stabproj(SYS); % decompose stable/anti-stable parts
% Stable part
Wcs = gram(SYSs,'c'); % controllability matrix
Wos = gram(SYSs,'o'); % observability matrix
outl = sqrt(eig(Wcs*Wos)); % Hankel singular values1
[no_use,index] = sort(outl); % index for sorting
outl = outl(index); % sorted singular values
% Anti-stable part
[Au,Bu,Cu,Du]=ssdata(SYSu); % obtain the system matrices of SYSu
SYSu=ss(-Au,-Bu,Cu,Du,SYSu); % change the dynamic and the input matrices to '-ar' and '-br'.
Wcu = gram(SYSu,'c'); % controllability matrix
Wou = gram(SYSu,'o'); % observability matrix
outr = sqrt(eig(Wcu*Wou)); % Hankel singular values
[no_use,index] = sort(outr); % index for sorting
outr = outr(index); % sorted singular values
out = [outl;outr]; % gather two cases
Wc={Wcs,Wcu}; % controllability gramian
Wo={Wos,Wou}; % observability gramian
end
% end of HKSV_MOD