WIP - implementation and debugging of 224 Gbd PAM4 system

This commit is contained in:
Silas Oettinghaus
2023-05-17 14:47:56 +02:00
parent c8f1ee17e9
commit 8d098a6c80
12 changed files with 274 additions and 254 deletions

View File

@@ -18,7 +18,6 @@ classdef AWG
lpf_active = 0;
lpf_type ;
f_cutoff ;
lowpass ;
signal_length;
H_lpf;
@@ -32,7 +31,7 @@ classdef AWG
% Detailed explanation goes here
arguments
options.preset = [] ;
options.preset = [] ;
options.kover = 16;
options.repetitions = 1;
options.normalize = 1;
@@ -41,62 +40,57 @@ classdef AWG
options.dac_min = -0.5;
options.dac_max = 0.5;
options.skew_active = 0;
options.awg_skew = 0;
options.awg_skew = 0;
options.lpf_active = 0;
options.lpf_type = 0;
options.f_cutoff = 32e9;
options.lowpass = 1;
end
if isempty(options.preset)
obj.skew_active = 0;
obj.lpf_active = 0;
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
elseif options.preset == "M8196A"
if options.preset == "M8196A"
% M8196A (92GBd) https://www.keysight.com/us/en/product/M8196A/92-gsa-s-arbitrary-waveform-generators.html
obj.dac_max = 0.5;
obj.dac_min = -.5;
obj.lowpass = 1; %LP
obj.f_cutoff = 50e9;
elseif options.preset == "M8199B"
%https://www.keysight.com/us/en/assets/3120-1465/data-sheets/M8199A-128-256-GSa-s-Arbitrary-Waveform-Generator.pdf
% obj.fdac = 256e9;
obj.dac_max = 0.5;
obj.dac_min = -.5;
obj.f_cutoff = 80e9;
end
obj.kover = options.kover; %oversampling factor e.g. 16
obj.repetitions = options.repetitions; %repeat the signal to generate a longer sequence?
obj.fdac = options.fdac;
obj.normalize = options.normalize;%want to normalize at first? either 0 or 1
obj.bit_resolution = options.bit_resolution;%bit res. of quantizer (e.g. 5 bit)
obj.dac_min = options.dac_min;
obj.dac_max = options.dac_max;
obj.lpf_active = options.lpf_active;
obj.skew_active = options.skew_active;
obj.awg_skew = options.awg_skew;
end
function signalclass_out = process(obj,signalclass_in)
% actual processing of the signal (steps 1. - 3.)
signalclass_in.signal = obj.process_(signalclass_in.signal);
% actual processing of the signal (steps 1. - 3.)
signalclass_in.signal = obj.process_(signalclass_in.signal);
% 4. Apply LPF on the signal
if obj.lpf_active
lpf = Filter('filtdegree',4,"f_cutoff",obj.f_cutoff,"fsamp",obj.kover*obj.fdac,"filterType",filtertypes.bessel_inp);
lpf = Filter('filtdegree',5,"f_cutoff",obj.f_cutoff,"fsamp",obj.kover*obj.fdac,"filterType",obj.lpf_type);
signalclass_in = lpf.process(signalclass_in);
end
% cast the inform. signal to electrical signal
% cast the inform. signal to electrical signal
signalclass_in = Electricalsignal(signalclass_in,"fs",obj.fdac*obj.kover,"logbook",signalclass_in.logbook);
% append to logbook
% append to logbook
signalclass_in = signalclass_in.logbookentry();
% write to output
% write to output
signalclass_out = signalclass_in;
end
@@ -106,25 +100,24 @@ classdef AWG
% Detailed explanation goes here
arguments(Input)
obj
data_in
data_in
end
arguments(Output)
elec_out
elec_out
end
obj.signal_length = length(data_in);
if obj.normalize
% 5.1. Normalize the signal to 1 Vpp and set the amplitude of the signal
% 0a Normalize the signal to 1 Vpp and set the amplitude of the signal
data_in = data_in/(max(data_in)-min(data_in));
else
% 5.1. Cut the Signal at -1 and 1 and scale to amplitude
% 0b Cut the Signal at -1 and 1 and scale to amplitude
data_in(data_in > 1) = 1;
data_in(data_in < -1) = -1;
end
% 1. Quantize the signal
if obj.bit_resolution>0
elec_out = obj.quantization(data_in) ;
@@ -139,9 +132,7 @@ classdef AWG
% 3. Add skew
if obj.skew_active
elec_out = obj.skew(elec_out);
end
end
end
@@ -194,92 +185,5 @@ classdef AWG
end
function lpf_out = lpf(obj,x_in)
lpf_out = ifft(obj.H_lpf.*fft(x_in));
end
function H = buildFilter(obj,filterType)
filtdegree = 3;
fsimu = obj.kover*obj.fdac;
rp = 0.5; %passband ripple
rs = 0.5; %stopband ripple
switch filterType
case 1
% Bessel filter, impulse invariant transformed
[B, A] = besself(filtdegree, 2*pi*obj.f_cutoff);
[B ,A] = impinvar(B,A,fsimu);
case 2
% Bessel filter, impulse bilinear transformed
[Z, P, K] = besself(filtdegree, 2*pi*obj.f_cutoff);
[Z ,P, K] = bilinear(Z,P,K,fsimu);
[B ,A] = zp2tf(Z ,P ,K);
case 3
% Butterworth filter
if obj.lowpass == 1 %lowpass
[B, A] = butter(filtdegree, obj.f_cutoff/(fsimu/2),'low');
else % highpass
[B, A] = butter(filtdegree, obj.f_cutoff/(fsimu/2),'high');
end
case 4
% Chebyshev 1 filter
[B, A] = cheby1(filtdegree,rp, obj.f_cutoff/(fsimu/2));
case 5
% Chebyshev 2 filter
[B, A] = cheby2(filtdegree,rs, obj.f_cutoff/(fsimu/2));
case 6
% Elliptic filter
[B, A] = ellip(filtdegree,rp,rs,obj.f_cutoff/(fsimu/2));
case 7
% Hamming filter
g=(filtdegree-1)/2;
wc=obj.f_cutoff/(fsimu/2);
B = wc*sinc(wc*(-g:g)).*hamming(filtdegree)';
A=1;
case 8
% Raised Cosine filter
B = firrcos(filtdegree,obj.f_cutoff,para.df,fsimu);
A=1;
case 9
% Sinc filter
g=(filtdegree-1)/2;
wc=obj.f_cutoff/(fsimu/2);
B = wc*sinc(wc*(-g:g));
A=1;
end
H = freqz(B, A, obj.repetitions*obj.kover*obj.signal_length,'whole');
end
end
end

View File

@@ -1,155 +1,162 @@
classdef Amplifier
%UNTITLED Summary of this class goes here
% Detailed explanation goes here
properties
type
amp_mode
amplification_db
properties
amp_mode
gain_mode
nase_mode
amplification_db
noifig
saturation_mode
saturation_power
fsimu
end
methods
function obj = Amplifier(options)
%UNTITLED Construct an instance of this class
% Detailed explanation goes here
arguments
options.type
options.amp_mode
options.amplification_db
options.amp_mode amp_mode = amp_mode.ideal_no_noise
options.gain_mode gain_mode = gain_mode.output_power
options.nase_mode nase_mode = nase_mode.pass_ase
options.nase_mode = 0;
options.noifig = 0;
options.saturation_mode = 0;
options.saturation_power = 0;
options.fsimu = []
options.amplification_db double = 0
options.noifig double = 0;
end
obj.type = options.type;
obj.amp_mode = options.amp_mode;
obj.amplification_db = options.amplification_db;
obj.nase_mode = options.nase_mode;
obj.noifig = options.noifig;
obj.saturation_mode = options.saturation_mode;
obj.saturation_power = options.saturation_power;
obj.fsimu = options.fsimu;
obj.saturation_power=1/1000*10^(obj.saturation_power/10);
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
end
function signalclass_out = process(obj,signalclass_in)
% actual processing of the signal (steps 1. - 3.)
signalclass_in.signal = obj.process_(signalclass_in.signal);
function signalclass_out = process(obj,signalclass_in)
% append to logbook
lbdesc = ['Amp '];
% actual processing of the signal
signalclass_in = obj.process_(signalclass_in);
% append to logbook
lbdesc = ['Amp '];
signalclass_in = signalclass_in.logbookentry(lbdesc);
% write to output
% write to output
signalclass_out = signalclass_in;
end
function [X_out] = process_(obj,X_in)
function [y_out,nase] = process_(obj,x_in,optional)
arguments
obj
x_in = [];
optional.nase = 0;
end
%calc gain for output power mode, amp mode and saturated mode
a_lin = obj.calculateGain(x_in);
a_lin = obj.calculateGain(X_in.signal);
y_out = a_lin * x_in;
%apply amplification
X_in.signal = a_lin * X_in.signal;
% now, optical ase noise:
if class(X_in) == "Opticalsignal"
if obj.type == "ideal"
if obj.amp_mode == amp_mode.ideal_no_noise
%don't add/ remove noise, but scale it accordingly
if optional.nase ~= 0
nase=state.a^2*optional.nase;
%don't add/ remove noise, but scale it accordingly
X_in.nase = obj.onlyAmplifyAse(X_in.nase, a_lin);
elseif obj.amp_mode == amp_mode.edfa_increase_nase
%calculate ASE-noise
X_in.nase = obj.increaseAse(X_in.nase, a_lin, obj.noifig , X_in.lambda );
elseif obj.amp_mode == amp_mode.edfa_replace_nase
%calculate ASE-noise
X_in.nase = obj.replaceAse(X_in.nase, a_lin, obj.noifig , X_in.lambda );
end
elseif obj.type == "edfa"
if obj.nase_mode == nase_mode.generate_ase
nase = X_in.nase;
fs = X_in.fs;
dimension = size(X_in.signal);
%calculate ASE-noise
if optional.nase ~= 0
Nase_old = state.gain^2*optional.nase ;
nase_numeric = obj.generateAseNoise(nase, fs, dimension);
h = Constant.Planck;
c = Constant.LightSpeed;
[X_in.signal, osnr] = obj.applyAseToSignal(X_in.signal, nase_numeric);
Nase_new = 0.5* 10^(obj.noifig/10) * h*c/(lambda_T*1e-9) * (a_lin^2-1) ;
X_in.nase = 0;
elseif obj.nase_mode == nase_mode.pass_ase
X_in.nase = X_in.nase;
nase = Nase_old + Nase_new ;
end
end
pow_in_lin = mean(abs(x_in.^2)) ;
pow_in_dbm = 10*log10(pow_in_lin)+30;
pow_out_lin = mean(abs(y_out.^2)) ;
pow_out_dbm = 10*log10(pow_out_lin)+30;
X_out = X_in;
if obj.amp_mode == "output_power"
seemsright = pow_out_dbm == obj.amplification_db;
elseif obj.amp_mode == "gain"
seemsright = pow_out_dbm == pow_in_dbm + obj.amplification_db;
end
if ~seemsright
% warning("Amplifier output not correct, please check the reason");
end
end
function a_lin = calculateGain(obj,xin)
if obj.amp_mode == "output_power"
if obj.gain_mode == gain_mode.output_power
%get linear gain for output power mode
pow_in = mean(abs(xin.^2)) ; % lin input power
pow_out = 10^(obj.amplification_db/10 - 3) ; % dBm to lin
a_lin = sqrt(pow_out/pow_in) ;
elseif obj.amp_mode == "gain"
elseif obj.gain_mode == gain_mode.gain
%get linear gain for classic gain mode
%get linear gain for classic gain mode
a_lin=10^(obj.amplification_db/20);
end
if obj.saturation_mode
Pin = sum(mean((abs(xin)).^2, 2)) ;
gain_power=fzero(inline(['log(G/' num2str(a_lin^2,'%1.16e') ')/(1-G)-log(2)*' num2str(Pin,'%1.16e') '/' ...
num2str(obj.saturation_power,'%1.16e')],'G'),[1+eps,a_lin^2]);
end
end
function nase_amped = onlyAmplifyAse(~,nase_old, a_lin)
nase_amped = a_lin^2 * nase_old;
end
function nase_new = calculateAseFromThisAmp(~, a_lin, noisefig, lambda)
h = Constant.Planck;
c = Constant.LightSpeed;
nase_new = 0.5* 10^(noisefig/10) * h*c/lambda * (a_lin^2-1) ;
end
function ase_increased = increaseAse(obj,nase_old, a_lin, noisefig, lambda)
nase_fromThisAmp = obj.calculateAseFromThisAmp(a_lin, noisefig, lambda);
nase_old = a_lin^2 * nase_old;
ase_increased = nase_old + nase_fromThisAmp;
end
function nase_new = replaceAse(~,a_lin, noisefig, lambda_nm)
nase_fromThisAmp = obj.calculateAseFromThisAmp(a_lin, noisefig, lambda_nm);
nase_new = nase_fromThisAmp;
end
function nase_numeric = generateAseNoise(~, nase, fs, dimension)
nase_numeric = (randn(dimension) + 1i*randn(dimension))*sqrt(nase/2*fs) ;
end
function [noisy_sig, osnr] = applyAseToSignal(~, opticalsignal, nase_numeric)
noisy_sig = opticalsignal + nase_numeric ;
osnr = pow2db(mean(abs(opticalsignal.^2))/mean(abs(nase_numeric.^2)));
disp(osnr);
end
end
end

View File

@@ -6,13 +6,15 @@ classdef Filter
H
filterType
f_cutoff
lowpass
signal_length
filtdegree
passband_ripple
passband_ripple
stopband_ripple
fsamp
w
end
methods
@@ -20,36 +22,34 @@ classdef Filter
%FILTER Construct an instance of this class
% Detailed explanation goes here
arguments
options.filterType = 1;
options.filterType filtertypes = filtertypes.bessel_inp ;
options.f_cutoff = 0;
options.fsamp = 0;
options.filtdegree = 3;
options.passband_ripple = 0.5;
options.stopband_ripple = 0.5;
options.lowpass = 1;
end
obj.filterType = options.filterType;
obj.f_cutoff = options.f_cutoff;
obj.filtdegree = options.filtdegree;
obj.passband_ripple = options.passband_ripple;
obj.stopband_ripple = options.stopband_ripple;
obj.fsamp = options.fsamp;
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
end
function signalclass_out = process(obj,signalclass_in)
function signalclass_out = process(obj,signalclass_in)
% actual processing of the signal
signalclass_in.signal = obj.process_(signalclass_in.signal);
% actual processing of the signal
signalclass_in.signal = obj.process_(signalclass_in.signal);
% append to logbook
% append to logbook
filterdesc = [num2str(obj.filtdegree),'. order ',char(obj.filterType),' filter with f_cutoff at ', num2str(obj.f_cutoff*1e-9), ' GHz.'];
signalclass_in = signalclass_in.logbookentry(filterdesc);
% write to output
% write to output
signalclass_out = signalclass_in;
end
@@ -58,7 +58,7 @@ classdef Filter
obj.signal_length = length(xin);
obj.H = obj.buildFilter(obj.filterType);
[obj.H,obj.w] = obj.buildFilter(obj.filterType);
yout = obj.applyFilter(xin);
@@ -70,8 +70,8 @@ classdef Filter
end
function H = buildFilter(obj,filterType)
function [H,w] = buildFilter(obj,filterType)
w = [];
rp = obj.passband_ripple; %passband ripple
rs = obj.stopband_ripple; %stopband ripple
@@ -143,9 +143,22 @@ classdef Filter
B = wc*sinc(wc*(-g:g));
A=1;
case 10
% Gaussian Filter
%check if order ist multiple of 1/2
blocklen=obj.signal_length;
faxis=linspace(-obj.fsamp/2,obj.fsamp/2,blocklen+1)';%generates arow vector faxis of blocklen+1 points linearly spaced between and including -para.fs/2 and para.fs/2
faxis=ifftshift(faxis(1:end-1));
H=exp(-((faxis)/(obj.f_cutoff*2)).^(2*obj.filtdegree)*log(2)*2^(2*obj.filtdegree-1));
end
H = freqz(B, A, obj.signal_length,'whole');
% Build Filter from coefficients
if filterType ~= 10
[H,w] = freqz(B, A, obj.signal_length,'whole');
end
end
end

View File

@@ -71,7 +71,7 @@ classdef Scope
function signalclass_out = process(obj,signalclass_in)
% apply LPF
lpf = Filter('filtdegree',4,"f_cutoff",obj.lpf_bw,"fsamp",obj.fsimu,"filterType",obj.filtertype);
lpf = Filter('filtdegree',3,"f_cutoff",obj.lpf_bw,"fsamp",obj.fsimu,"filterType",obj.filtertype);
signalclass_in = lpf.process(signalclass_in);
% actual processing of the signal

View File

@@ -91,8 +91,7 @@ classdef Signal
if isa(obj,'Electricalsignal')
%convert to optical
disp("Convert signal: elec. -> opt.!");
o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase);