Files
imdd_silas/Classes/05_Lab/AwgKeysight.m
2024-09-26 14:50:25 +02:00

418 lines
16 KiB
Matlab
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
classdef AwgKeysight
% AWG implementation only for Keysight devices:
% a) M8196A (92 GSa/s 4Ch)
% b) M8199A (128 GSa/s 4Ch) % TODO
% c) M8199A_ILV (256 GSa/s 2Ch) % TODO
% d) M8199B (256 GSa/s 2Ch) % Commands worked in 2023
% Summary of SCPI Commands:
% *IDN? — Identify the instrument.
% *RST — Reset the instrument.
% :SOUR:FREQ:RAST <frequency> — Set the DAC sample frequency.
% :OUTP<channel>:STAT ON — Enable output for a channel.
% :VOLT<channel>:LEVel[:IMMediate][:AMPLitude] — Set output voltage.
% :TRAC<channel>:DATA <data> — Load waveform data.
% :INIT:IMM — Start waveform generation.
% Construct Class:
% AWG = AwgKeysight("model","M8196A","fdac",92e9,"scaletodac",[1,1,1,1],"skews",[0,0,0,0],"voltages",[0,0,0,0.6]);
% Upload new Signal (use either normal array of doubles or Silas Informationsignal class)
% AWG.upload("Signal4",Signal);
properties(Access=protected)
model awg_model
fdac double
skews double
voltages double
scaletodac logical
numChannels double
waveformGranularity double
sampleMemorySize double
numProvidedSignals double
end
methods (Access=public)
function obj = AwgKeysight(options)
arguments
options.model awg_model
options.fdac double
options.skews double
options.voltages double
options.scaletodac logical
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
switch obj.model
case awg_model.M8196A
obj.numChannels = 4;
obj.waveformGranularity = 128;
obj.sampleMemorySize = 512000;
case awg_model.M8199A
obj.numChannels = 4;
obj.waveformGranularity = 512;
obj.sampleMemorySize = 1024000; %to check
case awg_model.M8199A_ILV
obj.numChannels = 2;
obj.waveformGranularity = 512;
obj.sampleMemorySize = 1024000; %to check
case awg_model.M8199B
obj.numChannels = 2;
obj.waveformGranularity = 512;
obj.sampleMemorySize = 1024000; %to check
end
end
function upload(obj,channels,options)
arguments
obj
% leave this as it is! Important for further handling/ parsing
channels.signal1 Informationsignal = Informationsignal([])
channels.signal2 Informationsignal = Informationsignal([])
channels.signal3 Informationsignal = Informationsignal([])
channels.signal4 Informationsignal = Informationsignal([])
% add new optional arguments here
options.bla = 1;
end
% Every signal at this point is an Informationsignal!
% Argument block above casts everything to Informaionsignal...
% Count the number of nonempty Informationsignal
obj.numProvidedSignals = sum(structfun(@(x) ~isempty(x.signal), channels));
assert(obj.numProvidedSignals<=obj.numChannels,['Only ',num2str(obj.numChannels),' AWG Channels. User provided ',num2str(obj.numProvidedSignals),' waveforms!'])
% Additional check for invalid signals beyond available channels
if obj.numChannels == 2
% Check if Signal3 or Signal4 is nonempty
if ~isempty(channels.signal3.signal) || ~isempty(channels.signal4.signal)
error('Only 2 channels are available. Signals for Channel 3 and 4 should not be defined.');
end
end
% Extract the actual signals to cell array
unpackedSignals = {};
fn = fieldnames(channels);
for s = 1:obj.numChannels
unpackedSignals{s} = channels.(fn{s}).signal;
end
success = obj.upload_(unpackedSignals);
end
end
methods(Access=private)
function success = upload_(obj,signal)
%%%%%%%%%%%%%%%%%%%%%%
%%% PREPARE SIGNALS %%
%%%%%%%%%%%%%%%%%%%%%%
for s = 1:obj.numChannels
% assure laying row vector
signal{s} = reshape(signal{s},[1, length(signal{s})]);
% set to zeros if voltage == 0
if obj.voltages(s) == 0
signal{s} = signal{s} .* 0;
end
%scale to max DAC resolution
if obj.scaletodac(s)
factor = max(abs(signal{s}));
if factor~=0
signal{s} = signal{s}./factor;
end
end
% clip warning (if autoscale is off)
if ~isempty(signal{s})
clip(s) = sum(signal{s}>1);
if clip(s)
warning(['Clipwarning CH ',num2str(s),'. Clipped Samples', num2str(clip(s))]);
end
end
% The waveform granularity is e.g. 128. This means that the waveform length must be a multiple of this granularity. The minimum waveform length is 128 samples.
if rem(length(signal{s}),obj.waveformGranularity) ~= 0
signal{s} = [signal{s} signal{s}(:,1:(obj.waveformGranularity-rem(length(signal{s}),obj.waveformGranularity)))];
warning(['data should have a length multiple of ',num2str(obj.waveformGranularity),'!']);
end
% Digitally apply skews to the signals
if obj.skews(s) > 0
delay_sec = obj.skews(s)*1e-12;
signal{s} = delayseq(signal{s},delay_sec,obj.fdac);
end
% Scale to DAC values
signal{s} = int8(round(127 * signal{s}));
% Check signal length, truncate if too long for AWG
siglen = min(length(signal{s}),obj.sampleMemorySize);
signal{s} = signal{s}(1:siglen);
if siglen==obj.sampleMemorySize
warning(['Signal ',num2str(s),' was truncated to ',num2str(obj.sampleMemorySize),' to fit into AWG sample memory'])
end
end
%%%%%%%%%%%%%%%%%%%%%%
%%% CONNECT TO AWG %%%
%%%%%%%%%%%%%%%%%%%%%%
% read/write lock file to our ntserver/scratch
try
obj.check_lock(char(obj.model),1);
obj.lock_device(char(obj.model));
catch
warning('Could not reach ntserver to write lock!')
end
% Open Visa
switch obj.model
case awg_model.M8196A
v = visadev('TCPIP0::zizou.tf.uni-kiel.de::hislip0::INSTR');
case awg_model.M8196A
v = visadev('TCPIP0::localhost::hislip0::INSTR');
end
%check if channel config matches
writeline(v,'*opt?');
aw=readline(v);
assert(obj.numChannels==sscanf(aw, '%f'),['Wrong Channel config. AWG response: ',num2str(sscanf(aw, '%f')), ' Channels']);
switch obj.model
case awg_model.M8196A
obj.writeNcheck(v,':INST:DACM FOUR');
obj.writeNcheck(v,':ABORt');
obj.writeNcheck(v,sprintf(':FREQuency:RASTer %.15g;', obj.fdac));
case awg_model.M8199B
obj.writeNcheck(v,':ABORt ''M2'''); %war auf M2, das wird wohl der CH sein oder?
obj.writeNcheck(v,sprintf(':FREQ ''M1.ClkGen'', %.15g;', obj.fdac));
end
%%%%%%%%%%%%%%%%%%%%%%
%%% UPLOAD SIGNALS %%%
%%%%%%%%%%%%%%%%%%%%%%
for chan = 1:obj.numChannels
segm_num = 1; %we usually have one segment
segm_len = length(signal{chan});%there are max signl lengths
if segm_len > 0
switch obj.model
case awg_model.M8196A
% 1) TRAC DEl - delete the previous waveform
obj.writeNcheck(v,sprintf(':TRACe%d:DELete %d', chan, segm_num));
% 2) TRAC DEF - define properties of waveform
obj.writeNcheck(v,sprintf(':TRACe%d:DEFine %d,%d', chan, segm_num, segm_len));
% 3) TRAC DATA - load the new waveform
offset = 0;
cmd = sprintf(':TRACe%d:DATA %d,%d,', chan, segm_num, offset);
binblockwrite(v, signal{chan}, 'int8', cmd);
aw = obj.writeReceiveCheck(v,'*opc?');
% 4) SET VOLTAGE (not possible to set 0v)
obj.writeNcheck(v, sprintf(':VOLTage%d:AMPLitude %g', chan, obj.voltages(chan)));
% 5) SET OUTPUTS ON/ OFF (generation not started at this point)
obj.writeNcheck(v, sprintf(':OUTPut%d ON', chan));
case awg_model.M8199B
% 1) TRAC DEl - delete the previous waveform
xfprintf(f, sprintf(':TRAC:DEL ''M2.DataOut1'', %d', segm_num), 1);
xfprintf(f, sprintf(':TRAC:DEL ''M2.DataOut2'', %d', segm_num), 1);
% 2) TRAC DEF - define properties of waveform
xfprintf(f, sprintf(':TRAC:DEF ''M2.DataOut1'', %d,%d', segm_num, len));
xfprintf(f, sprintf(':TRAC:DEF ''M2.DataOut2'', %d,%d', segm_num, len));
% 3) TRAC DATA - load the new waveform
% Example command: :TRAC:DATA 'M1.DataOut1', 1, 0 , -127,-126,,-1,0,1,,127,-127,,-1,0,1,,127
% means : :TRAC:DATA 'identifier' , <segment_id>, <offset>, <block>|<numeric_values>
cmd = sprintf(':TRAC:DATA ''M2.DataOut1'', %d,%d,', segm_num, offset);
binblockwrite(f, data(1+offset:offset+len), dataFormat, cmd);
cmd = sprintf(':TRAC:DATA ''M2.DataOut2'', %d,%d,', segm_num, offset);
binblockwrite(f, data(1+offset:offset+len), dataFormat, cmd);
% 4) SET VOLTAGE (not possible to set 0v)
xfprintf(f, sprintf(':VOLT:AMPL ''M2.DataOut1'', %gv', awg_volt(1)));
xfprintf(f, sprintf(':VOLT:AMPL ''M2.DataOut2'', %gv', awg_volt(2)));
% xfprintf(f, ':VOLT:AMPL ''M2.DataOut2'', 0.1v');
% 5) SET OUTPUTS ON/ OFF
xfprintf(f, ':OUTP ''M2.DataOut1'', ON'); %sioe 11.23: Hier habe ich absichtlich auf OFF gestellt, wenn Kanal nicht bentigt!
xfprintf(f, ':OUTP ''M2.DataOut2'', ON');
end
end
end
% Set global output on/ off to start generation
switch obj.model
case awg_model.M8196A
obj.writeNcheck(v,':INIT:IMMediate');
case awg_model.M8199B
% :OUTP:GLOB 'M1.System', OFF
obj.writeNcheck(v, ':OUTP:GLOB ''M1.System'', ON');
obj.writeNcheck(v, ':OUTP:GLOB ''M2.System'', ON');
obj.writeNcheck(v,':INIT:IMMediate ''M2''');
end
% operation ceomplete (opc)
obj.writeNcheck(v,'*OPC');
aw = obj.writeReceiveCheck(v,'*opc?');
% close visa connection
delete(v);
% delete lock file to our ntserver/scratch
try
obj.release_lock(char(obj.model));
catch
warning('Could not reach ntserver to write lock!')
end
% at this point the signal upload should be successful :-)
success = 1;
end
function writeNcheck(~,visaobj,command)
% 1) send command to intstument
% 2) check if everything is okay
writeline(visaobj,char(command));
writeline(visaobj,':syst:err?');
err_aw=readline(visaobj);
assert(strfind(err_aw,"No error"),['AWG System Error? after command: ', char(command)])
end
function [aw]=writeReceiveCheck(~,visaobj,command)
% 1) send command to intstument
% 2) receive answer from instrument
% 3) check if everything is okay
writeline(visaobj,char(command));
aw = readline(visaobj);
writeline(visaobj,':syst:err?');
err_aw=readline(visaobj);
assert(strfind(err_aw,"No error"),['AWG System Error? after command: ', char(command)])
end
function check_lock(~,aDeviceName,lock_timeout)
pause(0.2);
rdy = 0;
for n = 1:(lock_timeout*100)
pause(0.1);
if rem(n,100) == 0
display(['waiting for device lock! (' num2str(n/10) 's)']);
button = questdlg(['Lab Measurement is LOCKED! ',...
'be careful'],...
'LOCKED','Wait','Unlock','Abort Meas','Wait') ;
switch button
case {'Abort Meas' ,''}
delete(findobj('name','Simulation Status Window'));
error('Measurement aborted by the user') ;
case 'Unlock'
rdy = 1;
break;
case 'Wait'
%wait another ten secs
end
end
% file_handle = fopen(['\\ntserver\scratch\labor\lock\' aDeviceName '.lock'],'r');
file_handle = fopen([filesep, filesep, 'ntserver.tf.uni-kiel.de', filesep, 'scratch', filesep 'Labor', filesep, 'lock', filesep, aDeviceName, '.lock'],'r');
if file_handle == -1
rdy = 1;
break;
end
time_flag=datenum(clock);
dev_flag = fread(file_handle,time_flag,'double');
fclose(file_handle);
if time_flag > dev_flag+60/(24*60)
rdy = 1;
break;
end
end
if ~rdy
errordlg([aDeviceName ' is locked by someone else!!!']);
errormsg('AWG2SCOPE',[aDeviceName ' is locked by someone else!!!']);
else
end
end
function lock_device(~,aDeviceName)
file_handle = fopen([filesep, filesep, 'ntserver.tf.uni-kiel.de', filesep, 'scratch', filesep 'Labor', filesep, 'lock', filesep, aDeviceName, '.lock'],'wb');
if ~isempty(file_handle)
time_flag=datenum(clock);
fwrite(file_handle,time_flag,'double');
fclose(file_handle);
end
end
function release_lock(~,aDeviceName)
pause(0.5);
delete([filesep, filesep, 'ntserver.tf.uni-kiel.de', filesep, 'scratch', filesep 'Labor', filesep, 'lock', filesep, aDeviceName '.lock']);
end
end
end