Code covered by the BSD License  

Highlights from
res_meas

image thumbnail

res_meas

by

 

16 May 2007 (Updated )

GUI for resonator measurement and data download from Signal Analyzers and Oscilloscopes

res_rd=ringdownR(res,plots,verbose)
function res_rd=ringdownR(res,plots,verbose)
% RES_RD=RINGDOWNR(RES,PLOTS,VERBOSE)
% RINGDOWNR analyzes a ringdown measurement (binary RES format) and returns
%  RES with new fields.
%
% RES is a resonator data structure (trace1.x, etc), such as produced by
%  RINGREAD.
% PLOTS is a value (0,1,2) indicating the number and detail of plots
%  desired.
%
% Example:
% >> r = readring('rce944108-19_r10_c21_t01__G04');
% >> r_analyzed = ringdownR(r,2)
%
% MH May2010
% v1.42 bugfixes related to centering data
% v1.4  minsearchfit results included in output
% v1.3  included minsearch fit
% v1.2  sorted out the isolation of relevant data
% v1.1  integration with res_meas

dmax = 0; % envelope noise filter threshold. Use 0 for no filter
if nargin < 2, plots = 0; end
if nargin < 3, verbose = 2; end
if plots >= 1, datafig=figure; end

%% format data
% which trace?
    if isfield(res,'trace1')
        t=res.trace1.x; V=res.trace1.y;
        if verbose >= 2, fprintf(1,'ringdown: Analyzing trace1 as ringdown.\n'); end
    elseif isfield(res,'trace2')
        t=res.trace2.x; V=res.trace2.y;
        if verbose >= 2, fprintf(1,'ringdown: Analyzing trace2 as ringdown.\n'); end
    elseif isfield(res,'trace3')
        t=res.trace3.x; V=res.trace3.y;
        if verbose >= 2, fprintf(1,'ringdown: Analyzing trace3 as ringdown.\n'); end
    elseif isfield(res,'trace4')
        t=res.trace4.x; V=res.trace4.y;
        if verbose >= 2, fprintf(1,'ringdown: Analyzing trace4 as ringdown.\n'); end
    else
        fprintf(1,'ringdown: Error: no data trace from oscilloscope.\n');
    end
    
% determine sample interval
time_length=t(end)-t(1);
sample_int=time_length/(length(t)-1);
if sample_int ~= mean(diff(t))
    if verbose >= 1, fprintf(1,'ringdown: Warning: sampling interval calculation does not match: %f %f\n',sample_int,mean(diff(t))); end
else
    if verbose >= 2, fprintf(1,'ringdown: Data sampling interval: %g sec\n',sample_int); end
end

% check for negative time, discard pre-trigger data
%  (this works if the oscilloscope trigger is set correctly)
if any(sign(t)==-1)
    V=V(sign(t)>=0);
    t=t(sign(t)>=0);
    if verbose >= 2, fprintf(1,'ringdown: Pre-trigger data detected and discarded.\n'); end
end

% remove any offset
Vmean=mean(V); V=V-Vmean; % center data at 0
if verbose >= 1, fprintf(1,'ringdown: Offset of %+g V subtracted.\n',Vmean); end

% find first zero crossing and discard initial rings
%  code based on "crossing.m" by Steffen Brueckner
ind0 = find(V==0);
ind1 = find(sign(V(1:end-1).*sign(V(2:end)))<0);
indZ = sort([ind0 ind1]);
% skip the first few oscillations
if length(indZ)>25
    t=t(indZ(5):end);
    V=V(indZ(5):end);
    %indZ=indZ(5:end);
else
    error('ringdown: Error: Number of zero crossings is suspiciously low (<25)\n');
end

% plot data that will be analyzed
if plots >= 2
    figure(datafig);
    hold on;
    plot(t,V,'-c'); % plot all data
    ha = axis;
%    plot(tfit(indZ),0,'ok'); % show zero crossings
    %zline=plot([t(indZ(5)) t(indZ(5))],[ha(3) ha(4)], '-b'); % show data start
    plot([ha(1) ha(2)],[0 0], '-k'); % plot zero line
    title('Ringdown Data'); xlabel('time [sec]'); ylabel('Data [V]');
end


% Find envelope and isolate active portion
[ue le]=envelope([t V],dmax); % find envelope of signal
t_ue=ue(:,1); t_le=le(:,1);
V_ue=ue(:,2); V_le=le(:,2);
% Use the first portion of the ringdown based on the upper envelope
rexdown = 0.1*max(V); % end of the ringdown slope at 10% of max
%rdtime=max(t_ue((V_ue-mean(V))>rexdown));
rdtime=max(t_ue((V_ue)>rexdown));
rdind=find(t==rdtime);
Vfit=V(1:rdind);
tfit=t(1:rdind);
indZ=indZ(indZ<length(tfit));

% plot data that will be analyzed
if plots >= 1
    figure(datafig);
    hold on;
    plot(tfit,Vfit,'.-'); % plot all data
    ha = axis;
    plot(tfit(indZ),0,'ok'); % show zero crossings
    %zline=plot([t(indZ(5)) t(indZ(5))],[ha(3) ha(4)], '-b'); % show data start
    plot([ha(1) ha(2)],[0 0], '-k'); % plot zero line
    title('Ringdown Data'); xlabel('time [sec]'); ylabel('Data [V]');
end

% length of our "final" data
LV = length(Vfit);


%% FFT for frequency estimation
%
P = abs(fft(Vfit)); Ppos=P(1:(round(LV/2)+1));
faxis = 1/sample_int*(0:round(LV/2))/LV;
peakf=faxis(find(Ppos==max(P(1:(round(LV/2)+1))))); %#ok<FNDSB>
if plots >= 2
    fftfig=figure; subplot(1,2,1);
    plot(faxis,Ppos,'-'); hold on
    plot(peakf,max(Ppos),'db');
    title('FFT'); xlabel('Frequency (Hz)'); ylabel('Power')
    fa=axis; text((fa(2)-fa(1))/2,(fa(4)-fa(3))/2,['f_0: ' num2str(peakf,'%.1f') ' Hz']);
end


%% Estimate Q from ringdown
% fit only to a portion of the envelope for Q
%Efitu=V_ue((V_ue-mean(V))>rexdown); %Efitu=ue;
Efitu=V_ue((V_ue)>rexdown); %Efitu=ue;
%Etu=t_ue((V_ue-mean(V))>rexdown); %Etu=t_ue;
Etu=t_ue((V_ue)>rexdown); %Etu=t_ue;
%Efitl=V_le((mean(V)-V_le)>rexdown); %Efitl=le;
Efitl=V_le((0-V_le)>rexdown); %Efitl=le;
%Etl=t_le((mean(V)-V_le)>rexdown); %Etl=t_le;
Etl=t_le((0-V_le)>rexdown); %Etl=t_le;

% find the linear fit to log envelope
envfitu=polyfit(Etu,real(log(Efitu)),1);
envfitl=polyfit(Etl,real(log(Efitl)),1);

% extract Q values
Qestu=-pi*peakf/real(envfitu(1));
Qestl=-pi*peakf/real(envfitl(1));
Qest=mean([Qestu Qestl]);

% plot the envelope
if plots >= 1
    figure(datafig);
    plot(Etu,Efitu,'.-r',Etl,Efitl,'.-g');
%     plot([Etu(end) Etu(end)],[ha(3) ha(4)],'-r');
%     plot([Etl(end) Etl(end)],[ha(3) ha(4)],'-g');
end

% plot Q estimated results
if plots >= 2
    figure(fftfig); subplot(1,2,2);
    plot(Etu,real(log(Efitu)),'.-r'); 
    hold on;
    plot(Etl,real(log(Efitl)),'.-g');
    title('Envelope Comparison & Fit');
    xlabel('Time [sec]'); ylabel('log(Amplitude) [V]');
    legend(['Upper Q: ' num2str(Qestu,'%.0f')],['Lower Q: ' num2str(Qestl,'%.0f')]);

    plot(Etu,polyval(envfitu,Etu),'-m');
    plot(Etl,polyval(envfitl,Etl),'-c');
    qa=axis; text(0.1*(qa(2)-qa(1))+qa(1),(qa(4)-qa(3))/2+qa(3),['Q: ' num2str(Qest,'%.0f') ' (mean)']);
end


%% Fit to the ringdown data
%if verbose >= 2
    %d is the data used for the exponential fit
    d=[tfit Vfit];

    % minimum search function for fit to data
    rparams(1)=peakf;    % use fft for initial value
    rparams(2)=Qest;     % use estimate from exponential fit for initial Q value
    rparams(3)=max(Vfit);   % initial amplitude
    rparams(4)=0;        % initial phase
    rparams(5)=0.01;      % initial value- data is centered at zero
    %rparams(5)=mean(Vfit);
    %disp(rparams)

    options.MaxFunEvals=10000;
    if plots >=3, options.PlotFcns=@optimplotx; end
    [rfit rfun] = fminsearch(@(p) ringfitD(p,d), rparams, options);

    % plot fit: yfit = amp.*exp(-pi.*freq.*t./Q).*sin(2.*pi.*freq.*t+phi)+yinit;
    if plots >= 1
        fitdata = rfit(3).*exp(-pi.*rfit(1).*tfit./rfit(2)).*sin(2*pi.*rfit(1).*tfit+rfit(4))+rfit(5);
        figure(datafig);
        plot(tfit,fitdata,'-m');
    end
    
    
    
%end


%% output results
res_rd=res;
res_rd.fft_max=peakf;
res_rd.Q=Qest;
res_rd.env_upper.x=Etu; res_rd.env_upper.y=Efitu+Vmean;
res_rd.env_lower.x=Etl; res_rd.env_lower.y=Efitl+Vmean;
res_rd.fit_freq=rfit(1);
res_rd.fit_Q=rfit(2);


%% function: envelope
function [upperenv lowerenv] = envelope(data, dmax, method)
% [UPPERENV LOWERENV] = ENVELOPE(SIG, METHOD)
% Based on ENVELOPE by omid_dr@yahoo.com
%

% noise threshhold
if nargin < 2, dmax = 0.05; end
% default method for extrapolating for envelope
if nargin < 3, method = 'linear'; end

sig = data(:,min(size(data)));

% the envelope is where the signal turns over
upperind = find(diff(sign(diff(sig))) < 0) + 1; %upperind(1:5)=[];
lowerind = find(diff(sign(diff(sig))) > 0) + 1; %lowerind(1:5)=[];
le=sig(lowerind);
ue=sig(upperind);
    
% filter outliers based on dmax
if dmax > 0
    for i=2:length(ue)
        if abs(ue(i)-ue(i-1))>dmax
            ue(i)=ue(i-1);
        end
    end

    for i=2:length(le)
        if abs(le(i)-le(i-1))>dmax
            le(i)=le(i-1);
        end
    end
end

if min(size(data))==1  % extrapolate to create the envelope with the same number of points as the
                       %  original data
    
    % extend envelope to length of data set
    f = 1;
    l = length(sig);
    try
        upperind = [f upperind l];
        lowerind = [f lowerind l];
    catch 
        upperind = [f; upperind; l];
        lowerind = [f; lowerind; l];
    end
    
    xi = f : l;
    upperenv = interp1(upperind, sig(upperind), xi, method, 'extrap');
    lowerenv = interp1(lowerind, sig(lowerind), xi, method, 'extrap');

elseif min(size(data))==2 % save envelope with max points only
    upperenv(:,1) = data(upperind,1);
    upperenv(:,2) = ue;
    %upperenv(:,2) = data(upperind,2);
    lowerenv(:,1) = data(lowerind,1);
    lowerenv(:,2) = le; 
    %lowerenv(:,2) = data(lowerenv,2); 
end

return


%% function: ringfitD
function sumsq = ringfitD(params,data)
% RINGFIT fit function for ringdown data
%  returns the sum of squares of difference between data and fit
% Fit function:
% y=amp*exp(-pi*freq*t/Q)*sin(2*pi*freq*t+phi)+yinitial
%
% DATA should be two columns: time and value
%
% params is a vector of:
%  freq  Q  amp  phi  yinit
%
t=data(:,1);
y=data(:,2);

yfit = params(3).*exp(-pi.*params(1).*t./params(2)).*sin(2*pi.*params(1).*t+params(4))+params(5);
sumsq = sum((y-yfit).^2);

return

Contact us