| 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
|
|