Code covered by the BSD License  

Highlights from
KPIB: Kenny-Purpose Interface Bus

KPIB: Kenny-Purpose Interface Bus

by

 

KPIB operates laboratory instruments that are controlled by GPIB or serial port interfaces.

TCF_res(tools,temp_profile,boardnum,center,spans,verbose)
function retval = TCF_res(tools,temp_profile,boardnum,center,spans,verbose)
%% TCF_res(TOOLS,TEMP_PROFILE,BOARDNUM,CENTER,SPANS,VERBOSE);
%
% TCF_res runs a TCF experiment: measuring resonant frequency vs
%   temperature. Resonant frequency is measured with a network analyzer
%   (frequency sweep). Temperature is controlled by a thermal chamber or
%   chuck and measured by an independent sensor.
%
% Inputs: 
%
% TOOLS         [structure] The list of instrument names and GPIB addresses.
%                You can use Setup_Tools.m to create TOOLS.
% TEMP_PROFILE  [string] A text file describing the temperature profile for the
%                   test. One temperature step per line, in degrees C. The
%                   temperature can also be entered as an array of temperature
%                   steps, in deg C.
% BOARDNUM      [string] The serial number of the resonator/board being tested.
%                   This string is added to the data file name.
% CENTER        [number] The starting measurement center frequency (Hz). If
%                   CENTER=0, the current setting of the analyzer is used.
%                   Default: current setting of analyzer.
% SPANS         [number array] The spans for the multi-part resonator measurement (Hz).
%                   Successive sweeps are taken at each span in order to
%                   "zoom in" on the resonator peak.
%                   Default: [10000 5000 500]
% VERBOSE       [number] Display status messages? (0/1/2) Default: 1.
%
% This program uses the following instruments:
%   tools.analyzer: any network or signal analyzer supported by kpib.m.
%                   Used to measure the resonator characteristics with a
%                   frequency sweep.
%   tools.oven: any thermal chamber/temperature controller supported by kpib.m.
%                   Used to control the temperature of the resonator during
%                   measurement.
%   tools.sensor: (see Note 1) A Multimeter reading a temperature sensor
%                   HP_3478A with LM35 or HP_34420A with Pt100 RTD
%   tools.biasset: (optional) A power supply or voltage source used for
%                   providing the bias voltage to the resonator.
%
% You can use Setup_Tools.m to create TOOLS.
% Note 1: If no independent temperature sensor is used, make the sensor instrument
%  the same as the thermal chamber (tools.sensor=tools.oven).
% All instruments must be prepared for measurement; no instrument setup is
%  performed. All instruments will be stopped at the end of the test.
% Press Alt-Q to stop the test cleanly at any time. After pressing Alt-Q,
%  the current measurement cycle will complete and the test will stop.
%
% Data will be saved to text and binary files.
% Output file prefix: TCF_BOARDNUM_[DATESTRING]
%
% Data text file format: 8 columns, no header
%
% temp_step freq amp Q 3dB temp_sensor elapsedtime bias_voltage
%
% Binary file format: .mat file using structure generated by measure_res.m.
%
%
% Requires: kpib (v2.76+), stabiltemp4, measure_res (v3.5+), etime2, convertdate
%
% Based on TCF_profile.
%
%
versionstr='TCF_res v3.02';

% MH May2010
% v3.02 updated for distribution on File Exchange
% MH NOV2009
% v3.01 small typos, M-lint fixes
% MH SEP2006
% v3.0  use diary
%       enable quit (alt-q)
% MH AUG2006
% v2.92 fixed binary file output
% v2.9  added oven to log
%       window positions
%       analyzer title
%       handles sensor='none' correctly - use oven!
%       added tools as an input
%       traces are no longer saved as text files, only binary
%       Changed to TCF_res for consistency
% MH JUL2006
% v2.62 added completion time, fixed startup display, fixed comments,
%        removed oven stability text file
% MH MAY2006
% v2.5 use struct arrays instead of cells
%      remove extraneous inputs
%      uses stabiltemp4
% MH MAY2006
% v2.3
%  added support for HP_4395A
%  uses measure_res
%  saves trace data in binary format
%  uses stabiltemp3
% MH MAY2006
% v 1.94
%  fixed the timestamp using etime2
% MH MAR2006
% v 1.82
%  updated instrument specifications, bias checking
%
% MH FEB2006
% v 1.7
%  updated to stabiltemp2
%  relax tempcompliance for low temperatures
%
% MH JAN2006
% v 1.64
%  made span list arbitrary
%  modified verbose levels, logfile entries
%
% MH AUG2005
% v 1.5
%  fixed returned values
%  implemented source power check
%  auto-zero every loop
%  

% MH JUL2005
% v 1.4 AUG2005 changes based on RTD version
% v 1.2
% v 0.93 MAY2005
% v 0.91 NOV2004
%


%%%%%%%%
%% Program Constants - edit as necessary

% TEMPCOMPLIANCE [number] Temperature must be +/- this value before a
%    measurement is taken (degrees C) (0.11 typical)
tempcompliance = 0.11;

% MEASITERATION [number] How many frequency measurements to take at each
%                   temperature? Need enough for averaging. 5 is typical.
measiteration = 5;

% AVER          [string] Averaging 'on' or 'off' for the Analyzer.
%'on' for 89410A, 'off' for most others.
if strcmp(tools.analyzer.instr,'HP_89410A')
    aver='on';
else 
    aver='off';
end

% AVERNUM       [number] How many measurements to average or how many seconds to
%                   wait per measurement.
avernum=5;

% NUMSTABLE     [number] How many temperature measurements should be "stable" before
%                   the measurements are taken at each step.
numstable=20; % 20

% STABWAIT      [number] Time to wait in seconds between temperature measurements
%                   when determining temperature stability.
stabwait=60; % 60

% SAVPATH       [string] Path to save the data. Folders and files will be created
%                   here. Note that a folder is created in the current
%                   directory with the name of today's date to store the
%                   data files in.
savpath=pwd;



%% Default values for inputs
if nargin < 6
    verbose = 1; % 1
end
if nargin < 4
    spans=[10000 5000 500];
end
if nargin < 3 || center == 0
    center = kpib(tools.analyzer.instr,tools.analyzer.gpib,'center','query',0,0,verbose);
end
%%%%%%%%


% suppress directory warning
warning off MATLAB:MKDIR:DirectoryExists


%% File names and paths
starttime = clock;
mkdir(savpath,upper(convertdate(starttime,'day')));
fold = [savpath '\' upper(convertdate(starttime,'day'))];
fileheader = ['TCF_' boardnum '_' upper(convertdate(starttime,'file'))];
% save the results from each measurement as a binary file
ressav = [fold '\' fileheader '.mat'];
% save the oven temperature stabilization steps
ovensav = [fold '\' fileheader  '_oven.mat'];
% save a log file
logsav = [fold '\' fileheader  '_log.txt'];
% save the TCF measurement results in a text file
tcfsav = [fold '\' fileheader  '.txt'];




%%%%
%% Startup
%%%%
% start recording the screen output (log)
diary(logsav)

% print the welcome message
if verbose >= 1
	fprintf(1,'  %s\n\n',versionstr);
	fprintf(1,'  %s %s\n\n','Test Start Time:',convertdate(starttime,'all'));
end

% check to see if source is on
source = kpib(tools.analyzer.instr,tools.analyzer.gpib,'source?',0,0,0,verbose);
switch source.state
    case 'off' % fatal error if the source is not on
        error('TCF_res: The analyzer source power is off.');
end
retval.source = source.level;

% Take a stab at checking the bias. Do not fail if bias is not available.
try
    bias_set=kpib(tools.biasset.instr,tools.biasset.gpib,'read',0,tools.biasset.channel,0,verbose);
catch
    fprintf(1,'\nTCF_res: Bias voltage not available.\n\n');
    bias_set.volt=0; bias_set.curr=0;
end
% title on the analyzer
kpib(tools.analyzer.instr,tools.analyzer.gpib,'label',['TCF_res: Test Start ' upper(convertdate(starttime,'file'))],'right',0,verbose);



fprintf(1,'\n  %s\n','Test Parameters:');
fprintf(1,'    %s %s\n','Board serial number:',boardnum);
fprintf(1,'    %s %s (%d)\n','Signal Analyzer:',tools.analyzer.instr,tools.analyzer.gpib);
fprintf(1,'    %s %s (%d)\n','Thermal chamber:',tools.oven.instr,tools.oven.gpib);
fprintf(1,'    Measurement Frequencies (center, spans) (Hz):\n       %d, %s\n',center,num2str(spans));
fprintf(1,'    %s %s %s %d\n','Averaging:', aver,'; Count/Wait:',avernum);
fprintf(1,'    %s %g %s\n','Source power:',retval.source,'dBm');
fprintf(1,'    Bias Voltage setpoint: %.2f V\n',bias_set.volt);
fprintf(1,'    %s %d\n','Number of Frequency Measurements at each temperature step:', measiteration);
fprintf(1,'    Number of Measurements for Temperature Stability: %d (%d sec interval)\n', numstable,stabwait);
fprintf(1,'    Data files saved to: %s\n',fold);
if isnumeric(temp_profile)
    fprintf(1,'    Temperature Profile:\n    ');
    fprintf(1,'%d ',temp_profile);
    fprintf(1,'\n');
else
    fprintf(1,'    Temperature Profile:\n    %s\n',temp_profile);
end
 
% load the temperature profile
% The text file should be just decimal numbers (degrees C), one per line
%  or, it should be an array of numbers
if ~isnumeric(temp_profile)
	temp_profile=load(temp_profile, '-ascii');
end
% estimate the time required for the test
tem=((numstable+6)*stabwait)*length(temp_profile)+(measiteration*length(temp_profile)); % tem in seconds
tef=datestr(now+(tem/3600/24));
% print result
if verbose >= 1
    fprintf(1,'    %s %d\n','Number of temperature steps:',length(temp_profile));
    fprintf(1,'    Estimated time of completion: %s\n\n',tef);
end

%% plot the test progress during the test
tcffig=figure; set(gcf,'Position',[10    70   500   620]);
    set(tcffig,'KeyPressFcn', @stoptest); % so we can stop by pressing alt-q
	% plot for temperature profile
	subplot(3,1,1); plot(temp_profile,'-o');
      title(['TCF: ' fileheader]); set(get(gca,'Title'),'Interpreter','none');
	set(gca,'Xlim',[1 length(temp_profile)]);
	ylabel('Temperature (\circC)'); xlabel('Temp Step #');
	hold on;
	
	% plot for freq data
	subplot(3,1,2);
	set(gca,'Xlim',[1 length(temp_profile)]);
	title('Resonant Frequency'); xlabel('Temp Step #'); ylabel('Frequency (Hz)');
	hold on;
	
	% plot for Q data
	subplot(3,1,3);
	set(gca,'Xlim',[1 length(temp_profile)]);
	title('Quality Factor'); xlabel('Temp Step #'); ylabel('Q');
	hold on;

% plot for oven
ovenfig=figure; set(gcf,'Position',[520   330   500   360]);
title('Oven Plot');


% run an auto-zero on the analyzer
if strcmp(tools.analyzer.instr,'HP_89410A');
    kpib(tools.analyzer.instr,tools.analyzer.gpib,'autozero','once',0,0,verbose);
end
%% make sure that the analyzer is ready
kpib(tools.analyzer.instr,tools.analyzer.gpib,'wait',0,0,0,verbose);


%%%%
% Measurements
%%%%

% update the center frequency in each loop
mcenter=center;
%%%%
% Loop over the temperature steps
global RUNTEST;
RUNTEST=1;
tic; % start timer
timestart=fix(clock);
for i = 1:length(temp_profile)
    
    %%%%
    if verbose >= 1
        fprintf(1,'\nTCF_res: waiting for temperature to stabilize at %d C (step %d/%d).\n',temp_profile(i),i,length(temp_profile));
    end
    
    %%%%
    % wait for temperature to stabilize

    % some ovens have difficulty stabilizing at low temperatures, so reduce the
    %  stability requirements for temperature < 0
    if temp_profile(i) < 0
        tempcompliance_i = tempcompliance+temp_profile(i)/-75;
    else
        tempcompliance_i = tempcompliance;
    end

    oventemp(i) = stabiltemp4(temp_profile(i),numstable,stabwait,tempcompliance_i,tools,ovenfig,verbose);
    %%%%
    % save temperature stability data
    save(ovensav,'oventemp');
    
    % time check
    timecheck=fix(clock);
    elapsedtime=etime2(timestart,timecheck);
    elt_hour=elapsedtime(2)+elapsedtime(1)*24; elt_min=elapsedtime(3);
    timetotal=elt_hour*60*60+elt_min*60+elapsedtime(4);
    
    % print status
    if verbose >= 1
        fprintf(1,'\nTCF_res: Temperature step: %d / %d (%d C)\n', i, length(temp_profile), temp_profile(i));
        fprintf(1,'%s %s %s %g %s %g %s\n','TCF_res: Time:',convertdate(timecheck,'all'),...
            'Elapsed Time:', elt_hour, 'hours', elt_min, 'min.');
    end
    
    %%%%
    % loop for multiple measurements at each temperature step
    freq_meas=[]; amp_meas=[];  ohm_meas=[];
    
    % run an auto-zero on the analyzer
    if strcmp(tools.analyzer.instr,'HP_89410A');
        kpib(tools.analyzer.instr,tools.analyzer.gpib,'autozero','once',0,0,verbose);
    end
    %% make sure that the analyzer is ready
    kpib(tools.analyzer.instr,tools.analyzer.gpib,'wait',0,0,0,verbose);

    tic;
    if verbose >= 1, fprintf(1,'\nTCF_res: Measuring (%d):',measiteration); end
    for j = 1:measiteration
        % print status
        if verbose >= 1, fprintf(1,' %d', j); end
        
        %%%%
        %% Do the frequency measurement
        % measure frequency response
        res = measure_res(tools,mcenter,spans,aver,avernum,verbose-1);

        % how long did it take?
        elt_meas(j)=toc;
        res.meas_time=elt_meas(j);
        res.elapsed_time=elt_meas(j)+timetotal;
        
        res.temp_step=temp_profile(i);
              
        % save trace data in binary file
        resntr(i,j)=res;
        save(ressav,'resntr');
       
        % Save TCF data to file
        fid = fopen(tcfsav,'a');
        fprintf(fid,'%+03.1f %.2f %f %.0f %.2f %+03.2f %.0f %f\n',...
            temp_profile(i),resntr(i,j).mark1.x,resntr(i,j).mark1.y,resntr(i,j).Q,resntr(i,j).bandwidth,resntr(i,j).Tsensor,elt_meas(j),resntr(i,j).Vbias_read);
        fclose(fid);
       
        % format trace data
        freq_meas(j) = resntr(i,j).mark1.x;
        amp_meas(j) = resntr(i,j).mark1.y;
        Q_meas(j) = resntr(i,j).Q;
        bnwd_meas(j) = resntr(i,j).bandwidth;
        temp_meas(j) = resntr(i,j).Tsensor;
        
        pause(0.1); % to allow for moving windows, cancelling, etc.
       
    end % end multiple measurement loop
    if verbose >= 1, fprintf(1,'\n'); end
    %%%%
    
    % determine the average of the measurements for this temperature step
    freq(i) = mean(freq_meas);
    amp(i) = mean(amp_meas);
    Q(i) = mean(Q_meas);
    bandwidth(i) = mean(bnwd_meas);
    meastemp(i) = mean(temp_meas);
    settemp(i) = temp_profile(i);
    %meastime(i) = mean(elt_meas);
    
    % set the center for the next measurement
    mcenter=freq(i);
    
    % terse results summary with estimated ppm change
    if verbose >= 1
        fprintf(1,'TCF_res: Average Frequency for step %d (%d C): %.2f Hz\n', i, temp_profile(i), freq(i));
        if (i >= 2) && (temp_profile(i) ~= temp_profile(i-1))
            fprintf(1,'             Frequency change from previous measurement: %.2f ppm/C\n',...
                (freq(i)-freq(i-1))/freq(1)*1e6/(meastemp(i)-meastemp(i-1)));
        end
    end
     
    if verbose >= 2, fprintf(1,'\n'); end
    
    %%%%
    %% plot results
    figure(tcffig);
    % temperature plot
    subplot(3,1,1);
    plot(i,meastemp(i),'-or');
    plot(i,temp_meas,'x');
    
    % frequency plot
    subplot(3,1,2);
    pfreq=plot(i,freq(i),'-o');
    plot(i,freq_meas,'x');
%     % estimate 2nd order TCF
%     if (i >= 3)
%         ftcf=polyfit(meastemp,freq,2);
%         legend(pfreq,['Est. TCf: ' num2str(ftcf(1),'%.2f') ' ^2 ' num2str(ftcf(2),'%.2f') ' ^1 Hz/C']);
%     end    
    
    % Q plot
    subplot(3,1,3);
    plot(i,Q(i),'-og');
    plot(i,Q_meas,'x');
    
    drawnow;
    
    if RUNTEST == 0
        fprintf(1,'TCF_res: stopping test (alt-q)\n');
        break
    end
    
end % end temperature step


%%%%
%% End measurements, clean up
%%%%
 
% format the output
clear retval;
retval.settemp = settemp';
retval.freq = freq';
retval.amp = amp';
retval.Q = Q';
retval.bandwidth = bandwidth';
retval.meastemp = meastemp';
%retval.time = meastime';

% save the figure with the test data
saveas(tcffig,[fold '\' fileheader '.fig']);


% time check
timeend=fix(clock);
elapsedtime=etime2(timestart,timeend);
elt_hour=elapsedtime(2)+elapsedtime(1)*24; elt_min=elapsedtime(3);


%%%%%%%%%%%%%%%%%
%% Finish: close and stop instruments
if verbose >= 1, fprintf(1,'\n'); end
% stop the oven
kpib(tools.oven.instr,tools.oven.gpib,'stop',0,0,0,verbose);
if verbose >= 1, fprintf(1,'TCF_res: Oven stopped.\n'); end
% turn off stimulus power (doesn't affect 4395A)
kpib(tools.analyzer.instr,tools.analyzer.gpib,'source','off',1,0,verbose);
if verbose >= 1, fprintf(1,'TCF_res: Stimulus off.\n'); end
% turn off bias
try
    kpib(tools.biasset.instr,tools.biasset.gpib,'set',0,tools.biasset.channel,0,verbose);
    kpib(tools.biasset.instr,tools.biasset.gpib,'off',0,0,0,verbose);
    if verbose >= 1, fprintf(1,'TCF_res: Bias voltage off.\n'); end
catch
    fprintf(1,'TCF_res: Bias voltage not available.\n\n');
    bias_set.volt=0; bias_set.curr=0;
end
% clear title on the analyzer
kpib(tools.analyzer.instr,tools.analyzer.gpib,'label',' ','right',0,verbose);

%%%%%%%%%%%%%%%%%


% print results
fprintf(1,'\n');

fprintf(1,'  Test End Time: %s [Estimate: %s]\n',convertdate(timeend,'all'),tef);
fprintf(1,'  %s %g %s %g %s\n','Elapsed Time:', elt_hour, 'hours', elt_min, 'min.');
fprintf(1,'  %s %d\n','Temperature Steps:',length(temp_profile));
fprintf(1,'  %s %s\n','Board serial number:',boardnum);
fprintf(1,'  %s %s\n','File name:',fileheader);

fprintf(1,'\n');

% close gpib ports
kpib('clear',0,0,0,0,0,verbose);

% stop recording log file
diary off

return


function [] = stoptest(src,evnt)
% STOPTEST is a local function that acts as the stop condition to cleanly
%  exit the test. It sets the value runtest to 0 when the user presses
%  alt-q in the progress figure. 
global RUNTEST;
if length(evnt.Modifier) == 1 && strcmp(evnt.Modifier{:}, 'alt') && evnt.Key == 'q'
    RUNTEST = 0;
    fprintf('\nTCF_res: Operator quit by pressing (alt-q)\n');
    fprintf('\nTCF_res: Current measurement cycle will complete\n');
end
return

Contact us