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

ZNLDetect(a,t,varargin);
function [varargout] = ZNLDetect(a,t,varargin);
% [Fmat, f, ts_zc] = ZNLDetect(a, t);
% or
% ZNLDetect(a, t);
%
% Nonlinearity detection scheme based on zeroing the initial time response
% over various intervals and computing the FFT of each, as described in: M.
% S. Allen and R. L. Mayes, "Estimating the Degree of Nonlinearity in 
% Transient Responses with Zeroed Early-Time Fast Fourier Transforms," in 
% International Modal Analysis Conference Orlando, Florida, 2009.
%
% It works by deleting the beginning of the time history up to some zero 
% crossing and then taking the FFT.  In this way one can see when certain 
% features in the frequency domain drop out in time.  This is quite
% different than most time-frequency methods such as wavelets because the
% initial response is zeroed rather than nullified using a window.  ZEFFTs 
% are especially  effective if nonlinear events occur very early and decay
% in only a few cycles, such as macroslip of a joint due to impulsive loading.  
% 
% User inputs
%   a   time history vector (Nt x 1) or (1 x Nt)
%   t   time vector (Nt x 1) or (1 x Nt)
%
% Optional inputs
% [Fmat, f, ts_zc] = ZNLDetect(a, t, [tfirst, tlast], fmax, nz_skip);
%
%   [tfirst, tlast] - the time interval that will be searched for zero
%       crossings.
%   fmax - the maximum frequency that will be retained in the FFTs.
%   nz_skip - number of zero crossings to skip when storing and plotting
%       ZEFFTs.  For example, if nz_skip = 5, every 5th zero crossing will
%       be retained.
% 
% Outputs
%   Fmat - each column is one of the ffts
%   f - frequency vector for Fmat
%   ts_zc - times where zeroing ends for each column of Fmat
%
%   A menu also appears on Figure 102 entitled Fit-Extrap, containing the
%   following options.  All of these work by having the user first select a
%   line of interest on the plot using the plot's arrow tool, then choosing
%   one of the following features.
%       AMI Fit - fit a modal model to the active line using the AMI
%       algorithm.  Select "Finished" on the AMI menu when done to return
%       to the figure.
%       SSI Fit - fit a modal model to the active line using the SSI 
%           algorithm by Van Overschee & De Moor.  Some filtering has been 
%           added to attempt to eliminate spurious modes, but this feature
%           does not always work perfectly.
%       BEND: Extrapolate - extrapolate the curve fit, either backwards or 
%           forwards in time to the time of the selected line.  For
%           example, if you fit the FFT starting at 30 ms, you could select
%           a line at 0 ms, or at 50 ms and use this to see what the zeroed 
%           FFT should have been at those times if the system behaved linearly.
%       Clear Lines - Use this feature to clear all of the lines from
%           the plot except the curve fit and the measurement and any pairs
%           of measured and extrapolated lines.
%       IBEND: Integral Metric - Compute the integral metric IBEND
%           described in the paper.
%       Show Line Info - prints the number and start time of the selected
%       	line to the command window.
%
% A plot of ZEFFTs computed using equally distributed points is also
% generated, but it is not currently used for curve-fitting or
% extrapolation.
%
% Original algorithm by Randy L. Mayes, rlmayes@sandia.gov
% Modified by Matt S. Allen (July 2005-May 2009), msallen@engr.wisc.edu
%

tfirst = []; fmax = [];
if nargin > 3
    fmax = varargin{2};
    if nargin > 2;
        tspan = varargin{1};
        tfirst = tspan(1); tlast = tspan(end);
    end
end
if nargin > 4
    nz_skip = varargin{3};
else
    nz_skip = 1;
end
if nargin > 5
    %
end

if length(a) ~= length(t);
    error('Sizes of a and t are not compatible');
end

% Initial Plot of data & Start and Stop Times
time_sf = 1000;
figure(101); set(gcf,'Name','Time Response');
plot(t*time_sf,a,'.-'); grid on;
xlabel('\bfTime (ms)');
ylabel('\bfResponse');

if isempty(tfirst);
    tfirst = input('Input time to start looking for zero crossings (tfirst) ')
    tlast = input('Input time to stop looking for zero crossings (tlast) ')
end
%title('PICK RANGE FOR ANALYSIS!');
%[tpicks,apicks] = ginput(2);
%    Calculate the frequency vector for fft displays

% Frequencies to Compute
    delf=1/((t(2)-t(1))*length(t));
    disp(['Nyquist Frequency = ',num2str(delf*length(t)/2)]);
    if isempty(fmax);
        fmax=input('Input highest frequency to plot ');
    end
    freq=0:delf:fmax;freq=freq';
    lfreq=length(freq);

% Beginning of Algorithm
%   First find the times for zero crossings
ts_zc(1)=t(1);    %   The first fft will be for the entire time history
indts_zc(1)=1;
istart = max(find(t<=tfirst));    % Find the first index to start looking for 0 crossings
ilast = max(find(t<=tlast));      % Find the last index to look for 0 crossings
astart=a(istart);
%   This finds out whether the data starts out positive or negative
    flag = sign(astart);
        % assign 0 a positive sign.
        if flag == 0; flag = 1; end
%   This loop finds all the zero crossings
    ncross = 2;    %  This initializeds the next value index for ts_zc
    for k = (istart + 1):ilast
        metric = flag*a(k);
        if metric < 0  % this is the zero crossing catcher
            indts_zc(ncross) = k;  % this is the index of t for the zero crossing
            ts_zc(ncross)=t(k);
            flag=-flag;
            ncross=ncross+1;
        else
        end
    end
    % Discard some of the zero crossings if desired
    length(ts_zc)
        ts_zc = ts_zc(1:nz_skip:end);
        indts_zc = indts_zc(1:nz_skip:end);
        if isempty(ts_zc);
            error('No Crossings:  Number of zero crossings to skip too large, or time interval to search too small');
        end
    lts_zc=length(ts_zc);   %  This is the number of ffts to do
%   Alternate Approach - Find FFT for arbitrary start times, evenly spaced
%   between the first and last times found previously
    delta_edistind = round((indts_zc(end)-indts_zc(1))/length(indts_zc));
    edistind = indts_zc(2):delta_edistind:indts_zc(end);
    ts_edist = t(edistind); ts_edist = ts_edist(:);
%   Now FFT the entire time signal for column 1 of Fmat
    Fmat=zeros(length(a),lts_zc);
    amat=Fmat;
    amat(:,1)=a;
    for k=2:lts_zc
        amat(:,k)=a;
        amat(1:indts_zc(k),k)=0;
    end
    Fmat=fft(amat);
%   Repeat for evenly distributed indices
    Fmat_ed=zeros(length(a),length(edistind));
    amat_ed=Fmat_ed;
    amat_ed(:,1)=a;
    for k=2:length(edistind)
        amat_ed(:,k)=a;
        amat_ed(1:edistind(k),k)=0;
    end
    Fmat_ed=fft(amat_ed);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Plotting and Visualization
% Add points to previous figure showing zero crossings & truncation points
line(t(indts_zc)*time_sf,a(indts_zc),'LineStyle','None','Marker','o','Color','r');
% line(t(edistind)*time_sf,a(edistind),'LineStyle','None','Marker','^','Color','g');
legend('Response','Zero Pts');%,'Eq. Dist. Pts');

% Check that the response is close to zero at these crossing points
if any(abs(a(indts_zc)) > 0.2*max(abs(a)));
    warning('RESPONSE HAS LARGE AMPLITUDE NEAR SOME ZERO CROSSINGS');
end

figure(102); clf(gcf);  set(gcf,'Name','TFFT Zeros');
set(gcf,'Units', 'normalized', 'Position', [0.166,0.15,0.691,0.762])
cols = jet(size(Fmat,2));
for k = 1:size(Fmat,2);
    hl(k) = line(freq,abs(Fmat(1:lfreq,k)),'Color',cols(k,:),'UserData',k);
end
set(gca,'YScale','Log'); grid on;
%semilogy(freq,abs(Fmat(1:lfreq,:)))
xlabel('\bfFrequency (Hz)'); ylabel('\bfMagnitude');
title('\bfNLDetect: FFT of Time Response - Truncated at zero points');
% Skip entries if legend is very long
    if length(ts_zc) < 7
        ts_zcs=num2str(ts_zc'*1e3,4);
        legend(ts_zcs)
    else
        nskip = round(length(ts_zc)/7);
        ts_zcs = num2str(ts_zc(1:nskip:end).'*1e3,4);
        legend(hl(1:nskip:end),ts_zcs);
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Create global variables so curve fitting and backwards extrapolating
    % functions can use this data.
    global ZNLD
    ZNLD.ws = freq*2*pi;
    ZNLD.ts_zc = ts_zc;
    ZNLD.Hmat = Fmat(1:lfreq,:);
    for k = 1:size(ZNLD.Hmat,2); % Remove phase delay so this can be curve fit.
        ZNLD.Hmat(:,k) = ZNLD.Hmat(:,k).*exp(i*ZNLD.ws*ZNLD.ts_zc(k));
    end
    % Add time records for SSI
    ZNLD.a = a;
    ZNLD.t = t;

    % Create Menu to fit and extrapolate
        hf = gcf;
        huimenu(1) = uimenu(hf,'Label','Fit-Extrap','enable','on');
        huimenu(2) = uimenu(huimenu(1),'Label','AMI Fit','Callback', ...
            ['ZNLD_CF;']);
        huimenu(3) = uimenu(huimenu(1),'Label','SSI Fit','Callback', ...
            ['ZNLD_SSICF;']);
        huimenu(4) = uimenu(huimenu(1),'Label','BEND: Extrapolate','Callback', ...
            ['ZNLD_BE;']); 
        huimenu(5) = uimenu(huimenu(1),'Label','Clear Lines','Callback', ...
            ['ZNLD_ClearLines;']);
        huimenu(6) = uimenu(huimenu(1),'Label','IBEND: Integral Metric','Callback', ...
            ['ZNLD_Metric;']);
        huimenu(7) = uimenu(huimenu(1),'Label','Show Line Info','separator','on',...
            'Callback', ['ZNLD_LineInfo;']);
        
        % Misc. Settings:
        format short g
        format compact
    
figure(103); clf(gcf); set(gcf,'Name','TFFT Eq. Dist');
% set(gcf,'Position', [75    26   560   420]);
cols = jet(size(Fmat_ed,2));
for k = 1:size(Fmat_ed,2);
    hl_ed(k) = line(freq,abs(Fmat_ed(1:lfreq,k)),'Color',cols(k,:));
end
set(gca,'YScale','Log'); grid on;
%semilogy(freq,abs(Fmat_ed(1:lfreq,:)))
%eval(['title(jt4_4.accel(',int2str(aval),').title)']);
xlabel('\bfFrequency (Hz)'); ylabel('\bfMagnitude');
title('\bfNLDetect:  FFT of Time Response - Truncated at evenly distributed points)');
% Skip entries if legend is very long
    if length(ts_edist) < 7
        ts_edists=num2str(ts_edist*1e3,4);
        legend(ts_edists)
    else
        nskip = round(length(ts_edist)/7);
        ts_edists = num2str(ts_edist(1:nskip:end)*1e3,4);
        legend(hl_ed(1:nskip:end),ts_edists);
    end

figure(102); % make this one current.
    
if nargout > 0
    % Correct phases on "Fmat" so that it can be curve fit if desired
    Fmat = Fmat(1:lfreq,:);
    for k = 1:size(Fmat,2);
        Fmat(:,k) = Fmat(:,k).*exp(i*freq*2*pi*ts_zc(k));
    end
    varargout = {Fmat, freq, ts_zc};
end

Contact us at files@mathworks.com