image thumbnail
from Nonlinearity Detection using Zeroed Early-Time FFTs by Matt Allen
Nonlinearity detection scheme for free response based on FFTs

ZNLD_SSICF.m
global ZNLD
% ZNLD.Hmat ZNLD.ws ZNLD.ts_zc ZNLD.fline_ind ZNLD.lhands

% Messages to user
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Stochastic Subspace Curve Fitting Initialized')
% disp('Click on finished under AMI-Menu on Figure 1000 when finished');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

% Get the index of the currently selected line on figure 102
fit_lineh = gco;
ZNLD.fline_ind = get(fit_lineh,'UserData');
    if isempty(fit_lineh) | isempty(get(get(fit_lineh,'Parent'),'Parent'))
        error('No Line on Figure 102 was selected. Select a line and run the script again.');
    end
    if get(get(fit_lineh,'Parent'),'Parent') ~= 102;
        error('No Line on Figure 102 was selected. Select a line and run the script again.');
    end

% Increase this line's width
set(gco,'LineWidth',2);

% Create ZNLD.lhands if needed
if ~isfield(ZNLD,'lhands')
    ZNLD.lhands.hands = []; ZNLD.lhands.legend = [];
end

	% Add this to the list of lines to keep - this should be the first in
    % the list.
    ZNLD.lhands.hands = fit_lineh; ZNLD.lhands.legend = [];
    ZNLD.lhands.legend{1} = num2str(ZNLD.ts_zc(ZNLD.fline_ind)*1e3,3);

% Low-pass filter and decimate data to try to help System ID
fs=1/(ZNLD.t(2)-ZNLD.t(1));   %Sampling freq
nu_lp = max(ZNLD.ws)*1.5/(2*pi*fs/2);
[b,a]=butter(8,nu_lp,'low'); 
a_filt=filtfilt(b,a,ZNLD.a);
n_decim = round(1/(2*nu_lp)); % gives new bandwidth = 2*lp filter bandwidth
a_filt = a_filt(1:n_decim:end);
t_filt = ZNLD.t(1:n_decim:end); % this may have different zero crossings

% Ask user how many block rows to use
% Typically: NBHank = 2 * (max order)/(#outputs)
nbh_str = inputdlg('Enter Number of Block Rows in Hankel Matrix (typically 2*max model order)',...
    'N Block Rows',1,{num2str(50)});
NBHank = str2num(nbh_str{1});
    
% Run SSI
% t_band = find(ZNLD.t >= ZNLD.ts_zc(ZNLD.fline_ind));
% [A,B,C,D,K,R] = subid(ZNLD.a(t_band),[],NBHank);
t_band = find(t_filt >= ZNLD.ts_zc(ZNLD.fline_ind));
[A,B,C,D,K,R] = subid(a_filt(t_band),[],NBHank);

% Convert eigenvalues to continuous time and sort
lamd = eig(A);
% lamc = ln(lamd)/(ZNLD.t(2)-ZNLD.t(1));
lamc = ln(lamd)/(t_filt(2)-t_filt(1));
[junk,sind] = sort(abs(lamc)+ 1e-4*abs(lamc).*(imag(lamc > 0)));
lamc = lamc(sind);
% view_vecs(lamlin/2/pi, lamc/2/pi);

% Eliminate some spurrious eig from lamc
lamc_orig = lamc;
lamc = sortcceig(lamc);
lamc = lamc(1:end/2); % keep eig with positive imag parts only.
% Eliminate eigenvalues that are way out of band - can't find their
% Residues accurately.
lamc = lamc(find(abs(lamc) < 1.2*max(ZNLD.ws)));
lamc = lamc(find(abs(lamc) > 0.8*min(ZNLD.ws)));

% Find data around peaks for curve fit.
[band_inds] = FindPeakBand(ZNLD.ws,lamc,1);

% The following is probably not as elegant as it could be, but for now it
% is easier than identifying B and D from the time series.
[A_fit,Pex] = mvecs(ZNLD.Hmat(band_inds,ZNLD.fline_ind),ZNLD.ws(band_inds),...
    lamc,0,[],'D');


%% Filter Eigenvalues by examining backwards propagation
% Eliminate heavily damped modes until backwards prediction over a short
% time interval is sucessful.
    % Ask user how far back to compare and what level of error is
    % acceptable
    val_str = inputdlg({'Enter time (ms) to backup','Enter maximum allowable error (1.0 = 100%)'},...
        'Elim. Heavily Damped',1,{num2str(10),num2str(0.1)});
    t_back = str2num(val_str{1})/1e3;
    tb_used_ind = find(ZNLD.ts_zc > ZNLD.ts_zc(ZNLD.fline_ind) - t_back,1);
    tb_used = ZNLD.ts_zc(tb_used_ind);
    err_limit = str2num(val_str{2});
    
    % Ask user how far out in frequency to go
    fmax_str = inputdlg('Enter Maximum Frequency for Metric (Hz)',...
        'Max. Freq.',1,{num2str(max(ZNLD.ws/2/pi))});
    band_ind = find(ZNLD.ws <= str2num(fmax_str{1})*2*pi);
    
    A_fit_back = A_fit.*exp(-lamc*(ZNLD.ts_zc(ZNLD.fline_ind) - ZNLD.ts_zc(tb_used_ind)));
    IntHdata = trapz(ZNLD.ws(band_ind),abs(ZNLD.Hmat(band_ind,tb_used_ind)));
    
    % Eliminate most heavily damped modes one at a time until the error is
    % acceptable.
    curr_err = 1;
    while curr_err > err_limit & length(lamc) > 0;
        % Compute fit FRF and integral error metric
        H_fit_back = ss_model(lamc,A_fit_back,ZNLD.ws,'D','s');
        HDiff = abs(ZNLD.Hmat(band_ind,tb_used_ind))-abs(H_fit_back(band_ind,1));
        IntHD = trapz(ZNLD.ws(band_ind), abs(HDiff));
        curr_err = IntHD./IntHdata; % IntHDn
        if curr_err > err_limit
            [junk,lamc_elim_ind] = min(real(lamc)); % real parts are negative
            lamc_keep_ind = setdiff([1:length(lamc)],lamc_elim_ind);
            lamc = lamc(lamc_keep_ind);
            A_fit = A_fit(lamc_keep_ind,:);
            A_fit_back = A_fit_back(lamc_keep_ind,:);
        end
    end
    
    if isempty(lamc)
        disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
        warning('Subspace Identification Unsucessfull - no modes remain after filtering!');
        disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
    end
    
%% Create final model and plot
H_fit = ss_model(lamc,A_fit,ZNLD.ws,'D','s');

% Add line to figure
figure(102);
hl = line(ZNLD.ws/2/pi,abs(H_fit),'Color','k','LineWidth',3,'LineStyle',':',...
    'UserData',['Fit ',num2str(ZNLD.fline_ind)])
    % Note:  A robust version of this program would have a plotting
    % function that would add lines to the plot so that changes could be
    % made in one place only.

    % Add this to the list of lines to keep
    ZNLD.lhands.hands = [ZNLD.lhands.hands; hl];
    ZNLD.lhands.legend{length(ZNLD.lhands.legend)+1} = ['Fit to ',num2str(ZNLD.ts_zc(ZNLD.fline_ind)*1e3,3)];

% Add Data to ZNLD structure
ZNLD.lam_fit = lamc;
ZNLD.A_fit = A_fit;

    
    

Contact us at files@mathworks.com