halfway merged and pulled?!

This commit is contained in:
Silas Labor Zizou
2025-12-15 15:41:02 +01:00
parent 7d0a634b87
commit b8cecae895
145 changed files with 19835 additions and 0 deletions

View File

@@ -0,0 +1,240 @@
classdef DP_Fiber
% Dual-Polarization fiber propagation (CNLSE / Manakov) class version
% Runs full setup + propagation inside process_ (no external loop).
properties (Access=public)
% ---- User options (public) ----
L % [km] fiber length
dz % [m] step size target (kept for compatibility; adaptive dz uses SS_* below)
lambda % [nm] reference wavelength
rng % RNG seed
gamma % [1/W/m] nonlinear coefficient
% Optional / advanced options (match legacy names where possible)
fa % [Hz] sampling frequency
X_alpha % [dB/100km] attenuation per 100 km (per-pol, used for X and Y)
X_beta % [1..4] dispersion coefficients vector for X (Y mirrors X)
D % [ps/(nm*km)]
Ds % [ps/(nm^2*km)] dispersion slope
Dpmd % [ps/sqrt(km)] PMD coefficient
beat_len % [m] beat length (for beta(1) if X_beta is zero)
corr_len % [m] correlation length (not directly used; kept for compatibility)
manakov % 1=Manakov (legacy behavior: manakov=eq-1)
SS_dphimax % [rad] max nonlinear phase per step (adaptive SSFM)
SS_dzmax % [m] max dz (adaptive SSFM)
SS_dzmin % [m] min dz (adaptive SSFM)
n_waveplates % number of PMD waveplates
% ---- Internal state (persistent between calls) ----
state % struct mirroring legacy 'state'
end
methods (Access=public)
function obj = DP_Fiber(options)
% Constructor copies fields from 'options' and sets defaults.
arguments
options.L
options.dz
options.lambda
options.rng = 0
options.gamma
% optional but recommended
options.fa
% legacy-compatible optional params
options.X_alpha = 4.605170185988092e-05 % dB/100km
options.X_beta = [0,0,-2.16826193914149e-26,3.56839456298263e-41]
options.D = 17 % ps/(nm*km)
options.Ds = 0.06 % ps/(nm^2*km)
options.Dpmd = 3 % ps/sqrt(km)
options.beat_len = 50 % m
options.corr_len = 50 % m (kept)
options.manakov = 0 % 1=Manakov
options.SS_dphimax = 5e-3 % rad
options.SS_dzmax = 2e4 % m
options.SS_dzmin = 100 % m
options.n_waveplates = 100
end
% Copy provided options into properties
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
% Initialize empty state; will be built on first process_ call
obj.state = struct();
end
function signalclass_out = process(obj, signalclass_in)
% Public entry point: takes a signal class with .signal (2xN)
% and writes back the propagated signal.
signalclass_in.signal = obj.process_(signalclass_in.signal, signalclass_in.fs);
% logbook (kept as in new framework skeleton)
lbdesc = 'DP_Fiber propagation (CNLSE_plain)';
if ismethod(signalclass_in, 'logbookentry')
signalclass_in = signalclass_in.logbookentry(lbdesc);
end
signalclass_out = signalclass_in;
end
function signal_out = process_(obj, signal_in,fs)
% Core processing builds legacy 'state' and calls CNLSE_plain
% data_in: [2 x N] complex, dual-pol envelope
arguments (Input)
obj
signal_in
fs
end
% ---- Basic checks
if isempty(signal_in) || size(signal_in,2) ~= 2
error('DP_Fiber:Input','Expected data_in of size [2 x N].');
end
if isempty(fs)
error('DP_Fiber:Config','Sampling frequency options.fa is required.');
end
obj.fa = fs;
% ---- RNG (legacy behavior)
R = RandStream("twister","Seed",obj.rng);
% ---- (Re)build state if empty or size-dependent fields changed
need_rebuild = ~isfield(obj.state,'nt') || (obj.state.nt ~= size(signal_in,2));
if need_rebuild
% Constants
c0 = 299792458; % [m/s]
% Legacy state mapping
st = struct();
% High-level
st.L = obj.L * 1000; % [m] legacy expects meters
st.polNames = {'X','Y'};
st.dt = 1/obj.fa;
st.nt = max(size(signal_in));
st.omega = 2*pi*[(0:st.nt/2-1),(-st.nt/2:-1)]/(st.dt*st.nt);
st.lambda = obj.lambda * 1e-9; % [m]
st.D = obj.D * 1e-6; % ps/(nm*km) -> s/(nm*m)
st.Dpmd = obj.Dpmd * 1e-6; % ps/sqrt(km) -> s/sqrt(km)
st.Ds = obj.Ds * 1e3; % ps/(nm^2*km) -> s/(nm^2*m)
st.beat_len = obj.beat_len;
st.SS_dzmax = obj.SS_dzmax;
st.SS_dzmin = obj.SS_dzmin;
st.SS_dphimax= obj.SS_dphimax;
st.chi = 0; % legacy placeholders
st.psi = 0;
st.manakov = obj.manakov; % 1 if eq==2 (Manakov), 0 if eq==1 (CNLSE)
st.wave_plates = obj.n_waveplates;
% Alpha (same X/Y)
st.alpha.X = obj.X_alpha;
st.alpha_lin.X = st.alpha.X/10*log(10)/1000;
st.alpha.Y = st.alpha.X;
st.alpha_lin.Y = st.alpha_lin.X;
% Beta (dispersion) for X (Y mirrors X)
if ~any(obj.X_beta)
% Populate from (beat_len, D, Ds, lambda) like legacy
if obj.beat_len ~= 0
b1 = pi/obj.beat_len;
else
b1 = 0;
end
b2 = 0; % PMD freq term handled elsewhere in legacy
b3 = -(st.lambda.^2/(2*pi*c0))*st.D;
b4 = (st.lambda^2/(2*pi*c0))^2*st.Ds + (2/st.lambda)*(st.lambda.^2/(2*pi*c0)).^2*st.D;
st.beta.X = [b1, b2, b3, b4]; % keep 4 terms, legacy had 0 for beta0; b2 unused
% Note: legacy stored [b0,b1,b2,b3]? Here we mirror their usage.
% We follow their X_beta layout length=4.
else
st.beta.X = obj.X_beta;
end
st.beta.Y = st.beta.X;
% Gamma
st.gamma = obj.gamma;
% PMD / birefringence (legacy waveplate model)
st.corr_length = st.L / st.wave_plates;
% DGD formula (Agrawal 1.1.18)
st.dgd = st.Dpmd * sqrt(st.L/1000); % Dpmd in s/sqrt(km), L in m -> convert: sqrt(m/1000)
% For exact legacy match, they used: state.dgd = Dpmd * sqrt(L) wisth L in meters and Dpmd already scaled.
% Using their pattern:
st.dgd = obj.Dpmd*1e-6 * sqrt(st.L); % match old: state.Dpmd already 1e-6*ps/sqrt(km); they used sqrt(L) with L [m]
brf_multiplier = 1;
if st.Dpmd == 0
brf_multiplier = 0;
end
% Waveplate random parameters
st.pauli_mats.s0 = eye(2);
st.pauli_mats.s2 = [0 1; 1 0];
st.pauli_mats.s3i = [0 1; -1 0];
st.brf.theta = (R.rand(st.wave_plates,1)*pi - 0.5*pi) * brf_multiplier;
st.brf.epsilon = 0.5*asin(R.rand(st.wave_plates,1)*2-1) * brf_multiplier;
st.brf.stokes = NaN(st.wave_plates,3);
st.Ttest = zeros(1, st.wave_plates);
st.brf.matR = cell(st.wave_plates,1);
for n=1:st.wave_plates
matRth = cos(st.brf.theta(n)) * st.pauli_mats.s0 - sin(st.brf.theta(n)) * st.pauli_mats.s3i;
matRepsilon = complex(cos(st.brf.epsilon(n))*st.pauli_mats.s0, sin(st.brf.epsilon(n))*st.pauli_mats.s2);
matR = matRth * matRepsilon;
st.brf.matR{n} = matR;
u1 = matR(1,1);
u2 = matR(1,2);
st.Ttest(n) = abs(u1).^2 + abs(u2).^2;
st.brf.stokes(n,:) = [abs(u1).^2 - abs(u2).^2 , (u1) * conj(u2) + conj(u1) .* u2 , 1i*(u1) * conj(u2) - conj(u1) .* u2];
end
% Frequency-dependent PMD phase term (legacy form)
st.brf.db0 = (R.rand(st.wave_plates,1)*2*pi - pi) * brf_multiplier;
st.brf.db1 = sqrt(3*pi/8)*(st.dgd/obj.fa)/st.wave_plates .* st.omega;
st.brf.simdgd = 0;
% cumsum used in legacy only for debug; keep compatibility variable:
~cumsum(st.brf.db0); % no-op to mirror legacy path
% Bookkeeping
st.missing_dz = 0;
st.n_plates_done = 0;
st.test_plates = [];
st.test_plate_numbers = [];
st.lin_z_test = 0;
st.propagated_length = 0;
% Cache
obj.state = st;
end
% ---- Call the exact same CNLSE_plain as in the old framework
x_in = signal_in(:,1).';
y_in = signal_in(:,2).';
[x_out, y_out, obj.state] = CNLSE_plain(x_in, y_in, obj.state);
obj.state.propagated_length = obj.state.propagated_length + obj.state.L;
signal_out = [x_out; y_out].';
end
end
end

View File

@@ -0,0 +1,149 @@
classdef Optical_Demultiplex < handle
% Dual-Polarization optical demultiplexer
% - Input: total-field signal
% - Output: single-channel dual-pol signal objects in cell array
%
% Notes:
% Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/4,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx);
% Opt_sig_wdm_demux{1}.spectrum("fignum",1100,"displayname",'bla','normalizeTo0dB',0,'max_num_lines',4);
% Opt_sig_wdm_demux{2}.spectrum("fignum",1100,"displayname",'bla','normalizeTo0dB',0,'max_num_lines',4);
properties (Access=public)
fs_in % [Hz] (optional; inferred from data_in.fs if omitted)
fs_out % [Hz]
lambda_center % [nm] center wavelength of the WDM grid
wavelengthplan % [nm]
attenuation = 0 % [dB] insertion loss
filtype = 1 % 1=Gaussian, 2=Rectangle, 3=No filter
B = 200e9 % [Hz] 3 dB bandwidth (Gaussian) or width (Rect)
mgauss = 3 % Gaussian order (multiple of 1/2)
% Derived/utility
c = physconst('lightspeed') % [m/s]
end
methods (Access=public)
function obj = Optical_Demultiplex(options)
arguments
options.fs_in = []
options.fs_out
options.lambda_center
options.wavelengthplan
options.attenuation = 0
options.filtype = 1
options.B = 2.5e10
options.mgauss = 3
end
fn = fieldnames(options);
for n = 1:numel(fn)
try obj.(fn{n}) = options.(fn{n}); end
end
end
function signalclasses_out = process(obj, signalclass_in)
% ---- Infer wavelength: either given or from input total signal
if isempty(obj.wavelengthplan)
obj.wavelengthplan = signalclass_in.lambda; %meter
else
if all(500e-9 < obj.wavelengthplan) && all(obj.wavelengthplan < 1500e-9) %check if given in nm
obj.wavelengthplan = obj.wavelengthplan.*1e-9;
end
end
% ---- Infer input sampling rates
if isempty(obj.fs_in)
assert(isprop(signalclass_in,'fs') && ~isempty(signalclass_in.fs), ...
'Dual_Pol_Demultiplexer: data_in.fs missing and options.fs_in not provided.');
obj.fs_in = signalclass_in.fs;
end
% Runs demultiplexing in one go and appends a logbook entry.
[x_envelopes,y_envelopes] = obj.process_(signalclass_in.signal);
for n = 1:min(size(x_envelopes))
signalclasses_out{n} = signalclass_in;
signalclasses_out{n}.signal = [x_envelopes(:,n), y_envelopes(:,n)];
signalclasses_out{n} = signalclasses_out{n}.resample("fs_in",obj.fs_in,"fs_out",obj.fs_out);
signalclasses_out{n}.lambda = obj.wavelengthplan(n);
lbdesc = ['Opt. Demux ', num2str( obj.wavelengthplan(n)),' nm'];
signalclasses_out{n} = signalclasses_out{n}.logbookentry(lbdesc);
end
end
function [x_envelopes,y_envelopes] = process_(obj, signal_in)
% Core demux:
% - frequency translate target channel to baseband
% - apply optical filter H
% - resample to fs_out
arguments (Input)
obj
signal_in
end
w = obj.fs_out ./ obj.fs_in ;
blocklen_in = length(signal_in);
blocklen_out = w*blocklen_in;
att = 1/10^(obj.attenuation/10);
faxis=linspace( -obj.fs_in/2 , obj.fs_in/2 , blocklen_in+1 );
faxis=ifftshift(faxis(1:end-1));
switch obj.filtype
case 1
H=exp(-(faxis/obj.B).^(2*obj.mgauss)*log(2)*2^(2*obj.mgauss-1)).';
case 2
%all zero filter
H=zeros(1,length(faxis)).';
%set filter = 1 inside bandwidth -B/2 <-> B/2
H(abs(faxis)<=obj.B/2)=1;
case 3
H = 1;
end
f_mid = obj.c/(obj.lambda_center*1e-9); % center frequency of WDM grid [Hz]
f_channels = obj.c./(obj.wavelengthplan) ;
N = numel(f_channels);
df_T = f_mid - f_channels;
pha = mod(-2*pi*(0:blocklen_in-1).'.*df_T/obj.fs_in, 2*pi);
lo = cos(pha)+1i*sin(pha);
% x_envelopes = ifft(fft(att.*signal_in(:,1).*lo).*H);
% y_envelopes = ifft(fft(att.*signal_in(:,2).*lo).*H);
N = size(lo,1);
C = size(lo,2);
x_envelopes = zeros(N, C, 'like', signal_in);
y_envelopes = zeros(N, C, 'like', signal_in);
s1 = signal_in(:,1);
s2 = signal_in(:,2);
% Reusable work buffers (avoid reallocations)
wrk_time = zeros(N,1, 'like', signal_in);
wrk_freq = zeros(N,1, 'like', signal_in);
for c = 1:C
% ---- X branch ----
wrk_time(:) = att .* s1 .* lo(:,c); % N×1
wrk_freq(:) = fft(wrk_time); % N×1
wrk_freq(:) = wrk_freq .* H; % N×1
x_envelopes(:,c) = ifft(wrk_freq); % N×1
% ---- Y branch ----
wrk_time(:) = att .* s2 .* lo(:,c);
wrk_freq(:) = fft(wrk_time);
wrk_freq(:) = wrk_freq .* H;
y_envelopes(:,c) = ifft(wrk_freq);
end
end
end
end

View File

@@ -0,0 +1,171 @@
classdef Optical_Multiplex < handle
% Takes a cell array of signals
% returns a total field signal
% WDM spacing is given in wavelength plan OR via delta_F
% The grid is stored in the output signal -> the demux will ideally
% look this up and use this as the demux frequencies...
% signal_cell = {Opt_sig_1, Opt_sig_2};
% Opt_sig_wdm = Optical_Multiplex("fs_in",Opt_sig.fs,"fs_out",4*Opt_sig.fs,...
% "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9,"delta_f",400e9).process(signal_cell);
properties(Access=public)
fs_in
fs_out
lambda_center
delta_f
random_key
attenuation
B
mgauss
filtype
c = physconst('lightspeed')
f_center
f_T
lambda_T
df_T
end
methods (Access=public)
function obj = Optical_Multiplex(options)
%NAME Construct an instance of this class
% Detailed explanation goes here
arguments
options.fs_in
options.fs_out
options.lambda_center
options.B = 200e9
options.mgauss = 3
options.filtype = 2
options.delta_f = 0
options.random_key
options.attenuation = 0;
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
end
function signalclass_out = process(obj,signalclasses_in)
% actual processing of the signal (steps 1. - 3.)
signalclass_out = obj.process_(signalclasses_in);
% append to logbook
lbdesc = ['Opt. Mux. '];
signalclass_out = signalclass_out.logbookentry(lbdesc);
end
function data_out = process_(obj,data_in)
%METHOD1 Summary of this method goes here
% Detailed explanation goes here
arguments(Input)
obj
data_in cell
end
% assert(data_in{1}.fs == obj.fs_in,'Sampling rate');
att = 1/10^(obj.attenuation/10);
N = numel(data_in);
w = obj.fs_out/data_in{1}.fs;
blocklen_in = length(data_in{1});
blocklen_out = w*blocklen_in;
freqaxis = linspace(-obj.fs_out/2, obj.fs_out/2, blocklen_out+1);
obj.f_center = obj.c/(obj.lambda_center.*1e-9);
if obj.random_key ~= 0
res = freqaxis(2)-freqaxis(1);
R = RandStream("twister","Seed",obj.random_key);
laser_frequency_imperfection = res .* round(R.randn(N,1)*10); %in mutliples of the fft resolution, i.e. the distance between two freq. bins
else
laser_frequency_imperfection = zeros(blocklen_in,1);
end
obj.f_T = [];
obj.df_T = [];
polrots = [];
for o = 1:N
if obj.delta_f ~= 0
% user defined a channel spacing in GHz. Build plan
% left and right from zero
obj.df_T(o) = (-length(data_in)/2-0.5+o) .* obj.delta_f;
obj.df_T(o) = obj.df_T(o)+ laser_frequency_imperfection(o);
obj.f_T = [obj.f_T obj.f_center+obj.df_T(o)];
else
%center frequencies of channels
obj.f_T = [obj.f_T obj.c/(data_in{o}.lambda)];
obj.lambda_T = [obj.lambda_T data_in{o}.lambda];
%difference between mid frequency of MUX and channels
obj.df_T = [obj.df_T obj.f_center - obj.f_T(o)];
end
% adapt frequency shifts to match the FFT grid! Find nearest grid point
[glitch(o),pos] = min(abs( freqaxis-obj.df_T(o) ));
obj.df_T(o) = freqaxis(pos);
polrots = [polrots, data_in{o}.polrot];
end
obj.lambda_T = obj.c ./ (obj.f_center-obj.df_T);
obj.B = 200e9; %200GHz
faxis = linspace(-obj.fs_out/2,obj.fs_out/2, blocklen_out+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));
switch obj.filtype
case 1
H =exp(-(faxis/obj.B).^(2*obj.mgauss)*log(2)*2^(2*obj.mgauss-1)).';
case 2
H=zeros(length(faxis),1);
H(find(abs(faxis)<=obj.B/2))=1;
case 3
H = 1;
end
x_envelopes = NaN([blocklen_out N]);
y_envelopes = x_envelopes;
for o = 1:N
pha = mod(2*pi*(0:blocklen_out-1)*obj.df_T(o)/obj.fs_out,2*pi).';
lo = cos(pha)+1i*sin(pha);
data_in_resampled = data_in{o}.resample("fs_out",obj.fs_out);
res_env = ifft(fft(data_in_resampled.signal(:,1)).*H);
x_envelopes(:,o) = att.*res_env.*lo;
res_env = ifft(fft(data_in_resampled.signal(:,2)).*H);
y_envelopes(:,o) = att.*res_env.*lo;
end
data_out = data_in_resampled;
data_out.signal = [sum(x_envelopes,2), sum(y_envelopes,2)];
data_out.lambda = obj.lambda_T;
data_out.polrot = polrots;
end
end
methods (Access=private)
% Cant be seen from outside! So put all your functions here that can/
% shall not be called from outside
end
end

View File

@@ -0,0 +1,92 @@
classdef Polarization_Controller
%Input can be "normal" - output will be DP!
properties(Access=public)
mode
desired_angle
desired_power
rotation_angle
rotation_matrix
end
methods (Access=public)
function obj = Polarization_Controller(options)
%NAME Construct an instance of this class
% Detailed explanation goes here
arguments
options.mode polarization_control_mode = polarization_control_mode.rot_power
options.desired_angle
options.desired_power
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
% do more stuff
end
function signalclass_out = process(obj,signalclass_in)
% actual processing of the signal (steps 1. - 3.)
[signalclass_in.signal,signalclass_in.polrot] = obj.process_(signalclass_in.signal,signalclass_in.polrot);
% append to logbook
lbdesc = ['Logbookentry'];
signalclass_in = signalclass_in.logbookentry(lbdesc);
% write to output
signalclass_out = signalclass_in;
end
function [data_out, polrot_out] = process_(obj,data_in,polrot_in)
% Rotate polarization of am opt signal
arguments(Input)
obj
data_in double
polrot_in double
end
if obj.mode ~= polarization_control_mode.deactivate
switch obj.mode
case polarization_control_mode.random
obj.rotation_angle = 2*pi*rand ;
case polarization_control_mode.rot_angle
obj.rotation_angle = obj.desired_angle*pi/180 ;
case polarization_control_mode.rot_power
obj.rotation_angle = -polrot_in + acos(sqrt(obj.desired_power/100)) ;
end
obj.rotation_matrix = [cos(obj.rotation_angle) -sin(obj.rotation_angle) ; sin(obj.rotation_angle) cos(obj.rotation_angle)].' ;
if min(size(data_in)) == 1
data_in = reshape(data_in,[],1);
data_in = [data_in, zeros(length(data_in),1)];
end
data_out = data_in * obj.rotation_matrix;
polrot_out = polrot_in + obj.rotation_angle ;
end
end
end
end

View File

@@ -0,0 +1,135 @@
function [opt_out_struct,state] = CNLSE(opt_in_struct,state)
% init transfer functions h.X and h.Y
h = struct('X',0,'Y',0);
state.common_beta=struct('X',0,'Y',0);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pre calculations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% calculate transfer function and rotate coordines for both
% polarizations
for n=1:2
% get current polarization name and contrary one
curPol = state.polNames{n};
% extend linear transfer function depending on beta values for the
% current polarization
for n_beta = 1:length(state.beta.(curPol))
% h.(curPol) = h.(curPol) - 1j*state.beta.(curPol)(n_beta)*(state.omega).^(n_beta-1)/factorial(n_beta-1);
% if n_beta ~= 2
state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
% end
end
opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
end
state.h=h;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Splitstep method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% state.SS_dzs = zeros(1,state.max_nonlin_its);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Split Step Method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% get nonlinear step size
[state.dz] = getNLstepsize(state,opt_out_struct);
state.n_step = 0;
state.z_prop = 0;
state.test_dz = [];
state.powers = [];
while state.z_prop < state.L
if state.z_prop + state.dz > state.L
state.dz = state.L - state.z_prop;
end
% lin conv
% opt_out_struct = [ opt_out_struct 0 0 0 0 0 ];
% opt_out_struct
%%%%%%%%%%%%%
% STEP
% update step number
state.n_step=state.n_step+1;
state.dzs(state.n_step)=state.dz;
% half linear step
[opt_out_struct,state] = lin_step(state,opt_out_struct,state.dz/2);
% complete nonlinear step
[opt_out_struct,state] = nl_step(state,opt_out_struct,state.dz);
% half linear step
[opt_out_struct,state] = lin_step(state,opt_out_struct,state.dz/2);
%%%%%%%%%%%%%
% prepare next STEP
% overlap(n_step+1,:) = opt_out_struct(M+1:end);
% opt_out_struct = opt_out_struct(1:M);
% get nonlinear step size
[state.dz] = getNLstepsize(state,opt_out_struct);
end
% figure(88);clf;subplot(2,1,1);stem(state.test_plates);subplot(2,1,1); hold all;stem(-1000*state.test_plate_numbers);subplot(2,1,2);stem(state.dzs)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Post Calculations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% opt_out_struct.X.envelope = ( cos(state.psi)*cos(state.chi) + 1j*sin(state.psi)*sin(state.chi))*opt_out_struct.X + ...
% (-sin(state.psi)*cos(state.chi) - 1j*cos(state.psi)*sin(state.chi))*opt_out_struct.Y;
%
buffer.X.envelope = opt_out_struct.X;
buffer.X.type = opt_in_struct.X.type;
buffer.X.wavelength = opt_in_struct.X.wavelength;
if isfield(buffer.X,'Nase')
buffer.X.Nase = opt_in_struct.X.Nase;
else
buffer.X.Nase = 0;
end
opt_out_struct.X =[];
opt_out_struct.X.envelope = buffer.X.envelope;
opt_out_struct.X.type = buffer.X.type;
opt_out_struct.X.wavelength = buffer.X.wavelength;
opt_out_struct.X.Nase = buffer.X.Nase;
%
% opt_out_struct.Y.envelope = ( sin(state.psi)*cos(state.chi) - 1j*cos(state.psi)*sin(state.chi))*opt_out_struct.X.envelope + ...
% ( cos(state.psi)*cos(state.chi) - 1j*sin(state.psi)*sin(state.chi))*opt_out_struct.Y;
%
buffer.Y.envelope = opt_out_struct.Y;
buffer.Y.type = opt_in_struct.Y.type;
buffer.Y.wavelength = opt_in_struct.Y.wavelength;
if isfield(buffer.Y,'Nase')
buffer.Y.Nase = opt_in_struct.Y.Nase;
else
buffer.Y.Nase = 0;
end
opt_out_struct.Y =[];
opt_out_struct.Y.envelope = buffer.Y.envelope;
opt_out_struct.Y.type = buffer.Y.type;
opt_out_struct.Y.wavelength = buffer.Y.wavelength;
opt_out_struct.Y.Nase = buffer.Y.Nase;
% figure(100+loop);plot([real(opt_out_struct.X.envelope);real(opt_out_struct.Y.envelope)].');
end

View File

@@ -0,0 +1,120 @@
function [opt_out_x,opt_out_y,state] = CNLSE_plain(opt_in_x,opt_in_y,state)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pre calculations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
state.common_beta=struct('X',0,'Y',0);
for n=1:2
% get current polarization name and contrary one
curPol = state.polNames{n};
% extend linear transfer function depending on beta values for the current polarization
% Was ist der Sinn dieser komischen beta notation? zB. state.beta.X = [0.3142 0 -9.1105e-28 5.1068e-41]
for n_beta = 1:length(state.beta.(curPol))
state.common_beta.(curPol) = state.common_beta.(curPol) + state.beta.(curPol)(n_beta) * (state.omega).^(n_beta-1) / factorial(n_beta-1);
end
%opt_out_struct.(curPol)=opt_in_struct.(curPol).envelope;
end
beta_const = state.beta.('X')(1);
beta_1 = state.beta.('X')(2);
beta_2 = state.beta.('X')(3);
beta_3 = state.beta.('X')(4);
deltaomega = state.omega;
beta_x = beta_const + beta_1 * deltaomega + 1/2 * beta_2 * deltaomega.^2 + 1/6 *beta_3 * deltaomega.^3;
% opt_in_x = gpuArray(opt_in_x);
% opt_in_y = gpuArray(opt_in_y);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Split Step Method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% [opt_out_x,opt_out_y] = split_step_loop(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
% state.chi,state.manakov,state.beat_len);
% [opt_out_x,opt_out_y] = split_step_loop_mex(state.L,opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin,...
% state.lin_z_test,state.corr_length,state.n_plates_done,state.missing_dz,state.brf,state.common_beta,....
% state.chi,state.manakov,state.beat_len);
% get nonlinear step size
[state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
%[state.dz] = getNLstepsize_original(state,opt_out_struct);
state.n_step = 0;
state.z_prop = 0;
state.test_dz = [];
state.powers = [];
tic
while state.z_prop < state.L
% reduce step length (dz) if we are to overshoot the fiber length
% (L) in the next step
if state.z_prop + state.dz > state.L
state.dz = state.L - state.z_prop;
end
% update step number (n)
state.n_step=state.n_step+1;
% append current step length to logbook (dzs)
state.dzs(state.n_step)=state.dz;
% half linear step
[opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
= lin_step(...
opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.dz/2,state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
% complete nonlinear step
[opt_in_x,opt_in_y] = nl_step(opt_in_x,opt_in_y, state.dz, state.gamma, state.chi, state.manakov, state.beat_len ,state.alpha_lin.X, state.alpha_lin.Y);
% half linear step
[opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X]...
= lin_step...
(opt_in_x,opt_in_y,state.z_prop,state.lin_z_test,...
state.dz/2,state.corr_length,state.n_plates_done,state.missing_dz,state.n_step,...
state.test_plates,state.test_plate_numbers,state.brf,state.common_beta.X,...
state.common_beta.Y,state.alpha_lin.X,state.alpha_lin.X);
% get nonlinear step size
[state.dz] = getNLstepsize(opt_in_x,opt_in_y,state.gamma,state.SS_dzmin,state.SS_dzmax,state.SS_dphimax,state.alpha_lin);
%[state.dz] = getNLstepsize_original(state,opt_out_struct);
end
toc
opt_out_x = (opt_in_x);
opt_out_y = (opt_in_y);
% opt_out_x = gather(opt_in_x);
% opt_out_y = gather(opt_in_y);
end

View File

@@ -0,0 +1,25 @@
function [rDZ] = getNLstepsize(ux,uy,gamma,dzmin,dzmax,dphimax,alpha_lin)
maxPow = max(gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2));
Leff = dphimax/maxPow;
alpha_lin = max([alpha_lin.X alpha_lin.Y]);
nl_att_len_ratio = alpha_lin*Leff;
if nl_att_len_ratio >= 1
rDZ = dzmax;
else
if alpha_lin == 0
step = Leff;
else
%effective length?
step = -1/alpha_lin*log(1-nl_att_len_ratio);
end
rDZ = min([step dzmax]);
rDZ = max([rDZ dzmin]);
end
end

View File

@@ -0,0 +1,27 @@
function [rDZ] = getNLstepsize_original(state,aOpt)
ux = aOpt.X;
uy = aOpt.Y;
maxPow = max(state.gamma.*max(real(ux).^2+imag(ux).^2+real(uy).^2+imag(uy).^2));
Leff = state.SS_dphimax/maxPow;
alpha_lin = max([state.alpha_lin.X state.alpha_lin.Y]);
nl_att_len_ratio = alpha_lin*Leff;
if nl_att_len_ratio >= 1
rDZ = state.SS_dzmax;
else
if alpha_lin == 0
step = Leff;
else
%effective length?
step = -1/alpha_lin*log(1-nl_att_len_ratio);
end
rDZ = min([step state.SS_dzmax]);
rDZ = max([rDZ state.SS_dzmin]);
end
end

View File

@@ -0,0 +1,125 @@
%function [rOpt,state] = lin_step(state,aOpt,aStepSize)
function [rOpt_x,rOpt_y,z_prop,lin_z_test,...
corr_length,n_plates_done,missing_dz,n_step,test_plates,...
test_plate_numbers,brf,common_beta_x,common_beta_y,alpha_lin_x,alpha_lin_y]...
= lin_step(...
opt_x,opt_y,z_prop,lin_z_test,aStepSize,corr_length,...
n_plates_done,missing_dz,n_step,test_plates,test_plate_numbers,...
brf,common_beta_x,common_beta_y,alpha_lin_x,alpha_lin_y)
%%%%%% 1) Update and Check Distances etc. %%%%%%
% update propgated distance z_prop
z_prop = z_prop + aStepSize;
% calculate the number of plates needed for the so far propagated fiber length
n_plates = ceil(z_prop/corr_length);
% subtract the number of plates which were already processed
n_plates_left = n_plates - n_plates_done;
% compute last plate size ( if it fits, it should be 0)
if missing_dz > aStepSize
last_plate = aStepSize;
missing_dz = missing_dz-aStepSize;
plate_sizes = last_plate;
plate_numbers = n_plates;
else
last_plate = aStepSize - missing_dz - (n_plates_left-1)*corr_length;
if missing_dz == 0
missing_dz = [];
end
%build vector of plate lengths with missing plate part from prev.
%iterartion , then some normal plates and finally a fraction of a plate
%to fit into the step length
plate_sizes = [missing_dz corr_length*ones(1,n_plates_left-1) last_plate];
if n_plates_done == 0
plate_numbers =[(n_plates_done+1):(n_plates-1) n_plates];
else
plate_numbers = [n_plates_done (n_plates_done+1):(n_plates-1) n_plates]; % not wrking yet
end
%remember for next step
missing_dz = corr_length - last_plate;
end
plate_steps = repmat(n_step,1,length(plate_sizes));
%
%figure;stem(plate_sizes);
test_plates = [test_plates,plate_sizes];
test_plate_numbers = [test_plate_numbers, plate_numbers];
%%%%%% 2) Apply Waveplate Model %%%%%%
% transfer optical envelope to frequency domain for effective convolution with transfer function h
opt_x=fft(opt_x);
opt_y=fft(opt_y);
% db1 = gpuArray(brf.db1);
% db0 = gpuArray(brf.db0);
% common_beta_x = gpuArray(common_beta_x);
% common_beta_y = gpuArray(common_beta_y);
db1 = (brf.db1);
db0 = (brf.db0);
common_beta_x = (common_beta_x);
common_beta_y = (common_beta_y);
% process every waveplate with given sizes in plate_sizes
for n=1:length(plate_sizes)
dz = plate_sizes(n);
% figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150)));
% MOV1=[MOV1 getframe(87)];
% extract rotation matrix from pre calculated matrices
matR = brf.matR{plate_numbers(n)};
% transform to eigenvalue of of fiber segment
tOpt.X = conj(matR(1,1))*opt_x + conj(matR(2,1))*opt_y;
tOpt.Y = conj(matR(1,2))*opt_x + conj(matR(2,2))*opt_y;
% calculate statistical delta beta for pmd
delta_beta = 0.5*(db1+db0(n))/corr_length;
% build transfer function with delta beta
%common.beta = beta1+beta2*omega^2
%accumulate delta beta for log...
brf.simdgd = brf.simdgd + (db1(length(db1)/2+1)+db0(n))/corr_length;
h.X = exp(-1j*(common_beta_x-delta_beta)*dz);
h.Y = exp(-1j*(common_beta_y+delta_beta)*dz);
% delta_beta has to be added to the transfer function
% process with transfer function
tOpt.X = h.X.*tOpt.X ;
tOpt.Y = h.Y.*tOpt.Y ;
% rotate back
opt_x = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y;
opt_y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y;
end
lin_z_test = lin_z_test + sum(plate_sizes,2);
%update the number of processed plates so far
n_plates_done = n_plates_done + n_plates_left;
% attanuate the signal each linear state with alpha
% ( 0.2dB = 4.6052e-05 )
rOpt_x=ifft(exp(-alpha_lin_x*aStepSize/2).*opt_x); % /2 not sure why (have to find it in formulas)
rOpt_y=ifft(exp(-alpha_lin_y*aStepSize/2).*opt_y); % but not relevant for now
end

View File

@@ -0,0 +1,106 @@
function [rOpt,state] = lin_step_original(state,aOpt,aStepSize)
% update propgated distance z_prop
state.z_prop = state.z_prop + aStepSize;
% if state.synchronous_plates % not waveplate model (just rotation with dz)
% state.plate_sizes = aStepSize;
%
% else
% calculate the number of plates needed for the so far propagated fiber
% length
state.n_plates = ceil(state.z_prop/state.corr_length);
% subtract the number of plates which were already be processed
state.n_plates_left = state.n_plates - state.n_plates_done;
% compute last plate size ( if it fits, it should be 0)
if state.missing_dz > aStepSize
state.last_plate = aStepSize;
state.missing_dz = state.missing_dz-aStepSize;
state.plate_sizes = state.last_plate;
state.plate_numbers = state.n_plates;
else
state.last_plate = aStepSize - state.missing_dz - (state.n_plates_left-1)*state.corr_length;
if state.missing_dz == 0
state.missing_dz = [];
end
state.plate_sizes = [state.missing_dz state.corr_length*ones(1,state.n_plates_left-1) state.last_plate];
if state.n_plates_done == 0
state.plate_numbers =[(state.n_plates_done+1):(state.n_plates-1) state.n_plates];
else
state.plate_numbers = [state.n_plates_done (state.n_plates_done+1):(state.n_plates-1) state.n_plates]; % not wrking yet
end
state.missing_dz = state.corr_length - state.last_plate;
end
state.plate_steps = repmat(state.n_step,1,length(state.plate_sizes));
% figure;stem(state.plate_sizes);
state.test_plates = [state.test_plates,state.plate_sizes];
state.test_plate_numbers = [state.test_plate_numbers, state.plate_numbers];
% end
% transfer optical envelope to frequency domain for effective
% convolution with transfer function h
aOpt.X=fft(aOpt.X);
aOpt.Y=fft(aOpt.Y);
% process every waveplate with given sizes in state.plate_sizes
for n=1:length(state.plate_sizes)
dz = state.plate_sizes(n);
% figure(87);subplot(2,1,1);plot(real(x(900:1150)));subplot(2,1,2);plot(real(y(900:1150)));
% state.MOV1=[state.MOV1 getframe(87)];
% extract rotation matrix from pre calculated matrices
matR = state.brf.matR{state.plate_numbers(n)};
% transform to eigenvalue of of fiber segment
tOpt.X = conj(matR(1,1))*aOpt.X + conj(matR(2,1))*aOpt.Y;
tOpt.Y = conj(matR(1,2))*aOpt.X + conj(matR(2,2))*aOpt.Y;
% calculate statistical delta beta for pmd
delta_beta = 0.5*(state.brf.db1+state.brf.db0(n))/state.corr_length;
% db1 = sqrt(3*pi/8)*(para.dgd/para.fa)/state.wave_plates.*state.omega;
% delta_beta = 0.5*(state.brf.db0(n))/state.corr_length;
% build transfer function with delta beta
% common.beta = beta1+beta2*omega^2
% delta beta
h.X = exp(-1j*(state.common_beta.X-delta_beta)*dz);
h.Y = exp(-1j*(state.common_beta.Y+delta_beta)*dz);
% delta_beta has to be added to the transfer function
% process with transfer function
tOpt.X = h.X.*tOpt.X ;
tOpt.Y = h.Y.*tOpt.Y ;
% rotate back
aOpt.X = matR(1,1)*tOpt.X + matR(1,2)*tOpt.Y;
aOpt.Y = matR(2,1)*tOpt.X + matR(2,2)*tOpt.Y;
end
state.lin_z_test = state.lin_z_test + sum(state.plate_sizes,2);
%update the number of processed plates so far
state.n_plates_done = state.n_plates_done + state.n_plates_left;
% attanuate the signal each linear state with alpha
% ( 0.2dB = 4.6052e-05 )
rOpt.X=ifft(exp(-state.alpha_lin.X*aStepSize/2).*aOpt.X); % /2 not sure why (have to find it in formulas)
rOpt.Y=ifft(exp(-state.alpha_lin.Y*aStepSize/2).*aOpt.Y); % but not relevant for now
end

View File

@@ -0,0 +1,39 @@
%function [rOpt,state] = nl_step(state,aOpt,aDz)
function [rOpt_x,rOpt_y] = nl_step(opt_x,opt_y, dz, gamma, chi, use_manakov, beatlength, alpha_lin_x, alpha_lin_y)
if ~use_manakov % CNLSE
rOpt_x = opt_x .* exp( (-1j*(1/3)*gamma*dz).* ...
( (2 + cos(2*chi)^2)*(abs(opt_x).^2) + ...
(2+2*sin(2*chi)^2)*(abs(opt_y).^2) ) );
rOpt_y = opt_y .* exp( (-1j*(1/3)*gamma*dz).* ...
( (2 + cos(2*chi)^2)*(abs(opt_y).^2) + ...
(2+2*sin(2*chi)^2)*(abs(opt_x).^2) ) );
% A_x = opt_x;
% A_y = opt_y;
%
% rOpt_x = 1i* gamma * (abs(A_x).^2 + (2/3 .* abs(A_y).^2) ) .* A_x + ((1i * gamma / 3) * conj(A_x).*(A_y.^2) * exp(-2i * dz * 2*pi / beatlength ));
% rOpt_y = 1i* gamma * (abs(A_y).^2 + (2/3 .* abs(A_x).^2) ) .* A_y + ((1i * gamma / 3) * conj(A_y).*(A_x.^2) * exp(-2i * dz * 2*pi / beatlength ));
else
% estimate effective length of dz (ref?)
if (alpha_lin_x == 0) && (alpha_lin_y == 0)
Leff = dz;
else
Leff = (1-exp(-alpha_lin_x*dz))/alpha_lin_x;
end
%compute power
power = real(opt_x).^2+imag(opt_x).^2+real(opt_y).^2+imag(opt_y).^2;
% power= abs(opt_x).^2+abs(opt_y).^2;
% powers = [powers;power];
Hnl = exp( -1j*8/9*gamma*power*Leff);
rOpt_x = opt_x .* Hnl;
rOpt_y = opt_y .* Hnl;
end
end

View File

@@ -0,0 +1,30 @@
function [rOpt,state] = nl_step(state,aOpt,aDz)
if ~state.manakov % CNLSE
rOpt.X = aOpt.X .* exp( (-1j*(1/3)*state.gamma*aDz).* ...
( (2 + cos(2*state.chi)^2)*(abs(aOpt.X).^2) + ...
(2+2*sin(2*state.chi)^2)*(abs(aOpt.Y).^2) ) );
rOpt.Y = aOpt.Y .* exp( (-1j*(1/3)*state.gamma*aDz).* ...
( (2 + cos(2*state.chi)^2)*(abs(aOpt.Y).^2) + ...
(2+2*sin(2*state.chi)^2)*(abs(aOpt.X).^2) ) );
else
% estimate effective length of dz (ref?)
if (state.alpha_lin.X == 0) && (state.alpha_lin.Y == 0)
Leff = aDz;
else
Leff = (1-exp(-state.alpha_lin.X*aDz))/state.alpha_lin.X;
end
%compute power
power = real(aOpt.X).^2+imag(aOpt.X).^2+real(aOpt.Y).^2+imag(aOpt.Y).^2;
% power= abs(aOpt.X).^2+abs(aOpt.Y).^2;
% state.powers = [state.powers;power];
Hnl = exp( -1j*8/9*state.gamma*power*Leff);
rOpt.X = aOpt.X .* Hnl;
rOpt.Y = aOpt.Y .* Hnl;
end
end

View File

@@ -0,0 +1,93 @@
function [opt_x,opt_y] = split_step_loop(L,opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin,...
lin_z_test,corr_length,n_plates_done,missing_dz,brf,common_beta,...
chi,manakov,beat_len)
%SPLIT_STEP_LOOP Summary of this function goes here
% Detailed explanation goes here
%Optical Input
% opt_x;
% opt_y;
%
% %required for loop condition
% z_prop = 0;
% L;
%
% %required for NLstepsize
% gamma;
% SS_dzmin;
% SS_dzmax;
% SS_dphimax;
% alpha_lin;
%
% %required for lin_step
% z_prop;
% lin_z_test;
% corr_length;
% n_plates_done;
% missing_dz;
% n_step = 0;
% brf;
% common_beta.X;
% common_beta.Y;
% alpha_lin.X;
% alpha_lin.X;
%
% %required fr nonlin step
% chi;
% manakov;
% beat_len ;
% alpha_lin.X;
% alpha_lin.Y;
% get nonlinear step size
[dz] = getNLstepsize(opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin);
n_step = 0;
z_prop = 0;
while z_prop < L
% reduce step length (dz) if we are to overshoot the fiber length
% (L) in the next step
if z_prop + dz > L
dz = L - z_prop;
end
% update step number (n)
n_step=n_step+1;
% half linear step
[opt_x,opt_y,z_prop,lin_z_test,...
corr_length,n_plates_done,missing_dz,n_step,...
brf,common_beta.X,...
common_beta.Y,alpha_lin.X,alpha_lin.X]...
= lin_step(...
opt_x,opt_y,z_prop,lin_z_test,...
dz/2,corr_length,n_plates_done,missing_dz,n_step,...
brf,common_beta.X,...
common_beta.Y,alpha_lin.X,alpha_lin.X);
% complete nonlinear step
[opt_x,opt_y] = nl_step(opt_x,opt_y, dz, gamma, chi, manakov, beat_len ,alpha_lin.X, alpha_lin.Y);
% half linear step
[opt_x,opt_y,z_prop,lin_z_test,...
corr_length,n_plates_done,missing_dz,n_step,...
brf,common_beta.X,...
common_beta.Y,alpha_lin.X,alpha_lin.X]...
= lin_step...
(opt_x,opt_y,z_prop,lin_z_test,...
dz/2,corr_length,n_plates_done,missing_dz,n_step,...
brf,common_beta.X,...
common_beta.Y,alpha_lin.X,alpha_lin.X);
% get nonlinear step size
[dz] = getNLstepsize(opt_x,opt_y,gamma,SS_dzmin,SS_dzmax,SS_dphimax,alpha_lin);
end
end

View File

@@ -0,0 +1,351 @@
classdef FFE_DCremoval_adaptive_mu < handle
% Implementation of plain and simple FFE.
% 1) Training mode (stable performance when you use NLMS)
% 2) Decision directed mode
% Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0);
properties
sps % usually 2
order
e
e_tr
error
len_tr
mu_tr
epochs_tr
mu_dd
epochs_dd
mu_dc
dc_buffer_len
adaptive_mu_mode
ffe_buffer_len
smoothing_buffer_length
smoothing_buffer_update
constellation
decide
end
methods
function obj = FFE_DCremoval_adaptive_mu(options)
arguments(Input)
options.sps = 2;
options.order = 15;
options.len_tr = 4096;
options.mu_tr = 0;
options.epochs_tr = 5;
options.mu_dd = 1e-5;
options.epochs_dd = 5;
options.mu_dc = 0.05;
options.dc_buffer_len = 1;
options.ffe_buffer_len = 1;
options.adaptive_mu_mode = 1;
options.smoothing_buffer_length = 0;
options.smoothing_buffer_update = 0;
options.decide = false;
end
assert(options.dc_buffer_len>0);
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
obj.e = zeros(obj.order,1);
obj.error = 0;
obj.dc_buffer_len = floor(obj.dc_buffer_len);
end
function [X,Noi] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
X = X.normalize("mode","rms");
obj.constellation = unique(D.signal);
% if obj.smoothing_buffer_length > 0
% % Apply A1 filter smoothing
% % Calculate the moving sum with the window size N1
% moving_sum = movsum(X.signal, [obj.smoothing_buffer_length,0]);
%
% % Initialize the output smoothed signal
% X.signal = X.signal - (1 / obj.smoothing_buffer_length) * moving_sum;
% end
% Training Mode
training = 1;
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training);
obj.e_tr = obj.e;
% Decision Directed Mode
N = X.length;
training = 0;
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training);
% Output Signal
if obj.decide
X.signal = decision;
else
X.signal = signal;
end
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
lbdesc = [num2str(obj.order),' tap FFE'];
X = X.logbookentry(lbdesc); % append to logbook
Noi = X - D;
end
function [y,d_hat] = equalize(obj, x, d, mu_lms, epochs, N, training)
% Equalize with adaptive DC-removal, VSS, and parallel-buffered DC updates
% Added: FFE gradient buffering in DD mode (error buffer) with update every obj.dc_buffer_len symbols
arguments
obj
x
d
mu_lms % LMS step-size (or 0 for NLMS)
epochs % number of training/DD epochs
N % number of samples to process
training % boolean flag: true->training mode, false->DD mode
end
if isempty(obj.e)
obj.e = zeros(obj.order,1);
end
% Zero-padding for filter memory
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
% Initialize storage
numSymbols = ceil(N/obj.sps);
y = zeros(numSymbols,1);
d_hat = zeros(numSymbols,1);
err = NaN(numSymbols,numel(obj.constellation));
e_dc_save= zeros(numSymbols,1);
% DC-adaptation parameters
P_err = 0; % running error power
alpha = 0.98; % forgetting factor for error power
err_prev = 0; % previous error sample for VSS correlation
gamma_dc = 1e-6; % meta step-size for DC VSS
mu_min = 1e-6; % lower bound for mu_dc
mu_max = 3e-1; % upper bound for mu_dc
% DC removal buffer
L = obj.dc_buffer_len; % buffer length
e_dc_buf = NaN(L,1);
e_dc_est = 0;
% FFE gradient buffer (DD mode only)
L_grad = obj.ffe_buffer_len; % buffer length
if ~training
% each column holds one past gradient of length obj.order
grad_buf = NaN(obj.order, L_grad);
end
smth_buffer = zeros(1, obj.smoothing_buffer_length);
smth_mean = 0;
% Main loop
for epoch = 1:epochs
s = 0;
for sample = 1:obj.sps:N
s = s + 1;
if obj.smoothing_buffer_length > 0
smth_buffer = circshift(smth_buffer,1,2);
smth_buffer(1) = x(sample);
if mod(s, obj.smoothing_buffer_update) == 0
smth_mean = mean(smth_buffer);
end
x(sample:sample+obj.sps-1) = x(sample:sample+obj.sps-1)-smth_mean;
end
U = x(obj.order+sample-1:-1:sample);
%-- 1) filter output with DC correction
y(s) = e_dc_est + obj.e.'*U;
%-- 2) decision
if training
[~, idx] = min(abs(d(s) - obj.constellation));
else
[~, idx] = min(abs(y(s) - obj.constellation));
end
d_hat(s) = obj.constellation(idx);
%-- 3) error
e_val = y(s) - d_hat(s);
if epoch == epochs
err(s,idx) = e_val;
true_err(s,idx) = y(s) - d(s);
end
%-- 4) tap-weight update: training immediate, DD buffered
if training
% immediate update (LMS or NLMS)
if mu_lms ~= 0
obj.e = obj.e - mu_lms * e_val * U;
else
normU = (U.'*U) + eps;
obj.e = obj.e - e_val * U / normU;
end
else
if 0
% buffer gradient
if mu_lms ~= 0
grad = e_val * U;
else
normU = (U.'*U) + eps;
grad = e_val * U / normU;
end
% shift and insert
grad_buf = circshift(grad_buf, 1, 2);
grad_buf(:,1) = grad;
% update once every L symbols
if mod(s, L_grad) == 0
avg_grad = mean(grad_buf, 2, 'omitnan');
if mu_lms ~= 0
obj.e = obj.e - mu_lms * avg_grad;
else
obj.e = obj.e - avg_grad;
end
end
end
end
%-- 5) DC adaptation
if obj.mu_dc ~= 0
if obj.adaptive_mu_mode
% VSS for mu_dc
delta_mu = gamma_dc * e_val * err_prev * (U.'*U);
obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max);
err_prev = e_val;
% DC buffer update & periodic estimate
P_err = alpha*P_err + (1-alpha)*e_val^2;
mu_dc_norm = obj.mu_dc / (P_err + eps);
else
% DC buffer update & periodic estimate
% P_err = alpha*P_err + (1-alpha)*e_val^2;
% mu_dc_norm = obj.mu_dc / (P_err + eps);
mu_dc_norm = obj.mu_dc;
end
e_dc_buf = circshift(e_dc_buf, 1);
e_dc_buf(1) = e_dc_est - mu_dc_norm * e_val;
if mod(s, L) == 0
e_dc_est = median(e_dc_buf, 'omitnan');
end
P_err_save(s) = P_err;
% Pcorr_save(s) = e_val * err_prev;
Ucorr_save(s) = (U.'*U);
mu_dc_save(s) = mu_dc_norm;
e_dc_save(s) = e_dc_est;
end
% store instantaneous squared error
obj.error(epoch, s) = e_val^2;
end
end
% Optional plotting in DD mode (uncomment if needed)
if 0%~training
constellation = unique(d);
lvlcol = cbrewer2('Paired', numel(constellation)*2);
lvlcol = lvlcol(2:2:end, :);
true_err(true_err==0) = NaN;
true_errmoverr = movsum(true_err, 4096, 'omitnan');
true_errmoverr = true_errmoverr./rms(true_errmoverr);
moverr = movsum(err, [100,100], 'omitnan');
moverr = moverr./rms(moverr);
figure(500); clf
hold on
% 1st subplot: true_errmoverr
% subplot(2,2,1); hold on
% for k = 1:4
% scatter(1:numSymbols, true_errmoverr(:,k), 1, lvlcol(k,:), '.');
% end
% scatter(1:numSymbols, Ucorr_save./rms(Ucorr_save), 1, lvlcol(1,:), '.','DisplayName','Ucorr_save');
% scatter(1:numSymbols, Pcorr_save./rms(Pcorr_save), 1, lvlcol(1,:), '.','DisplayName','P_corr');
% scatter(1:numSymbols, P_err_save, 1, lvlcol(1,:), '.','DisplayName','P_err');
% scatter(1:numSymbols, mu_dc_save, 1, lvlcol(2,:), '.','DisplayName','adapted value of $\mu_{DC}$');
% scatter(1:numSymbols, sum(moverr,2,'omitnan'), 1, lvlcol(1,:), '.','DisplayName','Mov Error $\hat{d}$ - x over all levels');
scatter(1:numSymbols, sum(e_dc_save,2,'omitnan'), 1, lvlcol(2,:), '.','DisplayName','Est. Error that is subtracted');
title('Moving Sum Error');
hold off
legend
% 2nd subplot: moverr
subplot(2,2,2); hold on
for k = 1:4
scatter(1:numSymbols, moverr(:,k), 1, lvlcol(k,:), '.');
end
title('Moving Sum Error');
hold off
legend
% 3rd subplot: err
subplot(2,2,3); hold on
for k = 1:4
scatter(1:numSymbols, err(:,k), 1, lvlcol(k,:), '.');
end
title('Error');
hold off
legend
% 4th subplot: err + obj.constellation'
subplot(2,2,4); hold on
for k = 1:4
scatter(1:numSymbols, err(:,k) + obj.constellation(k), 1, lvlcol(k,:), '.');
end
yline(obj.constellation, '--k');
title('Error + Constellation');
hold off
legend
sgtitle('Error Analysis Subplots');
end
end
end
end

View File

@@ -0,0 +1,240 @@
classdef FFE_DCremoval_level < handle
% Implementation of plain and simple FFE.
% 1) Training mode (stable performance when you use NLMS)
% 2) Decision directed mode
% Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0);
properties
sps % usually 2
order
e
error
len_tr
mu_tr
epochs_tr
mu_dd
epochs_dd
mu_dc
dc_buffer_len
constellation
decide
KF_meas_noise = 0;
KF_process_noise = 0;
KF_state_cov = 0;
end
methods
function obj = FFE_DCremoval_level(options)
arguments(Input)
options.sps = 2;
options.order = 15;
options.len_tr = 4096;
options.mu_tr = 0;
options.epochs_tr = 5;
options.mu_dd = 1e-5;
options.epochs_dd = 5;
options.mu_dc = 0.05;
options.dc_buffer_len = 1;
options.decide = false;
end
assert(options.dc_buffer_len>0);
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
obj.e = zeros(obj.order,1);
obj.error = 0;
obj.dc_buffer_len = floor(obj.dc_buffer_len);
end
function [X,Noi] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
X = X.normalize("mode","rms");
obj.constellation = unique(D.signal);
% Training Mode
training = 1;
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training);
% Decision Directed Mode
N = X.length;
training = 0;
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training);
% Output Signal
if obj.decide
X.signal = decision;
else
X.signal = signal;
end
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
lbdesc = [num2str(obj.order),' tap FFE'];
X = X.logbookentry(lbdesc); % append to logbook
Noi = X - D;
end
function [y, d_hat, logs] = equalize(obj, x, d, mu_lms, epochs, N, training)
% Equalize with Kalman-based DC removal; training epochs estimate KF noise parameters
arguments
obj
x
d
mu_lms % LMS step-size (or 0 for NLMS)
epochs % number of training or DD epochs
N % number of samples to process
training % true => training mode (tap-training + noise estimation)
end
% Zero-pad for filter memory
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
numSym = ceil(N/obj.sps);
% Pre-allocate outputs
y = zeros(numSym,1);
d_hat = zeros(numSym,1);
% --- Training: estimate noise stats and train taps ---
if training
% Pre-allocate error accumulator
totalTrain = epochs * numSym;
trainErrs = zeros(totalTrain,1);
te_idx = 0;
for ep = 1:epochs
s = 0;
for n = 1:obj.sps:N
s = s + 1;
U = x(obj.order + n - 1 : -1 : n);
% Equalizer output (no DC correction yet)
y(s) = obj.e.' * U;
% Decision based on known symbol
[~, idx] = min(abs(d(s) - obj.constellation));
d_hat(s) = obj.constellation(idx);
% Instantaneous error
e_n = y(s) - d_hat(s);
% Collect error for noise estimation
te_idx = te_idx + 1;
trainErrs(te_idx) = e_n;
% Tap-weight update (LMS or NLMS)
if mu_lms ~= 0
obj.e = obj.e - mu_lms * e_n * U;
else
normU = (U.'*U) + eps;
obj.e = obj.e - e_n * U / normU;
end
end
end
% Estimate measurement noise R and process noise Q
R_est = var(trainErrs(1:te_idx));
Q_est = 1e-3 * R_est; % Q/R ratio = 1e-3 (tune as needed)
% Store into object for DD pass
obj.KF_meas_noise = R_est;
obj.KF_process_noise = Q_est;
obj.KF_state_cov = 5*R_est; % or 5*R_est for a more eager start
% No Kalman in training; return
logs = struct();
return;
end
% --- Decision-Directed with Kalman DC tracking ---
% Initialize Kalman state
x_est = 0;
P = obj.KF_state_cov; % initial P (tune in obj; e.g. 1)
% Logging containers
logs.y_raw = zeros(numSym,1);
logs.y_corr = zeros(numSym,1);
logs.err = zeros(numSym,1);
logs.K_gain = zeros(numSym,1);
logs.x_est = zeros(numSym,1);
logs.P = zeros(numSym,1);
logs.normU = zeros(numSym,1);
logs.tap_norm = zeros(numSym,1);
for ep = 1:epochs
s = 0;
for n = 1:obj.sps:N
s = s + 1;
U = x(obj.order + n - 1 : -1 : n);
% 1) Kalman prediction
P = P + obj.KF_process_noise;
x_prior = x_est;
% 2) raw equalizer output
y_raw = obj.e.' * U;
logs.y_raw(s) = y_raw;
% 3) DC-corrected output
y_corr = y_raw + x_prior;
y(s) = y_corr;
logs.y_corr(s) = y_corr;
% 4) decision-directed symbol
[~, idx] = min(abs(y_corr - obj.constellation));
d_hat(s) = obj.constellation(idx);
% 5) error
e_n = y_corr - d_hat(s);
logs.err(s) = e_n;
% 6) tap-weight update (LMS/NLMS)
if mu_lms ~= 0
obj.e = obj.e - mu_lms * e_n * U;
else
normU = U.' * U + eps;
logs.normU(s) = normU;
obj.e = obj.e - e_n * U / normU;
end
logs.tap_norm(s) = norm(obj.e);
% 7) Kalman update
K_gain = P / (P + obj.KF_meas_noise);
x_est = x_prior + K_gain * (e_n - x_prior);
P = (1 - K_gain) * P;
% 8) log Kalman state
logs.K_gain(s) = K_gain;
logs.x_est(s) = x_est;
logs.P(s) = P;
end
end
end
end
end

View File

@@ -0,0 +1,233 @@
classdef FFE_MLSE < handle
% Implementation of plain and simple FFE.
% 1) Training mode (stable performance when you use NLMS)
% 2) Decision directed mode
%LMS: mu in order of 0.0001 for acceptable convergence speed
%NLMS: mu in order of 0.01 for acceptable convergence speed
%RLS: mu is lambda -> 0.99 -> 1 (has a strong dependency on this! use a loop to find out best values)
% FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
properties
sps % usually 2
order
e
e_tr
error
len_tr
mu_tr
epochs_tr
dd_mode % 1 or 0 to set DD-mode on or off
mu_dd %weight update in dd mode
epochs_dd
constellation
L %viterbi memory length
alpha
DIR
DIR_flip
trellis_states
traceback_depth
end
methods
function obj = FFE_MLSE(options)
arguments(Input)
options.sps = 2;
options.order = 15;
options.len_tr = 4096;
options.mu_tr = 0;
options.epochs_tr = 5;
options.dd_mode = 1;
options.mu_dd = 1e-5;
options.epochs_dd = 5;
options.traceback_depth = 1024;
options.L = 1
end
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
obj.e = zeros(obj.order,1);
obj.error = 0;
end
function [X,X_viterbi] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
X = X.normalize("mode","rms");
obj.constellation = unique(D.signal);
if length(X)/length(D) ~= obj.sps
warning('Signal length does not fit to reference!');
end
% Training Mode
n = obj.len_tr;
training = 1;
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_dd,n,training);
obj.e_tr = obj.e;
% Decision Directed Mode
n = X.length;
training = 0;
[y,y_vit]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,n,training);
X_viterbi = X;
X.signal = y;
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
lbdesc = [num2str(obj.order),' tap FFE'];
X = X.logbookentry(lbdesc); % append to logbook
X_viterbi.signal = y_vit;
X_viterbi.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
lbdesc = [num2str(obj.order),'order FFE + PF + Viterbi'];
X_viterbi = X_viterbi.logbookentry(lbdesc); % append to logbook
end
function [y,y_vit] = equalize(obj,x,d,mu,epochs,N,training)
% ==============================================================
% FFE + Whitening + Viterbi Equalizer (reference implementation)
% ==============================================================
% --- Input padding and preallocation
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
N_ = N / obj.sps;
y = zeros(N_,1);
y_white = zeros(N_,1);
for epoch = 1:epochs
% ==============================================================
% INITIALIZATION (only before final epoch and detection mode)
% ==============================================================
if epoch == epochs && ~training
% --- Parameters
S = numel(unique(d));
L = obj.L;
nStates = S^L;
nFeasible = S^(L-1)*S;
% --- Trellis setup
obj.DIR = arburg(y-d, L);
obj.DIR_flip = flip(obj.DIR);
obj.trellis_states = reshape(unique(d),1,[]);
pre_comb_mat = repmat(obj.trellis_states, L, 1);
pre_comb_cell = mat2cell(pre_comb_mat, ones(1,L), size(pre_comb_mat,2));
combs = fliplr(combvec(pre_comb_cell{:}).');
first_sym = combs(:,1);
last_sym = combs(:,end);
nStates = size(combs,1);
noise_free_received = inf(nStates,nStates);
valid = false(nStates);
for from = 1:nStates
for to = 1:nStates
if all(combs(to,2:end) == combs(from,1:end-1))
noise_free_received(to,from) = ...
dot(combs(to,:), obj.DIR_flip(end:-1:2)) + last_sym(from)*obj.DIR_flip(1);
valid(to,from) = true;
end
end
end
nf_vec = noise_free_received(valid);
[valid_to, valid_from] = find(valid);
from_per_to = arrayfun(@(to)find(valid(to,:)), 1:nStates, 'UniformOutput', false);
% --- Noise stats
y_ideal = conv(d(:), obj.DIR(:), "same");
sigma2 = mean(abs(y - y_ideal).^2);
inv2s2 = 1/(2*sigma2);
% --- Vector initialization
bm_vec = zeros(1,nFeasible);
pm = zeros(nStates,1);
pm_next = zeros(nStates,1);
bm_fw = zeros(nStates,nStates,length(y));
zi = zeros(max(numel(obj.DIR)-1,0),1);
end
% ==============================================================
% RUNTIME LOOP (FFE update + Viterbi detection in last epoch)
% ==============================================================
symbol = 0;
for sample = 1:obj.sps:N
symbol = symbol + 1;
% --- FFE output
U = x(obj.order+sample-1:-1:sample);
y(symbol,1) = obj.e.' * U;
% --- Decision
if training
d_hat(symbol,1) = d(symbol);
else
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation));
d_hat(symbol,1) = obj.constellation(symbol_idx);
end
% --- LMS weight update
err(symbol) = d_hat(symbol) - y(symbol);
obj.e = obj.e + mu * (err(symbol) * U);
% --- Whitening + Viterbi (final epoch only)
if epoch == epochs && ~training
[y_white(symbol), zi] = filter(obj.DIR,1,y(symbol), zi);
if symbol == 1
pm = -inf(nStates,nStates);
pm(:,1:nStates) = 0;
else
bm_vec = -(y_white(symbol) - nf_vec).^2 * inv2s2;
bm_mat = -inf(nStates,nStates);
bm_mat(valid) = bm_vec;
pm_new = pm + bm_mat;
[pm_survive(:,symbol), pm_survivor_fw_idx(:,symbol)] = max(pm_new,[],2);
pm = repmat(pm_survive(:,symbol).', nStates,1);
end
% --- Traceback
if mod(symbol,obj.traceback_depth) == 0
[~,viterbi_path(symbol)] = max(pm_survive(:,symbol));
for n = symbol:-1:symbol-obj.traceback_depth+2
viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n);
end
end
end
end
% --- Final reconstruction
if epoch == epochs && ~training
y_vit = first_sym(viterbi_path);
end
end
end
end
end

View File

@@ -0,0 +1,355 @@
classdef ML_MLSE < handle
% ---------------------------------------------------------------------
% W. Lanneer and Y. Lefevre,
% Machine Learning-Based Pre-Equalizers for Maximum Likelihood
% Sequence Estimation in High-Speed PONs, EUSIPCO 2023
% ---------------------------------------------------------------------
% This implementation reproduces the closed-loop ML-based
% pre-equalizer training for MLSE, supporting both training and
% detection (decision-directed) modes.
% ---------------------------------------------------------------------
properties
sps
order
e
e_tr
error
len_tr
mu_tr
epochs_tr
dd_mode
mu_dd
epochs_dd
adaptive_mu
constellation
L
alpha
DIR
DIR_flip
trellis_states
traceback_depth
delta
% Internal variables
S
Nf
nStates
nFeasible
combs
first_sym
last_sym
valid
valid_to_idx
valid_from_idx
w
% Fast lookup
nSym
key_table
trans_index
true_to_state_idx
% Debug metrics
ber = []
ce = ones(1,1)
end
methods
function obj = ML_MLSE(options)
arguments(Input)
options.sps = 2;
options.order = 15;
options.len_tr = 4096;
options.mu_tr = 0.001;
options.epochs_tr = 5;
options.dd_mode = 1;
options.mu_dd = 1e-5;
options.epochs_dd = 5;
options.adaptive_mu = 1;
options.delta = 0;
options.traceback_depth = 1024;
options.L = 1;
end
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
obj.e = zeros(obj.order,1);
obj.error = 0;
end
% ==============================================================
% PROCESS
% ==============================================================
function [X,X_viterbi] = process(obj, X, D)
% Normalize input RMS
X = X.normalize("mode","rms");
obj.constellation = sort(unique(D.signal),'ascend');
obj.nSym = numel(obj.constellation);
if length(X)/length(D) ~= obj.sps
warning('Signal length does not fit to reference!');
end
% --- Parameters
obj.S = obj.nSym;
obj.Nf = obj.order * obj.sps;
obj.nStates = obj.S^obj.L;
obj.nFeasible = obj.nStates * obj.S;
% --- Trellis mapping
obj.trellis_states = reshape(obj.constellation,1,[]);
pre_comb_mat = repmat(obj.trellis_states, obj.L, 1);
pre_comb_cell = mat2cell(pre_comb_mat, ones(1,obj.L), size(pre_comb_mat,2));
obj.combs = fliplr(combvec(pre_comb_cell{:}).');
obj.first_sym = obj.combs(:,1);
obj.last_sym = obj.combs(:,end);
obj.nStates = size(obj.combs,1);
% --- Valid transitions
obj.valid = false(obj.nStates);
for from = 1:obj.nStates
for to = 1:obj.nStates
if all(obj.combs(to,2:end) == obj.combs(from,1:end-1))
obj.valid(to,from) = true;
end
end
end
[obj.valid_to_idx,obj.valid_from_idx] = find(obj.valid);
% --- Initialize weights
if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+1,obj.nFeasible])
obj.w = randn(obj.Nf+1,obj.nFeasible);
end
% --- Fast lookup tables
[~, sym_idx_mat] = ismember(obj.combs, obj.constellation);
key_vals = 1 + sum((sym_idx_mat - 1) .* (obj.nSym .^ (0:obj.L-1)), 2);
max_key = obj.nSym^obj.L;
obj.key_table = zeros(max_key,1,'uint32');
obj.key_table(key_vals) = 1:obj.nStates;
obj.trans_index = sparse(obj.nStates,obj.nStates);
for i = 1:length(obj.valid_from_idx)
f = obj.valid_from_idx(i);
t = obj.valid_to_idx(i);
obj.trans_index(t,f) = i;
end
% ==============================================================
% TRAINING
% ==============================================================
fprintf('\n--- Training mode ---\n');
obj.equalize(X.signal, D.signal, obj.mu_tr, obj.epochs_tr, obj.len_tr, true);
obj.e_tr = obj.e;
% ==============================================================
% DECISION-DIRECTED / TESTING
% ==============================================================
fprintf('--- Decision-directed / detection mode ---\n');
[y, y_vit] = obj.equalize(X.signal, D.signal, obj.mu_dd, obj.epochs_dd, X.length, false);
X_viterbi = X;
X.signal = y;
X_viterbi.signal = y_vit;
end
% ==============================================================
% EQUALIZE
% ==============================================================
function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training)
debug = 0;
showPlots = 0;
y = zeros(N,1);
nSymbols = ceil(N/obj.sps);
for epoch = 1:epochs
pm = zeros(obj.nStates,1);
pred = zeros(nSymbols,obj.nStates,'uint32');
pm_sto = nan(obj.nStates,nSymbols,'like',pm);
CE_accum = 0;
start_sample = 1;
end_sample = N;
start_symbol = 1 + floor((start_sample - 1)/obj.sps);
% --- initialize true state
if numel(d) >= obj.L && start_symbol >= obj.L
init_seq = d(start_symbol-obj.L+1:start_symbol);
key_init = obj.seq2key(init_seq);
true_to_state_idx = obj.key_table(key_init);
if true_to_state_idx==0, true_to_state_idx=1; end
else
true_to_state_idx = uint32(1);
end
for sample = start_sample:obj.sps:end_sample
symbol = (sample - start_sample)/obj.sps + 1;
sym_idx = start_symbol + (symbol - 1);
% --- Observation window (with delta)
i1 = sample - obj.Nf + 1 + obj.delta;
i2 = sample + obj.delta;
buf = x(max(1,i1):min(length(x),i2));
padL = max(0,1 - i1);
padR = max(0,i2 - length(x));
yk = [zeros(padL,1); buf(:); zeros(padR,1)];
yk = [yk;1];
% --- Branch metrics
c_hat = (yk.' * obj.w).';
pm = pm - min(pm);
v_tilde = pm(obj.valid_from_idx) + c_hat;
% --- allocate once
if epoch==1 && symbol==1
obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32');
end
% --- previous "to" becomes "from"
if symbol>1
true_from_state_idx = obj.true_to_state_idx(symbol-1);
else
true_from_state_idx = 1;
end
% --- compute or reuse "to" state
if epoch==1
if sym_idx>=obj.L
key_to = obj.seq2key(d(sym_idx-obj.L+1:sym_idx));
state_idx = obj.key_table(key_to);
if state_idx==0
state_idx = true_from_state_idx;
end
obj.true_to_state_idx(symbol) = state_idx;
else
obj.true_to_state_idx(symbol) = true_from_state_idx;
end
end
true_to_state_idx = obj.true_to_state_idx(symbol);
% --- fast Dirac creation
dirac = zeros(obj.nFeasible,1);
trans_idx = obj.trans_index(true_to_state_idx,true_from_state_idx);
if trans_idx~=0
dirac(trans_idx)=1;
end
% ===================================================================
% TRAINING MODE (weight update)
% ===================================================================
if training
% --- Softmax and CE
v_shift = -(v_tilde - min(v_tilde));
v_shift = min(v_shift,100);
expv = exp(v_shift);
p = expv./(sum(expv)+eps);
CE_symbol(symbol) = -log(p(dirac==1)+eps);
% --- CE smoothing and adaptive μ
if sym_idx>obj.L
CE_smooth(symbol)=0.01*CE_symbol(symbol)+0.99*CE_symbol(symbol-1);
else
CE_smooth(symbol)=CE_symbol(symbol);
end
CE_accum=CE_accum+CE_symbol(symbol);
% --- Gradient update
dmp=(dirac-p)';
dL_Dw=(yk).*dmp;
if sym_idx>=obj.L
if obj.adaptive_mu
mu_eff=CE_smooth(symbol);
mu_eff=max(min(mu_eff,0.2),1e-4);
else
mu_eff=mu;
end
obj.w=obj.w - mu_eff.*dL_Dw;
end
end
% ===================================================================
% DECODING MODE (Viterbi only)
% ===================================================================
% Compare-Select (always executed)
vmat=inf(obj.nStates,obj.nStates);
vmat(obj.valid)=v_tilde;
[pm_next,pred(symbol,:)]=min(vmat,[],2);
pm_next=pm_next-min(pm_next);
pm=pm_next;
pm_sto(:,symbol)=pm;
end
% --- Traceback
[~,s_end]=min(pm);
vpath=zeros(symbol,1,'uint32');
vpath(symbol)=s_end;
for n=symbol:-1:2
vpath(n-1)=pred(n,vpath(n));
end
y_ref=d(start_symbol:end);
y=obj.first_sym(vpath);
% --- BER/CE reporting and plots
if training
err=sum(y~=y_ref(1:length(y)));
ser=err/length(y);
try
ref_bits=PAMmapper(obj.S,0).demap(y_ref(1:length(y)));
eq_bits=PAMmapper(obj.S,0).demap(y);
[~,~,ber,~]=calc_ber(ref_bits,eq_bits,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
fprintf('Epoch %d - BER: %.2e\n',epoch,ber);
obj.ber(epoch)=ber;
catch
fprintf('Epoch %d - SER: %.2e\n',epoch,ser);
obj.ber(epoch)=ser;
end
obj.ce(epoch)=CE_accum/symbol;
if debug && mod(epoch,10)==1 && showPlots
figure(10);clf
subplot(3,2,1:2);
imagesc(obj.w);axis xy;colorbar;title('Filter W');
subplot(3,2,3);
vtilde_mat=NaN(obj.nStates,obj.nStates);
vtilde_mat(obj.valid)=v_tilde;
imagesc(vtilde_mat);axis xy;colorbar;title('Path Metrics (v\_tilde)');
subplot(3,2,4);
plot(1:symbol,pm_sto);title('Path Metric Evolution');
subplot(3,2,5);hold on;
scatter(1:symbol,CE_symbol,1,'.');
scatter(1:symbol,CE_smooth,1,'.');
title('Cross Entropy');
subplot(3,2,6);hold on;
yyaxis left
scatter(1:length(obj.ce),obj.ce,10,'s','filled');
ylabel('Cross Entropy');
yyaxis right
scatter(1:length(obj.ber),obj.ber,10,'d','filled');
set(gca,'YScale','log');
ylabel('BER (log)');
xlabel('Epoch');grid on;
title('Convergence');
drawnow;
end
end
end
end
% ==============================================================
% Helper: Sequence key (always scalar)
% ==============================================================
function key = seq2key(obj, seq)
[~, idx] = ismember(flip(seq), obj.constellation);
pow = (obj.nSym .^ (0:obj.L-1)).';
key = 1 + sum((idx(:) - 1) .* pow);
end
end
end

View File

@@ -0,0 +1,152 @@
classdef MLSE_new < handle
%MLSE: BCJRbased softoutput for PAMM sequence estimation & GMI
properties
M % PAM order (e.g. 4)
DIR % channel impulse response
trellis_states % PAM constellation levels (e.g. [-3 -1 1 3])
end
methods
function obj = MLSE_new(opts)
arguments
opts.M double = 4;
opts.DIR double = 1;
opts.trellis_states double = [-3 -1 1 3];
end
obj.M = opts.M;
obj.DIR = opts.DIR;
obj.trellis_states = opts.trellis_states;
end
function [hd_out, LLR, NGMI] = process(obj, signalclass,ref_symbolclass)
% rx, tx: column vectors of equalized and reference symbols
data_in = signalclass.signal;
shape_in = size(data_in);
data_ref = ref_symbolclass.signal;
[data_out_hd,LLR,GMI] = obj.bcjr_soft(data_in,data_ref);
try
data_out_hd = reshape(data_out_hd,shape_in(1),shape_in(2));
catch
warning('output reshaping failed after MLSE');
end
signalclass_hd = signalclass;
signalclass_hd.signal = data_out_hd;
[hd_out, LLR, NGMI] = obj.bcjr_soft(rx, tx);
end
end
methods (Access=private)
function [hd_sym, LLR, NGMI] = bcjr_soft(obj, y, x)
%--- build trellis states & branch outputs ---
DIR = flip(obj.DIR(:));
S = combvec(obj.trellis_states, obj.trellis_states)';
prev = S(:,1); cur = S(:,2);
branch_out = prev*DIR(1) + cur*DIR(2); % for 2tap
N = numel(y);
M = obj.M;
m = log2(M);
%--- forward/backward metrics (maxlog) ---
nStates = numel(obj.trellis_states);
alpha = -inf(nStates,N);
beta = -inf(nStates,N);
bm_fw = zeros(nStates,nStates,N);
% branch metrics
sigma2 = var(y - x);
inv2sigma2 = 1/(2*sigma2);
for n=1:N
bm = -((y(n)-branch_out).^2)* inv2sigma2;
bm = reshape(bm, nStates, nStates);
bm_fw(:,:,n) = bm;
end
% forward
alpha(:,1) = max(bm_fw(:,:,1),[],2);
for n=2:N
mat = alpha(:,n-1) + bm_fw(:,:,n);
alpha(:,n) = max(mat,[],2);
end
% backward
beta(:,N) = 0;
for n=N-1:-1:1
mat = beta(:,n+1).' + bm_fw(:,:,n+1);
beta(:,n) = max(mat,[],2);
end
% Precompute the zeroth forward metric
alpha0 = zeros(nStates,1);
LLP = -inf(nStates,nStates,N);
for n = 1:N
% Select the correct previous alpha column
if n == 1
a_prev = alpha0;
else
a_prev = alpha(:,n-1);
end
% Combine α, branch metric, and β
mat = a_prev + bm_fw(:,:,n) + beta(:,n).';
LLP(:,:,n) = mat;
end
%--- compute LLRs symbolwise via logsumexp over branches ---
levels = obj.trellis_states;
bit_map = PAMmapper(M,0).demap(levels');
LLR = zeros(N,m);
for n=1:N
% sum branch posteriors per symbol
llp_n = LLP(:,:,n);
logZ = logsumexp(llp_n(:));
post = exp(llp_n - logZ);
% aggregate per symbol index
Psym = zeros(M,1);
for i=1:nStates
for j=1:nStates
% branch (i->j) emits symbol 'cur(j)'
idx = find(levels==cur(j));
Psym(idx) = Psym(idx) + post(i,j);
end
end
% bitLLR from symbol posterior
for k=1:m
idx1 = bit_map(:,k)==1;
idx0 = ~idx1;
P1 = sum(Psym(idx1));
P0 = sum(Psym(idx0));
LLR(n,k) = log(P1/P0);
end
end
%--- hard decisions ---
[~,symIdx] = max(LLR,[],2);
hd_sym = levels(symIdx);
%--- GMI via Alvarado Eq.(30) ---
tx_bits = PAMmapper(M,0).demap(x);
MI = zeros(1,m);
for k=1:m
r0 = LLR(tx_bits(:,k)==0,k);
r1 = LLR(tx_bits(:,k)==1,k);
I0 = mean(log2(1+exp(-r0)));
I1 = mean(log2(1+exp(+r1)));
MI(k) = 1 - 0.5*(I0+I1);
end
GMI = sum(MI);
NGMI = GMI/m;
end
end
end
function s = logsumexp(a)
m = max(a(:));
s = m + log(sum(exp(a-m), 'all'));
end

View File

@@ -0,0 +1,68 @@
classdef Equalizerstruct
% Equalizerstruct - Class to store and manage equalizer structure data
properties
eq_id (1,1) double {mustBeNumeric} = NaN
equalizer_structure equalizer_structure = equalizer_structure.ffe
eq
mlse
comment char = string.empty() % Changed to string with proper empty initialization
hash char = string.empty() % Changed to string with proper empty initialization
end
methods
function obj = Equalizerstruct(varargin)
% Constructor method for Equalizerstruct
% Can be called empty or with name-value pairs
if nargin > 0
for i = 1:2:nargin
if isprop(obj, varargin{i})
obj.(varargin{i}) = varargin{i+1};
else
error('Property %s does not exist in Equalizerstruct', varargin{i});
end
end
end
end
function s = toStruct(obj)
% Convert the object to a struct
s = struct();
props = properties(obj);
for i = 1:length(props)
s.(props{i}) = obj.(props{i});
end
end
function str = toString(obj)
% Convert the object to a formatted string
s = obj.toStruct();
str = sprintf('Equalizerstruct:\n');
fields = fieldnames(s);
for i = 1:length(fields)
val = s.(fields{i});
if isempty(val)
str = sprintf('%s%s: []\n', str, fields{i});
elseif isnumeric(val) && length(val) > 1
str = sprintf('%s%s: [%s]\n', str, fields{i}, num2str(val'));
else
str = sprintf('%s%s: %s\n', str, fields{i}, string(val));
end
end
end
end
methods (Static)
function obj = fromStruct(s)
% Create an Equalizerstruct object from a struct
obj = Equalizerstruct();
fields = fieldnames(s);
for i = 1:length(fields)
if isprop(obj, fields{i})
obj.(fields{i}) = s.(fields{i});
end
end
end
end
end

View File

@@ -0,0 +1,147 @@
classdef Metricstruct
% ResultData - Class to store and manage metric data from signal processing results
properties
result_id (1,1) double {mustBeNumeric} = NaN
run_id (1,1) double {mustBeNumeric} = NaN
eqParam_id (1,1) double {mustBeNumeric} = NaN
date_of_processing (1,1) datetime = datetime('now')
numBits (1,1) double {mustBeInteger, mustBeNonnegative} = 0
BER (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER,1)} = 0
numBitErr (1,1) double {mustBeInteger, mustBeNonnegative} = 0
BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER_precoded,1)} = 0
numBitErr_precoded (1,1) double {mustBeInteger, mustBeNonnegative} = 0
SNR (1,1) double {mustBeNumeric} = NaN
SNR_level (:,1) double {mustBeNumeric} = []
STD (1,1) double {mustBeNumeric} = NaN
STD_level (:,1) double = []
STDrx (1,1) double {mustBeNumeric} = NaN
STDrx_level (:,1) double = []
EVM (1,1) double {mustBeNumeric} = NaN
EVM_level (:,1) double {mustBeNumeric} = []
GMI (1,1) double {mustBeNumeric} = NaN
AIR (1,1) double {mustBeNumeric} = NaN
Alpha (:,1) double {mustBeNumeric} = NaN
MLSE_dir (:,1) double {mustBeNumeric} = []
end
methods
function obj = Metricstruct(varargin)
% Constructor method for ResultData
% Can be called empty or with name-value pairs
% Process name-value pairs if provided
if nargin > 0
for i = 1:2:nargin
if isprop(obj, varargin{i})
obj.(varargin{i}) = varargin{i+1};
else
error('Property %s does not exist in ResultData', varargin{i});
end
end
end
end
function s = toStruct(obj)
% Convert the object to a struct
s = struct();
props = properties(obj);
for i = 1:length(props)
s.(props{i}) = obj.(props{i});
end
end
function str = toString(obj)
% Convert the object to a formatted string
s = obj.toStruct();
str = sprintf('ResultData:\n');
fields = fieldnames(s);
for i = 1:length(fields)
val = s.(fields{i});
if isempty(val)
str = sprintf('%s%s: []\n', str, fields{i});
elseif isnumeric(val) && length(val) > 1
str = sprintf('%s%s: [%s]\n', str, fields{i}, num2str(val'));
else
str = sprintf('%s%s: %s\n', str, fields{i}, string(val));
end
end
end
function print(obj,options)
% Print method to display key metrics in a formatted way
arguments
obj
options.description = '';
end
% Define the width for formatting
nameWidth = 15; % Width for parameter names
valueWidth = 12; % Width for values
% Print header
fprintf('\n%s\n', repmat('=', 1, nameWidth + valueWidth));
fprintf([char(options.description),' Results \n']);
fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth));
% Function to format numbers with appropriate precision
function str = formatNumber(value)
if isnan(value)
str = 'N/A';
elseif abs(value) < 0.1 && value ~= 0
str = sprintf('%.2e', value);
else
str = sprintf('%.4f', value);
end
end
% Print each metric
% BER metrics
fprintf('%-*s: %*s\n', nameWidth, 'BER', valueWidth, formatNumber(obj.BER));
fprintf('%-*s: %*s\n', nameWidth, 'BER (precoded)', valueWidth, formatNumber(obj.BER_precoded));
% SNR
fprintf('%-*s: %*s\n', nameWidth, 'SNR', valueWidth, formatNumber(obj.SNR));
% Information rates
fprintf('%-*s: %*s\n', nameWidth, 'GMI', valueWidth, formatNumber(obj.GMI));
fprintf('%-*s: %*s GBd \n', nameWidth, 'AIR', valueWidth, formatNumber(obj.AIR.*1e-9));
% Alpha (if it's not empty, show first few values)
if ~isempty(obj.Alpha)
if length(obj.Alpha) <= 3
alphaStr = sprintf('%.4f ', obj.Alpha);
else
alphaStr = sprintf('%.4f %.4f %.4f...', obj.Alpha(1:3));
end
fprintf('%-*s: %s\n', nameWidth, 'Alpha', alphaStr);
else
fprintf('%-*s: %*s\n', nameWidth, 'Alpha', valueWidth, '[]');
end
% Print footer
fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth));
end
end
methods (Static)
function obj = fromStruct(s)
% Create a ResultData object from a struct
obj = Metricstruct();
fields = fieldnames(s);
for i = 1:length(fields)
if isprop(obj, fields{i})
obj.(fields{i}) = s.(fields{i});
end
end
end
end
end

View File

@@ -0,0 +1,169 @@
% QueryFilter - Class for building SQL WHERE conditions for database queries.
%
% Usage:
% qf = QueryFilter();
% qf.where('Runs', 'fiber_length', SqlOperator.GREATER_THAN, 5);
% qf.where('Runs', 'bitrate', SqlOperator.IN, [224, 336, 448]);
% qf.where('Runs', 'is_mpi', SqlOperator.EQUALS, 0);
%
% % Convert to struct for use in DBHandler or other query functions:
% filterStruct = qf.toStruct();
%
% % Display current filters:
% disp(qf);
classdef QueryFilter < handle
properties (Access = private)
filters struct = struct()
end
methods
function obj = QueryFilter(oldFormat)
% Constructor - optionally convert from old filter format
if nargin > 0 && isstruct(oldFormat)
tableNames = fieldnames(oldFormat);
for t = 1:length(tableNames)
tableName = tableNames{t};
if isstruct(oldFormat.(tableName))
fields = fieldnames(oldFormat.(tableName));
for f = 1:length(fields)
fieldName = fields{f};
value = oldFormat.(tableName).(fieldName);
if ~isempty(value)
obj.where(tableName, fieldName, value);
end
end
end
end
end
end
function where(obj, tableName, fieldName, operator, value)
% Add a WHERE condition to the query
%
% Inputs:
% tableName - Name of the database table (char)
% fieldName - Name of the field/column (char)
% operator - SqlOperator enum or value if using EQUALS
% value - Value to filter by
%
% Example:
% filter.where('Runs', 'fiber_length', SqlOperator.GREATER_THAN, 5)
arguments
obj
tableName char
fieldName char
operator SqlOperator
value = []
end
% Handle case where operator is the value (using default EQUALS)
if nargin < 5
value = operator;
operator = SqlOperator.EQUALS;
end
% Validate operator type
if ~isa(operator, 'SqlOperator')
error('Operator must be a SqlOperator enumeration');
end
% Validate value based on operator
obj.validateValue(operator, value);
% Create filter
filter = SqlFilter(value, operator.toSqlString());
% Add to filters
if ~isfield(obj.filters, tableName)
obj.filters.(tableName) = struct();
end
obj.filters.(tableName).(fieldName) = filter;
end
function s = toStruct(obj)
% Convert filters to struct format for database query
s = obj.filters;
end
function display(obj)
% Custom display of filter conditions
fprintf('QueryFilter with conditions:\n');
if isempty(fieldnames(obj.filters))
fprintf(' No filters set\n');
return;
end
tables = fieldnames(obj.filters);
for t = 1:length(tables)
tableName = tables{t};
if ~isempty(fieldnames(obj.filters.(tableName)))
fprintf('\nTable: %s\n', tableName);
fields = fieldnames(obj.filters.(tableName));
for f = 1:length(fields)
fieldName = fields{f};
filter = obj.filters.(tableName).(fieldName);
if isnumeric(filter.value)
if length(filter.value) > 1
valueStr = ['[', num2str(filter.value), ']'];
else
valueStr = num2str(filter.value);
end
elseif isempty(filter.value)
valueStr = 'empty';
else
valueStr = char(filter.value);
end
fprintf(' %s %s %s\n', fieldName, filter.operator, valueStr);
end
end
end
end
function clear(obj, tableName)
% Clear all filters or filters for a specific table
if nargin < 2
obj.filters = struct();
else
if isfield(obj.filters, tableName)
obj.filters = rmfield(obj.filters, tableName);
end
end
end
function remove(obj, tableName, fieldName)
% Remove a specific filter
if isfield(obj.filters, tableName) && ...
isfield(obj.filters.(tableName), fieldName)
obj.filters.(tableName) = rmfield(obj.filters.(tableName), fieldName);
end
end
end
methods (Access = private)
function validateValue(~, operator, value)
% Validate value based on operator type
switch operator
case SqlOperator.BETWEEN
if ~isnumeric(value) || length(value) ~= 2
error('BETWEEN operator requires array of 2 numbers');
end
case SqlOperator.IN
if ~isnumeric(value) || isempty(value)
error('IN operator requires non-empty array');
end
case SqlOperator.LIKE
if ~ischar(value) && ~isstring(value)
error('LIKE operator requires string value');
end
otherwise
% For other operators, just ensure value is not empty
if isempty(value)
error('Value cannot be empty');
end
end
end
end
end

View File

@@ -0,0 +1,22 @@
% SqlFilter - Internal class for holding a value and SQL operator for a filter condition.
%
% Usage:
% f = SqlFilter(10, SqlOperator.GREATER_THAN.toSqlString());
% % f.value == 10, f.operator == '>'
classdef SqlFilter
properties
value
operator char = '='
end
methods
function obj = SqlFilter(value, operator)
obj.value = value;
if nargin > 1
obj.operator = operator;
end
end
end
end

View File

@@ -0,0 +1,48 @@
% SqlOperator - Enumeration of supported SQL operators for query building.
%
% Usage:
% op = SqlOperator.GREATER_THAN;
% opStr = op.toSqlString(); % returns '>'
%
% Supported operators:
% EQUALS, GREATER_THAN, LESS_THAN, GREATER_EQUAL, LESS_EQUAL,
% NOT_EQUAL, IN, BETWEEN, LIKE
classdef SqlOperator < uint32
enumeration
EQUALS (1) % ==
GREATER_THAN (2) % >
LESS_THAN (3) % <
GREATER_EQUAL (4) % >=
LESS_EQUAL (5) % <=
NOT_EQUAL (6) % !=
IN (7) % IN
BETWEEN (8) % BETWEEN
LIKE (9) % LIKE
end
methods
function op = toSqlString(obj)
switch obj
case SqlOperator.EQUALS
op = '=';
case SqlOperator.GREATER_THAN
op = '>';
case SqlOperator.LESS_THAN
op = '<';
case SqlOperator.GREATER_EQUAL
op = '>=';
case SqlOperator.LESS_EQUAL
op = '<=';
case SqlOperator.NOT_EQUAL
op = '!=';
case SqlOperator.IN
op = 'IN';
case SqlOperator.BETWEEN
op = 'BETWEEN';
case SqlOperator.LIKE
op = 'LIKE';
end
end
end
end

View File

@@ -0,0 +1,32 @@
function cleanedTable = cleanUpTable(inputTable)
% Converts string numbers to numeric, 'NaN' to NaN, and tries to convert date strings to datetime.
cleanedTable = inputTable;
varNames = cleanedTable.Properties.VariableNames;
for i = 1:numel(varNames)
col = cleanedTable.(varNames{i});
if iscell(col)
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, 'NaN') | cellfun(@isempty, col))
cleanedTable.(varNames{i}) = numericCol;
else
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
cleanedTable.(varNames{i}) = string(col);
end
end
elseif isstring(col)
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, "NaN"))
cleanedTable.(varNames{i}) = numericCol;
else
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
catch
end
end
end
end
end

View File

@@ -0,0 +1,53 @@
function resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
% groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data.
%
% resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
%
% Inputs:
% fixedVars - Cell array of variable names to group by
% dataTable - Input MATLAB table
% aggregationFunction - Function handle (e.g., @mean, @min, @max)
%
% Output:
% resultTable - Grouped and aggregated table
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
varNames = dataTable.Properties.VariableNames;
nVars = numel(varNames);
aggData = cell(height(groupKeys), nVars);
groupCount = zeros(height(groupKeys), 1);
for i = 1:height(groupKeys)
idx = (G == i);
groupCount(i) = sum(idx);
for j = 1:nVars
colData = dataTable.(varNames{j});
if isnumeric(colData)
if any(idx)
aggData{i, j} = aggregationFunction(colData(idx));
else
aggData{i, j} = NaN;
end
else
if iscell(colData)
nonEmptyIdx = find(idx & ~cellfun(@isempty, colData), 1);
if ~isempty(nonEmptyIdx)
aggData{i, j} = colData{nonEmptyIdx};
else
aggData{i, j} = [];
end
else
nonEmptyIdx = find(idx, 1);
if ~isempty(nonEmptyIdx)
aggData{i, j} = colData(nonEmptyIdx);
else
aggData{i, j} = [];
end
end
end
end
end
resultTable = cell2table(aggData, 'VariableNames', varNames);
resultTable.nRows = groupCount;
end

View File

@@ -0,0 +1,61 @@
function [cleanedTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var)
% removeGroupOutliers removes outliers in y_var within each group defined by fixedVars.
%
% [cleanedTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var)
%
% Inputs:
% dataTable - Input MATLAB table
% fixedVars - Cell array of variable names to group by
% y_var - Name of the variable to check for outliers (string or char)
%
% Outputs:
% cleanedTable - Table with outliers removed
% outliersTable - Table of removed outlier rows
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
keepIdx = true(height(dataTable), 1);
outlierRecords = [];
for groupIdx = 1:height(groupKeys)
groupRows = (G == groupIdx);
y_values = dataTable.(y_var)(groupRows);
% Skip groups with fewer than 3 points
if sum(groupRows) < 3
continue;
end
% Detect outliers in log10 space (robust for BER, etc.)
y_log = log10(y_values);
outlierMask = isoutlier(y_log, 'quartiles', 1);
if any(outlierMask)
groupData = dataTable(groupRows, :);
outlierGroupTable = groupData(outlierMask, :);
% Optionally, add group key values for traceability
for k = 1:numel(fixedVars)
outlierGroupTable.(['Group_', fixedVars{k}]) = repmat(groupKeys{groupIdx, k}, height(outlierGroupTable), 1);
end
outlierRecords = [outlierRecords; outlierGroupTable]; %#ok<AGROW>
end
% Mark outliers for removal
groupRowIdx = find(groupRows);
keepIdx(groupRowIdx(outlierMask)) = false;
end
cleanedTable = dataTable(keepIdx, :);
if isempty(outlierRecords)
outliersTable = table();
else
outliersTable = outlierRecords;
end
nRemoved = sum(~keepIdx);
nTotalOriginal = height(dataTable);
fprintf('Removed %d outliers from the data table (%.2f%% of total %d entries).\n', ...
nRemoved, 100*nRemoved/nTotalOriginal, nTotalOriginal);
end

146
Classes/GifWriter.m Normal file
View File

@@ -0,0 +1,146 @@
classdef GifWriter < handle
%GIFWRITER Simple class to create GIFs from figures (parallel-safe)
%
% Example:
% g = GifWriter('Name','mySim','Parallel',true);
% parfor i = 1:10
% plot(rand(10,1));
% g.addFrame(1,i);
% end
% g.compile(1);
properties
Name (1,:) char = 'default' % GIF base name
DelayTime (1,1) double = 0.1 % Frame delay in seconds
Parallel (1,1) logical = false % Enable parallel-safe mode
BaseDir (1,:) char % Base directory for temp frames
OutputDir (1,:) char % Final GIF output directory
end
methods
%% Constructor
function obj = GifWriter(varargin)
% Parse name/value pairs
p = inputParser;
addParameter(p, 'Name', 'default', @ischar);
addParameter(p, 'DelayTime', 0.1, @isnumeric);
addParameter(p, 'Parallel', false, @islogical);
addParameter(p, 'OutputDir', fullfile(pwd, 'gif_output'), @ischar);
parse(p, varargin{:});
obj.Name = p.Results.Name;
obj.DelayTime = p.Results.DelayTime;
obj.Parallel = p.Results.Parallel;
obj.OutputDir = p.Results.OutputDir;
obj.BaseDir = fullfile(obj.OutputDir, 'tmp', obj.Name);
if ~exist(obj.BaseDir, 'dir'), mkdir(obj.BaseDir); end
if ~exist(obj.OutputDir, 'dir'), mkdir(obj.OutputDir); end
end
%%
function addFrame(obj, figInput, pos)
%ADDFRAME Add a figure frame to the GIF (supports parallel mode)
%
% Usage:
% obj.addFrame(figHandle)
% obj.addFrame(figNum)
% obj.addFrame(figHandle, pos) % parallel mode
% obj.addFrame(figNum, pos)
%
% In parallel mode, 'pos' must be a unique integer (loop index).
if nargin < 3, pos = []; end
% --- Resolve figure handle ---
if isnumeric(figInput)
% User passed a figure number
if ~ishandle(figInput)
warning('GifWriter:addFrame', 'Figure %d not found.', figInput);
return;
end
figHandle = figure(figInput);
elseif isa(figInput, 'matlab.ui.Figure')
figHandle = figInput;
else
error('GifWriter:addFrame:InvalidInput', ...
'Input must be a figure handle or figure number.');
end
% --- Parallel-safe frame writing ---
if obj.Parallel
if isempty(pos)
error('GifWriter:ParallelMode', ...
'In parallel mode, provide a unique ''pos'' identifier.');
end
% Directory for this figure number
frameDir = fullfile(obj.BaseDir, sprintf('fig_%d', figHandle.Number));
if ~exist(frameDir, 'dir')
mkdir(frameDir);
end
% File path for this frame
frameFile = fullfile(frameDir, sprintf('frame_%05d.png', pos));
% Export to PNG (headless-safe)
exportgraphics(figHandle, frameFile, 'Resolution', 150);
else
% --- Serial mode: append directly to GIF ---
gifFile = fullfile(obj.OutputDir, ...
sprintf('%s_fig_%d.gif', obj.Name, figHandle.Number));
% Export frame temporarily
tmpFile = [tempname, '.png'];
exportgraphics(figHandle, tmpFile, 'Resolution', 150);
img = imread(tmpFile);
delete(tmpFile);
% Append to GIF
[A, map] = rgb2ind(img, 256);
if ~isfile(gifFile)
imwrite(A, map, gifFile, 'gif', ...
'LoopCount', Inf, 'DelayTime', obj.DelayTime);
else
imwrite(A, map, gifFile, 'gif', ...
'WriteMode', 'append', 'DelayTime', obj.DelayTime);
end
end
end
%% Compile all PNGs into a GIF (and clean up)
function compile(obj, fignum)
figDir = fullfile(obj.BaseDir, sprintf('fig_%d', fignum));
gifFile = fullfile(obj.OutputDir, sprintf('%s_fig_%d.gif', obj.Name, fignum));
frames = dir(fullfile(figDir, 'frame_*.png'));
if isempty(frames)
warning('GifWriter:NoFrames', 'No frames found for figure %d.', fignum);
return;
end
% Sort by frame name
[~, idx] = sort({frames.name});
frames = frames(idx);
% Combine into a GIF
for i = 1:numel(frames)
img = imread(fullfile(frames(i).folder, frames(i).name));
[A, map] = rgb2ind(img, 256);
if i == 1
imwrite(A, map, gifFile, 'gif', ...
'LoopCount', Inf, 'DelayTime', obj.DelayTime);
else
imwrite(A, map, gifFile, 'gif', ...
'WriteMode', 'append', 'DelayTime', obj.DelayTime);
end
end
% Clean up temporary frames
rmdir(figDir, 's');
end
end
end

View File

@@ -0,0 +1,7 @@
classdef Parameter < StorageParameter
methods
function obj = Parameter(varargin)
obj@StorageParameter(varargin{:});
end
end
end

View File

@@ -0,0 +1,40 @@
params = struct;
params.sir = [20:2:36];
params.laser_linewidth = [1e5 1e6 10e6];
params.pn_key = [1:3];
params.vp = [0.25,0.5,0.75,1];
params.vb = [1:0.1:1.8];
params.rop = -5:0;
wh = DataStorage(params);
wh.addStorage("ber");
wh.addStorage("rop_save");
wh.addStorage("cspr");
wh.addStorage("mod_out_pow");
cnt = 1;
for sir = params.sir
for lw = params.laser_linewidth
for pnk = params.pn_key
for vp = params.vp
for vb = params.vb
for rop = params.rop
current_ber = randn;
wh.addValueToStorage(current_ber,'ber',sir,lw,pnk,vp,vb,rop);
wh.addValueToStorage(-10,'rop_save',sir,lw,pnk,vp,vb,rop);
wh.addValueToStorage(10,'cspr',sir,lw,pnk,vp,vb,rop);
wh.addValueToStorage(-6,'mod_out_pow',sir,lw,pnk,vp,vb,rop);
end
end
end
end
end
end
wh.getStoValue('ber',20,1e5,2,0.25,1.1,-3)
wh.showInfo

View File

@@ -0,0 +1,251 @@
% Script, that shows the data management routine :-)
loadExistingWareHouse = 0;
if loadExistingWareHouse
[file, path] = uigetfile();
wh = load([path filesep file]);
wh = wh.wh;
wh.showInfo;
else
% 1) Define all your parameters, best practice directly constructs a
% structure
params = struct;
params.l = [2,10];
params.dispersion = [0];
params.sgm = [0];
% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"];
params.pol = ["alternated","paired","copolarized"];
params.p_in = [3];
params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2];
params.pmd = [0.1];
params.gamma = [0.0023];
params.realization = [1:20];
params.numchannels = [16];
params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ;
params.center_wavelength = [1285 1287 1290 1292 1295];
params.center_wavelength = 1310;
params.channelspacing = [400e9];
params.random_zdw = [0];
%wh = warehouse :-)
wh = DataStorage(params);
wh.showInfo;
wh.addStorage("ber");
wh.addStorage("totalBer");
end
%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts
%from Sebastian
%3) Once the simulation folder is around, specifiy path and analyze dirs
path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations');
allMat = getAllFilesInFolder(path,'.mat');
allErr = getAllFilesInFolder(path,'.err');
%allMat = dir([path filesep '*.mat']);
%allErr = dir([path filesep '*.err']);
if numel(allMat) == 0
warning('You defined an empty folder. Could not locate any .mat file.')
else
fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n');
fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n');
fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n');
end
%4) Now load that data
f = waitbar(0,'Please wait...');
cnt = 0;
for num = 1:numel(allMat)
fileName = allMat(num).name;
fileFolder = allMat(num).path;
fileExt = allMat(num).ext;
%
matFile = load([fileFolder filesep fileName fileExt]);
matFile = matFile.loop_data;
% ____________________________________
% FIND THE DATAPOINT CURRENTLY LOADED
zdw = 1310;
channelplan = "symmetric";
channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9;
numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_',''));
center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.'));
center_wavelength = floor(center_wavelength * 1000) / 1000;
if center_wavelength == 2192
continue
end
center_wavelength = 1310;
random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd',''));
l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_',''));
d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_',''));
if d == 0
sgm = false;
else
sgm = true;
end
if numel(regexp(fileName,'(YYYY)','match')) > 1
pol = "copolarized";
elseif numel(regexp(fileName,'(YXXY)','match')) > 1
pol = "paired";
elseif numel(regexp(fileName,'(YXYX)','match')) > 1
pol = "alternated";
else
pol = "copolarized";
end
p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_',''));
pmd = 0.1;
gamma = 0.0023;
realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r',''));
% ____________________________________
% Get the information you want from current file
rop=[];
ber = [];
for pow = 2:12
module_number = '';
for p = 1:11 %11 because there are 11 ROP branches in model
% get ROP
if p == 1
p_out = matFile.dp_optatten_para.atten;
else
p_out = matFile.("dp_optatten__"+(p)+"_para").atten;
end
p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan)));
for c = 1:numel(matFile.config.parameters.common.wavelengthPlan)
ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber;
end
totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer;
if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated"
disp("stopping here");
pause;
end
% ____________________________________
% Add value to warehouse at the correct position
wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw);
end
end
waitbar(num/numel(allMat),f,'Loading your data');
end
close(f)
% 4) Hey! the warehouse is here and (hopefully) filled with data :-)
% Create a save dialog
defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\';
defaultExt = '*.mat';
[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat');
% Check if the user pressed Cancel
if isequal(filename, 0) || isequal(pathname, 0)
disp('Save operation canceled.');
else
% Save the variable to the selected file
save(fullfile(pathname, filename), 'wh');
disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]);
end
function matFileStructArray = getAllFilesInFolder(folderPath,extension)
% Get a list of all files in the current folder
currentFolderFiles = dir(fullfile(folderPath, '*'));
% Exclude '.' and '..' directories
currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'}));
% Initialize the structure array for .mat files
matFileStructArray = struct('path', {}, 'name', {}, 'ext', {});
% Loop over each file in the current folder
for i = 1:length(currentFolderFiles)
currentFile = currentFolderFiles(i);
% Check if the current item is a file and has a .mat extension
if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true)
% If it's a .mat file, add it to the structure array
[matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name));
elseif currentFile.isdir
% If it's a directory, recursively call the function
subfolderPath = fullfile(folderPath, currentFile.name);
subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension);
% Add .mat files from the subfolder to the structure array
matFileStructArray = [matFileStructArray, subfolderMatFiles];
end
end
end

View File

@@ -0,0 +1,250 @@
% Script, that shows the data management routine :-)
loadExistingWareHouse = 0;
if loadExistingWareHouse
[file, path] = uigetfile();
wh = load([path filesep file]);
wh = wh.wh;
wh.showInfo;
else
% 1) Define all your parameters, best practice directly constructs a
% structure
params = struct;
params.l = [2, 10];
params.dispersion = [0, 3];
params.sgm = [0, 1];
% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"];
params.pol = ["alternated","paired","copolarized"];
params.p_in = [3];
params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2];
params.pmd = [0.1];
params.gamma = [0.0023];
params.realization = [1:20];
params.numchannels = [1,2,4,8,16];
params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ;
params.center_wavelength = [1285 1287 1290 1292 1295];
params.center_wavelength = 1310;
params.channelspacing = [400e9];
params.random_zdw = [0,1];
%wh = warehouse :-)
wh = DataStorage(params);
wh.showInfo;
wh.addStorage("ber");
wh.addStorage("totalBer");
end
%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts
%from Sebastian
%3) Once the simulation folder is around, specifiy path and analyze dirs
path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations');
allMat = getAllFilesInFolder(path,'.mat');
allErr = getAllFilesInFolder(path,'.err');
%allMat = dir([path filesep '*.mat']);
%allErr = dir([path filesep '*.err']);
if numel(allMat) == 0
warning('You defined an empty folder. Could not locate any .mat file.')
else
fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n');
fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n');
fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n');
end
%4) Now load that data
f = waitbar(0,'Please wait...');
cnt = 0;
for num = 1:numel(allMat)
fileName = allMat(num).name;
fileFolder = allMat(num).path;
fileExt = allMat(num).ext;
%
matFile = load([fileFolder filesep fileName fileExt]);
matFile = matFile.loop_data;
% ____________________________________
% FIND THE DATAPOINT CURRENTLY LOADED
zdw = 1310;
channelplan = "symmetric";
channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9;
numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_',''));
center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.'));
center_wavelength = floor(center_wavelength * 1000) / 1000;
if center_wavelength == 2192
continue
end
center_wavelength = 1310;
random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd',''));
l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_',''));
d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_',''));
if d == 0
sgm = false;
else
sgm = true;
end
if numel(regexp(fileName,'(YYYY)','match')) > 1
pol = "copolarized";
elseif numel(regexp(fileName,'(YXXY)','match')) > 1
pol = "paired";
elseif numel(regexp(fileName,'(YXYX)','match')) > 1
pol = "alternated";
else
pol = "copolarized";
end
p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_',''));
pmd = 0.1;
gamma = 0.0023;
realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r',''));
% ____________________________________
% Get the information you want from current file
rop=[];
ber = [];
for pow = 2:12
module_number = '';
for p = 1:11 %11 because there are 11 ROP branches in model
% get ROP
if p == 1
p_out = matFile.dp_optatten_para.atten;
else
p_out = matFile.("dp_optatten__"+(p)+"_para").atten;
end
p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan)));
for c = 1:numel(matFile.config.parameters.common.wavelengthPlan)
ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber;
end
totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer;
if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated"
disp("stopping here");
pause;
end
% ____________________________________
% Add value to warehouse at the correct position
wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw);
end
end
waitbar(num/numel(allMat),f,'Loading your data');
end
close(f)
% 4) Hey! the warehouse is here and (hopefully) filled with data :-)
% Create a save dialog
defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\';
defaultExt = '*.mat';
[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat');
% Check if the user pressed Cancel
if isequal(filename, 0) || isequal(pathname, 0)
disp('Save operation canceled.');
else
% Save the variable to the selected file
save(fullfile(pathname, filename), 'wh');
disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]);
end
function matFileStructArray = getAllFilesInFolder(folderPath,extension)
% Get a list of all files in the current folder
currentFolderFiles = dir(fullfile(folderPath, '*'));
% Exclude '.' and '..' directories
currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'}));
% Initialize the structure array for .mat files
matFileStructArray = struct('path', {}, 'name', {}, 'ext', {});
% Loop over each file in the current folder
for i = 1:length(currentFolderFiles)
currentFile = currentFolderFiles(i);
% Check if the current item is a file and has a .mat extension
if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true)
% If it's a .mat file, add it to the structure array
[matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name));
elseif currentFile.isdir
% If it's a directory, recursively call the function
subfolderPath = fullfile(folderPath, currentFile.name);
subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension);
% Add .mat files from the subfolder to the structure array
matFileStructArray = [matFileStructArray, subfolderMatFiles];
end
end
end

View File

@@ -0,0 +1,415 @@
%automate plots
[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat");
wh = load([path filesep file]);
wh = wh.wh;
% fields = fieldnames(wh.parameter);
% for k = 1:numel(fields)
% oldParam = wh.parameter.(fields{k});
% % copy over the properties to your new class
% wh.parameter.(fields{k}) = StorageParameter(...
% oldParam.Name, oldParam.values);
% end
%
plotJob = struct();
width = 350;
height = 200;
plotJob.Position = [100 100 width 100+height];
cols = cbrewer2("paired",12);
plotJob.color = cols(1,:);
plotJob.l = 10;
plotJob.ch = 16;
plotJob.d = 0;
plotJob.sgm = 0;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 1;
plotJob.plot_ber_curve = 0;
plotJob.plot_3dber_curve = 0;
plotJob.plot_violin = 1;
plotJob.plot_wavelength_sweep = 0;
plotJob.plot_wavelength_sweep_failure_rate = 0;
plotJob.dataStatArg = 'Lineplot with quartiles';
plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'a';
plotJob.title = 'title';
plotJob.figName = '16 Chann';
plotJob.xAxisLabel = 'ROP per Channel in dBm';
plotJob.yAxisLabel = 'BER';
%%
% createbercurves(wh,plotJob)
P = [3];
for i = 1:2
plotJob.p_in = P(i);
createviolinplots(wh,plotJob);
end
% createsweepplots(wh,plotJob);
%% 1
function createbercurves(wh,plotJob)
width = 1650;
height = 400;
s = 100;
e = 100;
cols = cbrewer2("paired",12);
numRows = 2;
numCols = 4;
plotJob.figName = '16 Chann_200G';
plotJob.channelspacing = 400e9;
plotJob.ch = 16;
Len = [2,2,2,2,10,10,10,10];
Pol = ["copolarized","alternated","paired","copolarized","copolarized","alternated","paired","copolarized"];
Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",];
D = [0,0,0,3,0,0,0,3];
Sgm = [0,0,0,1,0,0,0,1];
colidx = [4,8,6];
P_launch = [3,6];
fig = figure('Name',plotJob.figName);
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 18 7];
t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact');
for idx = 1:(numRows * numCols)
% Create subplot
% sp = subplot(numRows, numCols, idx);
nexttile;
plotJob.l = Len(idx);
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
plotJob.randzdw = 1;
for i = 1:length(P_launch)
plotJob.p_in = P_launch(i);
plotJob.color = cols(colidx(i),:);
hold on
plotCurve(wh, plotJob);
end
%
if idx ~= 1 && idx ~= 5 % For example, hide y-axis for subplot 1
set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels
set(gca,'YGrid','on');
set(gca, 'YLabel', []);
end
if idx ~= 5 && idx ~= 6 && idx ~= 7 && idx ~= 8
set(gca, 'XLabel', []);
set(gca, 'XTickLabel', []);
end
grid on
g = gca;
pos = g.Position;
if idx <= 4
title(Title(idx),'FontSize',8);
% a = annotation('textbox', pos-[0.0020 -0.1434 0.0947 0.3121], 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on');
% a = annotation('textbox', pos, 'String', "2 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on');
else
% a = annotation('textbox', pos, 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on');
% a = annotation('textbox', pos, 'String', "10 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on');
end
end
% Create textbox
annotation(fig,'textbox',...
[0.0696078431372547 0.246851385390432 0.0656862745098043 0.0453400503778337],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.288235294117647 0.239294710327459 0.0656862745098043 0.0453400503778338],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.516666666666666 0.241813602015117 0.0656862745098042 0.0453400503778338],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.742156862745097 0.236775818639802 0.0656862745098042 0.0453400503778339],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.071996471804854 0.578899159967702 0.0656862745098039 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.29596893566113 0.576253721089996 0.0656862745098041 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.524883914268912 0.580785315705112 0.0656862745098041 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.753758338909501 0.581291504465311 0.0656862745098037 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
a=sgtitle(['N=',num2str(plotJob.ch),'; $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSIze',10);
a.Interpreter = "latex";
lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex');
lgd.NumColumns = 3;
lgd.Layout.Tile = 'south';
copygraphics(t,'BackgroundColor','none');
end
%% 2
function createviolinplots(wh,plotJob)
width = 350;
height = 200;
s = 100;
e = 100;
cols = cbrewer2("paired",12);
numRows = 1;
numCols = 4;
plotJob.ch = 16;
plotJob.randzdw = 1;
Pol = ["copolarized","copolarized","alternated","paired",];
Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."];
D = [0,3,0,0];
Sgm = [0,1,0,0];
colidx = [4];
Len = plotJob.l;
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
% fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
% t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on; grid on;
% t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
end
for idx = 1:(numRows * numCols)
% Create subplot
subplot(numRows, numCols, idx);
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
for i = 1
plotJob.color = cols(colidx(i),:);
plotJob.l = Len(i);
hold on
plotViolin(wh, plotJob);
end
if idx ~= 1 % For example, hide y-axis for subplot 1
%set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels
set(gca, 'YGrid','on');
set(gca, 'YLabel', []);
end
if idx <= 4
title(Title(idx));
end
end
% Create textbox
annotation(fig,'textbox',...
[0.300019607843137 0.816120906801009 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.530411764705882 0.81612090680101 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.755901960784313 0.816120906801011 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.0696274509803918 0.584382871536529 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex');
% lgd.NumColumns = 3;
% lgd.Layout.Tile = 'south';
end
%% 3
function createsweepplots(wh,plotJob)
width = 650;
height = 200;
s = 100;
e = 100;
plotJob.Position = [0 0 width e+height];
cols = cbrewer2("paired",12);
numRows = 1;
numCols = 4;
plotJob.channelspacing = 200e9;
plotJob.ch = 16;
plotJob.randzdw = 1;
plotJob.l = 10;
plotJob.p_in = 3;
Pol = ["copolarized","alternated","paired","copolarized"];
Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",];
D = [0,0,0,3];
Sgm = [0,0,0,1];
Channelspacing = [200e9, 200e9];
PlotTypeArg = ["--","-"];
colidx = [6,8,2,4];
Len = [2,10];
plotJob.figName = [num2str(plotJob.ch),num2str(plotJob.channelspacing*1e-9),num2str(plotJob.p_in),'...'];
plotJob.figName = "10km 400ghz";
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 18 7];
for j = 1
plotJob.channelspacing = Channelspacing(j);
plotJob.plotTypeArg = PlotTypeArg(j);
for idx = 1:4
plotJob.color = cols(colidx(idx),:);
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
plotJob.displayname = [char(plotJob.pol)];
hold on
plotBerVsZdwFailureRate(wh, plotJob);
end
end
legend('Location', 'southoutside', 'Orientation', 'horizontal');
%plot channel positions
hold on
chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, 1310);
xline(chpos,'LineWidth',2,'Alpha',0.4,'HandleVisibility','off');
chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, chpos(4));
xline(chpos,'LineWidth',2,'LineStyle','--','Alpha',0.1,'HandleVisibility','off');
title(['N=',num2str(plotJob.ch),' $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSize',10,'Interpreter','latex');
end

View File

@@ -0,0 +1,137 @@
% Select dataset
[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat");
wh = load(fullfile(path, file));
wh = wh.wh;
%% --- Plot Settings ---
cols = cbrewer2("Paired", 12);
plotJob = struct();
plotJob.Position = [100 100 600 400];
plotJob.channelspacing = 400e9;
plotJob.ch = 16;
plotJob.d = 0;
plotJob.sgm = 0;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.randzdw = 1;
plotJob.plot_ber_curve = 1;
plotJob.xAxisLabel = 'ROP per $\lambda$ [dBm]';
plotJob.yAxisLabel = 'BER';
plotJob.figName = 'avg BER_vs_Plaunch_combined';
plotJob.dataStatArg = 'Lineplot with quartiles';%'All Channels; mean(PMD Realizations)';Lineplot with quartiles
plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'bla';
% --- Parameter combinations ---
Len = [2, 10];
Pol = ["copolarized", "copolarized", "alternated", "paired"];
Title = ["CoPol","LS", "API", "PPI"];
D = [0, 3, 0, 0];
Sgm = [0, 1, 0, 0];
% Pol = ["copolarized", "copolarized"];
% Title = ["CoPol","LS"];
% D = [0, 3];
% Sgm = [0, 1];
colidx = [6,4,2,2]; % color indices for different schemes
P_launch = [0,3,6]; % input power sweep
%% --- Create Figure ---
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
% fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
% t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on; grid on;
% t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
end
dsa = ["AVG"];
for d = 1
plotJob.dataStatArg = dsa(d);
cnt = 1;
for l = 1:numel(Len)
subplot(1,2,cnt);
cnt = cnt+1;
for s = 1:length(P_launch)
for p = 1:numel(Title)
plotJob.l = Len(l);
plotJob.pol = Pol(p);
plotJob.d = D(p);
plotJob.sgm = Sgm(p);
% color + style per length
baseColor = cols(colidx(p), :);
if s == 1
plotJob.linestyle = '-';
elseif s == 2
plotJob.linestyle = '--';
else
plotJob.linestyle = ':';
end
if p == 1
% plotJob.linestyle = '-';
plotJob.markerstyle = 'o';
plotJob.markersize = 2;
elseif p == 2
% plotJob.linestyle = ':';
plotJob.markerstyle = 'square';
plotJob.markersize = 2;
elseif p == 3
% plotJob.linestyle = '-';
plotJob.markerstyle = 'x';
plotJob.markersize = 6;
else
% plotJob.linestyle = '-';
plotJob.markerstyle = 'diamond';
plotJob.markersize = 2;
end
% if d == 1
% plotJob.linestyle = '-';
% else
% plotJob.linestyle = ':';
% plotJob.markerstyle = 'none';
% end
plotJob.p_in = P_launch(s);
plotJob.displayname = sprintf('%s',Title(p));
plotJob.color = baseColor;% * (1 - 0.15*(p-1)); % slight shade for powers
plotCurve(wh, plotJob);
% h = findobj(gca,'Type','Line','-not','Tag','FEC');
% set(h(p),'DisplayName',sprintf('%s (%.0f km, %.0f dBm)',Title(p),Len(l),P_launch(s)));
% title(sprintf('%d km; %d Channels, \Delta f = %.0f GHz', ...
% plotJob.l, plotJob.ch, plotJob.channelspacing*1e-9));
end
end
end
end
set(gca, 'YScale', 'log');
xlabel(plotJob.xAxisLabel);
ylabel(plotJob.yAxisLabel);
% legend('Interpreter','latex','NumColumns',2,'Location','southoutside');
grid on; box on;
copygraphics(fig, 'BackgroundColor','none');

View File

@@ -0,0 +1,83 @@
%automate plots
[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\");
wh = load([path filesep file]);
wh = wh.wh;
plotJob = struct();
width = 350;
height = 200;
plotJob.Position = [100 100 width 100+height];
cols = cbrewer2("paired",12);
plotJob.color = cols(1,:);
plotJob.l = 1;
plotJob.ch = 1;
plotJob.sgm = 1;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 0;
plotJob.plot_ber_curve = 1;
plotJob.plot_3dber_curve = 0;
plotJob.plot_violin = 0;
plotJob.plot_wavelength_sweep = 0;
plotJob.plot_wavelength_sweep_failure_rate = 0;
plotJob.dataStatArg = 'Lineplot with quartiles';
plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'a';
plotJob.title = 'title';
plotJob.figName = '1 Chann__';
plotJob.xAxisLabel = 'ROP per Channel in dBm';
plotJob.yAxisLabel = 'BER';
plotJob.d = 0;
xAxis = wh.parameter.p_out.values;
D = wh.parameter.dispersion.values;
figure()
ber_ = [];
for d_ = 0:39
if d_ == 0
plotJob.sgm = 0;
ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)';
else
plotJob.sgm = 1;
ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)';
end
hold on
plot(xAxis,ber_(d_+1,:))
set(gca,'yscale','log');
end
yline(3.8e-3);
hdfec = 3.8e-3.*ones(size(xAxis));
for i = 1:size(ber_,1)
ber_series = ber_(i,:);
a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]);
cross(i) = a(2);
end
col = cbrewer2('Paired',8);
figure()
plot(D,cross,'Marker','o','MarkerSize',5,'MarkerEdgeColor',[1,1,1],'MarkerFaceColor',col(2,:),'Color',col(1,:),'LineWidth',1);
grid minor
xlabel('Accumulated Dispersion')
ylabel('Required ROP to reach FEC limit in dB')
line([D(16),D(16)],[-10,cross(16)],'linestyle','--')
line([0,D(16)],[cross(16),cross(16)],'linestyle','--')
line([D(29),D(29)],[-10,cross(29)],'linestyle','--')
line([0,D(29)],[cross(29),cross(29)],'linestyle','--')

View File

@@ -0,0 +1,52 @@
wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_with_variation.mat");
wh = wh.wh;
lambda = 1295;
figure(3)
plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'variation']);
yline(3.8e-3,'HandleVisibility','off');
legend
set(gca,'yscale','log');
grid(gca,'on');
grid(gca,'minor');
grid minor
fontsize(gca,8,"points")
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(gca,'TickLabelInterpreter','latex')
ylim([1e-5,0.5]);
xlim([min(xAxis),-3]);
wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_no_variation.mat");
wh = wh.wh;
figure(3)
hold on
plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'no variation']);
yline(3.8e-3,'HandleVisibility','off');
legend
set(gca,'yscale','log');
grid(gca,'on');
grid(gca,'minor');
grid minor
fontsize(gca,8,"points")
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(gca,'TickLabelInterpreter','latex')
ylim([1e-5,0.5]);
xlim([min(xAxis),-3]);
function ber = getber(wh,lambda)
realization = wh.parameter.realization.values(1:end);
xAxis = wh.parameter.p_out.values;
ber = [];
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
temp = wh.getStoValue('ber',10,0,0,"copolarized",3,p_out,0.1,0.0023,realization,1,lambda,400e9,1);
ber(xl) = mean(temp,'all');
end
end

View File

@@ -0,0 +1,61 @@
function generatePlots(wh,plotJob)
% 0) Test for valid query:
p_out = wh.parameter.p_out.values(1);
realization = 9;
if 1 %~isempty(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310))
% test violin
baseName = plotJob.figName;
width = 350;
height = 200;
s = 100;
e = 100;
if plotJob.plot_ber_curve
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotCurve(wh, plotJob);
end
if plotJob.plot_3dber_curve
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plot3dCurve(wh, plotJob);
end
if plotJob.plot_wavelength_sweep
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotBerVsZDW(wh, plotJob);
end
if plotJob.plot_wavelength_sweep_failure_rate
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotBerVsZdwFailureRate(wh, plotJob);
end
if plotJob.plot_violin
plotJob.Position = [s+width 100 width e+height];
plotJob.figName = [baseName, ' violin'];
plotViolin(wh, plotJob);
end
if 0
%2) plotHistogram
plotJob.Position = [s+2*width 100 width e+height];
plotJob.figName = [baseName, ' FEC crossing'];
plotHistogram(wh,plotJob)
end
else
warndlg('The requested Datapoint is not available... This can occur for some edgecase constellations... ')
end
end

View File

@@ -0,0 +1,248 @@
function plotCurve(wh,plotJob)
%PLOTCURVE Summary of this function goes here
% Detailed explanation goes here
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 499;
% realization = 0:7;
else
realization = wh.parameter.realization.values(1:end);
end
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = max(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "AVG"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "Best"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = min(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(:,xl) = mean(temp,1,"omitnan").';
linew = 1;
markersz = 3;
linestyle = '--';
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,"all","omitnan").';
upperq(xl) = quantile(temp,0.9,"all");
lowerq(xl) = quantile(temp,0.1,"all");
if lowerq(xl) == 0
lowerq(xl) = lowerq(xl-1);
end
% upperq(xl) = 0.5*std(tmp,1,'all','omitnan');
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
% upperq(xl) = max(dataNoNans);
% lowerq(xl) = min(dataNoNans);
% upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
% lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
linew = 1;
markersz = 1;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).',[],1);
ber(1:size(tmp,1),xl) = tmp;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
xAxis = xAxis;
% Plot Data
if string(plotJob.plotTypeArg) == "Scatter"
for rlz = 1:size(ber,1)
if rlz < size(ber,1)
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off');
else
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
end
elseif string(plotJob.plotTypeArg) == "Lines"
% if ~anynan(ber)
% [xAxis,ber] = interpCurve(xAxis, ber);
% end
for rlz = 1:size(ber,1)
%
if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
ch = mod(rlz,plotJob.ch);
if ch == 0; ch = plotJob.ch; end
else
ch = plotJob.dataStatArg;
end
if rlz <= size(ber,1)
s = plot3(xAxis,repmat(ch,1,numel(xAxis)),ber(rlz,:),linestyle,'Marker',"o",'MarkerSize',markersz,'MarkerFaceColor',plotJob.color,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber));
s.DataTipTemplate.DataTipRows(1);
s.DataTipTemplate.DataTipRows(2) = [];
else
if string(plotJob.dataStatArg) == "Lineplot with quartiles"
[hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-*', 'alpha','Color',col,'transparency', 0.2);
hp.LineWidth = 1.2;
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', col);
else
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]);
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber));
s.DataTipTemplate.DataTipRows(1)
s.DataTipTemplate.DataTipRows(2) = [];
end
end
end
end
% Draw FEC Threshold Line
%get x data of first children:
%get all linear Values
if 0
linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric");
linear = mean(linear,2);
lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline');
%
if isempty(lincurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
end
feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$');
%
if isempty(feccurve)
xdata = xAxis;
hdfec = 3.8e-3.*ones(size(xdata));
for ch = 1:plotJob.ch
plot3(xdata,repmat(ch,1,numel(xAxis)),hdfec,':','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',0.5,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off');
end
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
% Figure Settings
%title(AxesMain,plotJob.title,"Interpreter","none");
xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","none");
ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","none");
set(AxesMain,'zscale','log');
grid(AxesMain,'on');
grid(AxesMain,'minor');
grid minor
view(AxesMain,[42.0619302949062 23.4176470588235]);
%legend(AxesMain);
fontsize(AxesMain,8,"points")
fontname(AxesMain,"Arial")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','none')
set(AxesMain.Legend,'Interpreter','none')
% set(gcf,'Units','centimeters')
% set(gcf,'Position',[2 2 9 4.5])
zlim([1e-4,0.3]);
xlim([min(xAxis),-3]);
annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points")
hold off
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -0,0 +1,300 @@
function plotBerVsZDW(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% get all center wavelengths
wavelengths = wh.parameter.center_wavelength.values;
% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths));
% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths));
%get BER values for query
for w = 2:numel(wavelengths)
for xl = 1:numel(xAxis)
c_wavelen = wavelengths(w);
p_out = xAxis(xl);
% dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength
temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw));
ber(1:size(temp,1),:,xl,w) = temp;
end
end
hdfec = 3.8e-3.*ones(size(xAxis));
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
cf_tot = [];
cf_ = [];
S = [];
Stot = [];
S_chann = [];
cf_chann = [];
cnt = 0;
%get fec thresholds
%linear = squeeze(linear);
for c_wavelen = 1:size(ber,4)
for realiz = 1:size(ber,1)
for chann = 1:size(ber,2)
%finde Schnittpunkt zwischen FEC und BER Kurve
temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).';
if ~all(temp_ber == 0)
%nur wenn nicht alles nullen sind
crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]);
else
continue
end
%Req. FEC Ergebnis einsortieren
if ~isempty(crossing_ch)
if crossing_ch(2) == 0
print("d")
end
S(realiz,chann,c_wavelen) = crossing_ch(2);
else
S(realiz,chann,c_wavelen) = -1;
cnt = cnt +1;
end
end
end
end
temp_max = -inf;
for i = 1:plotJob.ch
hold on
%S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength
%squeeze a channel:
temp_data = squeeze(S(:,i,:));
%remove realizations that have no entry (only zero)
temp_data = removeZeros(temp_data);
%replace zeros with NAN (e.g. for the wavelengths that have missing realizations)
temp_data(temp_data==0) = NaN;
%plot required ROP for channel and all realizations that cross the
%FEC limit
scatter(wavelengths,temp_data ,5,plotJob.color,'Marker','.');
% %plot mean per channel
% temp_mean = mean(temp_data,'omitnan');
% hold on
% plot(wavelengths,temp_mean,'Marker','*');
%get max overall value
temp_max = max(temp_max,max(temp_data));
end
%plot mean overall
mean_overall = squeeze(mean(S,2));
mean_overall(mean_overall==0) = NaN;
%mean_overall(mean_overall==-1) = NaN;
mean_overall=mean(mean_overall,1,'omitnan');
plot(wavelengths,mean_overall,'Color',plotJob.color);
%plot max overall
scatter(wavelengths,temp_max ,35,plotJob.color,'Marker','v');
%plot channel positions
hold on
chpos = calcWavelengthPlan(plotJob.ch, 400e9, 1310);
xline(chpos,'LineWidth',2,'Alpha',0.2);
chpos = calcWavelengthPlan(plotJob.ch, 400e9, chpos(4));
xline(chpos,'LineWidth',2,'Alpha',0.2);
fig.Position = plotJob.Position;
ylabel('Penalty in dB');
xlabel('Wavelength in nm');
xlim([min(wavelengths),max(wavelengths) ]);
grid minor;
set(gca, 'color', 'none');
legend = [];
fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
%
%
%
%
%
% distinct_cf = unique(cf_chann);
%
% for i = 1:length(distinct_cf)
% indices = find(cf_chann==distinct_cf(i));
% cf(i) = distinct_cf(i);
% worst_fec_cross(i) = max(S_chann(indices));
% avg_fec_cross(i) = mean(S_chann(indices));
% end
%
% avg_fec_cross = smooth(avg_fec_cross,5);
%
% figure(224)
% hold on
% scatter(cf_,S,10.*abs(S-mean(S)).*ones(size(S)),'DisplayName',['AVG'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.');
% hold on
% scatter(cf(2:end),worst_fec_cross(2:end),15,'DisplayName',['Worst'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.','HandleVisibility','off');
% plot(cf(2:end),avg_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',1,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% plot(cf(2:end),worst_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',0.5,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9);
% set(gca,'xtick',sort(ghzgrid))
% xlim([min(cf(cf~=0)), 1310.1]);
%
%
% % With matlab internal errorbar function...
% figure(221)
% %plot(cf,avg_fec_cross,'DisplayName',['AVG'],'LineWidth',1,'Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% hold on
% %plot(cf_tot,min(S_chann(1:length(cf_tot),:),[],2),'DisplayName',['AVG'],'LineWidth',1,'LineStyle',':','Color',col,'Marker','^','MarkerFaceColor',col,'MarkerSize',2);
% plot(cf,worst_fec_cross,'DisplayName',['AVG'],'LineWidth',2,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9);
% set(gca,'xtick',sort(ghzgrid))
% xlim([1302, 1310.1]);
% xline(ghzgrid,'LineStyle',':','Color',[.7 .7 .7]);
%with
% Stot = movmean(Stot,5);
% figure(222)
% [hl,hp] = boundedline(cf_tot,Stot,[(Stot'-min(S_chann(1:length(cf_tot),:),[],2)),(max(S_chann(1:length(cf_tot),:),[],2)-Stot')], 'alpha','Color',col,'transparency', 0.05);
% ho = outlinebounds(hl,hp);
% set(ho, 'linestyle', ':', 'color', col, 'marker', '.','linewidth',0.5);
% hold on
%
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:12].*400e9);
% xline(ghzgrid);
%plot the total ber
% %figure(22);
% hold on;
% b= movmean(Stot,3);
% plot(AxesMain,cf_tot,b,'LineWidth',2,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'Color',col,'Marker','o');
% hold on
%scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
%ylim(AxesMain,[-9.3 -7]);
% for i = 1:numel(chp)
% hold on
% xline(AxesMain,chp(i),'Color',colr(i,:),'DisplayName',['CH: ', num2str(i)],'LineWidth',1.5);
% hold off
% end
%
% a = movmean(sortrows([zdw_; S]'),10,'Endpoints','discard');
%
% sorted = sortrows([zdw_; S]');
% figure(2)
% scatter(sorted(:,1),sorted(:,2))
%
% ber_sorted = sort(S);
% mean(ber_sorted);
% std(ber_sorted);
% z1 = [];
% penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2;
% for i = 1:length(penalty)
% l = penalty(i);
% if i == 1
% z1 = [z1 sum(ber_sorted(1,:)<l ) / length(ber_sorted) ];
% else
% z1 = [z1 sum(ber_sorted(1,:)<l & ber_sorted(1,:)>l-0.01) / length(ber_sorted) ];
% end
%
% end
%
% penalty_higherthan = 0.5;
% probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan)));
% disp(['A penalty of more than 1dB has a probability of: ', num2str(probability)]);
% %
% stem(AxesMain,penalty,z1,"filled",'Marker','o','MarkerSize',2,'Color',col);
%
%
% [f1,x1]=ecdf(S(end,:));
% %figure(23);plot(AxesMain,x1,f1,'r','LineWidth',3, 'Color',col);
%
% %plot(AxesMain,a(:,1),a(:,2),'Color',col+1,'Parent', AxesMain(1));
%
% % histogram(AxesMain,S,1000,'EdgeColor','none','FaceAlpha',0.4);
%
%
%
% %
% %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% hold on
% %scatter(AxesMain,zdw_chann(:,1),mean(S_chann,2),'Marker','diamond','MarkerEdgeColor',col,'MarkerFaceAlpha',0.6,'LineWidth',7,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% hold off
% %
% % for rlz = 1:size(S,2)
% % zdwval = zdw_(rlz);
% % feccrossing = S(rlz);
% % scatter(AxesMain,zdwval,feccrossing,10,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% % end
%
% xline([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618]);
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -0,0 +1,157 @@
function plotBerVsZdwFailureRate(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% get all center wavelengths
wavelengths = wh.parameter.center_wavelength.values;
%wavelengths = wavelengths(2:end);
% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths));
% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths));
%get BER values for query
for w = 1:numel(wavelengths)
for xl = 1:numel(xAxis)
c_wavelen = wavelengths(w);
p_out = xAxis(xl);
% dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength
temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw));
ber(1:size(temp,1),:,xl,w) = temp;
end
end
hdfec = 3.8e-3.*ones(size(xAxis));
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
cf_tot = [];
cf_ = [];
S = [];
Stot = [];
S_chann = [];
cf_chann = [];
cnt = 0;
%get fec thresholds
%linear = squeeze(linear);
for c_wavelen = 1:size(ber,4)
for realiz = 1:size(ber,1)
for chann = 1:size(ber,2)
%finde Schnittpunkt zwischen FEC und BER Kurve
temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).';
if ~all(temp_ber == 0)
%nur wenn nicht alles nullen sind
crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]);
else
continue
end
%Req. FEC Ergebnis einsortieren
if ~isempty(crossing_ch)
if crossing_ch(2) == 0
print("d")
end
S(realiz,chann,c_wavelen) = crossing_ch(2);
else
S(realiz,chann,c_wavelen) = -1;
cnt = cnt +1;
end
end
end
end
temp_max = -inf;
sum_FEC_not_crossed=[];
sum_FEC_crossed=[];
threshold = plotJob.p_in - 10;
for i = 1:plotJob.ch
hold on
%S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength
%squeeze a channel:
temp_data = squeeze(S(:,i,:));
%remove realizations that have no entry (only zero)
temp_data = removeZeros(temp_data);
%replace zeros with NAN (e.g. for the wavelengths that have missing realizations)
temp_data(temp_data==0) = NaN;
%for current channel
FEC_crossed = temp_data < threshold & ~isnan(temp_data);
FEC_not_crossed = temp_data >= threshold & ~isnan(temp_data);
%sum over channels for overall picture
sum_FEC_not_crossed(i,:) = sum(FEC_not_crossed);
sum_FEC_crossed(i,:) = sum(FEC_crossed);
failure_rate_channelwise(i,:) = sum_FEC_not_crossed(i,:)./ ( sum_FEC_crossed(i,:) + sum_FEC_not_crossed(i,:));
end
failure_rate_total = sum(sum_FEC_not_crossed,1) ./ ( sum(sum_FEC_crossed,1) + sum(sum_FEC_not_crossed,1) );
% plot failure rate (nbetween 0 and 1)
plot(wavelengths,failure_rate_total,'Color',plotJob.color,'LineWidth',1,'LineStyle',plotJob.plotTypeArg,'Marker','x','MarkerSize',5,'MarkerFaceColor',plotJob.color,'DisplayName',plotJob.displayname);
%plot max overall
%scatter(wavelengths,failure_rate_channelwise ,35,plotJob.color,'Marker','.');
ylabel('Failure Rate of Link');
xlabel('Wavelength in nm');
xlim([min(wavelengths),max(wavelengths) ]);
ylim([0,1]);
grid minor;
set(gca, 'color', 'none');
legend = [];
% fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 12 5 7];
try
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
end
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -0,0 +1,51 @@
function plotChannelSpacingAna(wh,plotJob)
xAxis = wh.parameter.p_out.values;
realization = wh.parameter.realization.values(1:end);
channelsp = wh.parameter.channelspacing.values(1:end);
channelsp = [200 400].*1e9;
for ch = 1:2
channspacing = channelsp(ch);
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing);
curzdw = wh.getStoValue('zdw',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing);
ber(1:size(curber,1),1:size(curber,2),xl) = curber;
zdw(1:size(curber,1),1,xl) = curzdw;
end
ber = squeeze(mean(ber,1));
zdw = squeeze(mean(zdw,1));
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
wavelength={};
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
S = [];
S_chann = [];
wl = round([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618],2);
wl = 1:16;
wl = [1.2930 1.2953 1.2975 1.2998 1.3020 1.3043 1.3066 1.3089 1.3111 1.3134 1.3157 1.3181 1.3204 1.3227 1.3251 1.3274];
a = InterX([hdfec(:)';xAxis],[mean(ber,1);xAxis]);
if ~isempty(a)
s(ch) = a(2);
else
s(ch) = NaN;
end
end
figure(2224)
hold on
plot(channelsp,s,'LineWidth',1,'Color',plotJob.color,'Marker','o');

View File

@@ -0,0 +1,294 @@
function plotCurve(wh,plotJob)
%PLOTCURVE Summary of this function goes here
% Detailed explanation goes here
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
% fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 499;
% realization = 0:7;
else
realization = wh.parameter.realization.values(1:end);
end
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
markerstyle = 'o';
linestyle = '-';
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = quantile(temp,0.9,"all");
% ber(xl) = max(temp,[],'all');
linew = 1.0;
markersz = plotJob.markersize;
markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "AVG"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,'all');
linew = 1.0;
markersz = plotJob.markersize;
markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "Best"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = min(temp,[],'all');
linew = 1.0;
markersz = plotJob.markersize;
markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(:,xl) = mean(temp,1,"omitnan").';
linew = 1;
markersz = 1;
markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,"all","omitnan").';
upperq(xl) = quantile(temp,0.99,"all");
lowerq(xl) = quantile(temp,0.04,"all");
% upperq(xl) = 0.5*std(tmp,1,'all','omitnan');
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
upperq(xl) = max(temp(:));
lowerq(xl) = min(temp(:));
if lowerq(xl) == 0
lowerq(xl) = 1e-8;
end
% upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
% lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
linew = 1;
markersz = 1;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).';
tmp = reshape(raw_fetch,[],1);
ber(1:size(tmp,1),xl) = tmp;
ber_per_chann(:,:,xl) = raw_fetch;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
%routine to remove total outliers (here those wehere the rop curve has a mean BER greater than 0.1)
% ber_per_chann_clean = NaN(size(ber_per_chann));
% for ch = 1:size(ber_per_chann,1)
% bla = squeeze(ber_per_chann(ch,:,:));
% ber_per_chann(ch,find(mean(bla,2)>0.25),:) = NaN;
% cleaned = rmoutliers(bla,"mean",'ThresholdFactor',2);
%
% ber_per_chann_clean(ch,1:size(cleaned,1),1:size(cleaned,2)) = cleaned;
%
% end
%
% ber = [];
% for rop = 1:size(ber_per_chann,3)
% temp = squeeze(ber_per_chann_clean(:,:,rop));
% ber(rop) = mean(temp,"all","omitnan").';
% upperq(rop) = quantile(temp,0.9,"all");
% lowerq(rop) = quantile(temp,0.1,"all");
% end
xAxis = xAxis;
% Plot Data
if string(plotJob.plotTypeArg) == "Scatter"
for rlz = 1:size(ber,1)
if rlz < size(ber,1)
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off');
else
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
end
elseif string(plotJob.plotTypeArg) == "Lines"
% if ~anynan(ber)
% [xAxis,ber] = interpCurve(xAxis, ber);
% end
cols = cbrewer2('RdBu',size(ber,1));
for rlz = 1:size(ber,1)
%
if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
ch = mod(rlz,plotJob.ch);
if ch == 0; ch = plotJob.ch; end
else
ch = plotJob.dataStatArg;
end
if rlz < size(ber,1)
col = cols(rlz,:);
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"none",'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber));
s.DataTipTemplate.DataTipRows(1);
s.DataTipTemplate.DataTipRows(2) = [];
else
if string(plotJob.dataStatArg) == "Lineplot with quartiles"
[hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-o','alpha','Color',col,'transparency', 0.06,'linewidth',0.7);
hl.MarkerFaceColor = col;
hl.MarkerSize = 2;
set(hp,'HandleVisibility','off');
%hp.LineWidth = 1.2;
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.6);
set(ho,'HandleVisibility','off');
% errorbar(xAxis,ber(rlz,:),ber(rlz,:)-lowerq(rlz,:),upperq(rlz,:)-ber(rlz,:),'-o','Color',col,'linewidth',0.7);
else
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',markerstyle,'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]);
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber));
s.DataTipTemplate.DataTipRows(1)
s.DataTipTemplate.DataTipRows(2) = [];
end
end
end
end
% Draw FEC Threshold Line
%get x data of first children:
%get all linear Values
if 0
linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric");
linear = mean(linear,2);
lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline');
%
if isempty(lincurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
end
feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$');
%
if isempty(feccurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,hdfec,'--','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',1,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
% Figure Settings
%title(AxesMain,plotJob.title,"Interpreter","none");
xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","latex");
ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","latex");
set(AxesMain,'yscale','log');
grid(AxesMain,'on');
grid(AxesMain,'minor');
grid minor
%legend(AxesMain);
fontsize(AxesMain,8,"points")
% fontname(AxesMain,"Arial")
% fig.Position = plotJob.Position;
% fig.Units = "centimeters";
% fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
% set(gcf,'Units','centimeters')
% set(gcf,'Position',[2 2 9 4.5])
ylim([1e-5,0.3]);
xlim([-10,-4]);
% annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points")
hold off
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -0,0 +1,151 @@
function plotHistogram(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
ber(xl) = max(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all');
linew = 2.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "AVG"
ber(xl) = mean(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),'all');
linew = 2.0;
markersz = 3;
linestyle = ':';
elseif string(plotJob.dataStatArg) == "Best"
ber(xl) = min(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all');
linew = 2.0;
markersz = 3;
linestyle = ':';
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
tmp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp));
if numel(tmp(tmp==0)) ~= 0
disp('Removed all zero values!');
tmp(tmp==0) = NaN;
end
ber(:,xl) = mean(tmp,1,"omitnan").';
linew = 0.7;
markersz = 2;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)).',[],1);
ber(1:size(tmp,1),xl) = tmp;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
disp('Removed all zero values!');
ber(ber==0) = NaN;
if ~anynan(ber)
[xAxis,ber] = interpCurve(xAxis, ber);
end
% plot FEC Crossing as histogram
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
for i = 1:size(ber,1)
a = InterX([hdfec;xAxis],[ber(i,:);xAxis]);
if ~isempty(a)
S(:,i) = a;
end
end
%% SUB 1
AxesMain = subplot(2,1,1);
hold on
if ~isempty(S)
histogram(S(end,:),300,'Normalization','probability','FaceColor',col,'EdgeColor',col,'Parent',AxesMain,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'FaceAlpha',0.4,'EdgeAlpha',0.4);
end
xlim([-3 ,9 ]);
ylim([0 .10]);
% Figure Settings
title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex');
xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex');
ylabel('PDF')
grid(AxesMain,'on');
grid(AxesMain,'minor');
%legend(AxesMain,'Interpreter','latex');
fontsize(AxesMain,24,"pixels")
hold off
%% SUB 2
AxesMain = subplot(2,1,2);
if ~isempty(S)
hold on
[f1,x1]=ecdf(S(end,:));
plot(x1,f1,'r','LineWidth',3, 'Color',col,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
xlim([-3 ,9 ]);
ylim([0 1]);
% Figure Settings
title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex');
xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex');
ylabel('CDF')
grid(AxesMain,'on');
grid(AxesMain,'minor');
fontsize(AxesMain,24,"pixels")
%legend(AxesMain,'Interpreter','latex');
fig.Position = plotJob.Position;
hold off
end

View File

@@ -0,0 +1,206 @@
function plotViolin(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
%% Violin
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 1;
else
realization = wh.parameter.realization.values(1:end);
end
%realization = 0:8;
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% ber = NaN(500,16,10);
% zdw = NaN(500,1,10);
%get BER values for query
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
curber= removeZeros(curber);
ber(1:size(curber,1),1:size(curber,2),xl) = curber;
end
% to remove outliers set the percentile range
% a = squeeze(mean(ber,2));
% out = isoutlier(mean(a,2),"percentiles",[0 100]);
% ber = ber(~out,:,:);
% disp(sum(out));
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
wavelength={};
S = [];
S_chann = [];
wl = calcWavelengthPlan(plotJob.ch,plotJob.channelspacing,1310);
%get fec thresholds
% [C,ia,ib] =intersect(linx,xAxis);
% linear = squeeze(linear);
%ber(ber==0) = NaN;
S_chann_no_crossing = zeros(1,plotJob.ch);
for chann = 1:size(ber,2)
for realiz = 1:size(ber,1)
ber_series = squeeze(ber(realiz,chann,:)).';
if mean(ber_series) > 0.1
continue
end
a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]);
if ~isempty(a)
S_chann(realiz,chann) = a(2);
% if a(2) > -7 && string(plotJob.pol) == "copolarized"
% continue
% end
S(end+1) = a(2);
wavelength{end+1} = num2str(wl(chann));
else
S(end+1) = 0;
wavelength{end+1} = num2str(wl(chann));
S_chann_no_crossing(realiz,chann) = 1;
S_chann(realiz,chann) = -1;
end
end
end
threshold = -6;
FEC_crossed = sum(S_chann < threshold & ~isnan(S_chann),1);
FEC_not_crossed = sum(S_chann >= threshold & ~isnan(S_chann),1);
failure_rate = FEC_not_crossed ./ (FEC_crossed + FEC_not_crossed) ;
S_chann(S_chann==0) = NaN;
total_avg = mean(S_chann,"all","omitnan");
%figure(2024)
%C = flip(cbrewer2('Spectral',8));
if numel(S) <= numel(wl)
vs = scatter(1:numel(S),S,50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1,'HandleVisibility','off');
%vs = scatter(1,mean(S),50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1);
else
vs = violinplot(S,wavelength,...
'ViolinColor',plotJob.color,...
'ViolinAlpha',0.1,...
'MarkerSize',1,...
'ShowMedian',false,...
'EdgeColor',plotJob.color,...
'ShowWhiskers',false,...
'ShowData',false,...
'ShowBox',false,...
'Bandwidth',0.051 ...
);
hold on
partly_failed = boolean(ceil(failure_rate));
avg = mean(S_chann,1,"omitnan");
notfailed = ~partly_failed .* avg;
notfailed(notfailed==0) = NaN;
scatter(1:size(S_chann,2),notfailed,10,'Marker','x','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',0.5,'HandleVisibility','off');
hold on
partly_failed = partly_failed.*avg;
partly_failed(partly_failed==0) = NaN;
s=scatter(1:numel(failure_rate),partly_failed,10,'Marker','x','LineWidth',0.5,'HandleVisibility','off','MarkerEdgeColor','red');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Fail Rate: ";
s.DataTipTemplate.DataTipRows(1).Value = failure_rate;
s.DataTipTemplate.DataTipRows(2) = [];
hold off
end
% ax = gca;
% ax.XTicks
hold on
yline(total_avg,'LineWidth',1,'LineStyle','--','DisplayName','System Avg.')
fig.Position = plotJob.Position;
xticklabels(1:16);
ylabel('Penalty in dB');
xlabel('Channel Number');
ylim([-9.3,-3]);
xlim([0,plotJob.ch+1]);
% grid minor;
set(gca, 'color', 'none');
legend = [];
fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
if 0
ber_sorted = sort(S);
z1 = [];
penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2;
for i = 1:length(penalty)
l = penalty(i);
if i == 1
z1 = [z1 sum(ber_sorted(1,:)<l ) / length(ber_sorted) ];
else
z1 = [z1 sum(ber_sorted(1,:)<l & ber_sorted(1,:)>l-0.01) / length(ber_sorted) ];
end
end
penalty_higherthan = 0.5;
probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan)));
disp(['A penalty of more than 0.5 dB has a probability of: ', num2str(probability)]);
end
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -0,0 +1,58 @@
%automate plots
% [file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\session_januar24\wh_complete_at_1310.mat");
% wh = load([path filesep file]);
% wh = wh.wh;
plotJob = struct();
plotJob.l = 10;
plotJob.ch = 16;
plotJob.d = 3;
plotJob.sgm = 1;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 0;
ber_per_chann = [];
% get all xAxis values
xAxis = wh.parameter.p_out.values;
realization = wh.parameter.realization.values(1:end);
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).';
ber_per_chann(:,:,xl) = raw_fetch;
end
%%
figure(2023);
for ch = [1,floor(plotJob.ch/2),ceil(plotJob.ch/2)+1,plotJob.ch] %1:15:size(ber_per_chann,1)
for p = 5%1:size(ber_per_chann,3)
% Extract data for the current row
row_data = squeeze(ber_per_chann(ch,:,p));
[f, xi] = ksdensity(row_data);
% Identify the peak point
[max_density, max_index] = max(f);
peak_x = xi(max_index);
end
% Create a histogram plot for the current row with a unique color
plot(xi, f, 'LineWidth', 2, 'DisplayName', ['Ch. ', num2str(ch)],'LineStyle','--');
%histogram(row_data,100, 'DisplayName', ['Channel ', num2str(ch)], 'EdgeColor', 'none');
hold on; % Hold the plot for the next iteration
text(peak_x, max_density, ['Ch ', num2str(ch)], 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'left');
end
legend show
%%

View File

@@ -0,0 +1,9 @@
classdef adaption_method < int32
enumeration
lms (1)
nlms (2)
rls (3)
end
end

View File

@@ -0,0 +1,10 @@
classdef polarization_control_mode < int32
enumeration
random (1)
rot_angle (2)
rot_power (3)
deactivate (4)
end
end

View File

@@ -0,0 +1,8 @@
classdef processingMode < int32
enumeration
serial (1)
parallel (2)
end
end

View File

@@ -0,0 +1,316 @@
function [output] = dsp_runid(run_id, options)
arguments
run_id
options.append_to_db = 0;
options.max_occurences = 4;
options.parameters = struct();
options.database_type
options.dataBase
options.load_file_path = struct();
options.storage_path
options.mode
end
try
% Initialize output structures
output.ffe_package = {};
output.mlse_package = {};
output.vnle_package = {};
output.dbtgt_package = {};
output.dbenc_package = {};
output.mlmlse_package = {};
if options.mode == "load_run_id" || options.append_to_db
% Initialize database connection
database = DBHandler("dataBase", [options.dataBase], "type", options.database_type );
if 0
% 2. Check if an equalizer configuration with the same hash exists
queryStr = sprintf('SELECT COUNT(DISTINCT eq_id) AS unique_eq_count, COUNT(*) AS entries_for_run FROM `Results` WHERE run_id = %d', run_id);
existing_results = database.fetch(queryStr);
if existing_results.unique_eq_count >= 6
if (existing_results.entries_for_run / existing_results.unique_eq_count) > 5
return
end
end
end
end
if options.mode == "load_run_id"
dataTable = queryRunid(run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
% if database.checkIfRunExists('Results','run_id',run_id)
% disp(['Already got at least one reulst for run id: ',num2str(run_id),' '])
% return
% end
% Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, options);
elseif options.mode == "load_files"
Tx_bits = load(options.load_file_path.tx_bits_path);
Symbols = load(options.load_file_path.tx_symbols_path);
Scpe_sig_raw = load(options.load_file_path.rx_raw_path);
Tx_bits = Tx_bits.Bits;
Symbols = Symbols.Symbols;
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
fsym = Symbols.fs;
M = Symbols.logbook.ModifierCopy{1}.M;
duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym);
[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
else
% Run quick Simulation
tx_simulation;
end
% Handle Settings and argument replacement
len_tr = 4096*2;
ffe_order = [50, 5, 5];
dfe_order = [0, 0, 0];
pf_ncoeffs = 1;
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
mu_dc = 0.005;
dc_buffer_len = 1;
mu_tr = 0;
mu_dd = 0.05;
adaption= 1;
use_dd_mode = 1;
use_ffe = 0;
use_dfe = 0;
use_vnle_mlse = 0;
use_dbtgt = 0;
use_dbenc = 0;
use_ml_mlse = 1;
addProcessingResultToDatabase = 0;
% Overwrite default parameters if given in options.parameters
paramStruct = options.parameters;
if ~isempty(paramStruct)
paramNames = fieldnames(paramStruct);
for i = 1:numel(paramNames)
thisName = paramNames{i};
thisValue = paramStruct.(thisName);
eval([thisName ' = thisValue;']);
end
end
% Configure equalizers
options.max_occurences = min(options.max_occurences,length(Scpe_cell));
for r = 1:options.max_occurences
%FFE
% eq_dfe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
%
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0,"adaption_technique","lms");
eq_post = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
% Duobinary signaling (db encoded)
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ...
"training_loops", 5, "dd_loops", 5, "K", 2, "DCmu", mu_dc, ...
"DDmu", [mu_ffe mu_dfe], "DFEmu", 0.005, "FFEmu", 0, "plotfinal", 0, "ideal_dfe", 1);
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym);
Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-6);
Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx');
ylim([-30,3]);
xlim([-5,100]);
% Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx');
% Scpe_sig.eye(fsym,M,"fignum",1024);
if duob_mode ~= db_mode.db_encoded
if use_ffe
ffe_order = [50, 0, 0];
eq_dfe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
output.ffe_package{r} = ffe_results;
ffe_results.metrics.print;
ffe_results.config.equalizer_structure = "ffe";
if options.append_to_db
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
end
end
if use_dfe
ffe_order = [50, 5, 5];
eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
output.ffe_package{r} = dfe_results;
dfe_results.config.equalizer_structure = "dfe";
dfe_results.metrics.print;
if options.append_to_db
database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config);
end
end
if use_vnle_mlse
pf_ncoeffs = 1;
ffe_order = [50, 5, 5];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
% eq_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0.0004,"order",[50,5,5],"sps",2,"decide",0);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
useviterbi = 0;
if useviterbi
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
else
if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation
trellexlusion = 1;
else
trellexlusion = 0;
end
%state_mode 3 -> stat lvl; state_mode 2 -> use target lvls
%scale_mode 2 -> mmse adaption
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',2);
end
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
ffe_results.metrics.print;
ffe_results.config.equalizer_structure = "vnle";
mlse_results.metrics.print;
output.mlse_package{r} = mlse_results;
output.vnle_package{r} = ffe_results;
if options.append_to_db
database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config);
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
end
end
if use_ml_mlse
%ML-based MLSE (L=2)
mu_ml = 0.01; training_epochs = 100;
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0);
[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode);
output.mlmlse_package{r} = ml_mlse_results;
if options.append_to_db
database.addProcessingResult(run_id, ml_mlse_results.metrics, ml_mlse_results.config);
end
end
if use_dbtgt
useviterbi = 0;
if useviterbi
mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
else
if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation
trellexlusion = 1;
else
trellexlusion = 0;
end
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3);
end
ffe_order = [50, 5, 5];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0,...
"postFFE", []);
dbt_results.metrics.print;
output.dbtgt_package{r} = dbt_results;
if options.append_to_db
database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config);
end
end
end
if duob_mode == db_mode.db_encoded
mlse_db_enc = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, M, Scpe_sig, Symbols, Tx_bits, "precode_mode",duob_mode, "showAnalysis",0,"postFFE",[]);
output.dbenc_package{r} = db_results;
if options.append_to_db
database.addProcessingResult(run_id, db_results.metrics, db_results.config);
end
end
end
catch ME
save('workerError.mat','ME');
rethrow(ME);
end
end

View File

@@ -0,0 +1,192 @@
function [ffe_results] = ffe(eq_, M, rx_signal, tx_symbols, tx_bits, options)
% FFE Processes signals through FFE equalizer
%
% Inputs:
% eq_ - Equalizer object
% M - Modulation order
% rx_signal - Received signal
% tx_symbols - Transmitted symbols
% tx_bits - Transmitted bits
% options - Optional parameters
%
% Outputs:
% ffe_results - Results from FFE processing
arguments
eq_
M
rx_signal
tx_symbols
tx_bits
options.precode_mode db_mode
options.showAnalysis = 0;
options.eth_style_symbol_mapping = 0;
options.postFFE = [];
options.database = [];
end
%% Process signals through equalizer
% FFE or VNLE
[eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols);
% Apply post-FFE if provided
if ~isempty(options.postFFE)
tic
[eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols);
toc
end
try
ch_coefficients = arburg(eq_noise.signal,1);
channel_alpha = ch_coefficients(2);
end
% Hard decision on FFE output
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
%% Calculate BER based on precoding mode
[bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
%% Calculate performance metrics
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
% [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
[gmi] = calc_ngmi(eq_signal_sd,tx_symbols);
gmi = max(gmi,0);
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
[evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols);
[std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols);
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
%% Display analysis if requested
if options.showAnalysis
displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, options.postFFE);
end
%% Prepare output structure
% Determine postFFE order
if ~isempty(options.postFFE)
npostFFE = options.postFFE.order;
else
npostFFE = 0;
end
% Create FFE results structure
ffe_results = struct();
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
eq_.b = [];
eq_.b2 = [];
eq_.b3 = [];
end
ffe_results.config = Equalizerstruct();
ffe_results.config.eq = jsonencode(eq_);
ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe);
ffe_results.config.comment = 'function: ffe';
ffe_results.metrics = Metricstruct;
ffe_results.metrics.result_id = NaN;
ffe_results.metrics.run_id = NaN;
ffe_results.metrics.eqParam_id = NaN;
ffe_results.metrics.date_of_processing = datetime('now');
ffe_results.metrics.BER = ber;
ffe_results.metrics.numBits = bits;
ffe_results.metrics.numBitErr = errors;
ffe_results.metrics.BER_precoded = ber_precoded;
ffe_results.metrics.numBitErr_precoded = errors_precoded;
ffe_results.metrics.SNR = snr;
ffe_results.metrics.SNR_level = snr_lvl;
ffe_results.metrics.STD = std_total;
ffe_results.metrics.STD_level = std_lvl;
ffe_results.metrics.STDrx = std_rxraw_total;
ffe_results.metrics.STDrx_level = std_rxraw_lvl;
ffe_results.metrics.GMI = gmi;
ffe_results.metrics.AIR = air;
ffe_results.metrics.EVM = evm_total;
ffe_results.metrics.EVM_level = evm_lvl;
ffe_results.metrics.Alpha = channel_alpha;
end
%% Helper Functions
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
% Calculate BER based on precoding mode
mapper = PAMmapper(M, 0, "eth_style", eth_style);
switch precode_mode
case db_mode.no_db
% TX Data is not precoded
% A) Emulate diff precoding
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M);
tx_symbols_precoded = Duobinary().encode(tx_symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
rx_bits = mapper.demap(eq_signal_hd_precoded);
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Just determine BER
rx_bits = mapper.demap(eq_signal_hd);
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
case db_mode.db_precoded
% Data is precoded on TX side
% A) Decode at Rx if no DB targeting was applied
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits_demapped = mapper.demap(tx_symbols);
rx_bits = mapper.demap(eq_signal_hd);
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
end
end
function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE)
% Display analysis plots and metrics
% Initialize figure handles
% Corrected line - added tx_symbols as second positional argument
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100);
warning off
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101);
figure(gcf);hold on; plot(((1:length(eq_noise.signal)) / eq_noise.fs) * 1e6,movmean(eq_noise.signal,2000,1), 'LineWidth',3,'Color','black')
warning on
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 102);
showEQNoisePSD(eq_noise, "fignum", 103, "displayname", 'Residual Noise after FFE');
% Figure 2: Post-FFE coefficients (if available)
if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 104);
end
try
figure(339);
showEQfilter(eq_.e_tr, eq_signal_sd.fs.*2,"displayname",'training','fignum',339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339);
legend on
end
try
figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training');
figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training');
figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd');
end
% eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105);
end

View File

@@ -0,0 +1,140 @@
function [ml_mlse_results] = ml_mlse(eq_, M, rx_signal, tx_symbols, tx_bits, options)
%
%
% Inputs:
% eq_ - Equalizer object
% M - Modulation order
% rx_signal - Received signal
% tx_symbols - Transmitted symbols
% tx_bits - Transmitted bits
% options - Optional parameters
%
% Outputs:
% ffe_results - Results from FFE processing
arguments
eq_
M
rx_signal
tx_symbols
tx_bits
options.precode_mode db_mode
options.eth_style_symbol_mapping = 0;
options.postFFE = [];
end
%% Process signals through equalizer
[eq_signal_hd,y_ref] = eq_.process(rx_signal,tx_symbols);
%% Calculate BER based on precoding mode
[bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
% Create FFE results structure
ml_mlse_results = struct();
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
eq_.b = [];
eq_.b2 = [];
eq_.b3 = [];
end
ml_mlse_results.config = Equalizerstruct();
eq_small = strip_eq(eq_, 10);
json_str = jsonencode(eq_small);
ml_mlse_results.config.eq = jsonencode(eq_);
ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse);
ml_mlse_results.config.comment = 'function: ML-based MLSE';
ml_mlse_results.metrics = Metricstruct;
% ml_mlse_results.metrics.result_id = NaN;
% ml_mlse_results.metrics.run_id = NaN;
% ml_mlse_results.metrics.eqParam_id = NaN;
ml_mlse_results.metrics.date_of_processing = datetime('now');
ml_mlse_results.metrics.BER = ber;
ml_mlse_results.metrics.numBits = bits;
ml_mlse_results.metrics.numBitErr = errors;
ml_mlse_results.metrics.BER_precoded = ber_precoded;
ml_mlse_results.metrics.numBitErr_precoded = errors_precoded;
% ml_mlse_results.metrics.SNR = NaN;
% ml_mlse_results.metrics.SNR_level = NaN;
% ml_mlse_results.metrics.STD = NaN;
% ml_mlse_results.metrics.STD_level = NaN;
% ml_mlse_results.metrics.STDrx = NaN;
% ml_mlse_results.metrics.STDrx_level = NaN;
% ml_mlse_results.metrics.GMI = NaN;
% ml_mlse_results.metrics.AIR = NaN;
% ml_mlse_results.metrics.EVM = NaN;
% ml_mlse_results.metrics.EVM_level = NaN;
% ml_mlse_results.metrics.Alpha = NaN;
end
%% Helper Functions
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
% Calculate BER based on precoding mode
mapper = PAMmapper(M, 0, "eth_style", eth_style);
switch precode_mode
case db_mode.no_db
% TX Data is not precoded
% A) Emulate diff precoding
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M);
tx_symbols_precoded = Duobinary().encode(tx_symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
rx_bits = mapper.demap(eq_signal_hd_precoded);
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Just determine BER
rx_bits = mapper.demap(eq_signal_hd);
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
case db_mode.db_precoded
% Data is precoded on TX side
% A) Decode at Rx if no DB targeting was applied
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits_demapped = mapper.demap(tx_symbols);
rx_bits = mapper.demap(eq_signal_hd);
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
end
end
function eq_out = strip_eq(eq_, max_elems)
% strip_eq removes all large fields from the ML_MLSE object
% eq_out = strip_eq(eq_, max_elems)
% max_elems ... maximum number of elements to keep (default = 10)
if nargin < 2
max_elems = 10; % default threshold
end
props = properties(eq_);
for i = 1:numel(props)
val = eq_.(props{i});
if ~isempty(val)
% Count total number of elements
if numel(val) > max_elems
eq_.(props{i}) = [];
end
end
end
eq_out = eq_;
end

View File

@@ -0,0 +1,85 @@
function [symbols_for_lvl,avg_for_lvl] = show2Dconstellation(eq_signal,ref_symbols,options)
arguments
eq_signal
ref_symbols
options.fignum (1,1) double = NaN % Default to NaN if not provided
options.displayname (1,:) char = '' % Default to an empty string if not provided
end
plot_shit = 1;
if isa(eq_signal,'Signal')
eq_signal = eq_signal.signal;
end
if isa(ref_symbols,'Signal')
ref_symbols = ref_symbols.signal;
end
if plot_shit
% Determine the figure number to use or create a new figure
if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
clf;
end
end
rx_symbols = eq_signal; %./ rms(eq_signal);
correct_symbols = ref_symbols;
col = cbrewer2('Paired',numel(unique(correct_symbols))*2);
ccnt = -1;
levels = unique(correct_symbols);
M = numel(levels);
symbols_for_lvl = NaN(numel(levels),length(correct_symbols));
start = 1;
ende = length(correct_symbols);
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
statistical_mean(l) = mean(symbols_for_lvl(l,:),'omitnan');
end
tx_even = correct_symbols(1:2:end);
tx_odd = correct_symbols(2:2:end);
rx_even = rx_symbols(1:2:end);
rx_odd = rx_symbols(2:2:end);
D_even = tx_even - rx_even;
D_odd = tx_odd - rx_odd;
D = sqrt(D_even.^2 + D_odd.^2);
[X,Y] = meshgrid(levels, levels);
[X_,Y_] = meshgrid(statistical_mean, statistical_mean);
hold on;
scatter(rx_even,rx_odd,5*ones(1,length(D)),D,'.','DisplayName',['Even/Odd PAM-',num2str(M)],'MarkerEdgeColor',col(2,:));
% colormap(gca,flip(cbrewer2('Spectral',100)))
colormap(gca,'hsv');
scatter(X_(:), Y_(:), 2, 'x', 'LineWidth', 10, 'MarkerEdgeColor', col(6,:),'DisplayName','Statistical Rx Levels');
scatter(X(:), Y(:), 2, 'x', 'LineWidth', 10, 'MarkerEdgeColor', col(4,:),'DisplayName','Tx Levels');
xlim([floor(min(X_(:)))-1, ceil(max(X_(:)))+1]);
ylim([floor(min(Y_(:)))-1, ceil(max(Y_(:)))+1]);
yticks((levels(1:end-1) + levels(2:end)) / 2);
xticks((levels(1:end-1) + levels(2:end)) / 2);
legend
xlabel('even symbols');
ylabel('odd symbols');
axis equal; grid on;
end

View File

@@ -0,0 +1,114 @@
function [Bits, Symbols, Scpe_cell, found_sync] = loadAndSyncSignalDataFromDb(dataTable, options)
% LOADSIGNALDATA Loads and synchronizes signal data from storage
%
% Inputs:d
% dataTable - Table with file paths and configuration
% options - Struct with storage_path and max_occurences
%
% Outputs:
% Symbols_mapped - Mapped symbols from bits
% Symbols - Original symbols
% Scpe_cell - Cell array of synchronized signals
% found_sync - Boolean indicating if synchronization was successful
found_sync = 0;
tempLocalStorage = 1;
% Define the fixed storage directory relative to the user's MATLAB preferences directory
storage_dir = fullfile(prefdir, 'temp_sync_data');
% Part A: Check and load from local storage if available-
if tempLocalStorage == 1
local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)));
if exist(local_filename, 'file')
% Load from local storage and return
try
load(local_filename, 'Bits', 'Symbols', 'Scpe_cell');
found_sync = 1;
return
catch
delete(local_filename);
end
end
end
% If not locally saved, load from storage
if ~found_sync
% Load transmitted bits
Bits = load(fullfile([options.storage_path, char(dataTable.tx_bits_path)]));
Bits = Bits.Bits;
% Map bits to symbols
M = double(dataTable.pam_level);
fsym = dataTable.symbolrate;
Symbols_mapped = PAMmapper(M,0).map(Bits);
Symbols_mapped.fs = fsym;
% Load original symbols
Symbols = load(fullfile([options.storage_path, char(dataTable.tx_symbols_path)]));
Symbols = Symbols.Symbols;
found_sync = 0;
Scpe_cell = {};
% Try to load pre-synchronized data
try
Scpe_load = load(fullfile([options.storage_path, char(dataTable.rx_sync_path)]));
Scpe_cell = Scpe_load.S;
[~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
catch
% Continue to next method if this fails
end
end
% If not found, try with raw data
if ~found_sync
try
Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym);
[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
catch
% Continue to next method if this fails
end
end
% Last attempt with mapped symbols
if ~found_sync && exist('Scpe_sig_raw', 'var')
if length(Symbols_mapped.signal) ~= sum(Symbols_mapped.signal == Symbols.signal)
[~, Scpe_cell, ~, found_sync] = Scpe_sig_raw.tsynch("reference", Symbols_mapped, "fs_ref", fsym, "debug_plots", 0);
end
end
% Part B: Save to local storage if data was loaded and synced
if tempLocalStorage == 1 && found_sync
% Create directory if it doesn't exist
if ~exist(storage_dir, 'dir')
mkdir(storage_dir);
end
% local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)));
% List existing files and remove oldest if more than N
max_local_files = 10; % Store up to N files
files = dir(fullfile(storage_dir, 'sync_data_run_*.mat'));
if length(files) >= max_local_files
% Sort by date
[~, idx] = sort([files.datenum]);
% Delete oldest file
delete(fullfile(storage_dir, files(idx(1)).name));
end
% Save current data
save(local_filename, 'Bits', 'Symbols', 'Scpe_cell');
end
% Limit number of occurrences
if found_sync
record_realizations = min(options.max_occurences, length(Scpe_cell));
Scpe_cell = Scpe_cell(1:record_realizations);
else
warning('Could not synchronize the received signal with the stored symbols!');
end
end

View File

@@ -0,0 +1,32 @@
function Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym)
% PREPROCESSSIGNAL Performs standard preprocessing on a signal
%
% Inputs:
% Scpe_sig - Input signal
% Symbols - Reference symbols for synchronization
% fsym - Symbol frequency
%
% Outputs:
% Scpe_sig - Preprocessed signal
% Resample to 2x symbol rate
Scpe_sig = Scpe_sig.resample("fs_out", 2*fsym);
% Synchronize with reference
[Scpe_sig, ~] = Scpe_sig.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
% Apply Gaussian filter
if 1
Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*0.52, ...
"fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ...
"active", true).process(Scpe_sig);
else
Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ...
"fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ...
"active", true).process(Scpe_sig);
end
%Remove DC offset
Scpe_sig = Scpe_sig - mean(Scpe_sig.signal);
end

View File

@@ -0,0 +1,47 @@
function printResults(run_id, M, Symbols, vnle_pf_package, dbtgt_package, occ)
% PRINTRESULTS Prints formatted results from equalization
%
% Inputs:
% run_id - Run identifier
% M - PAM level
% Symbols - Symbol data with fs property
% vnle_pf_package - VNLE+PF results package
% dbtgt_package - DB target results package (optional)
% occ - Occurrence index
% Print header
fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9);
% VNLE Results
if ~isempty(vnle_pf_package)
vnle_result = vnle_pf_package{occ}.resultsVNLE;
mlse_result = vnle_pf_package{occ}.resultsMLSE;
fprintf(">> VNLE Results:\n");
fprintf(" BER: %.2e\n", vnle_result.BER);
fprintf(" BER (pre-code): %.2e\n", vnle_result.BER_precoded);
fprintf(" SNR: %.2f dB\n", vnle_result.SNR);
fprintf(" GMI: %.4f\n", vnle_result.GMI);
fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9);
fprintf(" AIR: %.2f Gbps\n", vnle_result.AIR.*1e-9);
fprintf("\n");
% MLSE Results
fprintf(">> MLSE Results:\n");
fprintf(" BER: %.2e\n", mlse_result.BER);
fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded);
fprintf(" Channel Alpha: %.2f\n", mlse_result.Alpha);
fprintf("\n");
end
% DB Target Results
if ~isempty(dbtgt_package)
dbtgt = dbtgt_package{occ}.resultsDBtgt;
fprintf(">> DB Target Results:\n");
fprintf(" BER: %.2e\n", dbtgt.BER);
fprintf(" BER (pre-code): %.2e\n", dbtgt.BER_precoded);
fprintf("\n");
end
fprintf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n\n");
end

View File

@@ -0,0 +1,11 @@
function dataTable = queryRunid(run_id, database)
% Query database for run configuration
filterParams = database.tables;
filterParams.Runs = struct('run_id', run_id);
[dataTable, ~] = database.queryDB(filterParams, database.getTableFieldNames('Runs'));
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
dataTable = dataTable(uniqueIdx, :); % Extract unique configurations for each run_id
end

View File

@@ -0,0 +1,235 @@
function [results, wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options)
% SUBMITJOBS Submits dsp_runid jobs for processing (parallel or serial)
%
% [results, wh] = submitJobs(run_ids, dsp_options, submit_mode, submit_options)
%
% run_ids : scalar or vector of run IDs
% dsp_options : struct of dsp_runid name/value options
% submit_mode : processingMode.parallel or .serial
% submit_options : struct with fields
% .waitbar (logical)
% .wh (DataStorage object)
%
% results : cell(nJobsPerRunId, nRunIds)
% wh : updated DataStorage
arguments
run_ids int32 = 0
dsp_options struct = struct()
submit_mode processingMode = processingMode.serial
submit_options.waitbar (1,1) logical = true
submit_options.wh = DataStorage(struct())
end
% Normalize
run_ids = run_ids(:)';
nRunIds = numel(run_ids);
nJobsPerRunId = submit_options.wh.getLastLinIndice();
totalJobs = nRunIds * nJobsPerRunId;
% Preallocate
results = cell(nJobsPerRunId, nRunIds);
futures = parallel.FevalFuture.empty(totalJobs,0);
jobIndices = zeros(totalJobs,2);
% Optional waitbar
if submit_options.waitbar
h = waitbar(0, 'Processing Jobs...');
cleanupObj = onCleanup(@() delete(h));
end
switch submit_mode
case processingMode.parallel
% SET UP POOL & QUEUE
p = setupParallelPool(11, 300); % 10 workers, 300s idle timeout
% === submit all futures ===
jobCounter = 0;
for r = 1:nRunIds
for k = 1:nJobsPerRunId
jobCounter = jobCounter + 1;
jobIndices(jobCounter,:) = [r,k];
opt = buildOptionalVars(k, submit_options.wh);
futures(jobCounter) = parfeval( ...
p, @dsp_runid, 1, ...
run_ids(r), ...
"database_type", dsp_options.database_type, ...
"dataBase", dsp_options.dataBase, ...
"append_to_db", dsp_options.append_to_db, ...
"load_file_path", dsp_options.load_file_path, ...
"max_occurences", dsp_options.max_occurences, ...
"storage_path", dsp_options.storage_path, ...
"mode", dsp_options.mode, ...
"parameters", opt ...
);
fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k);
end
end
% W A I T B A R U P D A T E
if submit_options.waitbar
futureArray = futures;
updateWB = @() waitbar( ...
sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray))/totalJobs, ...
h, sprintf('Completed %d/%d jobs', ...
sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray)), totalJobs) ...
);
afterEach(futureArray, updateWB, 0);
end
% before the loop
% Keep track of which futures we've already handled:
consumedIdx = false(totalJobs,1);
% fetch in completion order, handling successes and errors
for n = 1:totalJobs
try
% This returns the value AND the linear index in 'futures'
[idx, val] = fetchNext(futures);
duration = futures(idx).RunningDuration;
startDT = futures(idx).StartDateTime;
finishDT = futures(idx).FinishDateTime;
duration = finishDT - startDT;
% Mark it consumed
consumedIdx(idx) = true;
% Map back to (r,k) and store
r = jobIndices(idx,1);
k = jobIndices(idx,2);
fprintf('[%s] JobID %d/%d (%.1f%%) %s RunID %d Subjob %d fetched.\n', ...
datestr(now,'yyyy-mm-dd HH:MM:SS'), ...
r, totalJobs, 100*n/totalJobs,char(duration), ...
run_ids(r), k);
% Update waitbar
if submit_options.waitbar
waitbar(n/totalJobs, h, ...
sprintf('Fetched %d/%d (%.1f% Percent)', n, totalJobs, 100*n/totalJobs));
drawnow; % force the GUI to refresh
end
storeResult(val, k, submit_options.wh);
results{k,r} = val;
catch fetchErr
% fetchNext has already set Read=true on the errored future.
% Find the one Read==true that we have _not_ yet consumed.
readMask = arrayfun(@(f) f.Read, futures);
idxErr = find(readMask & ~consumedIdx', 1);
consumedIdx(idxErr) = true;
% Pull the _real_ exception out of the future object
errInfo = futures(idxErr).Error;
if iscell(errInfo)
origME = errInfo{1};
else
origME = errInfo;
end
% Map back to (r,k) and log
r = jobIndices(idxErr,1);
k = jobIndices(idxErr,2);
handleError(origME, k, run_ids(r));
results{k,r} = origME;
end
end
case processingMode.serial
% SERIAL EXECUTION
jobCounter = 0;
for r = 1:nRunIds
for k = 1:nJobsPerRunId
jobCounter = jobCounter + 1;
optionalVars = buildOptionalVars(k, submit_options.wh);
try
fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k);
val = dsp_runid( run_ids(r), ...
"database_type", dsp_options.database_type, ...
"dataBase", dsp_options.dataBase, ...
"append_to_db", dsp_options.append_to_db, ...
"load_file_path", dsp_options.load_file_path, ...
"max_occurences", dsp_options.max_occurences, ...
"storage_path", dsp_options.storage_path, ...
"mode", dsp_options.mode, ...
"parameters", optionalVars );
fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k);
storeResult(val, k, submit_options.wh);
results{k,r} = val;
catch ME
handleError(ME, k, run_ids(r));
results{k,r} = ME;
end
if submit_options.waitbar
waitbar(jobCounter/totalJobs, h, ...
sprintf('Completed %d/%d jobs', jobCounter, totalJobs));
end
end
end
otherwise
error('Unknown submit_mode "%s".', string(submit_mode))
end
wh = submit_options.wh;
%% Local helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function handleError(ME, jobIndex, run_id)
fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', run_id, jobIndex, ...
ME.identifier, ME.message);
for st = ME.stack'
fprintf(' %s:%d (%s)\n', st.file, st.line, st.name);
end
fprintf('Full report:\n%s\n', getReport(ME,'extended'));
end
function optionalVars = buildOptionalVars(jobIndex, wh)
optionalVars = struct();
if ~isempty(wh.getDimension())
[vals, names] = wh.getPhysIndicesByLinIndex(jobIndex);
for pi = 1:numel(names)
optionalVars.(names{pi}) = vals{pi};
end
end
end
function storeResult(val, jobIndex, wh)
if ~isempty(wh)
wh.addValueToStorageByLinIdx(val.ffe_package, 'ffe_package', jobIndex);
wh.addValueToStorageByLinIdx(val.mlse_package, 'mlse_package', jobIndex);
wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex);
wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex);
wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex);
wh.addValueToStorageByLinIdx(val.mlmlse_package,'mlmlse_package',jobIndex);
end
end
function p = setupParallelPool(numWorkers, idleTimeout)
% Ensure a pool exists at the right size & timeout
p = gcp('nocreate');
if isempty(p) || p.NumWorkers~=numWorkers
if ~isempty(p)
delete(p);
end
p = parpool('local', numWorkers, 'IdleTimeout', idleTimeout);
end
% Cancel anything left in the pool's default queue
q = p.FevalQueue;
if ~isempty(q.QueuedFutures) || ~isempty(q.RunningFutures)
cancelAll(q);
fprintf('Canceled %d unfetched jobs from old queue.\n', ...
numel(q.QueuedFutures)+numel(q.RunningFutures));
end
end
end

View File

@@ -0,0 +1,158 @@
function [GMI,NGMI] = calc_gmi_bitwise(test_signal,reference_signal,options)
% https://cioffi-group.stanford.edu/doc/book/AppendixG.pdf
% Supports PAM2/4/8 (per-symbol) and PAM6 (pairwise: 5 bits per 2 symbols)
arguments(Input)
test_signal
reference_signal
options.skip_front (1,1) double = 0
options.skip_end (1,1) double = 0
options.returnErrorLocation (1,1) double = 0 %#ok<NASGU>
end
options.skip_end = abs(options.skip_end);
options.skip_front = abs(options.skip_front);
assert((options.skip_end+options.skip_front) < numel(test_signal), ...
"You can not skip more samples than overall length.");
if isa(reference_signal,'Signal'); reference_signal = reference_signal.signal; end
if isa(test_signal,'Signal'); test_signal = test_signal.signal; end
% TRIM
[test_signal,reference_signal] = trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% Common precompute
N = length(test_signal);
M = numel(unique(reference_signal));
noise = test_signal - reference_signal;
sigma2 = var(noise); % real AWGN variance
% --- Constellation (levels observed)
constellation = unique(reference_signal);
M = numel(constellation);
% --- Empirical P_X (uniform is fine too; keep your original behavior)
N = numel(reference_signal);
counts = arrayfun(@(c) sum(reference_signal==c), constellation);
priors = counts / N;
% ----- PAM6: 5 bits mapped to 2 consecutive symbols (36 transitions) -----
if M == 6
% Pair the stream (drop last if odd)
numPairs = floor(N/2);
if numPairs == 0, GMI = 0; NGMI = 0; return; end
y1 = test_signal(1:2:2*numPairs);
y2 = test_signal(2:2:2*numPairs);
% Use the same level order as PAMmapper to be consistent with labeling
levels = constellation;%PAMmapper(6,0,"eth_style",0).levels; % 1x6 (actual amplitudes used)
% Build all 36 transitions (i,j)
[S1,S2] = ndgrid(levels, levels); % 6x6
trans_pairs = [S1(:), S2(:)]; % 36x2
% 5-bit labels for each transition (ETH style).
% NOTE: keep the normalization you use with your PAMmapper (often /sqrt(10)).
bits72 = PAMmapper(6,0,"eth_style",0).demap( reshape(trans_pairs.',[],1) );
pairBits = reshape(bits72.', 5, []).'; % 36 x 5
% Transmitted 5-bit labels for each observed pair (from the reference)
tx_bits_pair = PAMmapper(6,0,"eth_style",0).demap( reshape(reference_signal(1:2* numPairs).',[],1) );
tx_bits_pair = reshape(tx_bits_pair.', 5, []).'; % numPairs x 5
% Precompute symbol log-priors (uniform => constant; included for shaped inputs)
logP = log(priors);
% Precompute masks for bit=0 / bit=1 in the 6x6 grid
mask1 = false(6,6,5);
mask0 = false(6,6,5);
for b = 1:5
tmp = false(6,6);
tmp(:) = pairBits(:,b)==1;
mask1(:,:,b) = tmp;
tmp = false(6,6);
tmp(:) = pairBits(:,b)==0;
mask0(:,:,b) = tmp;
end
% Exact LLRs via log-sum-exp over the 6x6 grid
LLR = zeros(numPairs,5);
cst = -0.5*log(2*pi*sigma2); % cancels in LLR but harmless
for k = 1:numPairs
li = cst - ((y1(k) - levels).^2)/(2*sigma2) + logP; % 1x6
lj = cst - ((y2(k) - levels).^2)/(2*sigma2) + logP; % 1x6
logW = li(:) + lj(:).'; % 6x6 (adds logP twice)
for b = 1:5
L1 = logsumexp(logW(mask1(:,:,b)));
L0 = logsumexp(logW(mask0(:,:,b)));
LLR(k,b) = L1 - L0; % natural-log LLR
end
end
% Bit-wise MI using consistency relation
MI_bits = zeros(1,5);
for b = 1:5
idx0 = (tx_bits_pair(:,b)==0);
idx1 = ~idx0;
r0 = LLR(idx0,b);
r1 = LLR(idx1,b);
I0 = mean( log2(1 + exp( r0)) ); % natural LLR inside exp()
I1 = mean( log2(1 + exp(-r1)) );
MI_bits(b) = 1 - 0.5*(I0 + I1);
end
% Per-symbol outputs (5 bits per 2 symbols)
GMI = sum(MI_bits)/2;
NGMI = GMI / 2.5;
% ----- Generic PAM2/4/8: per-symbol, Gray bits directly on symbols -----
else
nBits = log2(M);
levels = PAMmapper(M,0).levels' / PAMmapper(M,0).scaling; % match PAMmapper ordering
grayBits= PAMmapper(M,0).showBitMapping; % M x nBits
% Allocate
LLR = zeros(N, nBits);
for n = 1:N
y = test_signal(n);
ll_table = -((y - levels).^2)/(2*sigma2) + log(priors); % 1xM (log-domain)
for b = 1:nBits
idx0 = grayBits(:,b)==0;
idx1 = ~idx0;
L0 = logsumexp(ll_table(idx0));
L1 = logsumexp(ll_table(idx1));
LLR(n,b) = L1 - L0; % natural-log LLR
end
end
tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal);
% Bit-wise MI
MI_bits = zeros(1,nBits);
for b = 1:nBits
r0 = LLR(tx_bits(:,b)==0,b);
r1 = LLR(tx_bits(:,b)==1,b);
I0 = mean( log2(1 + exp( r0)) );
I1 = mean( log2(1 + exp(-r1)) );
MI_bits(b) = 1 - 0.5*(I0 + I1);
end
GMI = sum(MI_bits); % bits / symbol
NGMI = GMI / nBits;
end
% ===== Helpers =====
function s = logsumexp(a)
if isempty(a), s = -inf; return; end
m = max(a(:));
s = m + log(sum(exp(a(:) - m)));
end
function [data_,reference_] = trimseq(data,reference,skipstart,skip_end)
data_ = data(skipstart+1:end-skip_end,:);
delta = numel(reference) - numel(data_);
reference_ = reference(skipstart+1:end-(skip_end+delta),:);
end
end

View File

@@ -0,0 +1,31 @@
function burst_count = count_error_bursts(err_pos, max_burst_length)
% err_pos: Vector of error positions
% max_burst_length: Maximum length for which bursts are counted (e.g., 10)
% Sort the error positions to ensure they're in increasing order
err_pos = sort(err_pos);
% Initialize burst_count array to hold the counts for each burst length
burst_count = zeros(1, max_burst_length);
% Find the differences between consecutive error positions
diffs = diff(err_pos);
% Find the start of the bursts (where the difference is greater than 1)
burst_starts = [1, find(diffs > 1) + 1]; % Starts at index 1 and after gaps
burst_ends = [find(diffs > 1), length(err_pos)]; % Ends where gaps are
% Loop over the bursts
for b = 1:length(burst_starts)
burst_length = burst_ends(b) - burst_starts(b) + 1;
% Check if the burst length exceeds any of the thresholds
for i = 1:max_burst_length
if burst_length == i
burst_count(i) = burst_count(i) + 1;
else
end
end
end
end

View File

@@ -0,0 +1,27 @@
%% Target source entropy for PS-PAM8
clear; clc;
M = 8;
a = -(M-1):2:(M-1); % PAM-8 amplitude levels: [-7 -5 -3 -1 1 3 5 7]
H_target = 2.79; % desired entropy [bits/symbol]
% Objective: find nu such that H(PA) = H_target
f = @(nu) entropy_MB(a,nu) - H_target;
nu_opt = fzero(f, [0, 2]); % search ν in reasonable range
% Compute final distribution
P = exp(-nu_opt*a.^2);
P = P/sum(P);
H = -sum(P .* log2(P));
fprintf('Shaping parameter ν = %.4f\n', nu_opt);
fprintf('Entropy H(A) = %.3f bits/symbol\n', H);
disp('Probability vector (P_A):');
disp(P.');
%% Helper: entropy function
function H = entropy_MB(a,nu)
P = exp(-nu*a.^2);
P = P/sum(P);
H = -sum(P .* log2(P));
end

View File

@@ -0,0 +1,64 @@
% Parameters
N_values = [10 100 1000 4096]; % Different filter lengths to analyze
fs = 112e9;
% Create figure
figure;
% Plot frequency responses
subplot(211)
hold on
grid on
ylabel('Magnitude (dB)')
title('Frequency Response')
yline(-3,'--r')
ylim([-40 5])
subplot(212)
hold on
grid on
xlabel('Frequency (GHz)')
ylabel('Phase (rad)')
title('Phase Response')
% Color map for different lines
colors = cbrewer2('Set1',length(N_values));
% Loop through different filter lengths
for i = 1:length(N_values)
N = N_values(i);
% Filter coefficients
b = ones(1,N)/N;
a = 1;
% Frequency response
[h,w] = freqz(b,a,4096*8);
freq = (w/(2*pi))*fs;
h_db = 20*log10(abs(h));
% Plot magnitude response
subplot(211)
plot(freq/1e9, h_db, 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1)
% Plot phase response
subplot(212)
plot(freq/1e9, unwrap(angle(h)), 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1)
% Find -3dB frequency
cutoff_idx = find(h_db <= -3, 1);
f_cutoff = freq(cutoff_idx)/1e9;
fprintf('N=%d: Cutoff frequency (-3dB point): %.2f GHz\n', N, f_cutoff)
end
% Add legend and adjust axes
subplot(211)
legend('show')
xlim([0 16]) % Adjust x-axis limit to better see the differences
subplot(212)
legend('show')
xlim([0 16]) % Adjust x-axis limit to better see the differences
% Analytical approximation
f_3db_approx = 0.443 * fs./N_values ./ 1e9;

View File

@@ -0,0 +1,89 @@
%% ============================================================
% IM/DD Fading Notch λ_null vs. Bandwidth (Fixed 10 km)
% ============================================================
clear; clc;
%% Fiber and dispersion parameters
lambda0 = 1310e-9; % Zero-dispersion wavelength [m]
S0 = 0.09; % Dispersion slope at ZDW [ps/(nm²·km)]
L = 10e3; % Fiber length [m]
c = physconst('lightspeed');
%% Frequency sweep (defines the desired first-fading notch)
f_targets = linspace(40e9, 150e9, 200); % [Hz]
f_GHz = f_targets / 1e9;
%% Compute wavelength λ_null for each target f_null
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
lambda_nm = lambda_vec * 1e9; % Convert to nm
Dacc = Dacc_vec; % [ps/nm]
%% ------------------------------------------------------------
% Plot λ_null vs. f_null for 10 km fiber
% ------------------------------------------------------------
cols = cbrewer2('Paired',10);
figure('Color','w'); hold on;
hLine = plot(lambda_nm, f_GHz, ...
'LineWidth', 2, ...
'DisplayName', sprintf('L = %.1f km', L/1000), ...
'Color', cols(2,:));
xlabel('Wavelength λ [nm]');
ylabel('First fading notch f_{null} [GHz]');
title('IM/DD Fading Notch Position vs. Wavelength');
grid on; box on;
lim = (lambda0.*1e9) - [8, 40];
xlim([lim(2) lim(1)]);
yticks([56,75,90,112]);
%% ------------------------------------------------------------
% Custom DataTip Template
% ------------------------------------------------------------
% Add accumulated dispersion value to the DataTip
hLine.DataTipTemplate.DataTipRows(1).Label = 'λ [nm]';
hLine.DataTipTemplate.DataTipRows(2).Label = 'f_{null} [GHz]';
% Create a new row for Dacc
dRow = dataTipTextRow('D_{acc} [ps/nm]', Dacc);
hLine.DataTipTemplate.DataTipRows(end+1) = dRow;
%% ------------------------------------------------------------
% Helper function: lambda_for_first_null_full
% ------------------------------------------------------------
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
lambda_min = 1260e-9;
lambda_max = 1360e-9;
f_target = f_target(:);
N = numel(f_target);
lambda_vec = zeros(N,1);
Dacc_vec = zeros(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
lambda_sol = lambda_min;
end
lambda_sol = min(max(lambda_sol, lambda_min), lambda_max);
lambda_vec(k) = lambda_sol;
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
Dacc_val = min(max(Dacc_val, -100), 100);
Dacc_vec(k) = Dacc_val;
end
end

View File

@@ -0,0 +1,55 @@
% Gitter für lambda0 und S0
lambda0_vec = linspace(1260,1360,200);
S0_vec = linspace(0.06,0.1,200);
[Lambda0, S0] = meshgrid(lambda0_vec, S0_vec);
% Festen Betriebsparameter
lambda = 1293; % nm
L = 1; % km
% Dispersion berechnen (lineare Näherung)
D = S0 .* ( lambda - Lambda0 ) * L;
% D = (S0./4) .* ( lambda - (Lambda0.^4)./(lambda^3) ) * L;
%% 2D-Konturplot nur mit Linien und Text
figure('Color','w');
hold on
% Konturlinien
numLevels = 10;
levels = linspace(min(D(:)), max(D(:)), numLevels);
[C,h] = contour(S0, Lambda0, D, levels, ...
'LineWidth',1.5, ...
'ShowText','on', ...
'LabelFormat','%0.1f');
% cbrewer2-Colormap für die Linien
cmap = cbrewer2('div','RdYlGn', numLevels);
colormap(cmap);
% Achsenlinien
% yline(1310, '--k','ZDW_{mean}','LabelVerticalAlignment','top','LabelHorizontalAlignment','center');
x0 = 0.09;
% xline(x0, '--k','S_{0}','LabelHorizontalAlignment','left');
% Gaussian auf der x-Linie (S0 = 0.09)
mu_zwd = 1310; % nm
sigma_zwd = 2; % nm
zwd_vals = linspace(min(lambda0_vec), max(lambda0_vec), 500);
% PDF berechnen
gauss_pdf = (1/(sigma_zwd*sqrt(2*pi))) * exp(-0.5*((zwd_vals-mu_zwd)/sigma_zwd).^2);
% Normieren und auf eine sichtbare Breite skalieren
scale = 0.005; % passt die Maximal-Auslenkung in x-Richtung an
x_gauss = x0 + (gauss_pdf/max(gauss_pdf)) * scale;
% Plot
% plot(x_gauss, zwd_vals, 'LineWidth',2);
% Achsenbeschriftung & Titel
% Achsenbeschriftung & Titel
xlabel('S0 [ps / nm2 km]', 'FontSize', 12);
ylabel('ZDW [nm]', 'FontSize', 12);
title (sprintf('Dispersion: %d km; %d nm', L, lambda), 'FontSize', 14);
grid on
hold off

View File

@@ -0,0 +1,146 @@
%% ------------------------------------------------------------
% Contour plot: λ_null as function of bandwidth (f_target) and reach (L)
% ------------------------------------------------------------
% Parameters
lambda0 = 1310e-9; % [m]
S0 = 0.08; % [ps/(nm²·km)]
c = physconst('lightspeed');
% Sweep dimensions
f_targets = linspace(50e9, 120e9, 100); % [Hz] (x-axis)
L_values = linspace(0.5e3, 10e3, 100); % [m] (y-axis)
lambda_surface = zeros(numel(L_values), numel(f_targets));
Dacc_surface = zeros(numel(L_values), numel(f_targets));
% Outer loop over fiber length (since L must be scalar)
for iL = 1:numel(L_values)
L = L_values(iL);
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
lambda_vec = 2*abs(lambda0 - lambda_vec);
if 0
fprintf('\n- %d km ------------------------------------\n',L);
fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n');
fprintf('----------------------------------------------\n');
fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].');
fprintf('----------------------------------------------\n\n');
end
lambda_surface(iL, :) = lambda_vec; % λ for each f_target
Dacc_surface(iL, :) = Dacc_vec; % corresponding accumulated dispersion
end
% Convert for plotting
lambda_surface_nm = lambda_surface * 1e9; % [nm]
L_km = L_values / 1000; % [km]
f_GHz = f_targets / 1e9; % [GHz]
%% Contour plot
figure('Color','w');
% Define wavelength contour levels [nm]
lambda_levels = [1260:10:1290, 1290:5:1300, 1300:2.5:1310];
lambda_levels = [100:-20:50, 50:-10:30,30:-5:0];
% Contour plot
contour(f_GHz, L_km, lambda_surface_nm, lambda_levels, ...
'LineWidth', 1.5, ...
'ShowText', 'on', ...
'LabelFormat', '%.0f nm');
% Colormap and colorbar
colormap((cbrewer2('RdYlGn',100)));
colorbar;
clim([0 100]);
% Axis formatting
xlabel('Signal Bandwidth [GHz]');
ylabel('Fiber length [km]');
legend('$\Delta \lambda$')
% X-axis ticks at 56 : 16 : 150 GHz
xticks(56:8:150);
grid on; box on;
%% Optional: overlay accumulated-dispersion contours
if 0
hold on;
[CS, h] = contour(f_GHz, L_km, Dacc_surface, 10, 'k--', 'LineWidth', 0.8);
clabel(CS, h, 'Color','k', 'FontSize',8);
end
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
% lambda_for_first_null_full (stable, single-branch + validity checks)
% --------------------------------------------------------------------
% Computes the wavelength(s) at which the first IM/DD fading null
% occurs at frequency/ies f_target using the full dispersion model:
%
% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3)
%
% Restricted to the NORMAL-dispersion branch (λ < λ0),
% and valid only in the O-band (12601360 nm).
%
% Inputs:
% f_target - scalar or vector of target null frequencies [Hz]
% L - fiber length [m]
% lambda0 - zero-dispersion wavelength (ZDW) [m]
% S0 - dispersion slope at ZDW [ps/(nm²·km)]
%
% Outputs:
% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band)
% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range)
% --------------------------------------------------------------------
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
% Define O-band boundaries (in meters)
lambda_min = 1255e-9;
lambda_max = 1361e-9;
% Force column vector
f_target = f_target(:);
N = numel(f_target);
lambda_vec = NaN(N,1);
Dacc_vec = NaN(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
% Normal-dispersion branch (λ < λ0)
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
% Limit the search to [λ_min, λ0)
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
% If the zero is not within bounds, skip this point
lambda_sol = NaN;
end
% Validate solution
if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
continue
end
% Compute D(lambda) and accumulated dispersion
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
% Sanity bound on dispersion (avoid unphysical > ±100 ps/nm)
if abs(Dacc_val) > 100
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
else
lambda_vec(k) = lambda_sol;
Dacc_vec(k) = Dacc_val;
end
end
end

View File

@@ -0,0 +1,38 @@
%% ------------------------------------------------------------
% Plot: Maximum usable IM/DD bandwidth vs wavelength
% ------------------------------------------------------------
% Fiber and dispersion parameters
lambda0 = 1310e-9; % [m]
S0 = 0.08; % [ps/(nm²·km)]
L = 10000; % [m]
c = physconst('lightspeed');
% Wavelength range around ZDW
lambda_vec = linspace(1250e-9, 1350e-9, 200); % [m]
% Compute D(lambda) using full model
lambda_nm = lambda_vec * 1e9;
lambda0_nm = lambda0 * 1e9;
D_lambda = (S0/4) .* (lambda_nm - (lambda0_nm.^4) ./ (lambda_nm.^3)); % [ps/(nm·km)]
% Convert D to [s/m²]
D_si = D_lambda * 1e-6;
% Compute first null frequency (f) for each wavelength
f_null = sqrt(c*(0.5) ./ (abs(D_si).*lambda_vec.^2*L)); % [Hz]
% Plot
figure('Color','w');
plot(lambda_vec*1e9, f_null/1e9, 'LineWidth', 1.6);
grid on; box on;
xlabel('Wavelength [nm]');
ylabel('First Fading Null Frequency [GHz]');
title(sprintf('IM/DD Bandwidth Limit vs. Wavelength (L = %.1f km)', L/1000));
% Highlight useful bandwidth thresholds
yline(25, '--', '25 GHz','Color',[0.4 0.4 0.4],'LabelHorizontalAlignment','left');
yline(50, '--', '50 GHz','Color',[0.2 0.6 0.2],'LabelHorizontalAlignment','left');
yline(100,'--', '100 GHz','Color',[0.6 0.2 0.2],'LabelHorizontalAlignment','left');
legend('First fading notch (f_{null})','Location','best');

View File

@@ -0,0 +1,165 @@
%% Chromatic Dispersion Power Fading Demonstration
% ------------------------------------------------------------
% This script computes and visualizes power fading after
% photodiode detection caused by chromatic dispersion in IM/DD links.
%
% It also determines the wavelength λ that produces the first
% fading null at a specified RF frequency f_target using the
% full physical dispersion model:
%
% D(λ) = (S0/4) * (λ - λ0^4 / λ^3)
%
% and compares the analytic null frequency with simulation.
% ------------------------------------------------------------
% clear; close all; clc;
%% Fiber and wavelength parameters
lambda0 = 1310e-9; % Zero-dispersion wavelength (ZDW) [m]
S0 = 0.08; % Dispersion slope at ZDW [ps/(nm^2·km)]
L = 10000; % Fiber length [m]
alpha_dB = 0; % Attenuation [dB/m] (ignored here)
%% Target null frequency
f_targets = linspace(55e9,58e9,10);
f_targets = 56e9;
% f_targets = 80e9;
% Compute wavelength that gives the first null at f_target
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
% lambda_vec = 1293e-9;
fprintf('\n----------------------------------------------\n');
fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n');
fprintf('----------------------------------------------\n');
fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].');
fprintf('----------------------------------------------\n\n');
%% Frequency grid
f_simu = 500e9; % Simulation bandwidth [Hz]
N_freq = 500000;
faxis = linspace(-f_simu/2, f_simu/2, N_freq);
%% Derived fiber parameters
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/m³
% Convert wavelengths to nm for the D(lambda) model
lambda_nm = lambda_vec(end) * 1e9;
lambda0_nm = lambda0 * 1e9;
% Dispersion parameter [ps/(nm·km)]
D_lambda = (S0/4) * (lambda_nm - (lambda0_nm^4)/(lambda_nm^3));
% Convert to [s/m²]
D_si = D_lambda * 1e-6;
% β2 in [s²/m]
b2 = -D_si * lambda_vec(end)^2 / (2*pi*c);
%% IM/DD intensity response (simulation)
phi = 2*pi^2*b2*faxis.^2*L;
H_field_pos = exp(-1j*phi); % +f sideband
H_field_neg = exp(+1j*phi); % -f sideband
H_intensity = 0.5 * (H_field_pos + H_field_neg); % PD beating term
H_sim = abs(H_intensity);
%% Theoretical analytical IM/DD response
phi = 2*pi^2 * abs(b2) * faxis.^2 * L;
H_theoretical = abs(cos(phi));
%% Analytic first null (for verification)
f_null_analytic = sqrt(c*(0.5)/(abs(D_si)*lambda_vec(end)^2*L));
fprintf('Analytic first null from D,λ,L: %.2f GHz\n\n', f_null_analytic/1e9);
%% Plot
cols = linspecer(5);
figure('Color','w'); hold on; grid on; box on;
plot(faxis*1e-9, 10*log10(H_sim), 'DisplayName','$|H_{sim}|$ (IM/DD simulation)','Color',cols(1,:));
plot(faxis*1e-9, 10*log10(H_theoretical), 'DisplayName','|cos($\phi$)| (theory)','Color',cols(2,:),'LineStyle','--');
xline(f_targets(end)/1e9,'k:','LineWidth',1.2,'DisplayName','Target null (56 GHz)');
xline(f_null_analytic/1e9,'Color',[0.2 0.6 0.2],'LineStyle','-.','LineWidth',1.2,'DisplayName','Analytic null');
xlabel('Frequency [GHz]');
ylabel('Magnitude [dB]');
title(sprintf('Power Fading for %.2f nm, L = %.1f km',lambda_nm,L/1000));
legend('Location','best'); ylim([-30 0]);
%% Plot Bandwidth vs Lambda max
figure();
hold on;
plot(lambda_vec.*1e6,f_targets.*1e-9)
xlabel('wavelength');
ylabel('max. Bandwidth')
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
% lambda_for_first_null_full (stable, single-branch + validity checks)
% --------------------------------------------------------------------
% Computes the wavelength(s) at which the first IM/DD fading null
% occurs at frequency/ies f_target using the full dispersion model:
%
% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3)
%
% Restricted to the NORMAL-dispersion branch (λ < λ0),
% and valid only in the O-band (12601360 nm).
%
% Inputs:
% f_target - scalar or vector of target null frequencies [Hz]
% L - fiber length [m]
% lambda0 - zero-dispersion wavelength (ZDW) [m]
% S0 - dispersion slope at ZDW [ps/(nm²·km)]
%
% Outputs:
% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band)
% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range)
% --------------------------------------------------------------------
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
% Define O-band boundaries (in meters)
lambda_min = 1255e-9;
lambda_max = 1361e-9;
% Force column vector
f_target = f_target(:);
N = numel(f_target);
lambda_vec = NaN(N,1);
Dacc_vec = NaN(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
% Normal-dispersion branch (λ < λ0)
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
% Limit the search to [λ_min, λ0)
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
% If the zero is not within bounds, skip this point
lambda_sol = NaN;
end
% Validate solution
if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
continue
end
% Compute D(lambda) and accumulated dispersion
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
% Sanity bound on dispersion (avoid unphysical > ±100 ps/nm)
if abs(Dacc_val) > 100
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
else
lambda_vec(k) = lambda_sol;
Dacc_vec(k) = Dacc_val;
end
end
end

View File

@@ -0,0 +1,32 @@
%% Dependency f_null vs Delta_lambda
lambda0 = 1310e-9;
S0 = 0.09; % ps/(nm²·km)
L = 10e3; % m
c = physconst('lightspeed');
% Convert slope to SI
S0_si = S0 * 1e3; % s/m³
Delta_lambda = linspace(5e-9, 80e-9, 300); % [m] detuning
f_null_2 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L) );
L = 2e3; % m
f_null_10 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L) );
cols = cbrewer2('Paired',10);
figure('Color','w');hold on
cnt = 2;
for L = 10%[2,5,10]
f_null_10 = sqrt( c * 0.5 ./ (S0_si .* abs(Delta_lambda) .* lambda0.^2 .* L*1e3) );
plot(1310-Delta_lambda*1e9, f_null_10/1e9, 'LineWidth',2,'DisplayName',sprintf('%d km',L),'Color',cols(cnt,:));
cnt = cnt+2;
end
yticks([56,75,90,112])
tickse = 1310-[7.5, 12, 17, 31.5];
xticks(flip(tickse));
xlabel('$\Delta \lambda$ from ZDW [nm]');
ylabel('$F_{null}$ [GHz]');
grid on; box on;
lim=1310-[5,35];
xlim([lim(2) lim(1)]);
ylim([40,130])

View File

@@ -0,0 +1,111 @@
%% ============================================================
% IM/DD Fading Notch Design Map
% Shows λ_null vs. bandwidth (f_target) and fiber length (L)
% ============================================================
clear; close all; clc;
%% Parameters
lambda0 = 1310e-9; % Zero-dispersion wavelength [m]
S0 = 0.08; % Dispersion slope at ZDW [ps/(nm²·km)]
c = physconst('lightspeed');
% Frequency and length sweep
f_targets = linspace(20e9, 140e9, 80); % [Hz] x-axis
L_values = linspace(0.5e3, 12e3, 80); % [m] y-axis
% Preallocate result matrices
lambda_surface = zeros(numel(L_values), numel(f_targets));
Dacc_surface = zeros(numel(L_values), numel(f_targets));
%% Compute λ_null and Dacc for each (f_target, L)
for iL = 1:numel(L_values)
L = L_values(iL);
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
lambda_surface(iL, :) = lambda_vec; % [m]
Dacc_surface(iL, :) = Dacc_vec; % [ps/nm]
end
%% Convert to display units
lambda_surface_nm = lambda_surface * 1e9; % [nm]
L_km = L_values / 1000; % [km]
f_GHz = f_targets / 1e9; % [GHz]
%% ------------------------------------------------------------
% Contour plot (λ_null as function of f_null and L)
% ------------------------------------------------------------
figure('Color','w');
% Define wavelength contour levels [nm]
lambda_levels = [1260:10:1290, 1290:5:1300, 1300:2:1310];
contourf(f_GHz, L_km, lambda_surface_nm, lambda_levels, ...
'LineWidth', 1.5, ...
'ShowText', 'on', ...
'LabelFormat', '%1.1d nm');
% Colormap and colorbar
colormap(flip(cbrewer2('RdYlGn',100)));
clim([1260 1310]);
% c = colorbar;
% ylabel(c, 'λ_{null} [nm]', 'Rotation', 90);
% Axis formatting
xlabel('Signal Bandwidth [GHz]');
ylabel('Fiber length L [km]');
% X-axis ticks (every 16 GHz starting at 56 GHz)
xticks(56:8:120);
xlim([56,120])
grid on; box on;
%% Optional overlay: accumulated dispersion contours
hold on;
[CS, h] = contour(f_GHz, L_km, Dacc_surface, 10, 'k--', 'LineWidth', 0.8);
clabel(CS, h, 'Color','k', 'FontSize',8);
legend('λ_{null} contours','|D_{acc}| [ps/nm]','Location','best');
%% ============================================================
% Helper function: lambda_for_first_null_full
% Stable, single-branch, clamped to O-band
% ============================================================
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
% Define O-band boundaries (in meters)
lambda_min = 1260e-9;
lambda_max = 1360e-9;
% Force column vector
f_target = f_target(:);
N = numel(f_target);
lambda_vec = zeros(N,1);
Dacc_vec = zeros(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
% Normal-dispersion branch (λ < λ0)
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
% Solve within the normal-dispersion range
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
lambda_sol = lambda_min;
end
% Clamp to O-band range
lambda_sol = min(max(lambda_sol, lambda_min), lambda_max);
lambda_vec(k) = lambda_sol;
% Compute D(lambda) and accumulated dispersion
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
% Clamp to physical range
Dacc_val = min(max(Dacc_val, -100), 100);
Dacc_vec(k) = Dacc_val;
end
end

View File

@@ -0,0 +1,120 @@
%% Matched Filter SNR Demonstration (Correct Timing)
% clear; close all; clc;
%% Parameters
M = 4; % QPSK
numSymbols = 1e6;
sps = 25; % samples per symbol
rolloff = 0.5;
EbNo_dB = 10;
%% Generate random data
data = randi([0 M-1], numSymbols, 1);
txSym = qammod(data, M, 'UnitAveragePower', true);
%% Root Raised Cosine filters
span = 64; % filter span in symbols
rrcTx = rcosdesign(rolloff, span, sps, 'sqrt');
rrcRx = rrcTx; % matched filter
txSignal2 = ifft(fft(rrcTx).*fft(txSym));
%% Transmit filtering (includes upsampling)
txSignal = upfirdn(txSym, rrcTx, sps, 1);
%% AWGN channel
rxSignal = awgn(txSignal, EbNo_dB + 10*log10(sps), 'measured');
%% Receiver matched filter
rxFilt = conv(rxSignal, rrcRx, 'same');
%% Symbol timing (group delay compensation)
delay = span * sps / 2; % total delay per filter is span*sps/2
rxAligned = rxFilt(delay+1 : end-delay);
%% Downsample to symbol rate
rxSampled = rxAligned(1:sps:end);
%% Align lengths
L = min(length(rxSampled), length(txSym));
rxSampled = rxSampled(1:L);
txSym = txSym(1:L);
%% Decision and BER
rxSym = qamdemod(rxSampled, M, 'UnitAveragePower', true);
[~, ber] = biterr(data(1:L), rxSym);
%% Compute effective SNR
snr_meas = 10*log10(mean(abs(txSym).^2) / mean(abs(txSym - rxSampled).^2));
fprintf('Measured BER: %.3e | Effective SNR: %.2f dB\n', ber, snr_meas);
%% Eye diagrams
eyediagram(rxSignal(1:4000), 2*sps);
title('Received Signal (Before Matched Filter)');
eyediagram(rxFilt(1:4000), 2*sps);
title('After Matched Filter (RRC)');
%% --------------------------------------------------------------
%% Spectrum analysis of shaped and filtered signals
%% --------------------------------------------------------------
Fs = sps; % normalized sample rate (symbol rate = 1)
Nfft = 2^16; % FFT size for high resolution
f = (-Nfft/2:Nfft/2-1)/Nfft * Fs; % normalized frequency axis (symbol-rate units)
% Spectra
S_tx = 20*log10(abs(fftshift(fft(txSignal, Nfft)))/max(abs(fft(txSignal, Nfft))));
S_rx = 20*log10(abs(fftshift(fft(rxFilt, Nfft)))/max(abs(fft(rxFilt, Nfft))));
% Unshaped (rectangular pulse) for comparison
txRect_unf = upfirdn(txSym, ones(1, sps), sps, 1);
S_rect = 20*log10(abs(fftshift(fft(txRect_unf, Nfft)))/max(abs(fft(txRect_unf, Nfft))));
% Plot
figure('Name','Spectrum after Pulse Shaping');
plot(f, S_rect, '--', 'DisplayName','Rectangular pulse');
hold on;
plot(f, S_tx, 'LineWidth',1.4, 'DisplayName','RRC (TX)');
plot(f, S_rx, 'LineWidth',1.4, 'DisplayName','After Matched Filter');
grid on;
xlabel('Normalized frequency (× symbol rate)');
ylabel('Magnitude [dB]');
title('Spectra Before and After RRC Pulse Shaping');
legend('Location','best');
xlim([-1.5 1.5]);
ylim([-60 0]);
%% --------------------------------------------------------------
%% Visualization: RRC and Raised-Cosine Frequency Responses
%% --------------------------------------------------------------
% Frequency axis for plotting (normalized to symbol rate)
Nfft = 4096;
H_rrc = fftshift(fft(rrcTx, Nfft));
H_rc = H_rrc .* H_rrc; % cascade of TX and RX RRC = full RC
f = linspace(-0.5, 0.5, Nfft); % normalized frequency (symbol-rate units)
figure('Name','Raised Cosine Filter Characteristics');
subplot(2,1,1);
plot(f, 20*log10(abs(H_rrc)/max(abs(H_rrc))), 'LineWidth', 1.5);
hold on;
plot(f, 20*log10(abs(H_rc)/max(abs(H_rc))), '--', 'LineWidth', 1.5);
grid on;
xlabel('Normalized frequency (× symbol rate)');
ylabel('Magnitude [dB]');
title(sprintf('RRC (rolloff = %.2f) and Full RC Spectrum', rolloff));
legend('Root Raised Cosine','Raised Cosine (TX×RX)','Location','best');
ylim([-60 5]);
subplot(2,1,2);
t = (-span*sps/2 : span*sps/2) / sps; % time axis in symbol durations
plot(t, rrcTx, 'LineWidth', 1.5);
grid on;
xlabel('Time [symbols]');
ylabel('Amplitude');
title('RRC Impulse Response');

View File

@@ -0,0 +1,40 @@
%% ============================================================
% Minimal IM/DD Power Fading Plot
% ============================================================
%% Fiber and system parameters
lambda0 = 1310e-9; % zero-dispersion wavelength [m]
lambda = 1275e-9; % operating wavelength [m]
S0 = 0.08; % dispersion slope [ps/(nm²·km)]
L = 10e3; % fiber length [m]
c = physconst('lightspeed');
%% Derived quantities
S0_si = S0 * 1e3; % s/m³
D_lambda = (S0/4) * (lambda*1e9 - (lambda0*1e9)^4/(lambda*1e9)^3); % ps/(nm·km)
D_si = D_lambda * 1e-6; % s/m²
b2 = -D_si * lambda^2 / (2*pi*c); % s²/m
%% Frequency grid
f_max = 150e9;
f = linspace(0, f_max, 4000); % [Hz]
%% IM/DD transfer function (power fading)
phi = 2*pi^2 * b2 * f.^2 * L;
H = abs(cos(phi));
%% Plot
figure('Color','w');
plot(f/1e9, 10*log10(H), 'LineWidth', 1.8);
grid on; box on;
xlabel('Frequency [GHz]');
ylabel('Magnitude [dB]');
title(sprintf('IM/DD Power Fading |H| for λ = %.1f nm, L = %.1f km', lambda*1e9, L/1000));
ylim([-30 0]);
%% Mark analytic first-null frequency
f_null = sqrt(c*(0.5)/(abs(D_si)*lambda^2*L));
xline(f_null/1e9, 'r--', 'LineWidth', 1.2, ...
'Label', sprintf('f_{null}=%.1f GHz', f_null/1e9), ...
'LabelOrientation', 'horizontal', 'LabelVerticalAlignment', 'bottom');

View File

@@ -0,0 +1,11 @@
function prop_time = propagation_time(fiber_len_m)
c = physconst('lightspeed'); % Speed of light in vacuum (m/s)
n = 1.46; % Refractive index of the fiber
% Calculate the propagation speed in the fiber
propagation_speed = c / n;
% Calculate the propagation time
prop_time = fiber_len_m ./ propagation_speed;
end

View File

@@ -0,0 +1,62 @@
%% ============================================================
% WavelengthFrequency Conversion Utilities
% ============================================================
% Example usage:
% f = lambda2freq(1310e-9); % 1310 nm -> Hz
% lambda = freq2lambda(224e12); % 224 THz -> m
% delta_lambda_nm = df2dlambda(224e12, 400e9); % 400 GHz @ 224 THz -> nm
% delta_freq_GHz = dlambda2df(1310e-9, 3.45); % 3.45 nm @ 1310 nm -> GHz
%% ---- Core conversion functions ----
function f = lambda2freq(lambda)
% lambda2freq Convert wavelength [m] frequency [Hz]
c = physconst('lightspeed');
f = c ./ lambda;
end
function lambda = freq2lambda(f)
% freq2lambda Convert frequency [Hz] wavelength [m]
c = physconst('lightspeed');
lambda = c ./ f;
end
%% ---- Differential conversions ----
function d_lambda = df2dlambda(f_center, d_f)
% df2dlambda Convert frequency spacing Δf [Hz] wavelength spacing Δλ [m]
% around a given center frequency f_center [Hz].
% Uses first-order differential: Δλ (c / f^2) * Δf
c = physconst('lightspeed');
d_lambda = (c ./ (f_center.^2)) .* d_f;
end
function d_f = dlambda2df(lambda_center, d_lambda)
% dlambda2df Convert wavelength spacing Δλ [m] frequency spacing Δf [Hz]
% around a given center wavelength λ_center [m].
% Uses first-order differential: Δf (c / λ^2) * Δλ
c = physconst('lightspeed');
d_f = (c ./ (lambda_center.^2)) .* d_lambda;
end
%% ============================================================
% Example section (can be commented out)
% ============================================================
if ~isdeployed
fprintf('--- Example conversions ---\n');
lambda_nm = 1310; % nm
lambda = lambda_nm * 1e-9; % m
f = lambda2freq(lambda); % Hz
fprintf('λ = %.1f nm f = %.3f THz\n', lambda_nm, f/1e12);
d_f = 2000e9; % 400 GHz spacing
d_lambda = df2dlambda(f, d_f); % [m]
fprintf('Δf = %.0f GHz @ %.1f nm Δλ = %.3f nm\n', d_f/1e9, lambda_nm, d_lambda*1e9);
% Verify reverse direction
d_f_back = dlambda2df(lambda, d_lambda);
fprintf('Δλ = %.3f nm @ %.1f nm Δf = %.0f GHz\n', d_lambda*1e9, lambda_nm, d_f_back/1e9);
end

33
Functions/getFigureSize.m Normal file
View File

@@ -0,0 +1,33 @@
% GETFIGURESIZE Retrieve the size of the current MATLAB figure window.
% [WIDTH, HEIGHT] = GETFIGURESIZE() returns the width and height of the
% current figure in pixels.
%
% Example:
% % Get size of current figure
% [w, h] = getFigureSize();
% fprintf('Current figure is %d pixels wide and %d pixels tall.\n', w, h);
%
% Adapt snippet for other figures:
% % Suppose H is a handle to any MATLAB figure (existing or new):
% H = figure; % or H = <some existing figure handle>;
% % Retrieve current size of the active (or any) figure:
% [wCur, hCur] = getFigureSize();
% % Set the other figure H to match that size, preserving its position:
% posH = get(H, 'Position'); % [left, bottom, width, height]
% newPos = [posH(1), posH(2), wCur, hCur];
% set(H, 'Position', newPos);
%
% Note:
% - Position vector is given as [left, bottom, width, height] in pixels.
% - If you want to specify a custom size directly, you can replace wCur/hCur
% with desired values.
function [width, height] = getFigureSize()
% Ensure a figure is available
fig = gcf;
% Get the position vector: [left, bottom, width, height]
pos = get(fig, 'Position');
% Extract width and height
width = pos(3);
height = pos(4);
end

View File

@@ -0,0 +1,133 @@
classdef WesPalette
% WESPALETTE Wes Anderson color palettes with auto-completion
% Usage:
% cmap = WesPalette.Zissou1.rgb()
% cmap = WesPalette.Zissou1.rgb(3)
% https://github.com/karthik/wesanderson?tab=readme-ov-file
enumeration
BottleRocket1
BottleRocket2
Rushmore1
Rushmore
Royal1
Royal2
Zissou1
Zissou1Continuous
Darjeeling1
Darjeeling2
Chevalier1
FantasticFox1
Moonrise1
Moonrise2
Moonrise3
Cavalcanti1
GrandBudapest1
GrandBudapest2
IsleofDogs1
IsleofDogs2
FrenchDispatch
AsteroidCity1
AsteroidCity2
AsteroidCity3
end
methods
function cmap = rgb(obj, n)
% Return palette as Nx3 RGB colormap [01]
hex = obj.hex();
rgb = hex2rgb(hex);
if nargin == 2
if n > size(rgb,1)
error('Requested %d colors, but only %d available.', ...
n, size(rgb,1))
end
cmap = rgb(1:n,:);
else
cmap = rgb;
end
end
end
methods (Access = private)
function hex = hex(obj)
% Internal HEX storage
switch obj
case WesPalette.BottleRocket1
hex = {'#A42820','#5F5647','#9B110E','#3F5151','#4E2A1E','#550307','#0C1707'};
case WesPalette.BottleRocket2
hex = {'#FAD510','#CB2314','#273046','#354823','#1E1E1E'};
case {WesPalette.Rushmore1, WesPalette.Rushmore}
hex = {'#E1BD6D','#EABE94','#0B775E','#35274A','#F2300F'};
case WesPalette.Royal1
hex = {'#899DA4','#C93312','#FAEFD1','#DC863B'};
case WesPalette.Royal2
hex = {'#9A8822','#F5CDB4','#F8AFA8','#FDDDA0','#74A089'};
case WesPalette.Zissou1
hex = {'#3B9AB2','#78B7C5','#EBCC2A','#E1AF00','#F21A00'};
case WesPalette.Zissou1Continuous
hex = {'#3A9AB2','#6FB2C1','#91BAB6','#A5C2A3','#BDC881', ...
'#DCCB4E','#E3B710','#E79805','#EC7A05','#EF5703','#F11B00'};
case WesPalette.Darjeeling1
hex = {'#FF0000','#00A08A','#F2AD00','#F98400','#5BBCD6'};
case WesPalette.Darjeeling2
hex = {'#ECCBAE','#046C9A','#D69C4E','#ABDDDE','#000000'};
case WesPalette.Chevalier1
hex = {'#446455','#FDD262','#D3DDDC','#C7B19C'};
case WesPalette.FantasticFox1
hex = {'#DD8D29','#E2D200','#46ACC8','#E58601','#B40F20'};
case WesPalette.Moonrise1
hex = {'#F3DF6C','#CEAB07','#D5D5D3','#24281A'};
case WesPalette.Moonrise2
hex = {'#798E87','#C27D38','#CCC591','#29211F'};
case WesPalette.Moonrise3
hex = {'#85D4E3','#F4B5BD','#9C964A','#CDC08C','#FAD77B'};
case WesPalette.Cavalcanti1
hex = {'#D8B70A','#02401B','#A2A475','#81A88D','#972D15'};
case WesPalette.GrandBudapest1
hex = {'#F1BB7B','#FD6467','#5B1A18','#D67236'};
case WesPalette.GrandBudapest2
hex = {'#E6A0C4','#C6CDF7','#D8A499','#7294D4'};
case WesPalette.IsleofDogs1
hex = {'#9986A5','#79402E','#CCBA72','#0F0D0E','#D9D0D3','#8D8680'};
case WesPalette.IsleofDogs2
hex = {'#EAD3BF','#AA9486','#B6854D','#39312F','#1C1718'};
case WesPalette.FrenchDispatch
hex = {'#90D4CC','#BD3027','#B0AFA2','#7FC0C6','#9D9C85'};
case WesPalette.AsteroidCity1
hex = {'#0A9F9D','#CEB175','#E54E21','#6C8645','#C18748'};
case WesPalette.AsteroidCity2
hex = {'#C52E19','#AC9765','#54D8B1','#B67C3B','#175149','#AF4E24'};
case WesPalette.AsteroidCity3
hex = {'#FBA72A','#D3D4D8','#CB7A5C','#5785C1'};
end
end
end
end

View File

@@ -0,0 +1,20 @@
function rgb = hex2rgb(hex)
% HEX2RGB Convert HEX color codes to RGB [01]
if ischar(hex)
hex = {hex};
end
n = numel(hex);
rgb = zeros(n,3);
for k = 1:n
h = hex{k};
h = strrep(h,'#','');
rgb(k,1) = hex2dec(h(1:2));
rgb(k,2) = hex2dec(h(3:4));
rgb(k,3) = hex2dec(h(5:6));
end
rgb = rgb / 255;
end

View File

@@ -0,0 +1,64 @@
x = -10:2:25; % Input power [dBm]
y1 = 1e-5 * 10.^(0.12*x); % Dispersion-only
y2 = 1e0 ./ (1 + exp(-0.4*(x-12))); % NLPN
y3 = 1e-6 * 10.^(0.45*x); % RP on gamma
y4 = 1e-2 * 10.^(0.18*(x-8)); % RP on beta2
cmap = WesPalette.AsteroidCity1.rgb(4);
cmap = linspecer(4);
figure1=figure(202998);clf;hold on
lw = 0.8; ms = 4;
plot(x,y1,'LineWidth',lw,'Color',cmap(1,:),'Marker','o','MarkerEdgeColor',cmap(1,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y2,'LineWidth',lw,'Color',cmap(2,:),'Marker','square','MarkerEdgeColor',cmap(2,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y3,'LineWidth',lw,'Color',cmap(3,:),'Marker','o','MarkerEdgeColor',cmap(3,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y4,'LineWidth',lw,'Color',cmap(4,:),'Marker','o','MarkerEdgeColor',cmap(4,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
yline(3.8e-3)
grid on
xlabel('Input power [dBm]')
ylabel('NSD ($\%$)')
legend({'Dispersion','NLPN','RP','RP on $\beta_2$'}, ...
'Location','best')
grid off
set(gca,'MinorGridLineWidth',0.5);
set(gca,'GridLineWidth',0.5,'GridLineStyle','--','GridColor',[0.9,0.9,0.9]);
set(gca,'FontSize',12,'YScale','log');
ylim([1e-6 1e3])
xlim([-10 23])
% % Create textarrow
% annotation(figure1,'textarrow',[0.564444444444444 0.548148148148148],...
% [0.768523809523809 0.63047619047619],'String',{'(A)'});
%
% % Create doublearrow
% annotation(figure1,'doublearrow',[0.724444444444444 0.699259259259259],...
% [0.854238095238095 0.723809523809524]);
%
% % Create line
% annotation(figure1,'line',[0.700740740740741 0.699259259259259],...
% [0.554285714285714 0.405714285714286]);
%
% % Create textbox
% annotation(figure1,'textbox',...
% [0.578777777777778 0.194285714285714 0.104185185185185 0.0685714285714286],...
% 'String',{'BOX'},...
% 'FitBoxToText','off');
%
fig_path = 'C:\Users\Silas\Documents\Dissertation\00_Examples\tikz\textfig.tikz';
matlab2tikz(fig_path, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'xlabel style={font=\color{white!15!black},font=\small},',...
'ylabel style={font=\color{white!15!black},font=\small},',...
'legend columns=1', ...
'every axis/.append style={font=\scriptsize}',...
'legend columns=2',...
'legend style={at={(0.02,0.98)},font=\footnotesize,draw=black!60,rounded corners=2pt,inner sep=1pt,fill=white,column sep=6pt,anchor= north west}',...
});

View File

@@ -0,0 +1,79 @@
function palettes = wes_palettes()
% WES_PALETTES Full Wes Anderson color palette collection for MATLAB
% Colors are stored as HEX and converted to RGB on demand.
palettes = struct();
palettes.BottleRocket1 = { ...
'#A42820', '#5F5647', '#9B110E', '#3F5151', '#4E2A1E', '#550307', '#0C1707'};
palettes.BottleRocket2 = { ...
'#FAD510', '#CB2314', '#273046', '#354823', '#1E1E1E'};
palettes.Rushmore1 = { ...
'#E1BD6D', '#EABE94', '#0B775E', '#35274A', '#F2300F'};
palettes.Rushmore = palettes.Rushmore1;
palettes.Royal1 = { ...
'#899DA4', '#C93312', '#FAEFD1', '#DC863B'};
palettes.Royal2 = { ...
'#9A8822', '#F5CDB4', '#F8AFA8', '#FDDDA0', '#74A089'};
palettes.Zissou1 = { ...
'#3B9AB2', '#78B7C5', '#EBCC2A', '#E1AF00', '#F21A00'};
palettes.Zissou1Continuous = { ...
'#3A9AB2', '#6FB2C1', '#91BAB6', '#A5C2A3', '#BDC881', ...
'#DCCB4E', '#E3B710', '#E79805', '#EC7A05', '#EF5703', '#F11B00'};
palettes.Darjeeling1 = { ...
'#FF0000', '#00A08A', '#F2AD00', '#F98400', '#5BBCD6'};
palettes.Darjeeling2 = { ...
'#ECCBAE', '#046C9A', '#D69C4E', '#ABDDDE', '#000000'};
palettes.Chevalier1 = { ...
'#446455', '#FDD262', '#D3DDDC', '#C7B19C'};
palettes.FantasticFox1 = { ...
'#DD8D29', '#E2D200', '#46ACC8', '#E58601', '#B40F20'};
palettes.Moonrise1 = { ...
'#F3DF6C', '#CEAB07', '#D5D5D3', '#24281A'};
palettes.Moonrise2 = { ...
'#798E87', '#C27D38', '#CCC591', '#29211F'};
palettes.Moonrise3 = { ...
'#85D4E3', '#F4B5BD', '#9C964A', '#CDC08C', '#FAD77B'};
palettes.Cavalcanti1 = { ...
'#D8B70A', '#02401B', '#A2A475', '#81A88D', '#972D15'};
palettes.GrandBudapest1 = { ...
'#F1BB7B', '#FD6467', '#5B1A18', '#D67236'};
palettes.GrandBudapest2 = { ...
'#E6A0C4', '#C6CDF7', '#D8A499', '#7294D4'};
palettes.IsleofDogs1 = { ...
'#9986A5', '#79402E', '#CCBA72', '#0F0D0E', '#D9D0D3', '#8D8680'};
palettes.IsleofDogs2 = { ...
'#EAD3BF', '#AA9486', '#B6854D', '#39312F', '#1C1718'};
palettes.FrenchDispatch = { ...
'#90D4CC', '#BD3027', '#B0AFA2', '#7FC0C6', '#9D9C85'};
palettes.AsteroidCity1 = { ...
'#0A9F9D', '#CEB175', '#E54E21', '#6C8645', '#C18748'};
palettes.AsteroidCity2 = { ...
'#C52E19', '#AC9765', '#54D8B1', '#B67C3B', '#175149', '#AF4E24'};
palettes.AsteroidCity3 = { ...
'#FBA72A', '#D3D4D8', '#CB7A5C', '#5785C1'};
end

View File

@@ -0,0 +1,60 @@
load("ffe_debug_snapshot.mat");
dc_buffer_len = logspace(0,3,12);
dc_buffer_len = 1024;
mu_dc = logspace(-3,0,24);
parfor d = 1:length(mu_dc)
eq_lin = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",mu_dc(d),...
"dc_buffer_len",1024, ...
"ffe_buffer_len",1,...
"smoothing_buffer_length",0,...
"smoothing_buffer_update",1,...
"adaptive_mu_mode",0);
%
% eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0);
ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
ffe_results.metrics.print
% % eq_lin = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-4,"dfe_mu_dd",5e-4,"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",21,"dfe_order",2,"sps",2,"decide",0);
%
% eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0);
% pf_ = Postfilter("ncoeff",2,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',4,'trellis_states',PAMmapper(4,0).levels);
%
% [ffe_results2, mlse_results] = vnle_postfilter_mlse(eq_lin, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
% "precode_mode", duob_mode,...
% 'showAnalysis', 1, ...
% "postFFE", [],...
% "eth_style_symbol_mapping", 0);
ber(d) = ffe_results.metrics.BER;
end
figure(10)
hold on
plot(mu_dc,ber,'LineWidth',1,'DisplayName',sprintf('DC buffer len = 1024'),'Marker','.','MarkerSize',10);
xlabel('BER');
xlabel('MU DC');
title('BER Optimization over dc\_buffer\_len');
yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
ylim([9e-4, 0.5]);
set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
legend('show', 'Location', 'best');
grid on;

View File

@@ -0,0 +1,118 @@
% === SETTINGS ===
dsp_options.append_to_db = 0;
dsp_options.max_occurences = 15;
dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
dsp_options.database_name = 'silas_labor_newdsp_newstructure.db';
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.parameters = struct();
dsp_options.parameters.mu_dc = [0.005];
% === Get Run ID's ===
db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite");
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 5108);
fp.where('Runs', 'is_mpi','EQUALS', 0);
fp.where('Runs', 'fiber_length','EQUALS', 1);
fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', 1);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fp.where('Runs', 'pam_level','EQUALS', 4);
fp.where('Runs', 'bitrate','EQUALS', 360e9);
% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
% === Initialize DataStorage ===
wh = DataStorage(dsp_options.parameters);
wh.addStorage("ffe_package");
wh.addStorage("mlse_package");
wh.addStorage("vnle_package");
wh.addStorage("dbtgt_package");
wh.addStorage("dbenc_package");
% === RUN IT ===
% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true);
% wh.getStoValue('ffe_package',0.005);
% wh.getStoValue('mlse_package',0.005);
[dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]);
dataTable = cleanUpTable(dataTable);
% === Look at it ===
y_var = 'BER_precoded';
x_var = 'bitrate';
fixedVars = {'equalizer_structure', x_var};
[dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
% --- Group and aggregate ---
dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean);
dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max);
% Choose a color map
cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure)));
figure;
hold on;
% Get unique equalizer structures for grouping
unique_eq = unique(dataTableGrpd_mean.equalizer_structure);
for i = 1:numel(unique_eq)
eq_val = unique_eq(i);
% Filter grouped data for this equalizer structure
filt = dataTableGrpd_mean.equalizer_structure == eq_val;
x = dataTableGrpd_mean.(x_var)(filt);
y_mean = dataTableGrpd_mean.(y_var)(filt);
y_min = dataTableGrpd_min.(y_var)(filt);
y_max = dataTableGrpd_max.(y_var)(filt);
% Bounds for boundedline (distance from mean)
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
% --- Bounded line (mean ± min/max) ---
if exist('boundedline', 'file')
[hl, hp] = boundedline(x, y_mean, y_bounds, ...
'alpha', 'transparency', 0.1, ...
'cmap', cols(i,:), ...
'nan', 'fill', ...
'orientation', 'vert');
set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val));
set(hp, 'HandleVisibility', 'off');
else
% If boundedline is not available, use errorbar
errorbar(x, y_mean, y_lower, y_upper, ...
'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ...
'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off');
end
% --- Normal line (mean only) ---
plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ...
'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off');
% --- Scatter plot for individual points (from original data) ---
% Filter original data for this group
orig_filt = dataTableClean.equalizer_structure == eq_val;
x_scatter = dataTableClean.(x_var)(orig_filt);
y_scatter = dataTableClean.(y_var)(orig_filt);
scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ...
'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off');
end
yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
xlabel(x_var, 'Interpreter', 'none');
ylabel(y_var, 'Interpreter', 'none');
legend('show', 'Location', 'best');
grid on;
title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none');
hold off;

View File

@@ -0,0 +1,63 @@
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("type","mysql");
% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite");
filterParams = db.tables;
% filterParams.Configurations = struct('run_id', run_id);
filterParams.Configurations = struct( ...
'symbolrate', 112e9, ... %[224,336,360,390,420,448]
'fiber_length', 0, ...
'db_mode', '"no_db"', ...
'interference_attenuation', [], ...
'interference_path_length', 0, ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', [], ...
'v_bias', 2.65 ...
);
selectedFields = {'Runs.run_id','Runs.loop_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',...
'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',...
'Configurations.interference_attenuation', 'Configurations.interference_path_length'};
[dataTable,sql_query] = db.queryDB(filterParams, selectedFields);
dataTable(dataTable.loop_id~=217,:) = [];
num_occ = 10;
run_par = true;
run_id = dataTable.run_id;
params.dc_buffer_len = 224;
params.ffe_buffer_len = 1;
params.smoothing_buffer_length = 0;
params.smoothing_buffer_update = 0;
params.mu_dc = 0.005;
futures_list = parallel.FevalFuture.empty();
for id = 1:length(dataTable.run_id)
run_id = dataTable.run_id(id);
[out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params);
end
% Extract all ber_mlse values from the vnle_pf_package using cellfun
ber_mlse = cellfun(@(pkg) pkg.ber_mlse, future.OutputArguments{1,1}.vnle_pf_package);
ber_vnle = cellfun(@(pkg) pkg.ber_vnle, future.OutputArguments{1,1}.vnle_pf_package);
figure(101)
hold on;
scatter(1:num_occ,ber_mlse,15,'Marker','*');
scatter(1:num_occ,ber_vnle,15,'Marker','*');
legend('Interpreter', 'latex');
xlabel('Occurences');
ylabel('BER');
grid on;
beautifyBERplot;

View File

@@ -0,0 +1,58 @@
Scpe_sig = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_scopesignal.mat");Scpe_sig = Scpe_sig.Scpe_sig;
Tx_bits = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_bits.mat");Tx_bits = Tx_bits.Tx_bits;
Symbols = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_symbols.mat");Symbols = Symbols.Symbols;
eq_ = FFE_DCremoval("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",1e-5,"mu_tr",0,"order",50,"sps",2,"decide",0,"mu_dc",0.005,"dc_buffer_len",1);
% savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
% databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
% database_name = 'ecoc2025_loops.db';
% db = DBHandler("type","mysql");
params = (logspace(-6,-2,20));
params = floor((logspace(2,3,20)));
params = 224;
for i = 1:length(params)
eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",0.005,"dc_buffer_len",1, ...
"ffe_buffer_len",1,...
"smoothing_buffer_length",0,...
"smoothing_buffer_update",1);
result = ffe(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
ber_ffe(i) = result.metrics;
fprintf(" FFE Results: %.2e\n", ber_ffe(i));
% db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
% eq_ = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0003,"mu_tr",0,"order",50,"sps",2,"decide",1,"buffer_length",params(i));
%
% result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
% ber_dc(i) = result.ber_vnle;
%
% fprintf(" FFE+dc tr. Results: %.2e\n", ber_dc(i));
end
figure(103435)
hold on;
% scatter(params,ber_dc,15,'Marker','o','LineWidth',1,'DisplayName','DC tracking');
% [a,b]=min(ber_dc);
% scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1);
scatter(params,ber_ffe,15,'Marker','square','LineWidth',1);
[a,b]=min(ber_ffe);
scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1,'DisplayName','FFE');
legend('Interpreter', 'latex');
xlabel('Occurences');
ylabel('BER');
grid on;
beautifyBERplot;
ylim([1e-4,0.1 ])

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,163 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "Configurations" (
"configuration_id" INTEGER,
"run_id" INTEGER,
"unique_elab_id" TEXT,
"bitrate" REAL,
"symbolrate" REAL,
"pam_level" INTEGER,
"db_mode" TEXT,
"pulsef_alpha" INTEGER,
"v_bias" REAL,
"v_awg" REAL,
"precomp_amp" REAL,
"rop_attenuation" REAL,
"wavelength" REAL,
"laser_power" REAL,
"fiber_length" REAL,
"pd_in_desired" REAL,
"is_mpi" BIT,
"signal_attenuation" REAL,
"interference_path_length" REAL,
"interference_attenuation" REAL,
"pam_source" TEXT,
PRIMARY KEY("configuration_id" AUTOINCREMENT),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "EqualizerParameters" (
"eq_id" INTEGER,
"equalizer_structure" REAL,
"M" INTEGER,
"target_constellation" TEXT,
"db_target" INTEGER,
"diff_precode" INTEGER,
"postFFE" INTEGER,
"NpostFFE" INTEGER,
"Ne1" INTEGER,
"Ne2" INTEGER,
"Ne3" INTEGER,
"Nb1" INTEGER,
"Nb2" INTEGER,
"Nb3" INTEGER,
"K" INTEGER,
"DCmu" REAL,
"ideal_dfe" INTEGER,
"training_length" INTEGER,
"training_loops" INTEGER,
"TRmu1" REAL,
"TRmu2" REAL,
"TRmu3" REAL,
"TRmuDFE" REAL,
"dd_loops" INTEGER,
"DDmu1" REAL,
"DDmu2" REAL,
"DDmu3" REAL,
"DDmuDFE" REAL,
"MLSE_mode" TEXT,
"MLSE_trellis_states" TEXT,
"comment" TEXT,
"config_hash" TEXT,
UNIQUE("config_hash"),
PRIMARY KEY("eq_id" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Measurements" (
"measurement_id" INTEGER,
"run_id" INTEGER,
"power_laser" REAL,
"power_rop" REAL,
"power_pd_in" REAL,
"power_mpi_interference" REAL,
"power_mpi_signal" REAL,
"voa_class" TEXT,
"pdfa_class" TEXT,
"laser_class" TEXT,
PRIMARY KEY("measurement_id" AUTOINCREMENT),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "Results" (
"result_id" INTEGER,
"run_id" INTEGER,
"eqParam_id" INTEGER,
"date_of_processing" DATETIME DEFAULT (datetime('now', 'localtime')),
"numBits" INTEGER,
"numBitErr" INTEGER,
"BER" REAL,
"numBitErr_precoded" REAL,
"BER_precoded" REAL,
"SNR" REAL,
"SNR_level" TEXT,
"GMI" REAL,
"AIR" REAL,
"EVM" REAL,
"EVM_level" TEXT,
"Alpha" REAL,
"result_hash" TEXT UNIQUE,
"MLSE_dir" INTEGER,
PRIMARY KEY("result_id" AUTOINCREMENT),
FOREIGN KEY("eqParam_id") REFERENCES "EqualizerParameters"("eq_id"),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "Runs" (
"run_id" INTEGER,
"date_of_run" DATETIME DEFAULT (datetime('now', 'localtime')),
"tx_bits_path" TEXT,
"tx_symbols_path" TEXT,
"rx_sync_path" TEXT,
"rx_raw_path" TEXT,
"filename" TEXT,
"tx_signal_path" TEXT,
PRIMARY KEY("run_id" AUTOINCREMENT)
);
CREATE VIEW "View_ResultOverview" AS
SELECT
-- Run info
Runs.run_id,
Runs.date_of_run,
Results.BER,
Results.SNR,
Results.GMI,
Results.AIR,
Results.EVM,
Results.Alpha,
-- Configurations
Configurations.symbolrate,
Configurations.pam_level,
Configurations.db_mode,
Configurations.pulsef_alpha,
Configurations.v_bias,
Configurations.v_awg,
Configurations.precomp_amp,
Configurations.is_mpi,
Configurations.signal_attenuation,
Configurations.interference_path_length,
Configurations.interference_attenuation,
-- Measurement data
Measurements.power_laser,
Measurements.power_rop,
Measurements.power_pd_in,
Measurements.power_mpi_interference,
Measurements.power_mpi_signal,
-- Equalizer parameters
EqualizerParameters.equalizer_structure,
EqualizerParameters.db_target,
EqualizerParameters.diff_precode
FROM Results
-- Join related tables
LEFT JOIN Runs ON Results.run_id = Runs.run_id
LEFT JOIN Configurations ON Configurations.run_id = Runs.run_id
LEFT JOIN Measurements ON Measurements.run_id = Runs.run_id
LEFT JOIN EqualizerParameters ON Results.eqParam_id = EqualizerParameters.eq_id;
CREATE INDEX IF NOT EXISTS "idx_run_id_on_Configurations" ON "Configurations" (
"run_id"
);
CREATE INDEX IF NOT EXISTS "idx_run_id_on_Measurements" ON "Measurements" (
"run_id"
);
COMMIT;

View File

@@ -0,0 +1,85 @@
% This script is used to evaluate Fig. 1b) in the paper "Adaptive Removal of Multipath Interference in Short Reach 112 GBd PAM-4 IM/DD Systems"
%% Parameters
df = 1e6; % Laser linewidth [Hz]
SIR_dB = 20; % Interference attenuation [dB]
alpha = 10^(-SIR_dB/20); % Interference attenuation [linear]
n_fiber = 1.467; % Refractive index
c = physconst('lightspeed'); % [m/s]
L = linspace(0,250,50); % Interference delay [m]
tau = n_fiber./c.*L; % Interference time (= tau) [s]
tau_c = 1/(pi*df); % laser coherence time [s]
L_c = (c/n_fiber)*tau_c; % laser coherence length [m]
var_sat = 2*alpha^2; % Analytical saturation of variance
%% MonteCarlo Simulation
fs = 100e9; % sampling rate [Hz]
Tsim = 50e-6; % sim duration [s]
N = round(Tsim*fs); % number of samples for each realization
max_delay_samples = round(max(tau)*fs); % largest delay that is evaluated (based on max. Interference delay)
phase_noise_std = sqrt(2*pi*df/fs); % standard dev. phase noise
num_realizations = 50; % number of parallel runs
monte_carlo_variance = zeros(num_realizations, length(L));
parfor r = 1:num_realizations
% generate a realization of phase noise random walk
dphi = phase_noise_std * randn(1, N + max_delay_samples); % matlab randn process has std = 1
phi = cumsum(dphi);
phi_direct = phi(max_delay_samples+1 : max_delay_samples+N);
var_k = zeros(1, length(L));
for t = 1:length(tau)
nd = round( tau(t)*fs ); % delay in samples for current interference time
phi_delayed = phi(max_delay_samples+1-nd : max_delay_samples+N-nd); %cut out interfering signal part (was earlier)
E = exp(1j*phi_direct) + alpha*exp(1j*phi_delayed); % E-fields combined
I = abs(E).^2; % photo current as magnitude square of E-field
var_k(t) = var(I);
end
monte_carlo_variance(r, :) = var_k;
end
avg_of_mc_variances = mean(monte_carlo_variance, 1);
std_of_mc_variances = std(monte_carlo_variance, 0, 1);
%% Analytic variance
L_ = linspace(0,250,500); % Interference delay [m]
tau_ = n_fiber./c.*L_;
analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau_)).^2;
%% Plot
cols = [0.3467 0.5360 0.6907
0.9153 0.2816 0.2878
0.4416 0.7490 0.4322];
coherence_length_multiples = 0.5:0.5:ceil(L(end)/L_c);
figure();
hold on;
plot(L, avg_of_mc_variances, 'LineWidth',2, 'DisplayName','Simulation','Color',cols(1,:),'LineStyle','-');
errorbar(L, avg_of_mc_variances,std_of_mc_variances, 'LineWidth',0.7,'LineStyle','none', 'DisplayName','Simulation','Color',cols(1,:),'HandleVisibility','off');
plot(L_, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-');
xticks(coherence_length_multiples.*L_c);
xticklabels(round(coherence_length_multiples.*L_c,1));
norm_to_coherence_len = 1;
if norm_to_coherence_len
xticklabels(coherence_length_multiples);
xlabel('$n \cdot L_c$', 'FontSize',12);
else
xlabel('Interference Delay [m]', 'FontSize',12);
end
xline(L_c.*coherence_length_multiples, 'LineWidth',1.5, 'DisplayName','Coh. Length','HandleVisibility','off','Color',[0.7,0.7,0.7],'LineStyle','-');
xlim([0,L(end)]);
yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$');
grid on;
ylabel('Intensity Variance', 'FontSize',12);
title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14);
legend('Location','southeast');

View File

@@ -0,0 +1,32 @@
%% Parameters
df = linspace(1,50e6,10000); % Laser FWHM linewidth [Hz]
n_fiber = 1.467; % Fiber group index
c = 3e8; % Speed of light [m/s]
% Compute coherence length (1/e of mean-fringe decay)
tau_c = 1./(pi*df);
L_c = (c.* tau_c/n_fiber) ; % Coherence length [m]
%% Plot
figure('Color','w');
loglog(df/1e6, L_c, 'LineWidth',2,'LineStyle','-'); % linewidth in MHz
% xticks([0.1, 1, 10, 50]);
% yticks([1, 10, 100, 1000]);
% yticklabels({'1','10','100','1000'})
grid on; box on;
xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','latex');
ylabel('Coherence length [m]','FontSize',12,'Interpreter','latex');
title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','latex');
%% Annotate some key points
% hold on;
% freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz]
% for f = freqs
% x = f/1e6;
% y = (c/n_fiber) * (1/(pi*f));
% scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
%
% text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ...
% 'FontSize',10,'HorizontalAlignment','left');
%
% end

View File

@@ -0,0 +1,111 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 6;
fp.where('Runs', 'pam_level','EQUALS', M);
baudrate = 162e9;
fp.where('Runs', 'symbolrate','EQUALS', baudrate);
% fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
% fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')];
[dataTable,~] = db.queryDB(fp, fields);
eqstructures = unique(dataTable.equalizer_structure);
fiber_len = unique(dataTable.fiber_length);
cnt = 1;
f=figure();
clf
hold on
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles
for fl = 1:numel(fiber_len)
fl_filtered = dataTable(dataTable.fiber_length == fiber_len(fl),:);
for eqs = [equalizer_structure.vnle_pf_mlse]
eq_choice = equalizer_structure(eqs);
if sum(eqstructures == eq_choice)~=1
disp(eq_choice)
continue
end
eq_filtered = fl_filtered(fl_filtered.equalizer_structure == eq_choice,:);
dispersion_sorted = sortrows(eq_filtered, {'accumulated_dispersion'}, 'ascend');
% dispersion_sorted = dispersion_sorted(dispersion_sorted.wavelength <= 1320,:);
% dispersion_sorted = dispersion_sorted(dispersion_sorted.BER < 0.02,:);
% pull out your vectors
accumulated_dispersion = dispersion_sorted.accumulated_dispersion;
ber = dispersion_sorted.BER;
% ber = dispersion_sorted.BER_precoded;
run_ids = dispersion_sorted.run_id; % <-- this is what we want in the datatip
len = dispersion_sorted.fiber_length;
lambda = dispersion_sorted.wavelength;
cols = cbrewer2('Set1',8);
% cols = flip(cbrewer2('RdYlGn',14));
cols = linspecer(8);
ber_wavelen_grouped = groupsummary( ...
dispersion_sorted, ... % input table
"wavelength", ... % grouping variable
"min", ... % which summary statistic
"BER_precoded");
dname = sprintf('%s; %d km',eq_choice, fiber_len(fl));
h1 = plot(ber_wavelen_grouped.wavelength, ber_wavelen_grouped.min_BER_precoded,'LineWidth', 2, 'MarkerSize', 5,'Marker',markers(cnt),'LineStyle','-','Color',cols(cnt,:),'MarkerEdgeColor','auto','MarkerFaceColor','white','DisplayName',dname);
plotallscatters=0;
if plotallscatters
% plot the two curves and capture their Line handles
dname = sprintf('%s; %d km',eq_choice, fiber_len(fl));
h1 = plot(lambda, ber,'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','none','Color',cols(cnt,:),'MarkerFaceColor',cols(cnt,:),'DisplayName',dname);
% Add run_id as a datatip row
% For each line, tell the datatip template where to find the run_id:
h1.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('run\_id', run_ids);
h1.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('len', len);
h1.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('lambda', lambda);
end
xticks(sort(unique(lambda)));
xticklabels(sort(unique(lambda)));
grid on;
% Labels, scales, legend, etc.
xlabel('Wavelength in nm','FontSize',12);
ylabel('BER','FontSize',12);
tit = sprintf('%d GBd PAM-%d',baudrate.*1e-9, M);
title(tit,'FontSize',14,'FontWeight','bold');
set(gca, 'XScale','linear','YScale','log','FontSize',11);
legend
xlim([min(lambda)-2, max(lambda)+2]);
ylim([1e-4, 0.2]);
cnt = cnt+1;
end
end
yline([4.85e-3, 2e-2],'--','LineWidth',1,'HandleVisibility','off');
posH = get(f, 'Position'); % [left, bottom, width, height]
newPos = [posH(1), posH(2), 750, 300];
set(f, 'Position', newPos);

View File

@@ -0,0 +1,284 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 6;
fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','LESS_THAN', 310e9);
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 0);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_aug_nov_2025')];
[dataTable,~] = db.queryDB(fp, fields);
eqstructures = unique(dataTable.equalizer_structure);
% Create the figure
showFiltered = true;
showPrecoded = false;
show_bitrate = true;
figure(5);
hold on
for eqs = [equalizer_structure.vnle]
% figure('Name',string([char(eqs),'']));
% hold on
for pre_emph = [0,1]
dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
eq_choice = equalizer_structure(eqs);
if sum(eqstructures == eq_choice)~=1
disp(eq_choice)
continue
end
eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
% ===== NEW: compute averages + per-row keep masks (robust filtering) =====
[Tav, keepMask, keepMaskP] = avgBerBySymbolrate(eq_filtered); % <= NEW
% x-values (bitrate) for raw points (same mapping as your lines)
M = unique(eq_filtered.pam_level); % (assumes single PAM per curve)
if show_bitrate
x_raw = eq_filtered.symbolrate.*1e-9 .* floor(log2(M)*10)/10;
else
x_raw = eq_filtered.symbolrate.*1e-9;
end
% ===== NEW: scatter kept raw BER points (hidden from legend) =====
cols = cbrewer2('Paired',12);
thisColor = cols((2*eqs)+1+pre_emph,:);
scatter(x_raw(keepMask), ... % kept points
eq_filtered.BER(keepMask), ...
14, thisColor, 'filled', ...
'MarkerFaceAlpha', 0.35, ...
'MarkerEdgeAlpha', 0.35, ...
'HandleVisibility','off');
if showPrecoded
scatter(x_raw(keepMaskP), ... % kept precoded points
eq_filtered.BER_precoded(keepMaskP), ...
14, thisColor, 'filled', ...
'Marker', 'square', ...
'MarkerFaceAlpha', 0.35, ...
'MarkerEdgeAlpha', 0.35, ...
'HandleVisibility','off');
end
% ===== NEW: optionally show filtered-out points in red =====
if showFiltered
bad = ~keepMask;
if any(bad)
scatter(x_raw(bad), eq_filtered.BER(bad), ...
18, 'r', 'x', 'LineWidth', 1.2, ...
'HandleVisibility','off');
end
badp = ~keepMaskP;
if any(badp)
scatter(x_raw(badp), eq_filtered.BER_precoded(badp), ...
18, 'r', '+', 'LineWidth', 1.2, ...
'HandleVisibility','off');
end
end
% Keep your sorting and one-per-symbolrate behavior (using Tav)
symbolrate_sorted = sortrows(Tav,{'symbolrate','avg_BER_calc'}, 'ascend');
[~, ia] = unique(symbolrate_sorted.symbolrate, 'first');
symbolrate_sorted = symbolrate_sorted(ia, :);
if show_bitrate
% Bitrate for the averaged curves (unchanged)
xraw = symbolrate_sorted.symbolrate.*1e-9 .* floor(log2(M)*10)/10;
else
xraw = symbolrate_sorted.symbolrate.*1e-9;
end
% Use the MATLAB-averaged BERs
ber = symbolrate_sorted.avg_BER_calc;
ber_precoded = symbolrate_sorted.avg_BER_precoded_calc;
dname = strrep([char(eq_choice)],'_',' ');
if pre_emph
dname = [dname,' with pre-emph.'];
else
dname = [dname,' w/o pre-emph.'];
end
plot(xraw, ber, ...
'LineWidth', 1.5, 'MarkerSize', 5, ...
'Marker','o','LineStyle','-', ...
'Color',thisColor,'MarkerEdgeColor',thisColor,'MarkerFaceColor',[1,1,1], ...
'DisplayName', dname);
if showPrecoded
plot(xraw, ber_precoded, ...
'LineWidth', 1.5, 'MarkerSize', 5, ...
'Marker','square','LineStyle',':', ...
'Color',thisColor,'MarkerEdgeColor',thisColor,'MarkerFaceColor',[1,1,1], ...
'DisplayName', [dname,'; pre-coded']);
end
grid on;
if show_bitrate
xlabel('Net bitrate [GBps]', 'FontSize', 12);
else
xlabel('Symbol rate [GBd]', 'FontSize', 12);
end
ylabel('BER', 'FontSize', 12);
title('BER vs. Baud Rate','FontSize', 14, 'FontWeight', 'bold');
set(gca, 'XScale', 'linear', ...
'YScale', 'log', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
xticks(xraw);
if show_bitrate
% xticks(200:25:500);
% xlim([350 500]);
xlim([min(xraw), max(xraw)]);
else
xlim([min(xraw), max(xraw)]);
end
ylim([1e-4, 0.5]);
end
yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off');
end
function [Tav, keepAll, keepAllP] = avgBerBySymbolrate(T, ZT, MIN_G)
% Minimal robust averaging of BER per symbolrate (+ masks for kept points).
% Usage: [Tav, keepAll, keepAllP] = avgBerBySymbolrate(T, ZT, MIN_G)
% Defaults: ZT=3 (MAD z-thresh in log10), MIN_G=2 (min points to filter)
if nargin < 2, ZT = 5; end
if nargin < 5, MIN_G = 0; end
hasP = ismember('BER_precoded', T.Properties.VariableNames);
hasNB = ismember('numBits', T.Properties.VariableNames);
[G,~,idx] = unique(T.symbolrate);
nG = numel(G);
avgBER = nan(nG,1);
avgBERp = nan(nG,1);
keepAll = false(height(T),1);
keepAllP = false(height(T),1);
for gi = 1:nG
r = idx==gi;
x = T.BER(r);
nb = hasNB * T.numBits(r) + ~hasNB; % if missing, nb==1 (scalar expansion ok)
[avgBER(gi), keepAll(r)] = rmeanBer(x, nb, ZT, MIN_G);
if hasP
xp = T.BER_precoded(r);
[avgBERp(gi), keepAllP(r)] = rmeanBer(xp, nb, ZT, MIN_G);
end
end
Tav = table(G, avgBER, avgBERp, ...
'VariableNames', {'symbolrate','avg_BER_calc','avg_BER_precoded_calc'});
end
function [mu, keep] = rmeanBer(x, nb, ZT, MIN_G, onlyHighOutliers, minKeepThreshold)
% Robust arithmetic mean of BER with log-domain MAD filtering (returns keep mask)
%
% Params:
% x : BER values
% nb : numBits (for floor)
% ZT : MAD z-threshold
% MIN_G : min group size before filtering
% onlyHighOutliers : (bool) if true, only discard values above mean
% minKeepThreshold : values below this BER are always kept
%
% Returns:
% mu : robust mean
% keep : logical mask of kept samples
if nargin < 5, onlyHighOutliers = false; end
if nargin < 6, minKeepThreshold = 0; end
x(~isfinite(x)) = NaN;
if ~isscalar(nb), nb(~isfinite(nb)) = NaN; end
if isscalar(nb) && ~isfinite(nb), nb = 1; end
floorVal = realmin;
if ~isscalar(nb) || (isscalar(nb) && isfinite(nb) && nb~=1)
fv = 0.5 ./ max(nb, eps); % rule-of-three style floor
if isscalar(fv), floorVal = fv; else, floorVal = fv; end
end
xAdj = x;
bad = ~isfinite(xAdj) | xAdj <= 0;
if isscalar(floorVal)
xAdj(bad) = floorVal;
else
xAdj(bad) = floorVal(bad);
end
valid = isfinite(xAdj) & xAdj > 0;
keep = false(size(xAdj));
if nnz(valid)==0
mu = NaN; return
end
if nnz(valid) < MIN_G
mu = mean(xAdj(valid),'omitnan'); keep(valid)=true; return
end
lx = log10(xAdj(valid));
med = median(lx,'omitnan');
mad = median(abs(lx-med),'omitnan');
if mad<=0 || ~isfinite(mad)
keep(valid) = true;
mu = mean(xAdj(valid),'omitnan');
return
end
sigma = 1.4826*mad;
ksel = abs(lx-med) <= ZT*sigma;
% convert to linear indices
vIdx = find(valid);
% === Extension A: only drop high outliers ===
if onlyHighOutliers
logMean = mean(lx,'omitnan');
highIdx = lx > logMean;
ksel = ksel | ~highIdx; % always keep values below/equal to mean
end
% === Extension B: always keep values below minKeepThreshold ===
belowThr = xAdj(valid) < minKeepThreshold;
ksel = ksel | belowThr;
keep(vIdx(ksel)) = true;
if any(keep)
mu = mean(xAdj(keep),'omitnan');
else
mu = mean(xAdj(valid),'omitnan');
keep(valid) = true;
end
end

View File

@@ -0,0 +1,117 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 8;
fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','LESS_THAN', 310e9);
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 1);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard_ungrouped_after_nov_2025'));
eqstructures = unique(dataTable.equalizer_structure);
% Create the figure
f=figure(4);
hold on
eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse , equalizer_structure.vnle_db_mlse];
cols = [0.4660 0.6740 0.1880 ; 0.9290 0.6940 0.1250 ; 0 0.4470 0.7410; 0.4940 0.1840 0.5560]; %VNLE; PF ; DFE ; DB tgt
eq_choice = equalizer_structure.vnle;
pre_emph = 1;
dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend');
[~, ia] = unique(symbolrate_sorted.symbolrate, 'first');
symbolrate_sorted = symbolrate_sorted(ia, :);
symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud
bitrate = symbolrate * floor(log2(M)*10)/10;
ber = symbolrate_sorted.min_BER; % BER
ber_precoded = symbolrate_sorted.min_BER_precoded; % BER
plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','v','LineStyle',':','Color',cols(1,:),'MarkerEdgeColor',cols(1,:),'MarkerFaceColor',cols(1,:),'DisplayName',['Tx pre-emphasis + VNLE']);
eq_choice = equalizer_structure.vnle_pf_mlse;
pre_emph = 0;
dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend');
[~, ia] = unique(symbolrate_sorted.symbolrate, 'first');
symbolrate_sorted = symbolrate_sorted(ia, :);
symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud
bitrate = symbolrate * floor(log2(M)*10)/10;
ber = symbolrate_sorted.min_BER; % BER
ber_precoded = symbolrate_sorted.min_BER_precoded; % BER
plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','diamond','LineStyle',':','Color',cols(2,:),'MarkerEdgeColor',cols(2,:),'MarkerFaceColor',cols(2,:),'DisplayName',['VNLE+2-tap post-filter+MLSE']);
% eq_choice = equalizer_structure.dfe;
% pre_emph = 1;
% dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
% eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
% symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend');
% [~, ia] = unique(symbolrate_sorted.symbolrate, 'first');
% symbolrate_sorted = symbolrate_sorted(ia, :);
% symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud
% bitrate = symbolrate * floor(log2(M)*10)/10;
% ber = symbolrate_sorted.min_BER; % BER
% ber_precoded = symbolrate_sorted.min_BER_precoded; % BER
% plot(bitrate, ber, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','o','LineStyle','-','Color',cols(3,:),'MarkerEdgeColor',cols(3,:),'MarkerFaceColor',cols(3,:),'DisplayName',[char(eq_choice)]);
eq_choice = equalizer_structure.vnle_db_mlse;
pre_emph = 0;
dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
symbolrate_sorted = sortrows(eq_filtered,{'symbolrate','min_BER_precoded'}, 'ascend');
[~, ia] = unique(symbolrate_sorted.symbolrate, 'first');
symbolrate_sorted = symbolrate_sorted(ia, :);
symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud
bitrate = symbolrate * floor(log2(M)*10)/10;
ber = symbolrate_sorted.min_BER; % BER
ber_precoded = symbolrate_sorted.min_BER_precoded; % BER
plot(bitrate, ber_precoded, 'LineWidth', 1.5, 'MarkerSize', 5,'Marker','square','LineStyle',':','Color',cols(4,:),'MarkerEdgeColor',cols(4,:),'MarkerFaceColor',cols(4,:),'DisplayName',['DB precoding + DB tgt. + MLSE']);
% Axis labels and title with Arial font
xlabel('Gross bitrate [Gb/s]', 'FontSize', 12, 'FontName', 'Arial', 'Interpreter', 'none');
ylabel('BER', 'FontSize', 12, 'FontName', 'Arial', 'Interpreter', 'none');
title('', 'FontSize', 14, 'FontWeight', 'bold', 'FontName', 'Arial', 'Interpreter', 'none');
% Improve tick formatting
set(gca, 'XScale', 'linear', ...
'YScale', 'log', ...
'TickLabelInterpreter', 'none', ...
'FontSize', 11, ...
'FontName', 'Arial');
% Legend with Arial font
% legend('FontName', 'Arial', 'Interpreter', 'none','Location','best');
xticks(bitrate);
% Optional: tighten axis limits
xlim([min(bitrate), max(bitrate)]);
ylim([5e-4, 0.05]);
yline([3.8e-3], 'LineWidth', 2, 'LineStyle', '--', ...
'HandleVisibility', 'off', 'LabelHorizontalAlignment', 'left');
posH = get(f, 'Position'); % [left, bottom, width, height]
newPos = [posH(1), posH(2), 350, 200];
set(f, 'Position', newPos);
annotation(f,'textbox',...
[0.398095238095238 0.273381294964029 0.491428571428572 0.140287769784173],...
'String',{'PAM-8; 2 km; 1293 nm'},...
'LineWidth',0.5,...
'FitBoxToText','off',...
'BackgroundColor',[1 1 1]);

View File

@@ -0,0 +1,146 @@
%% ============================================================
% SETTINGS
% ============================================================
database_type = 'mysql';
db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
fiberL = 1; % km
wlen = 1310; % nm
bit = 300e9; % example (adjust if needed)
max_pd = 7; % ROP limit (same as before)
PAM_list = [4 6 8]; % formats to compare
% Colors for PAM formats
colors = {clr.Paired.red, clr.Paired.green, clr.Paired.blue};
% Best DSP selection:
bestDSP = struct;
bestDSP = struct;
bestDSP.P4 = equalizer_structure.vnle_db_mlse; % PAM-4
bestDSP.P6 = equalizer_structure.vnle; % PAM-6
bestDSP.P8 = equalizer_structure.vnle; % PAM-8
%% ============================================================
% LOOP over PAM formats extract data
% ============================================================
results = struct;
for pi = 1:numel(PAM_list)
M = PAM_list(pi);
eq = bestDSP.(sprintf('P%d', M));
% ---- DB FILTER ----
fp = QueryFilter();
fp.where('Runs','pam_level','EQUALS',M);
fp.where('Runs','fiber_length','EQUALS',fiberL);
fp.where('Runs','wavelength','EQUALS',wlen);
fp.where('Runs','bitrate','EQUALS',bit);
fp.where('Runs','power_pd_in','LESS_THAN',max_pd);
fields = [
db.getTableFieldNames('power_state_info');
db.getTableFieldNames('dashboard_ungrouped_alltime')
];
[T,~] = db.queryDB(fp, fields);
% ---- DSP OPTIONS ----
pre_emph = decide_preemph(M, eq);
precoded = decide_precoded(M, eq);
cfg = struct;
cfg.x_axis = 'power_mzm';
cfg.y_axis = 'BER';
cfg.agg = 'min';
cfg.outlier = 'none';
cfg.show_raw = false;
cfg.filters = struct( ...
'pam_level', M, ...
'fiber_length', fiberL, ...
'wavelength', wlen, ...
'bitrate', bit, ...
'is_mpi', 0, ...
'equalizer_structure', eq, ...
'pre_emph', pre_emph);
A = analyze_measurements_gpt(T, cfg);
results(pi).M = M;
results(pi).x = A.group{1}.x;
results(pi).color = colors{pi};
if precoded
results(pi).ber = A.group{1}.y_precoded;
else
results(pi).ber = A.group{1}.y;
end
end
%% ============================================================
% PLOT all PAM formats in one ROP plot
% ============================================================
fig = figure(91); hold on;
lw = 2.2; ms = 7;
for pi = 1:numel(results)
plot(results(pi).x, results(pi).ber, ...
'-o', ...
'LineWidth', lw, ...
'MarkerSize', ms, ...
'MarkerFaceColor', results(pi).color, ...
'Color', results(pi).color, ...
'DisplayName', sprintf('PAM-%d', results(pi).M));
end
set(gca,'YScale','log');
grid minor;
xlabel('ROP / Power (MZM) [dBm]');
ylabel('BER');
ylim([1e-4 2e-1]);
legend('Location','best');
title(sprintf('BER vs ROP Best DSP (4,6,8) at %.0f GBd, λ=%d nm, %.0f km', ...
bit*1e-9, wlen, fiberL));
beautifyBERplot();
set(fig,'Position',1e3*[0.35 0.45 1.0 0.45]);
%% ============================================================
% DECISION LOGIC (INLINE FUNCTIONS)
% ============================================================
function pe = decide_preemph(M, eq)
% PRE-EMPH RULES:
switch M
case 4
if eq == equalizer_structure.vnle
pe = 1; % PAM4: VNLE pre-emph on
else
pe = 0; % PAM4: all others off
end
case {6,8}
pe = 1; % PAM6/8: all pre-emph on
otherwise
pe = 0;
end
end
function flag = decide_precoded(M, eq)
% PRE-CODE RULES:
if eq == equalizer_structure.vnle_db_mlse
flag = 1; % Always for DB-target
elseif eq == equalizer_structure.ml_mlse && M == 4
flag = 1; % PAM4: ML-based precoded
else
flag = 0;
end
end

View File

@@ -0,0 +1,272 @@
%% ============================================================
% LOAD DATA FOR PAM = 4,6,8
% ============================================================
database_type = 'mysql';
db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
pam_levels = [4, 6, 8]; % three tiles
bitrate_set = 360e9;
fiberL = 10;
fields = [
db.getTableFieldNames('power_state_info');
db.getTableFieldNames('dashboard_ungrouped_alltime')
];
%% ============================================================
% DEFINE DSP SCHEMES
% ============================================================
curves = struct;
curves(1).name = 'VNLE';
curves(1).eq = equalizer_structure.vnle;
curves(1).color = clr.Paired.red;
curves(2).name = 'PF + MLSE';
curves(2).eq = equalizer_structure.vnle_pf_mlse;
curves(2).color = clr.Paired.green;
curves(3).name = 'DB-target + MLSE';
curves(3).eq = equalizer_structure.vnle_db_mlse;
curves(3).color = clr.Paired.blue;
curves(4).name = 'ML-based MLSE';
curves(4).eq = equalizer_structure.ml_mlse;
curves(4).color = clr.Paired.purple;
%% ============================================================
% ANALYSIS NO PLOTTING
% results(p, k) p: PAM index, k: DSP index
% ============================================================
results = struct;
for p = 1:length(pam_levels)
M = pam_levels(p);
% --- Load DB rows for this PAM ---
fp = QueryFilter();
fp.where('Runs','pam_level','EQUALS', M);
fp.where('Runs','fiber_length','EQUALS', fiberL);
fp.where('Runs','bitrate','EQUALS', bitrate_set);
fp.where('Runs','is_mpi','EQUALS', 0);
[dataTable, ~] = db.queryDB(fp, fields);
for k = 1:numel(curves)
%% =====================================================
% DECIDE PRE-EMPHASIS AND PRECoded BER
% ======================================================
pre_emph = decide_preemph(M, curves(k).eq);
use_precoded = decide_precoded(M, curves(k).eq);
%% ---- base config ----
cfg = struct;
cfg.x_axis = 'wavelength';
cfg.y_axis = 'BER';
cfg.agg = 'min';
cfg.outlier = 'none';
% cfg.group_by = {'wavelength'};
cfg.show_raw = false;
cfg.filters = struct( ...
'pam_level', M, ...
'is_mpi', 0, ...
'bitrate', bitrate_set, ...
'fiber_length', fiberL, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', pre_emph);
%% ---- Run analysis ----
A = analyze_measurements_gpt(dataTable, cfg);
results(p,k).wavelength = A.group{1}.x;
%% ---- store BER variant ----
if use_precoded
results(p,k).ber = A.group{1}.y_precoded;
else
results(p,k).ber = A.group{1}.y;
end
end
end
%% ============================================================
% PLOT 1×3 (PAM-4, PAM-6, PAM-8)
% ============================================================
fig = figure(9110); clf;
tiledlayout(1,3,'TileSpacing','compact','Padding','compact');
lw = 1.8;
ms = 6;
for p = 1:length(pam_levels)
nexttile; hold on;
for k = 1:numel(curves)
plot(results(p,k).wavelength, results(p,k).ber, ...
'-o', ...
'Color', curves(k).color, ...
'MarkerFaceColor', curves(k).color, ...
'MarkerSize', ms, ...
'LineWidth', lw, ...
'DisplayName', curves(k).name);
end
set(gca,'YScale','log');
grid on;
if p == 1
ylabel('BER');
else
ylabel('');
end
xlabel('wavelength');
ylim([4e-4, 0.1]);
beautifyBERplot();
yline([2.2e-4 4.85e-3 2e-2], ...
'LineWidth',1.1, 'Color',[0.2 0.2 0.2], ...
'LineStyle',':','HandleVisibility','off');
if p == 1
x1 = 1290;
x2 = 1297;
x3 = 1300;
x4 = 1323;
x5 = 1325;
x6 = 1330;
elseif p == 2
x1 = 1290;
x2 = 1295;
x3 = 1300;
x4 = 1323.5;
x5 = 1325;
x6 = 1330;
elseif p == 3
x1 = 1290;
x2 = 1292;
x3 = 1298;
x4 = 1323;
x5 = 1327.5;
x6 = 1330;
end
% --- Get current y-limits ---
yl = ylim;
% --- LEFT AREA BELOW KP4 FEC ---
patch([x1 x2 x2 x1], [yl(1) yl(1) yl(2) yl(2)], ...
clr.Set1.red, ... % RGB = red
'FaceAlpha', 0.1, ... % transparency 0.1
'EdgeColor', 'none'); % no border
% --- RIGHT AREA BELOW KP4 FEC ---
patch([x2 x3 x3 x2], [yl(1) yl(1) yl(2) yl(2)], ...
clr.Set1.blue, ... % RGB = red
'FaceAlpha', 0.10, ... % transparency 0.1
'EdgeColor', 'none'); % no border
% --- LEFT AREA BELOW O-FEC ---
patch([x4 x5 x5 x4], [yl(1) yl(1) yl(2) yl(2)], ...
clr.Set1.blue, ... % RGB = red
'FaceAlpha', 0.10, ... % transparency 0.1
'EdgeColor', 'none'); % no border
% --- RIGHT AREA BELOW O-FEC ---
patch([x5 x6 x6 x5], [yl(1) yl(1) yl(2) yl(2)], ...
clr.Set1.red, ... % RGB = red
'FaceAlpha', 0.10, ... % transparency 0.1
'EdgeColor', 'none'); % no border
uistack(findobj(gca,'Type','patch'),'bottom'); % send the patch behind curves
% ax = gca;
% axpos = ax.Position; % [x y w h] normalized
% xl = xlim;
% yl = ylim;
%
% % Convert axis coords normalized figure coords
% toNorm = @(x,y) [ ...
% axpos(1) + (x - xl(1)) / (xl(2)-xl(1)) * axpos(3), ...
% axpos(2) + (y - yl(1)) / (yl(2)-yl(1)) * axpos(4) ...
% ];
%
% % Choose vertical placement (10% above bottom of axis)
% y_arrow = yl(1) * (yl(2)/yl(1))^0.10; % works with log-scale axes
%
% % === Arrow 1: x3 <-> x4 ======================================
% p1 = toNorm(x3, y_arrow);
% p2 = toNorm(x4, y_arrow);
%
% annotation('doublearrow', ...
% [p1(1) p2(1)], [p1(2) p2(2)], ...
% 'Color', [0 0 0], 'LineWidth', 1.4);
%
% % === Arrow 2: x2 <-> x5 ======================================
% p3 = toNorm(x2, y_arrow);
% p4 = toNorm(x5, y_arrow);
%
% annotation('doublearrow', ...
% [p3(1) p4(1)], [p3(2) p4(2)], ...
% 'Color', [0 0 0], 'LineWidth', 1.4);
end
pos = 1e3.*[2.7770 1.2017 1.4000 0.3200];
set(fig, 'Position', pos);
%% === EXPORT ===
outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\wavelength_analysis.tikz';
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'legend columns=1' ...
});
%% ============================================================
% DECISION LOGIC (INLINE FUNCTIONS)
% ============================================================
function pe = decide_preemph(M, eq)
% PRE-EMPH RULES:
switch M
case 4
if eq == equalizer_structure.vnle
pe = 1; % PAM4: VNLE pre-emph on
else
pe = 0; % PAM4: all others off
end
case {6,8}
pe = 1; % PAM6/8: all pre-emph on
otherwise
pe = 0;
end
end
function flag = decide_precoded(M, eq)
% PRE-CODE RULES:
if eq == equalizer_structure.vnle_db_mlse
flag = 1; % Always for DB-target
elseif eq == equalizer_structure.ml_mlse && M == 4
flag = 1; % PAM4: ML-based precoded
else
flag = 0;
end
end

View File

@@ -0,0 +1,132 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';
db = DBHandler("dataBase", dataBase, "type", database_type);
%% FILTER QUERY
fp = QueryFilter();
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')];
[dataTable,~] = db.queryDB(fp, fields);
%% ---- CONFIG ----
cfg = struct;
cfg.x_axis = 'grossrate';
cfg.y_axis = 'BER';
cfg.y_scale = 'log';
cfg.outlier = 'mad';
cfg.show_raw = false;
cfg.show_spread = 'none';
cfg.agg = 'min';
cfg.show_precoded = 1;
cfg.fec_lines = [];
cfg.plot = struct;
cfg.plot.use_cbrewer2 = false;
cfg.plot.lineWidth = 2.0;
cfg.plot.errWidth = 1.2;
cfg.plot.scatterAlpha = 0.35;
cfg.plot.legendLocation = 'best';
cfg.plot.fecLineWidth = 2.4;
cfg.plot.custom_colors_scatter = []; % disabled
%% ---- DSP DEFINITIONS ----
DSP(1).name = 'VNLE';
DSP(1).eq = equalizer_structure.vnle;
DSP(1).color = clr.Paired.red;
DSP(1).lightcolor = clr.Paired.lightred;
DSP(2).name = 'VNLE PF MLSE';
DSP(2).eq = equalizer_structure.vnle_pf_mlse;
DSP(2).color = clr.Paired.green;
DSP(2).lightcolor = clr.Paired.lightgreen;
DSP(3).name = 'VNLE DB MLSE';
DSP(3).eq = equalizer_structure.vnle_db_mlse;
DSP(3).color = clr.Paired.blue;
DSP(3).lightcolor = clr.Paired.lightblue;
DSP(4).name = 'ML MLSE';
DSP(4).eq = equalizer_structure.ml_mlse;
DSP(4).color = clr.Paired.purple;
DSP(4).lightcolor = clr.Paired.lightpurple;
%% ---- GRID CONFIG ----
rows = 3; % PAM 4,6,8
cols = 4; % DSP schemes
pam = [4 6 8];
cfg.figure_number = 46;
fig = figure(cfg.figure_number); clf;
t = tiledlayout(rows, cols, ...
'TileSpacing','compact', ...
'Padding','compact');
cfg.group_by = {'equalizer_structure','pre_emph'};
cfg.plot.use_cbrewer2 = false;
%% ==== MAIN PLOT LOOP =====
for r = 1:rows
Mlev = pam(r);
for c = 1:cols
ax = nexttile(t, (r-1)*cols + c);
cfg.ax = ax;
% ---- PRE-EMPH = 1 ----
cfg.filters = struct('is_mpi',0,'pam_level',Mlev, ...
'equalizer_structure',DSP(c).eq, ...
'pre_emph',1);
cfg.plot.custom_colors = DSP(c).lightcolor;
cfg.plot.custom_linetypes = {'-'};
[~, M1] = plot_measurements_gpt(dataTable, cfg);
% ---- PRE-EMPH = 0 ----
cfg.filters.pre_emph = 0;
cfg.plot.custom_colors = DSP(c).color;
cfg.plot.custom_linetypes = {'-'};
[~, M0] = plot_measurements_gpt(dataTable, cfg);
% Axis limits
if Mlev == 4
ylim([1e-5 0.3]);
elseif Mlev == 6
ylim([6e-4 0.1]);
elseif Mlev == 8
ylim([9e-4 0.1]);
end
% ---- FEC lines ----
yline([2.2e-4 4.85e-3 2e-2], ...
'LineWidth',1.1, 'Color',[0.2 0.2 0.2], ...
'LineStyle',':','HandleVisibility','off');
beautifyBERplot;
% ---- Remove redundant labels ----
if c > 1, ax.YLabel = []; end
if r < rows, ax.XLabel = []; end
grid(ax,'on'); box(ax,'on');
end
end
%% ---- FIXED FIGURE SIZE ----
pos = 1e3.*[0.1070 0.5497 1.4113 0.6847];
set(fig, 'Position', pos);
% %% === EXPORT ===
% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_pre_emphasis.tikz';
% matlab2tikz(outfile, ...
% 'width','\fwidth', ...
% 'height','\fheight', ...
% 'showInfo',false, ...
% 'extraAxisOptions',{ ...
% 'legend style={font=\footnotesize}', ...
% 'legend columns=1' ...
% });

View File

@@ -0,0 +1,257 @@
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1;
database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
rate = [300e9];
cols = cbrewer2('BuPu',25);
cols = [cols(end-10:2:end,:)];
cols = cbrewer2('Set1',6);
fignum = 200;
fig=figure(fignum);clf;
dbmode = 0;
% 1 - PAM 4 with preemphasis
fp = QueryFilter();
M = 6;
fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'bitrate','EQUALS', rate);%360,390
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', dbmode);
fp.where('Runs', 'rop_attenuation','EQUAL', 0);
[dataTable,~] = db.queryDB(fp, database.getTableFieldNames('Runs'));
dataTable = queryRunid(dataTable.run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
% Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options);
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym);
Scpe_sig.eye(fsym,M,"fignum",M*10);
%% === EXPORT TO TIKZ ===
% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\eye_pam_',num2str(M),'.tikz'];
% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\vnle_optimization.tikz'];
% matlab2tikz(outfile, ...
% 'width','\fwidth', ...
% 'height','\fheight', ...
% 'showInfo',false, ...
% 'extraAxisOptions',{ ...
% 'legend style={font=\footnotesize}', ...
% 'legend columns=1' ...
% } );
%%
if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation
trellexlusion = 1;
else
trellexlusion = 0;
end
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3);
len_tr = 4096*2;
ffe_order = [50, 5, 5];
dfe_order = [0, 0, 0];
pf_ncoeffs = 1;
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
mu_dc = 0.005;
dc_buffer_len = 1;
mu_tr = 0;
mu_dd = 0.05;
adaption= 1;
use_dd_mode = 1;
ffe_order = [50, 5, 5];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, ...
'showAnalysis', 1,...
"postFFE", []);
%% === FINAL FIGURE SIZE ===
% Existing figure numbers
figEye = 249;
figConst = 341;
% Find axes in the source figures
srcAxEye = findobj(figEye, 'Type', 'axes');
srcAxConst = findobj(figConst, 'Type', 'axes');
% Create new combined figure
figCombined = figure;
t = tiledlayout(figCombined, 1, 2);
t.TileSpacing = 'compact';
t.Padding = 'compact';
% ------------------------------------------------------------
% LEFT TILE: EYE DIAGRAM
% ------------------------------------------------------------
ax1 = nexttile(t, 1);
hold(ax1, 'on')
% Copy children (images, lines, patches, hist objects, etc.)
copyobj(srcAxEye.Children, ax1);
% Copy labels and title
ax1.XLabel.String = srcAxEye.XLabel.String;
ax1.YLabel.String = srcAxEye.YLabel.String;
ax1.Title.String = srcAxEye.Title.String;
% Copy axis limits
ax1.XLim = srcAxEye.XLim;
ax1.YLim = srcAxEye.YLim;
ax1.YDir = srcAxEye.YDir;
% Copy ticks + labels EXACTLY (including remapped/scaled ones)
ax1.XTick = srcAxEye.XTick;
ax1.XTickLabel = srcAxEye.XTickLabel;
ax1.YTick = srcAxEye.YTick;
ax1.YTickLabel = srcAxEye.YTickLabel;
% Copy colormap + clim (important for density eye)
colormap(ax1, colormap(srcAxEye.Parent));
ax1.CLim = srcAxEye.CLim;
% Copy any style props that matter
ax1.TickDir = srcAxEye.TickDir;
ax1.TickLength = srcAxEye.TickLength;
ax1.FontSize = srcAxEye.FontSize;
ax1.Box = srcAxEye.Box;
grid(ax1,'on');
% ------------------------------------------------------------
% RIGHT TILE: CONSTELLATION HISTOGRAM
% ------------------------------------------------------------
ax2 = nexttile(t, 2);
hold(ax2, 'on')
copyobj(srcAxConst.Children, ax2);
% Copy labels and title
ax2.XLabel.String = srcAxConst.XLabel.String;
ax2.YLabel.String = srcAxConst.YLabel.String;
ax2.Title.String = srcAxConst.Title.String;
% The histogram uses the same y-axis as the eye
% Extract mapping from eye
rawTicks = ax1.YTick;
rawLabelsCell = ax1.YTickLabel;
trueVoltages = str2double(rawLabelsCell);
% Apply true voltages to the histogram axis
ax2.XTick = flip(trueVoltages);
ax2.XTickLabel = flip(rawLabelsCell);
% Set histogram y-limits to match the actual voltages
ax2.XLim = [min(trueVoltages) max(trueVoltages)];
% Ensure eye diagram prints the same (we *do not* touch ax1.YLim)
ax1.XTickLabel = rawLabelsCell;
% Copy colormap (your histogram uses same palette)
colormap(ax2, colormap(srcAxConst.Parent));
% Style properties
ax2.TickDir = srcAxConst.TickDir;
ax2.TickLength = srcAxConst.TickLength;
ax2.FontSize = srcAxConst.FontSize;
ax2.Box = srcAxConst.Box;
grid(ax2,'on');
% ============================================================
% remove right y-axis completely
% ============================================================
ax2.XAxis.Visible = 'off'; % hides ticks + labels + axis line
% BUT we still keep the YTick positions internally for alignment:
% ax2.YTick = <values already set earlier> ;
% ============================================================
% minimize distance between the two plots
% ============================================================
t.TileSpacing = 'none'; % no space between tiles
t.Padding = 'none'; % no outer padding
% Also reduce internal padding for each axis
ax1.Position(3) = ax1.Position(3) + 0.02; % widen eye a bit
ax2.Position(1) = ax2.Position(1) - 0.02; % pull histogram closer
% Keep left axis grid visible
ax2.YGrid = 'off';
%
% =====================================================================
% FINAL POLISHING: unified visual style
% =======================================================================
% --- unified font size ---
FS = 12;
set([ax1 ax2], 'FontSize', FS);
% --- unified axis line width (outline stroke thickness) ---
LW = 1.0;
set([ax1 ax2], 'LineWidth', LW);
% --- unified tick length ---
TL = [.015 .015];
set([ax1 ax2], 'TickLength', TL);
% --- unified grid style ---
set([ax1 ax2], 'XGrid', 'on', 'YGrid', 'on');
set([ax1 ax2], 'GridLineStyle', '--');
set([ax1 ax2], 'GridAlpha', 0.2);
% --- remove right y-axis ticks and labels ---
ax2.YAxis.Visible = 'off';
% --- copy colormap + CLim from the eye to histogram (synchronize look) ---
colormap(ax1, colormap(srcAxEye.Parent));
colormap(ax2, colormap(srcAxEye.Parent));
ax2.CLim = ax1.CLim;
% --- minimal spacing between tiles ---
t.TileSpacing = 'none';
t.Padding = 'none';
% --- pull the panels together (touching boundary effect) ---
pos1 = ax1.Position;
pos2 = ax2.Position;
% Shift histogram left until the outlines touch
pos2(1) = pos1(1) + pos1(3) - 0.002; % 0.002 = fine overlap control
ax2.Position = pos2;
% Expand histogram slightly, remove white band
pos2 = ax2.Position;
pos2(3) = pos2(3) + 0.01;
ax2.Position = pos2;
% Ensure the left plot stays correct after the move
ax1.Position = pos1;
% --- enforce same visible outline ---
% For ax2, create a fake left spine (since YAxis is hidden)
ax2.Box = 'on'; % keep outline but no ticks on the right
ax1.Box = 'on';
ax2.View = [90 -90];

View File

@@ -0,0 +1,221 @@
%% ============================================================
% GRID: NGMI, AIR, HD-NetRate, SD-NetRate (1 × 4)
% ============================================================
db = DBHandler("dataBase","labor_highspeed","type","mysql");
%% --- Base DB Filters (shared across all curves)
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 2);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','rop_attenuation','EQUALS', 0);
fp.where('Runs','is_mpi','EQUALS', 0);
fields = db.getTableFieldNames('dashboard_ungrouped_alltime');
[dataTable,~] = db.queryDB(fp, fields);
%% === Curve Definitions =======================================
curves = struct;
% PAM-8 VNLE PF MLSE no_emph = 1 RED
curves(1).pam = 8;
curves(1).eq = equalizer_structure.vnle_pf_mlse;
curves(1).pre = 0;
curves(1).color = clr.Paired.red;
curves(1).mkr = 'o';
% PAM-6 VNLE PF MLSE no_emph = 1 BLUE
curves(2).pam = 6;
curves(2).eq = equalizer_structure.vnle_pf_mlse;
curves(2).pre = 1;
curves(2).color = clr.Paired.blue;
curves(2).mkr = 'square';
% PAM-4 VNLE DB MLSE pre_emph = 0 GREEN
curves(3).pam = 4;
curves(3).eq = equalizer_structure.vnle_db_mlse;
curves(3).pre = 0;
curves(3).color = clr.Paired.green;
curves(3).mkr = 'diamond';
%% === Prepare Analysis Config ==================================
base = struct;
base.group_by = {'equalizer_structure','pre_emph'};
base.x_axis = 'symbolrate';
base.outlier = 'none';
base.show_raw = false;
base.filters = struct; % will be filled per curve
%% === Precompute All Curves ====================================
results = struct;
for k = 1:numel(curves)
% --- BER ---
cfg = base;
cfg.y_axis = 'BER';
cfg.agg = 'min';
cfg.filters = struct('pam_level', curves(k).pam, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', curves(k).pre);
A = analyze_measurements_gpt(dataTable, cfg);
cfg.x_axis = 'grossrate';
B = analyze_measurements_gpt(dataTable, cfg);
results(k).baudr = A.group{1}.x;
results(k).gross = B.group{1}.x;
if curves(k).pam == 4
results(k).ber = A.group{1}.y_precoded;
else
results(k).ber = A.group{1}.y;
end
% --- NGMI ---
cfg.y_axis = 'NGMI';
cfg.agg = 'max';
A = analyze_measurements_gpt(dataTable, cfg);
results(k).ngmi = A.group{1}.y;
% --- AIR ---
cfg.y_axis = 'AIR';
cfg.agg = 'max';
A = analyze_measurements_gpt(dataTable, cfg);
results(k).air = A.group{1}.y;
results(k).air = results(k).ngmi .* results(k).gross;
% --- Net Rates ---
tp = TransmissionPerformance;
results(k).ndr = tp.calculateNetRate(results(k).gross, ...
'NGMI', results(k).ngmi, ...
'BER', results(k).ber);
end
%% ============================================================
% FIGURE: 1 × 4 GRID
% ============================================================
fig = figure(71); clf;
t = tiledlayout(1,4, 'TileSpacing','compact', 'Padding','compact');
lw = 1.0;
% === NGMI vs Grossrate ===
ax = nexttile(t,1);
hold on;
for k = 1:3
plot(results(k).baudr, results(k).ngmi, ...
'LineWidth', lw, ...
'Color', curves(k).color, ...
'MarkerSize', 1, ...
'MarkerFaceColor', curves(k).color,...
'Marker',curves(k).mkr);
end
ylabel('NGMI');
xlabel('Baud rate [GBd]');
xlim([100 210]);
xticks(100:15:225);
ylim([0.9, 1]);
grid minor; box on;
beautifyBERplot("logscale",0,"setmarkers",0);
% === AIR vs Grossrate ===
ax = nexttile(t,2);
hold on;
for k = 1:3
plot(results(k).baudr, results(k).air, ...
'-', 'LineWidth', lw, ...
'Color', curves(k).color, ...
'MarkerSize', 2, ...
'MarkerFaceColor', curves(k).color,'Marker',curves(k).mkr);
end
ylabel('AIR [Gb/s]');
xlabel('Baud rate [GBd]');
ylim([280 430]);
yticks(280:30:440)
xlim([100 210]);
xticks(100:15:225);
grid minor; box on;
beautifyBERplot("logscale",0,"setmarkers",0);
yline(400,'LineStyle','--');
% === SD-FEC Net Rate ===
ax = nexttile(t,3);
hold on;
for k = 1:3
plot(results(k).baudr, results(k).ndr.SDHD.NetRate, ...
'LineWidth', lw, ...
'Color', curves(k).color, ...
'MarkerSize', 2, ...
'MarkerFaceColor', curves(k).color,...
'Marker',curves(k).mkr);
end
ylabel('NDR [Gb/s]');
xlabel('Baud rate [GBd]');
ylim([280 430]);
yticks(280:30:440)
xlim([100 210]);
xticks(100:15:225);
grid minor; box on;
beautifyBERplot("logscale",0,"setmarkers",0);
yline(400,'LineStyle','--');
% === HD-FEC Net Rate ===
ax = nexttile(t,4);
hold on;
for k = 1:3
% plot(results(k).baudr, results(k).ndr.STAIR.NetRate, ...
% '-', 'LineWidth', lw, ...
% 'Color', curves(k).color, ...
% 'MarkerSize', 4,'Marker','+', ...
% 'MarkerFaceColor', curves(k).color);
plot(results(k).baudr, results(k).ndr.O_FEC.NetRate, ...
':', 'LineWidth', lw, ...
'Color', curves(k).color, ...
'MarkerSize', 2,...
'MarkerFaceColor', curves(k).color,...
'Marker',curves(k).mkr);
plot(results(k).baudr, results(k).ndr.KP4_hamming.NetRate, ...
'--', 'LineWidth', lw, ...
'Color', curves(k).color, ...
'MarkerSize', 2,'Marker','diamond', ...
'MarkerFaceColor', curves(k).color,...
'Marker',curves(k).mkr);
end
yline(400,'LineStyle','--');
ylabel('');
xlabel('Baud rate [GBd]');
ylim([280 430]);
yticks(280:30:440)
xlim([100 210]);
xticks(100:15:225);
grid minor; box on;
beautifyBERplot("logscale",0,"setmarkers",0);
% === FINAL FIGURE SIZE ===
pos = 1e3.*[0.7950 1.1150 1.4113 0.1900];
set(fig, 'Position', pos);
% % % %% === EXPORT ===
outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_ndr_v3.tikz';
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'legend columns=1' ...
'every axis/.append style={font=\scriptsize}',...
'minor grid style={line width=0.2pt, solid, color=black!10}',...
'grid style={line width=0.4pt, solid, color=black!20}',...
'grid style={dashed}',...
});

View File

@@ -0,0 +1,180 @@
%% ============================================================
% GRID (1 × 4):
% 1) NGMI overview (PAM4+PAM6+PAM8 superimposed)
% 2) PAM-4 tile (AIR + SD-NDR + HD-NDR)
% 3) PAM-6 tile
% 4) PAM-8 tile
% ============================================================
db = DBHandler("dataBase","labor_highspeed","type","mysql");
%% --- Base DB Filters (shared across all curves)
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 2);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','rop_attenuation','EQUALS', 0);
fp.where('Runs','is_mpi','EQUALS', 0);
fields = db.getTableFieldNames('dashboard_ungrouped_alltime');
[dataTable,~] = db.queryDB(fp, fields);
%% === CURVE DEFINITIONS =================================================
curves = struct;
curves(1).pam = 8;
curves(1).eq = equalizer_structure.vnle_pf_mlse;
curves(1).pre = 0;
curves(1).color = clr.Paired.red;
curves(2).pam = 6;
curves(2).eq = equalizer_structure.vnle_pf_mlse;
curves(2).pre = 1;
curves(2).color = clr.Paired.blue;
curves(3).pam = 4;
curves(3).eq = equalizer_structure.vnle_db_mlse;
curves(3).pre = 0;
curves(3).color = clr.Paired.green;
% === ANALYSIS ENGINE (extract BER/NGMI/AIR/netrates) ===================
base = struct;
base.group_by = {'equalizer_structure','pre_emph'};
base.x_axis = 'grossrate';
base.outlier = 'none';
base.show_raw = false;
results = struct;
for k = 1:numel(curves)
% ========== BER ==========
cfg = base;
cfg.y_axis = 'BER';
cfg.agg = 'min';
cfg.filters = struct('pam_level', curves(k).pam, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', curves(k).pre);
A = analyze_measurements_gpt(dataTable, cfg);
results(k).gross = A.group{1}.x;
if curves(k).pam == 4
results(k).ber = A.group{1}.y_precoded;
else
results(k).ber = A.group{1}.y;
end
% ========== NGMI ==========
cfg.y_axis = 'NGMI'; cfg.agg = 'max';
A = analyze_measurements_gpt(dataTable, cfg);
results(k).ngmi = A.group{1}.y;
% ========== AIR ==========
cfg.y_axis = 'AIR'; cfg.agg = 'max';
A = analyze_measurements_gpt(dataTable, cfg);
results(k).air = A.group{1}.y;
% ========== NET RATES ==========
tp = TransmissionPerformance;
results(k).ndr = tp.calculateNetRate(results(k).gross, ...
'NGMI', results(k).ngmi, ...
'BER', results(k).ber);
end
% ============================================================
% FIGURE
% ============================================================
fig = figure(3);
t = tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
lw = 1.7;
% =======================================================================
% (1) NGMI OVERVIEW TILE (all 3 curves)
% =======================================================================
ax = nexttile(t,1); hold on;
for k = 1:3
plot(results(k).gross, results(k).ngmi, ...
'-o', 'Color', curves(k).color, ...
'LineWidth',lw,'MarkerSize',5, ...
'MarkerFaceColor',curves(k).color);
end
ylabel('NGMI');
xlabel('Grossrate [Gb/s]');
ylim([0.9 1]); % your chosen limits
xlim([300 480]);
xticks(300:30:480)
grid minor; box on;
beautifyBERplot;
% =======================================================================
% (24) PAM-SPECIFIC TILES: AIR, SD-NDR, HD-NDR
% =======================================================================
pam_order = [4 6 8]; % left right
for ti = 1:3
pam_target = pam_order(ti);
ax = nexttile(t, 1+ti); hold on;
% find matching curve
for k = 1:3
if curves(k).pam ~= pam_target, continue; end
col = curves(k).color;
% AIR
plot(results(k).gross, results(k).air, ...
'-','Color',col,'LineWidth',lw,'Marker','*', ...
'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','AIR');
% SD-based net rate
plot(results(k).gross, results(k).ndr.SDHD.NetRate, ...
'--','Color',col,'LineWidth',lw,'Marker','v', ...
'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','SD+HD');
% HD-based net rate
plot(results(k).gross, results(k).ndr.STAIR.NetRate, ...
':','Color',col,'LineWidth',lw,'Marker','x', ...
'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','HD-FEC (Staircase)');
% HD-based net rate
plot(results(k).gross, results(k).ndr.O_FEC.NetRate, ...
'LineStyle','-.','Color',col,'LineWidth',lw,'Marker','+', ...
'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','O-FEC');
% HD-based net rate
plot(results(k).gross, results(k).ndr.KP4_hamming.NetRate, ...
'LineStyle','-','Color',col,'LineWidth',lw,'Marker','x', ...
'MarkerSize',5,'MarkerFaceColor',col,'DisplayName','KP4+Hamming');
end
ylabel('NDR [Gb/s]');
xlabel('Grossrate [Gb/s]');
ylim([300 440]); % your chosen limits
yticks(300:20:480)
xlim([300 480]);
xticks(300:30:480)
grid minor; box on;
beautifyBERplot;
yline(400,'HandleVisibility','off');
end
% === FIX FIGURE SIZE FOR TIKZ ==========================================
if 0
pos = 1e3.*[0.3643 0.9943 1.4113 0.2120];
set(fig,'Position',pos);
% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_ndr.tikz';
% matlab2tikz(outfile, ...
% 'width','\fwidth', ...
% 'height','\fheight', ...
% 'showInfo',false, ...
% 'extraAxisOptions',{ ...
% 'legend style={font=\footnotesize}', ...
% 'legend columns=1' ...
% });
end

View File

@@ -0,0 +1,153 @@
%% ============================================================
% LOAD DATA (PAM-4, sweep over ROP)
% ============================================================
database_type = 'mysql';
db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
pam_level = 4;
fiberL = 1; % 1 km
wlen = 1310;
baudrate = 360e9;
fp = QueryFilter();
fp.where('Runs','pam_level','EQUALS', pam_level);
fp.where('Runs','fiber_length','EQUALS', fiberL);
fp.where('Runs','wavelength','EQUALS', wlen);
fp.where('Runs','bitrate','EQUALS', baudrate);
fp.where('Runs','power_pd_in','LESS_THAN', 7);
fields = [
db.getTableFieldNames('power_state_info');
db.getTableFieldNames('dashboard_ungrouped_alltime')
];
[dataTable,~] = db.queryDB(fp, fields);
%% ============================================================
% DSP SCHEMES (Best combinations only)
% ============================================================
curves = struct;
curves(1).name = 'VNLE';
curves(1).eq = equalizer_structure.vnle;
curves(1).color = clr.Paired.red;
curves(2).name = 'PF + MLSE';
curves(2).eq = equalizer_structure.vnle_pf_mlse;
curves(2).color = clr.Paired.green;
curves(3).name = 'DB-target + MLSE';
curves(3).eq = equalizer_structure.vnle_db_mlse;
curves(3).color = clr.Paired.blue;
curves(4).name = 'ML-based MLSE';
curves(4).eq = equalizer_structure.ml_mlse;
curves(4).color = clr.Paired.purple;
%% ============================================================
% ANALYSIS ENGINE (No plotting)
% ============================================================
results = struct;
for k = 1:numel(curves)
pre_emph = decide_preemph(pam_level,curves(k).eq);
precoded = decide_precoded(pam_level,curves(k).eq);
cfg = struct;
cfg.x_axis = 'power_mzm'; % ROP axis
cfg.y_axis = 'BER';
cfg.agg = 'min';
cfg.outlier = 'none';
cfg.show_raw = false;
cfg.filters = struct( ...
'pam_level', pam_level, ...
'fiber_length', fiberL, ...
'wavelength', wlen, ...
'bitrate', baudrate, ...
'is_mpi', 0, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', pre_emph);
A = analyze_measurements_gpt(dataTable, cfg);
results(k).x = A.group{1}.x;
if precoded
results(k).ber = A.group{1}.y_precoded;
else
results(k).ber = A.group{1}.y;
end
end
%% ============================================================
% PLOT BER vs ROP (Single Axis)
% ============================================================
fig = figure(); clf; hold on;
lw = 2.0;
ms = 7;
for k = 1:numel(curves)
plot(results(k).x, results(k).ber, ...
'-o', ...
'Color', curves(k).color, ...
'MarkerFaceColor', curves(k).color, ...
'MarkerSize', ms, ...
'LineWidth', lw, ...
'DisplayName', curves(k).name);
end
set(gca,'YScale','log');
grid on;
xlabel('ROP / Power (MZM) [dBm]');
ylabel('BER');
ylim([1e-4 2e-1]);
title(sprintf('BER vs ROP PAM-%d, %.0f km, %.0f GBd, %.0f nm', ...
pam_level, fiberL, baudrate*1e-9, wlen));
legend('Location','best');
beautifyBERplot();
pos = 1e3.*[0.2 0.6 1.3 0.4];
set(fig, 'Position', pos);
%% ============================================================
% DECISION LOGIC (INLINE FUNCTIONS)
% ============================================================
function pe = decide_preemph(M, eq)
% PRE-EMPH RULES:
switch M
case 4
if eq == equalizer_structure.vnle
pe = 1; % PAM4: VNLE pre-emph on
else
pe = 0; % PAM4: all others off
end
case {6,8}
pe = 1; % PAM6/8: all pre-emph on
otherwise
pe = 0;
end
end
function flag = decide_precoded(M, eq)
% PRE-CODE RULES:
if eq == equalizer_structure.vnle_db_mlse
flag = 1; % Always for DB-target
elseif eq == equalizer_structure.ml_mlse && M == 4
flag = 1; % PAM4: ML-based precoded
else
flag = 0;
end
end

View File

@@ -0,0 +1,182 @@
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1;
database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
rates = [300e9];
cols = cbrewer2('BuPu',25);
cols = [cols(end-10:2:end,:)];
cols = cbrewer2('Set1',6);
fignum = 200;
fig=figure(fignum);clf;
for dbmode = 0:1%length(rates)
if 0
rcalpha = 0.05;
fsym = rates/2;
pulsef = 1;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
Pamsource = PAMsource(...
"fsym",fsym,"M",4,"order",18,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.2,...
"applypulseform",pulsef,"pulseformer",Pform,...
"randkey",20,...
"db_precode",dbmode,"db_encode",0,...
"mrds_code",0,"mrds_blocklength",512);
[Digi_sig,Symbols,Bits] = Pamsource.process();
Digi_sig = Digi_sig.normalize("mode","rms");
%%% 1) PLOT FULL RESPONSE SIGNAL
Digi_sig.spectrum("displayname","Full Response","fignum",fignum+dbmode,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",[0.2,0.2,0.2],"linestyle",'-','addDCoffset',0,'normalizeToDC',1);
%%% 2) PLOT PREEMPH. TX SIGNAL
if dbmode == 0
maxamp = -37;
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp";
precomp_fn = "lab_high_speed";
Digi_sig_pre = precomp_est.precomp(Digi_sig,'maxampdb',maxamp,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig_pre = Digi_sig_pre.resample("fs_out",fdac);
Digi_sig_pre= Digi_sig_pre.normalize("mode","rms");
Digi_sig_pre.spectrum("displayname","Strong Precomp","fignum",fignum+dbmode,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",[0,0,0],"linestyle",'-.','addDCoffset',0,'normalizeToDC',1);
end
end
% 1 - PAM 4 with preemphasis
fp = QueryFilter();
M = 4;
fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'bitrate','EQUALS', rates);%360,390
fp.where('Runs', 'fiber_length','EQUALS', 10);
fp.where('Runs', 'wavelength','EQUALS', 1322.7); %1327.4
fp.where('Runs', 'db_mode','EQUALS', dbmode);
fp.where('Runs', 'rop_attenuation','EQUAL', 0);
[dataTable,~] = database.queryDB(fp, database.getTableFieldNames('Runs'));
dataTable = queryRunid(dataTable.run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
% Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options);
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym);
Scpe_sig = Scpe_cell{1};
%%% 3) PLOT DB Tgt. SIGNAL
if 1
DB_Symbols = Duobinary().encode(Symbols);
DB_Symbols.spectrum("fignum",fignum+dbmode,"normalizeTo0dB",1,"displayname",'DB-Response','addDCoffset',0,'color',clr.Set1.blue,'normalizeToNyquist',0,'linestyle','--');
end
%%% 4) Plot RX Signal
Scpe_sig.spectrum("fignum",fignum+dbmode,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',1,'color',[0,0,0],'normalizeToNyquist',0,'linestyle',':');
Scpe_sig.eye(fsym,M,"fignum",47,"displayname",' Eye of AVG Signal');
% xline(Symbols.fs/2.*1e-9,'Color',cols(r,:),'HandleVisibility','off');
average_signals = 1;
if average_signals
Scpe_sig_avg = Scpe_sig;
scope_mean = zeros(size(Scpe_cell{1}.signal));
for n=1:numel(Scpe_cell)
scope_mean = scope_mean + Scpe_cell{n}.signal;
end
scope_mean = scope_mean ./ n;
Scpe_sig_avg.signal = scope_mean;
Scpe_sig_avg.spectrum("displayname","Scope PSD","fignum",20,"normalizeTo0dB",1);
Scpe_sig_avg.plot("displayname","Scope raw signal","fignum",27,"clear",1);
Scpe_sig_avg.eye(fsym,M,"fignum",48,"displayname",' Eye of AVG Signal');
end
fig = figure(fignum+dbmode);
if dbmode == 0
ylim([-22,12]);
else
ylim([-22,2]);
end
xlim([0,105]);
xticks(-100:20:100);
yticks(-20:10:10);
beautifyBERplot("logscale",0,"setmarkers",0)
pos = [100.3333 991.6667 358.0000 192.6667];
set(fig, 'Position', pos);
%%%%%%%%%%%%
drawnow;
% Do EQ and find alpha's
len_tr = 4096*2;
ffe_order = [50, 5, 5];
dfe_order = [0, 0, 0];
pf_ncoeffs = 1;
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
mu_dc = 0.005;
%%% FULL RESP TARGET
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
pf_1 = Postfilter("ncoeff",1,"useBurg",1);
[eq_signal_sd, eq_noise] = eq_.process(Scpe_sig, Symbols);
% eq_noise.signal = eq_noise.signal - mean(eq_noise.signal);
% eq_noise = eq_noise.normalize("mode","rms");
[mlse_sig_sd,whitened_noise] = pf_1.process(eq_signal_sd, eq_noise);
fig = figure(fignum+dbmode+10); hold on
[h, w] = freqz(1, pf_1.coefficients, length(eq_noise), "whole", eq_noise.fs);
h = h / max(abs(h)); % Normalize the filter response
w_ = (w - eq_noise.fs / 2);
%%% DB TARGET
db_ref_sequence = Duobinary().encode(Symbols);
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
[eq_signal, db_noise] = eq_.process(Scpe_sig,db_ref_sequence);
% db_noise.signal = db_noise.signal - mean(db_noise.signal);
% db_noise = db_noise.normalize("mode","rms");
%%% 1-3) Plot EQ Noise EEN
figure(fignum+dbmode+10)
eq_noise.spectrum("displayname", 'Noise', "fignum", fignum+dbmode+10, "normalizeTo0dB", 0,"color",clr.Set1.green,"normalizeToDC",0,"addDCoffset",0);
if dbmode == 1
offset = 27.7;
else
offset = 29.8;
end
plot(w_ * 1e-9, 20 * log10(fftshift(abs(h)))-offset, 'DisplayName', ['Burg Coeffs: ', num2str(round(pf_1.coefficients, 2)), ' '], 'LineWidth', 1,'Color',clr.Set1.green,'LineStyle','--');
db_noise.spectrum("displayname", 'DBt. Noise', "fignum", fignum+dbmode+10, "normalizeTo0dB", 0,"color",clr.Set1.blue,"normalizeToDC",0,"addDCoffset",0);
ylim([-54,-25]);
xlim([0,105]);
xticks(0:20:110);
yticks(-50:10:10);
beautifyBERplot("logscale",0,"setmarkers",0)
pos = [100.3333 991.6667 358.0000 192.6667];
set(fig, 'Position', pos);
end
% === FINAL FIGURE SIZE ===

Some files were not shown because too many files have changed in this diff Show More