281 lines
10 KiB
Matlab
281 lines
10 KiB
Matlab
classdef AWG < handle
|
|
%AWG Summary of this class goes here
|
|
% Detailed explanation goes here
|
|
|
|
properties(Access=public)
|
|
|
|
kover %oversampling factor e.g. 16
|
|
upsampling_method
|
|
repetitions %repeat the signal to generate a longer sequence?
|
|
fdac %needed
|
|
precomp_sinc_rolloff = 1;
|
|
normalize2dac %want to normalize at first? either 0 or 1
|
|
bit_resolution %bit res. of quantizer (e.g. 5 bit)
|
|
dac_min
|
|
dac_max
|
|
|
|
skew_active = 0;
|
|
awg_skew ; %skew vector for each output channel
|
|
|
|
lpf_active = 0;
|
|
lpf_type ;
|
|
f_cutoff ;
|
|
|
|
signal_length;
|
|
H_lpf;
|
|
|
|
|
|
end
|
|
|
|
methods (Access=public)
|
|
function obj = AWG(options)
|
|
%AWG Construct an instance of this class
|
|
% Detailed explanation goes here
|
|
|
|
arguments
|
|
options.kover = 16;
|
|
options.upsampling_method upsampling_mode = upsampling_mode.samplehold;
|
|
options.precomp_sinc_rolloff = 1;
|
|
options.repetitions = 1;
|
|
options.normalize2dac = 1;
|
|
options.fdac = 92e9;
|
|
options.bit_resolution = 5.5
|
|
options.dac_min = -0.5;
|
|
options.dac_max = 0.5;
|
|
options.skew_active = 0;
|
|
options.awg_skew = 0;
|
|
options.lpf_active = 0;
|
|
options.lpf_type = filtertypes.butterworth;
|
|
options.f_cutoff = 32e9;
|
|
options.H_lpf Filter
|
|
|
|
end
|
|
|
|
fn = fieldnames(options);
|
|
for n = 1:numel(fn)
|
|
try
|
|
obj.(fn{n}) = options.(fn{n});
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function signalclass_out = process(obj,signalclass_in)
|
|
|
|
|
|
% 0) START AWG SIMULATION
|
|
|
|
len_in = length(signalclass_in.signal);
|
|
|
|
% disp(['Power Raw Input: ', num2str(mean(abs(real(signalclass_in.signal).^2)))]);
|
|
|
|
% 1) RESAMPLE IF NESSECARY
|
|
if signalclass_in.fs ~= obj.fdac
|
|
% min_ = min(signalclass_in.signal);
|
|
% max_ = max(signalclass_in.signal);
|
|
signalclass_in = signalclass_in.resample("fs_in",signalclass_in.fs,"fs_out",obj.fdac,"n",10,"beta",5);
|
|
% signalclass_in.signal = clip(signalclass_in.signal,-1.3414,1.3414);
|
|
% signalclass_in.signal = softclip(signalclass_in.signal,7,1.3414);
|
|
% signalclass_in.plot("displayname",'resampled and clipped','fignum',1);
|
|
end
|
|
|
|
% disp(['Power after resamp: ', num2str(mean(abs(real(signalclass_in.signal).^2)))]);
|
|
|
|
% 4) PROCESSING of the signal (normalize->quantize->sample hold)
|
|
signalclass_in.signal = obj.process_(signalclass_in.signal);
|
|
|
|
% cast the inform. signal to electrical signal
|
|
if ~isa(signalclass_in,'Electricalsignal')
|
|
signalclass_in = Electricalsignal(signalclass_in,"fs",obj.fdac*obj.kover,"logbook",signalclass_in.logbook);
|
|
end
|
|
|
|
% 4. Apply LPF on the signal
|
|
if obj.lpf_active
|
|
if isa(obj.H_lpf,'Filter')
|
|
%4.A) user already specified a complete filter class when
|
|
% initializing the AWG module
|
|
signalclass_in = obj.H_lpf.process(signalclass_in);
|
|
else
|
|
%4.B) just use a standard filter
|
|
lpf = Filter('filtdegree',5,"f_cutoff",obj.f_cutoff,"fs",obj.kover*obj.fdac,"filterType",obj.lpf_type);
|
|
signalclass_in = lpf.process(signalclass_in);
|
|
end
|
|
end
|
|
|
|
% disp(['AWG output power: ',num2str(signalclass_in.power),' dBm']);
|
|
|
|
% append to logbook
|
|
current_class = class(obj);
|
|
lbdesc = ['AWG ', current_class , '// k_over:',num2str(obj.kover),'. f_dac:',num2str(obj.fdac*1e-9),'GHz. Resolution:',num2str(obj.bit_resolution),' bits.'];
|
|
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
|
|
|
% write to output
|
|
signalclass_out = signalclass_in;
|
|
|
|
len_out = length(signalclass_out.signal);
|
|
|
|
if len_out ~= len_in * obj.kover
|
|
warning("AWG: Output length maybe not correct.")
|
|
end
|
|
|
|
end
|
|
|
|
function elec_out = process_(obj,data_in)
|
|
%METHOD1 Summary of this method goes here
|
|
% Detailed explanation goes here
|
|
arguments(Input)
|
|
obj
|
|
data_in
|
|
end
|
|
|
|
arguments(Output)
|
|
elec_out
|
|
end
|
|
|
|
obj.signal_length = length(data_in);
|
|
|
|
%%%%%%%%% PRECOMP SINC ROLLOFF %%%%%%%%%
|
|
if obj.precomp_sinc_rolloff
|
|
% X: design FIR filter for sinc precomp
|
|
% https://www.dsprelated.com/showarticle/1191.php
|
|
ntaps = 13;
|
|
npts = 32;
|
|
% least-squares FIR design
|
|
fmax = obj.fdac*0.5;
|
|
ff = linspace(0,fmax,npts);
|
|
hsinc = sin(pi*ff/obj.fdac)./(pi*ff/obj.fdac + eps); % transfer function of sample and hold DAC
|
|
hsinc(1) = 1;
|
|
h_goal= 1./hsinc; % goal function
|
|
f = 2.*ff./obj.fdac; %vector between 0 and 1, where 1 is nyquist is fsamp/2
|
|
b = firls(ntaps-1,f,h_goal);
|
|
data_in = conv(data_in,b,"same");
|
|
end
|
|
|
|
if 0
|
|
%compare different fir construction methods in matlab
|
|
b_fir = fir2(ntaps-1,f,h_goal);
|
|
b_pm = firpm(ntaps-1,f,h_goal);
|
|
b = firls(ntaps-1,f,h_goal);
|
|
|
|
figure(111)
|
|
freqz(b,1,[],obj.fdac);
|
|
hold on
|
|
freqz(b_fir,1,[],obj.fdac);
|
|
hold on
|
|
freqz(b_pm,1,[],obj.fdac);
|
|
plot(f.*obj.fdac./2.*1e-9,20*log10(h_goal),'Marker','o');
|
|
legend('firls','fir2','firpm','target')
|
|
end
|
|
|
|
if 0
|
|
% design filter as inverse of the goal transfer function
|
|
freq_vec = linspace(-obj.fdac/2,obj.fdac/2,length(data_in));
|
|
h = sin(pi*freq_vec/obj.fdac)./(pi*freq_vec/obj.fdac + eps);
|
|
max_amp_db = 45;
|
|
max_amp_lin = 10^(-max_amp_db/20);
|
|
p = find(h<max_amp_lin);
|
|
% h(p)=10^(-max_amp_db/20);
|
|
h_sinc = 1./h;
|
|
|
|
convol = fftshift(h_sinc).'.*fft(data_in);
|
|
data_in = ifft(convol);
|
|
data_in = real(data_in);
|
|
end
|
|
|
|
%%%%%%%%% Normalize to DAC range %%%%%%%%%
|
|
% X:
|
|
if obj.normalize2dac
|
|
% 0a Normalize the signal to full scale DAC range
|
|
data_in = data_in - min(data_in); %set "foot" to zero
|
|
data_in = data_in / (max(data_in)-min(data_in)); %scale between 0 and 1
|
|
data_in = data_in * (obj.dac_max-obj.dac_min); %scale to desired total range of DAC
|
|
data_in = data_in + obj.dac_min; %set "foot" to desired value
|
|
end
|
|
|
|
data_in = data_in - mean(data_in);
|
|
|
|
% disp(['Power after scaling to DAC: ', num2str(mean(abs(real(data_in).^2)))]);
|
|
|
|
%%%%%%%%% Quantize %%%%%%%%%
|
|
% X. Quantize the signal - Full Scale is between obj.dac_min
|
|
% and dac_max. If signal is smaller in between, you won't use
|
|
% the full bit-resolution.
|
|
if obj.bit_resolution>0
|
|
elec_out = obj.quantization(data_in) ;
|
|
else
|
|
elec_out = data_in;
|
|
end
|
|
|
|
%%%%%%%%% Sample and hold %%%%%%%%%
|
|
% X. Sample and hold + repeat (data_out: 1xsignal length)
|
|
if obj.upsampling_method == 1
|
|
% just use matlab function
|
|
elec_out = resample(elec_out,obj.kover,1);
|
|
elseif obj.upsampling_method == 2
|
|
% sample and hold
|
|
elec_out = repmat(elec_out,obj.repetitions,obj.kover);
|
|
elec_out = reshape(elec_out',[],1);
|
|
else
|
|
error('chosen upsampling method not implemented?');
|
|
end
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% 3. Add skew (not implemented so far)
|
|
if obj.skew_active
|
|
elec_out = obj.skew(elec_out);
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
methods (Access=private)
|
|
|
|
function quant_out = quantization(obj,x_in)
|
|
|
|
steps = round(2^obj.bit_resolution) + rem(round(2^obj.bit_resolution),2) ;
|
|
|
|
if isreal(x_in)
|
|
% shift signal and clip to quantizer intervall
|
|
x_in = min( max(x_in-obj.dac_min,0), obj.dac_max-obj.dac_min);
|
|
|
|
% quantize signal
|
|
x_in = round((steps-1)/(obj.dac_max-obj.dac_min)*x_in);
|
|
|
|
% scale and shift back
|
|
quant_out = x_in*(obj.dac_max-obj.dac_min)/(steps-1) +obj.dac_min;
|
|
|
|
else
|
|
|
|
% shift signal and clip to quantizer intervall
|
|
x_in = min(max(real(x_in)-obj.dac_min,0),obj.dac_max-obj.dac_min) + ...
|
|
1i*min(max(imag(x_in)-obj.dac_min,0),obj.dac_max-obj.dac_min);
|
|
|
|
% quantize signal and shift back signal
|
|
x_in = round((steps-1)/(obj.dac_max-obj.dac_min)*x_in);
|
|
|
|
% scale and shift back
|
|
quant_out = x_in*(obj.dac_max-obj.dac_min)/(steps-1) +(1+1i)*obj.dac_min;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
function skew_out = skew(obj,x_in)
|
|
|
|
% Exact delay calculation
|
|
|
|
% Calculate transfer function
|
|
fsimu = obj.kover*obj.fdac;
|
|
faxis = linspace(fsimu/2, fsimu/2, length(x_in)+1);
|
|
faxis = fftshift(faxis(1:end-1));
|
|
|
|
skew_out = ifft(fft(x_in) .* exp(-1i*2*pi*obj.awg_skew*faxis)' );
|
|
|
|
skew_out = real(skew_out) ; % get rid of negligible imaginary part
|
|
|
|
end
|
|
|
|
end
|
|
end
|