Merge branch 'main' of cau-git.rz.uni-kiel.de:nt/mitarbeiter/silas/imdd_simulation

This commit is contained in:
sutef391
2025-12-22 07:29:09 +01:00
164 changed files with 14366 additions and 3330 deletions

View File

@@ -344,10 +344,13 @@ classdef Signal
arguments arguments
obj obj
options.fignum options.fignum = 2025
options.displayname = ""; options.displayname = "";
options.color = []; options.color = [];
options.linestyle = '-';
options.normalizeToNyquist = 0; options.normalizeToNyquist = 0;
options.addDCoffset = 0;
options.normalizeToDC = 0;
options.normalizeTo0dB = 0; options.normalizeTo0dB = 0;
options.max_num_lines = []; % Leave empty or omit to disable line rotation options.max_num_lines = []; % Leave empty or omit to disable line rotation
options.fft_length = []; options.fft_length = [];
@@ -360,6 +363,8 @@ classdef Signal
options.fft_length = 2^(nextpow2(length(obj.signal))-9); options.fft_length = 2^(nextpow2(length(obj.signal))-9);
end end
if options.normalizeToNyquist == 0 if options.normalizeToNyquist == 0
[p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ... [p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ...
options.fft_length/2, options.fft_length, ... options.fft_length/2, options.fft_length, ...
@@ -373,6 +378,8 @@ classdef Signal
% We'll keep f_rad for the x-axis in that mode. % We'll keep f_rad for the x-axis in that mode.
end end
% p_lin = movmean(p_lin,4);
if options.normalizeTo0dB if options.normalizeTo0dB
p_lin = p_lin ./ max(p_lin); p_lin = p_lin ./ max(p_lin);
p_dbm = 10*log10(p_lin); % normalized to 0 dB p_dbm = 10*log10(p_lin); % normalized to 0 dB
@@ -415,11 +422,20 @@ classdef Signal
ax = gca; ax = gca;
hold on hold on
p_dbm = p_dbm+options.addDCoffset;
if options.normalizeToDC
[~,min_idx]=min(abs(f_GHz));
pow_at_dc = p_dbm(min_idx);
p_dbm = p_dbm-pow_at_dc;
end
% p_dbm = movmean(p_dbm,10);
for s = 1:min(size(p_dbm)) for s = 1:min(size(p_dbm))
if isempty(options.color) if isempty(options.color)
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1); plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1);
else else
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color); plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color,'LineStyle',options.linestyle);
end end
end end
@@ -449,21 +465,25 @@ classdef Signal
ylabel(ylab); ylabel(ylab);
% --- Y-Axis scaling (auto with margin) ---
y_min = min(p_dbm(:));
y_max = max(p_dbm(:));
% Add 5% dynamic range margin on both sides
y_range = y_max - y_min;
if y_range == 0
y_range = 10; % fallback if flat
end
y_margin = 0.05 * y_range;
ylim([y_min - y_margin, y_max + y_margin]);
% Set ticks automatically, avoid overpopulation
try try
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]); yticks(round(linspace(y_min, y_max, min(10, max(4, ceil(y_range/10))))));
catch
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
end end
if options.normalizeTo0dB
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
else
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
end
yticks(-200:10:10);
grid on; grid on;
legend
end end
@@ -961,8 +981,8 @@ classdef Signal
maxA = max(sig(100:end-100))*1.3; maxA = max(sig(100:end-100))*1.3;
minA = min(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3;
% maxA = 0.0015; maxA = 0.12;
% minA = 0; minA = -0.08;
difference= maxA-minA; difference= maxA-minA;
@@ -1012,7 +1032,7 @@ classdef Signal
% add information % add information
if 1 if 0
pwr_dbm = round(obj.power,3); pwr_dbm = round(obj.power,3);
pwr_lin = obj.power("unit",power_notation.W); pwr_lin = obj.power("unit",power_notation.W);
@@ -1121,6 +1141,11 @@ classdef Signal
end end
grid off
end
yticks(linspace(0,histpoints,6)); yticks(linspace(0,histpoints,6));
y_tickstring = sprintfc('%.2f', y_tickstring); y_tickstring = sprintfc('%.2f', y_tickstring);
yticklabels(y_tickstring); yticklabels(y_tickstring);
@@ -1129,12 +1154,7 @@ classdef Signal
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12); x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
xticklabels(x_tickstring); xticklabels(x_tickstring);
grid off %
end
end end
% disp('h'); % disp('h');

View File

@@ -139,6 +139,8 @@ classdef ChannelFreqResp < handle
fnew = linspace(0,fstarget/2,length(Target)/2+1); fnew = linspace(0,fstarget/2,length(Target)/2+1);
fnew = fnew(2:end-1); fnew = fnew(2:end-1);
% Old frequency axis (should be much coarser) % Old frequency axis (should be much coarser)
idx_old = find((obj.faxis > 0) .* (obj.faxis < fstarget/2)); %positions of all Frequencies smaller than fs/2 idx_old = find((obj.faxis > 0) .* (obj.faxis < fstarget/2)); %positions of all Frequencies smaller than fs/2
int_fold = obj.faxis(idx_old); %old frequencies from 0 to fs/2 int_fold = obj.faxis(idx_old); %old frequencies from 0 to fs/2
@@ -149,6 +151,10 @@ classdef ChannelFreqResp < handle
% interpolate the frequency response that had a coarse frequency resolution (e.g. 256 bins) to the current frequency resolution (e.g. 21843 bins) % interpolate the frequency response that had a coarse frequency resolution (e.g. 256 bins) to the current frequency resolution (e.g. 21843 bins)
iH = interp1(int_fold, real(H_inv(idx_old)) ,fnew, 'linear') + 1i*interp1(int_fold, imag(H_inv(idx_old)) ,fnew, 'linear'); iH = interp1(int_fold, real(H_inv(idx_old)) ,fnew, 'linear') + 1i*interp1(int_fold, imag(H_inv(idx_old)) ,fnew, 'linear');
if 0
figure(7);hold on;plot(fnew,20*log10(abs(iH)))
end
% set all NaN values to the fist/ last non-NaN value % set all NaN values to the fist/ last non-NaN value
nH = find(~isnan(iH),1,'first'); nH = find(~isnan(iH),1,'first');
iH(1:nH)=iH(nH); iH(1:nH)=iH(nH);
@@ -171,6 +177,8 @@ classdef ChannelFreqResp < handle
% five frequencies -> should be the vaue at f=0=DC component? % five frequencies -> should be the vaue at f=0=DC component?
iH = iH./mean(abs(iH)); %why 1:5?? iH = iH./mean(abs(iH)); %why 1:5??
dc = 20*log10(abs(mean(abs(iH(1:5)))));
% set maximum amplification % set maximum amplification
% set als values higher than hmax to hmax and keep the % set als values higher than hmax to hmax and keep the
% phase information by multiplication with respective % phase information by multiplication with respective
@@ -206,7 +214,7 @@ classdef ChannelFreqResp < handle
function plot(obj) function plot(obj)
figure(55); figure();
clf; clf;
Havg = obj.H; Havg = obj.H;
@@ -225,7 +233,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]); xlim([0.2 .5*max(obj.faxis)*1e-9]);
grid on; grid on;
figure(56); figure();
clf; clf;
@@ -246,7 +254,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
%%% plot for publication %%% plot for publication
figure(1234); figure(101);
hold all; hold all;
box on; box on;
title('Magnitude Freq. Response'); title('Magnitude Freq. Response');
@@ -269,6 +277,25 @@ classdef ChannelFreqResp < handle
legend('Interpreter','latex') legend('Interpreter','latex')
grid on; grid on;
%
figure(100); hold on;
Havg = obj.H;
Havg = Havg./max(Havg);
Hall = obj.H_all;
for i = 1:size(Hall,1)
Hall(i,:) = Hall(i,:)./max(Hall(i,:));
end
%1)
col = cbrewer2('Paired',8);
hold all;box on;title('Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(Hall)),'linewidth',0.1,'LineStyle','-','Color',col(1,:),'HandleVisibility','off') ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2,'Color',col(2,:));
grid on;
end end

View File

@@ -131,7 +131,7 @@ classdef FFE < handle
mask = ones(obj.order,1); mask = ones(obj.order,1);
maincursor_pos=ceil(length(obj.e)/2); maincursor_pos=ceil(length(obj.e)/2);
always_ideal_decision = 0; always_ideal_decision = 0;
save_debug = 1; save_debug = 0;
grad =0; grad =0;
weight = 0; weight = 0;
update = 0; update = 0;

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

@@ -6,6 +6,10 @@ classdef MLSE < handle
DIR DIR
trellis_states trellis_states
duobinary_output duobinary_output
trellis_state_mode
trellis_exclusion
debug
scale_mode
end end
methods (Access=public) methods (Access=public)
@@ -19,7 +23,10 @@ classdef MLSE < handle
options.DIR double = [1]; options.DIR double = [1];
options.trellis_states double = [-3 -1 1 3]; options.trellis_states double = [-3 -1 1 3];
options.duobinary_output logical = false; options.duobinary_output logical = false;
options.trellis_state_mode = 2;
options.trellis_exclusion = 0;
options.scale_mode = 2;
options.debug = 0;
end end
% %
@@ -33,6 +40,12 @@ classdef MLSE < handle
function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass) function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass)
arguments
obj
signalclass
ref_symbolclass
end
data_in = signalclass.signal; data_in = signalclass.signal;
shape_in = size(data_in); shape_in = size(data_in);
data_ref = ref_symbolclass.signal; data_ref = ref_symbolclass.signal;
@@ -54,18 +67,25 @@ classdef MLSE < handle
function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref) function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref)
debug = 0;
trellis_state_mode = 2; % General: States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target) arguments
obj
data_in
data_ref
end
debug = obj.debug;
trellis_state_mode = obj.trellis_state_mode; % General: States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target)
% 0 = use provided states (MUST provide the correct states); % 0 = use provided states (MUST provide the correct states);
% 1 = normalize to = 1 rms; % 1 = normalize to = 1 rms;
% 2 = use target symbols; % 2 = use target symbols;
% 3 = use statistical levels % 3 = use statistical levels
% 3 analyzes avg of rx signal levels - can help with nonlinear impairments % 3 analyzes avg of rx signal levels - can help with nonlinear impairments
trellis_exclusion = 0; % PAM-6 only (only if data is NOT precoded!) trellis_exclusion = obj.trellis_exclusion; % PAM-6 only (only if data is NOT precoded!)
scale_mode = 2; % scale_mode: scale_mode = obj.scale_mode; % scale_mode:
% 0 = no scaling, % 0 = no scaling,
% 1 = RMSscale MODEL, % 1 = RMSscale MODEL,
% 2 = MMSE/time-corrscale MODEL, % 2 = MMSE/time-corrscale MODEL,
@@ -98,7 +118,7 @@ classdef MLSE < handle
elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option) elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option)
obj.trellis_states = reshape(unique(data_ref),size(obj.trellis_states)); obj.trellis_states = reshape(unique(data_ref),1,length(unique(data_ref)));
elseif trellis_state_mode == 3 %use_statistical_levels elseif trellis_state_mode == 3 %use_statistical_levels
@@ -257,14 +277,107 @@ classdef MLSE < handle
if debug if debug
alpha_ = alpha - min(alpha) + eps; % alpha_ = alpha - min(alpha) + eps;
figure();hold on; % figure();hold on;
n = 10; % n = 10;
scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1); % scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1);
scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green'); % scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green');
% scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red'); % % scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red');
yticks(obj.trellis_states); % yticks(obj.trellis_states);
ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]); % ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]);
%% ----- Build true path from known sequence (data_ref) -----
% Memory length used by VA (L = length(obj.DIR)-1 symbols stored in state)
L = size(combs,2); % each row of combs is the L-tap state vector
N = length(data_in);
% Map each trellis level to an index (1..M)
[~, level_to_idx] = ismember(levels, levels); %#ok<ASGLU> % identity map
[ok_ref, ref_idx] = ismember(data_ref(:).', levels);
if ~all(ok_ref)
warning('Some data_ref symbols are not in "levels". True-path build may fail.');
end
% Precompute next_state(from_state, u_idx) LUT such that:
% combs(next_state,:) == [ combs(from_state,2:end) , levels(u_idx) ]
next_state = zeros(size(combs,1), numel(levels), 'uint32');
for from = 1:size(combs,1)
prefix = combs(from,2:end); % what must match in 'to' for a valid transition
for ui = 1:numel(levels)
target = [prefix, levels(ui)];
% find the unique 'to' whose state vector equals target
to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first');
if isempty(to), to = 0; end
next_state(from, ui) = to;
end
end
% Initialize true path at n=1: pick any state with last_sym == data_ref(1)
% Prefer one whose suffix matches the first available history if L>1.
cand = find(last_sym == data_ref(1));
if isempty(cand)
% fallback: choose closest in amplitude (should not happen if levels match)
[~,ix] = min(abs(last_sym - data_ref(1)));
cand = ix;
end
true_state_path = zeros(1,N,'uint32');
true_state_path(1) = cand(1);
% Propagate forward using the known inputs data_ref(n)
for n = 2:N
ui = ref_idx(n); % index of the actual transmitted level at time n
from = true_state_path(n-1);
if from==0 || ui==0
true_state_path(n) = 0;
else
true_state_path(n) = next_state(from, ui);
if true_state_path(n)==0
% Safety fallback: if no valid transition found (should not happen)
% choose any to-state whose vector matches shift+current symbol
target = [combs(from,2:end), levels(ui)];
to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first');
if isempty(to), to = from; end
true_state_path(n) = to;
end
end
end
%% ----- Collect branch metrics along decoded vs. true path -----
bm_decoded = nan(1,N);
bm_true = nan(1,N);
% n=1 in your code stores pm into bm_fw(:,:,1); real BMs start at n>=2
for n = 2:N
% Decoded path: to = viterbi_path(n), from = survivor that fed it
to_d = viterbi_path(n);
from_d = pm_survivor_fw_idx(to_d, n);
bm_decoded(n) = bm_fw(to_d, from_d, n);
% True path: transition true_state_path(n-1) -> true_state_path(n)
to_t = true_state_path(n);
from_t = true_state_path(n-1);
if to_t>0 && from_t>0
bm_true(n) = bm_fw(to_t, from_t, n);
end
end
% Convert to "cost" for intuitive plotting (your BM is a log-likelihood)
cost_dec = -bm_decoded;
cost_true= -bm_true;
%% ----- Plot a short window for clarity -----
win = max(2, N-20000):N; % last 200 samples (adjust as needed)
figure('Color','w'); hold on; grid on; box on;
plot(win, cost_true(win), 'LineWidth',1.2, 'DisplayName','True path cost (BM)');
plot(win, cost_dec(win), 'LineWidth',1.2, 'DisplayName','Decoded path cost (BM)');
xlabel('Time index n'); ylabel('Branch cost'); title('Branch metrics along true vs decoded path');
legend('Location','best');
%% ----- Optional: overlay symbol levels for the same window -----
yyaxis right
plot(win, data_in(win), ':', 'LineWidth',0.8, 'DisplayName','y(n)');
ylabel('Amplitude');
end end
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path); VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
@@ -544,11 +657,6 @@ classdef MLSE < handle
end end
function s = logsumexp(a,dim)
% returns log(sum(exp(a),dim)) safely
amax = max(a,[],dim);
s = amax + log(sum(exp(a - amax), dim));
end
end end
end end

View File

@@ -12,6 +12,7 @@ classdef Exfo_laser < handle
safety_mode safety_mode
end end
%% Any user can call these methods to alter the laser state
methods (Access=public) methods (Access=public)
function obj = Exfo_laser(options) function obj = Exfo_laser(options)
@@ -169,6 +170,10 @@ classdef Exfo_laser < handle
end end
end %public mehtods end %public mehtods
% DAU must not be able to call these funcitons outside of the class!
% Hence they are private! :-)
methods (Access=private) methods (Access=private)
function v = connectLaser_(obj) function v = connectLaser_(obj)
@@ -180,7 +185,7 @@ classdef Exfo_laser < handle
configureTerminator(v, "CR", "CR"); % Set the terminator to carriage return (CR) configureTerminator(v, "CR", "CR"); % Set the terminator to carriage return (CR)
elseif obj.lab_interface == lab_interface.gpib elseif obj.lab_interface == lab_interface.gpib
% Connect via GPIB adress % Connect via GPIB adress
visastring = ['GPIB0::',obj.connection_id,'::INSTR']; visastring = ['GPIB1::',obj.connection_id,'::INSTR'];
v = visadev(visastring); v = visadev(visastring);
else else
error('Interface not supported'); error('Interface not supported');
@@ -342,8 +347,13 @@ classdef Exfo_laser < handle
command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)]; command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)];
writeline(serialObj, command); writeline(serialObj, command);
if abs(obj.cur_wavelength - desiredWavelength_nm)>30
pause(5); %it takes time to tune the laser! If this is not here it WILL break!
else
pause(1.5);
end
if serialObj.Type == visalib.InterfaceType.serial if serialObj.Type == visalib.InterfaceType.serial
pause(0.2); pause(1.5);
response = readline(serialObj); response = readline(serialObj);
success = contains(response, 'OK'); success = contains(response, 'OK');
end end

View File

@@ -0,0 +1,83 @@
classdef LabDeviceTemplate < handle
% Minimal template for lab device control via VISA / SCPI
% Copy this file and adapt:
% - visaAddress
% - public API methods
% - private SCPI commands
properties (Access = public)
active logical = true
end
properties (Access = protected)
visaAddress string
end
methods (Access = public)
function obj = LabDeviceTemplate(options)
arguments
options.active logical = true
options.visaAddress string = ""
end
% apply options
fn = fieldnames(options);
for k = 1:numel(fn)
obj.(fn{k}) = options.(fn{k});
end
% optional: quick connectivity check
if obj.active
v = obj.connect();
writeline(v,"*IDN?");
idn = readline(v);
disp("Connected to: " + string(idn));
delete(v);
end
end
function success = set(obj, value)
% Example public API
v = obj.connect();
success = obj.setValue_(v, value);
delete(v);
end
function value = read(obj)
% Example readback
v = obj.connect();
value = obj.readValue_(v);
delete(v);
end
end
methods (Access = protected)
function v = connect(obj)
assert(obj.visaAddress ~= "", "No VISA address defined");
v = visadev(obj.visaAddress);
end
end
methods (Access = private)
function success = setValue_(~, v, value)
writeline(v, sprintf("SET %g", value));
success = true;
end
function value = readValue_(~, v)
writeline(v, "READ?");
value = str2double(readline(v));
end
end
end

View File

@@ -0,0 +1,327 @@
classdef OSA_Advantest < handle
% Advantest Q8384 Optical Spectrum Analyzer (GPIB)
% User inputs in nanometers
% EXAMPLE:
% span_nm = 70;
% lambda_c_nm = 1310;
% osa = OSA_Advantest( ...
% 'span_nm', span_nm, ...
% 'lambda_c_nm', lambda_c_nm, ...
% 'resolution_nm', 0.1, ...
% 'sampling_points', 10001 );
%
% osa.configure();
% [lambda_nm, psd_dBm] = osa.measure();
% figure()
% plot(lambda_nm,psd_dBm);
properties (Access = public)
span_nm double = 5
resolution_nm double = 0.05
lambda_c_nm double = 1550
sampling_points double = 1001
timeout_s double = 10
lambda_nm
psd_dBm
end
properties (Constant)
valid_resolutions_nm = [0.01 0.02 0.05 0.10 0.20 0.50]
valid_sampling_points = [101 201 501 1001 2001 5001 10001];
end
properties (Access = protected)
visaAddress string = "GPIB0::3::INSTR"
end
methods
function obj = OSA_Advantest(options)
arguments
options.visaAddress string = "GPIB0::3::INSTR"
options.span_nm double = 5
options.resolution_nm double = 0.05
options.lambda_c_nm double = 1550
options.sampling_points double = 1001
options.timeout_s double = 10
end
fn = fieldnames(options);
for k = 1:numel(fn)
obj.(fn{k}) = options.(fn{k});
end
assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ...
'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm');
assert(ismember(obj.sampling_points, obj.valid_sampling_points), ...
'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001');
v = obj.connect();
writeline(v,"*IDN?");
disp(readline(v));
delete(v);
end
function configure(obj)
assert(ismember(obj.resolution_nm, obj.valid_resolutions_nm), ...
'Invalid resolution. Allowed: 0.01 | 0.02 | 0.05 | 0.10 | 0.20 | 0.50 nm');
assert(ismember(obj.sampling_points, obj.valid_sampling_points), ...
'Invalid # of Samp. Points. Allowed: 101 201 501 1001 2001 5001 10001');
v = obj.connect();
writeline(v,"FMT0,HED0,SDL0");
writeline(v,"BUZ0");
writeline(v,"QUI0");
writeline(v, sprintf("RES %gnm", obj.resolution_nm));
writeline(v, sprintf("SPT %g", obj.sampling_points));
writeline(v, sprintf("CEN %gnm", obj.lambda_c_nm));
writeline(v, sprintf("SPA %gnm", obj.span_nm));
writeline(v,"SWE 0");
delete(v);
end
function [osa_x_nm, osa_y_dBm] = measure(obj)
v = obj.connect();
writeline(v,"CSB");
writeline(v,"MEA1");
active = 1; cnt = 0;
while active
pause(1);
writeline(v,"MEA?");
active = str2double(readline(v));
cnt = cnt + 1;
if cnt > obj.timeout_s
delete(v);
error("OSA_Q8384:Timeout","Measurement timeout");
end
end
writeline(v,"PKL");
writeline(v,"ODN?");
readline(v); % number of points (not strictly needed)
writeline(v,"FMT0,HED0,SDL0");
writeline(v,"OSD0"); % Y-axis
osa_y_dBm = str2num(readline(v));
writeline(v,"OSD1"); % X-axis
osa_x_m = str2num(readline(v));
osa_x_nm = osa_x_m.*1e9;
delete(v);
obj.lambda_nm=osa_x_nm;
obj.psd_dBm=osa_y_dBm;
end
% --- Replace / extend plot() in OSA_Q8384 class ---
function plot(obj, options)
arguments
obj
options.FigureNumber double = 1
options.Hold logical = true
options.Title string = "OSA Spectrum"
options.LineWidth double = 0.8
options.Grid logical = true
options.Color (1,3) double = [0 0.4470 0.7410]
options.DisplayName string = ""
end
if isempty(obj.psd_dBm) || isempty(obj.lambda_nm)
error('Record spectrum first: run osa.measure()');
end
fig = figure(options.FigureNumber);
fig.Color = 'w';
ax = gca;
if ~options.Hold
cla(ax);
else
hold(ax,'on');
end
plot(ax, obj.lambda_nm, obj.psd_dBm, ...
'LineWidth', options.LineWidth, ...
'Color', options.Color, ...
'DisplayName', options.DisplayName);
xlabel(ax,'Wavelength [nm]');
ylabel(ax,'Optical Power [dBm]');
if options.Title ~= ""
title(ax, options.Title);
end
xlim(ax,[min(obj.lambda_nm) max(obj.lambda_nm)]);
yl = ylim(ax);
ylim(ax,[yl(1)-1 yl(2)+1]);
if options.Grid
grid(ax,'on');
grid(ax,'minor');
end
set(ax, ...
'FontSize',11, ...
'LineWidth',1, ...
'Box','on');
if options.DisplayName ~= ""
legend(ax,'show','Location','best');
end
end
function osnr = computeOSNR(obj, options)
% OSNR computation based on ratio method (RBW-independent)
% Assumes obj.lambda_nm [nm], obj.psd_dBm [dBm/RBW] exist
arguments
obj
options.bw_signal_nm double = 0.1 % channel bandwidth (0.1 for CW?!?!)
options.ref_bw_nm double = 0.1 % OSNR reference bandwidth
options.carrier_frequency = NaN;
options.noise_guard_nm = 1;
options.noise_window_nm =1;
options.debugplots = 0;
end
assert(~isempty(obj.lambda_nm) && ~isempty(obj.psd_dBm), ...
'Run measure() before computing OSNR');
obj.lambda_nm = obj.lambda_nm;
psd = obj.psd_dBm;
% --- find carrier wavelength recorded in OSA ---
% options.carrier_frequency [nm], options.noise_guard_nm, options.noise_window_nm
if ~isnan(options.carrier_frequency)
% local search window around expected CW carrier
idx_search = obj.lambda_nm > (options.carrier_frequency - options.noise_guard_nm) & ...
obj.lambda_nm < (options.carrier_frequency + options.noise_guard_nm);
[~, imax_local] = max(psd(idx_search));
lambda_search = obj.lambda_nm(idx_search);
lambda_c = lambda_search(imax_local);
[~,imax] = find(obj.lambda_nm==lambda_c);
% define local noise windows directly adjacent to signal (guarded)
idx_noise_left = obj.lambda_nm > (lambda_c - options.noise_guard_nm - options.noise_window_nm) & ...
obj.lambda_nm < (lambda_c - options.noise_guard_nm);
idx_noise_right = obj.lambda_nm > (lambda_c + options.noise_guard_nm) & ...
obj.lambda_nm < (lambda_c + options.noise_guard_nm + options.noise_window_nm);
idx_noise = idx_noise_left | idx_noise_right;
else
% fallback: global maximum (unsafe for pathological ASE cases)
[~, imax] = max(psd);
lambda_c = obj.lambda_nm(imax);
% ASE from outer quarters of spectrum
N = numel(obj.lambda_nm);
idx_noise = false(size(obj.lambda_nm));
idx_noise([3:round(N/4), round(3*N/4):N-1]) = true;
end
% --- extract signal window ---
idx_signal = obj.lambda_nm > (lambda_c - options.bw_signal_nm/2) & ...
obj.lambda_nm < (lambda_c + options.bw_signal_nm/2);
if ~any(idx_signal)
idx_signal(imax) = true;
end
cc_lambda = obj.lambda_nm(idx_signal);
cc_psd = psd(idx_signal);
% --- estimate ASE from local noise (flat ASE assumption) ---
noise_lambda = obj.lambda_nm(idx_noise);
noise_psd = psd(idx_noise);
ase_psd = interp1(noise_lambda, noise_psd, cc_lambda, ...
'linear', 'extrap');
% --- convert to linear (mW / RBW) ---
sig_lin = 10.^(cc_psd/10);
ase_lin = 10.^(ase_psd/10);
% --- integrate (RBW cancels) ---
P_sig_lin = sum(sig_lin);
P_ase_lin = sum(ase_lin);
P_sig_dB = 10*log10(P_sig_lin);
P_ase_dB = 10*log10(P_ase_lin);
osnr.p_sig_db = P_sig_dB;
osnr.p_ase_db = P_ase_dB;
% --- OSNR direct from peak value to the noise estimate out of band ---
osnr.direct_dB = psd(imax) - 10*log10(mean(ase_lin)) ;
% --- OSNR in measurement bandwidth ---
osnr.measured_dB = P_sig_dB - P_ase_dB;
% --- normalize to reference bandwidth (usually 0.1 nm) ---
osnr.ref_dB = osnr.measured_dB + ...
10*log10(options.bw_signal_nm / options.ref_bw_nm);
% --- ASE-corrected OSNR (optional definition) ---
osnr.corrected_dB = ...
10*log10(10^(P_sig_dB/10) - 10^(P_ase_dB/10)) - P_ase_dB;
osnr.corrected_ref_dB = osnr.corrected_dB + ...
10*log10(options.bw_signal_nm / options.ref_bw_nm);
% metadata
osnr.lambda_c_nm = lambda_c;
osnr.bw_signal_nm = options.bw_signal_nm;
osnr.ref_bw_nm = options.ref_bw_nm;
if options.debugplots
figure(100)
xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8])
obj.plot("DisplayName",'OSNR','FigureNumber',100);
yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation');
yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation');
scatter(obj.lambda_nm(idx_noise),obj.psd_dBm(idx_noise),'Marker','.','LineWidth',4,'DisplayName','Noise Samples','MarkerEdgeColor','red');
scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green');
scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta');
ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]);
figure(101);hold on
xline(lambda_c,'LineWidth',2,'DisplayName','Max. OSA Value','Color',[0.8,0.8,0.8])
xline(lambda_c - options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW - 0.5 BW','Color',[0.6,0.6,0.8])
xline(lambda_c + options.bw_signal_nm/2,'LineWidth',2,'DisplayName','CW + 0.5 BW','Color',[0.6,0.8,0.8])
obj.plot("DisplayName",'OSNR','FigureNumber',101);
yline(osnr.p_ase_db,'LineWidth',2,'DisplayName','Signal Estimation');
yline(osnr.p_sig_db,'LineWidth',2,'DisplayName','Noise Estimation');
scatter(obj.lambda_nm(idx_signal),obj.psd_dBm(idx_signal),'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','green');
scatter(obj.lambda_nm(idx_signal),ase_psd,'Marker','.','LineWidth',4,'DisplayName','Signal Samples','MarkerEdgeColor','magenta');
xlim([lambda_c - options.bw_signal_nm, lambda_c + options.bw_signal_nm])
ylim([min(obj.psd_dBm), max([obj.psd_dBm,osnr.p_sig_db+5])]);
end
end
end
methods (Access = protected)
function v = connect(obj)
v = visadev(obj.visaAddress);
end
end
end

View File

@@ -0,0 +1,146 @@
classdef OptLaserN7714A < handle
% Minimal controller for Keysight N7714A tunable laser over VISA-LAN
% Methods: getLaserInfo, setPower, setWavelength
%
% Examples:
% L = OptLaserN7714A('TCPIP::192.168.0.50::INSTR');
% L.setWavelength(1550); % nm
% L.setPower(-3.0, true); % dBm, also turns output ON
% info = L.getLaserInfo();
properties (Access=private)
v % visadev handle
resource char % VISA resource string, e.g. 'TCPIP::...::INSTR'
end
methods
function obj = OptLaserN7714A(resource)
arguments
resource (1,:) char
end
obj.resource = resource;
% Robust connect with small retry loop
maxRetries = 3; pauseTime = 1.0; lastErr = [];
for k = 1:maxRetries
try
obj.v = visadev(obj.resource);
% Identify instrument (SCPI *IDN?)
writeline(obj.v, '*IDN?'); % SCPI common, identification
idn = strtrim(readline(obj.v)); %#ok<NASGU>
break
catch ME
lastErr = ME;
pause(pauseTime);
end
end
if isempty(obj.v) || ~isvalid(obj.v)
error('OptLaserN7714A:ConnectFailed', ...
'Could not connect to %s. Last error: %s', obj.resource, lastErr.message);
end
% Set power unit to dBm for SOURce tree to be explicit (optional)
% [:SOUR]:POWer:UNIT dBm (laser power unit)
% Ref: SOURce:POWer:UNIT. :contentReference[oaicite:0]{index=0}
try
writeline(obj.v, 'SOUR:POW:UNIT DBM');
catch
% non-fatal
end
end
function delete(obj)
if ~isempty(obj.v) && isvalid(obj.v); delete(obj.v); end
end
function setPower(obj, power_dBm, outputOn, channel)
arguments
obj
power_dBm (1,1) double
outputOn (1,1) logical = true
channel (1,1) double {mustBeMember(channel,0:4)} = 1
end
v = obj.v;
cmd = sprintf('SOUR%d:POW:LEV:IMM:AMPL %g DBM', channel, power_dBm);
writeline(v, cmd);
if outputOn
writeline(v, sprintf('SOUR%d:POW:STAT 1', channel)); %SOUR1:POW:STAT 0
else
writeline(v, sprintf('SOUR%d:POW:STAT 0', channel));
end
writeline(v, '*OPC?');
a=readline(v);
end
function setWavelength(obj, wavelength_nm, channel)
arguments
obj
wavelength_nm (1,1) double {mustBePositive}
channel (1,1) double {mustBeMember(channel,0:4)} = 1
end
v = obj.v;
cmd = sprintf('SOUR%d:WAV %gNM', channel, wavelength_nm);
writeline(v, cmd);
writeline(v, '*OPC?'); readline(v);
end
function setDither(obj, enable, port)
% Enable or disable laser frequency dither (stabilization).
% enable = false no dither (FIX), true stabilization (STAB).
% port = 1..4 (default 1).
if nargin < 3
port = 1;
end
v = obj.v;
if enable
writeline(v, sprintf('SOUR%d:CONTrol:FCDither ON', port)); %[:SOURce[n]][:CHANnel[m]]:CONTrol:FCDither
else
writeline(v, sprintf('SOUR%d:CONTrol:FCDither OFF', port));
end
% optional confirmation
writeline(v, sprintf('SOUR%d:CONTrol:FCDither?', port));
modeStr = strtrim(readline(v));
fprintf('Port %d dither mode set to %s\n', port, modeStr);
end
function info = getLaserInfo(obj, port)
% Returns struct with IDN, output state, wavelength (nm), power (dBm)
% port = 1..4 (for N7714A with 4 outputs). Default = 1.
if nargin < 2
port = 1;
end
v = obj.v;
% *IDN?
writeline(v, '*IDN?');
idn = strtrim(readline(v));
% Output state: :SOURn:POW:STAT?
writeline(v, sprintf('SOUR%d:POW:STAT?', port));
out_state = str2double(readline(v)) ~= 0;
% Wavelength query (returns meters convert to nm)
writeline(v, sprintf('SOUR%d:WAV?', port));
wav_m = str2double(readline(v));
wav_nm = wav_m * 1e9;
% Power (set level) query (dBm)
writeline(v, sprintf('SOUR%d:POW:LEV:IMM:AMPL?', port));
p_dBm = str2double(readline(v));
info = struct('idn', idn, ...
'output_on', out_state, ...
'wavelength_nm', wav_nm, ...
'power_dBm', p_dBm);
end
end
end

View File

@@ -0,0 +1,168 @@
classdef OptPowerMeter8153A < handle
% Code for HEWLETT-PACKARD 8153A Optical Power Meter (2-channel).
% https://www.artisantg.com/info/Agilent_8153A_Manual.pdf?srsltid=AfmBOoqTe38sbGRNHUFXjw5uO6Br7I7aSfgAdRpx-hVvM4sTIhFYyD5x
% PAGE 166++
% Typical usage:
% pm = HPPowerMeter8153A( ...
% 'active', [false true], ...
% 'wavelength_nm', [1550 1310] );
%
% pm.setWavelength([1550 1310]); % only CH2 is modified
% p = pm.readPower(); % reads only active channels
properties (Access = public)
active logical = [true true] % channel enable
wavelength_nm double = [1550 1550] % per-channel wavelength [nm]
power_dBm double = [NaN NaN] % last read power
offset_dB double = [0 0] % per-channel power offset [dB]
avgTime_s double = [0.2 0.2];
end
properties (Constant)
avgTime_min_s = 0.02 % 20 ms
avgTime_max_s = 3600 % 1 hour
end
properties (Access = protected)
visaAddress string = "GPIB1::22::INSTR"
numChannels double = 2
end
methods
function obj = OptPowerMeter8153A(options)
arguments
options.visaAddress string = "GPIB1::22::INSTR"
options.active logical = [true true]
options.wavelength_nm double = [1550 1550]
end
fn = fieldnames(options);
for k = 1:numel(fn)
obj.(fn{k}) = options.(fn{k});
end
assert(numel(obj.active)==obj.numChannels)
assert(numel(obj.wavelength_nm)==obj.numChannels)
v = obj.connect();
writeline(v,"*IDN?");
disp(readline(v));
pause(0.1);
delete(v);
pause(0.1);
obj.setWavelength(obj.wavelength_nm);
end
function setWavelength(obj, lambda_nm)
if nargin > 1
obj.wavelength_nm = lambda_nm;
end
v = obj.connect();
for ch = 1:obj.numChannels
if obj.active(ch)
writeline(v, ...
sprintf("SENS%d:POW:WAVE %gNM", ch, obj.wavelength_nm(ch)));
end
end
pause(0.1);
delete(v);
pause(0.1);
end
function p = readPower(obj)
v = obj.connect();
for ch = 1:obj.numChannels
if obj.active(ch)
writeline(v, sprintf("READ%d:POW?", ch));
obj.power_dBm(ch) = str2double(readline(v));
else
obj.power_dBm(ch) = NaN;
end
end
delete(v);
p = obj.power_dBm;
end
function setOffsetCorrection(obj, offset_dB)
obj.offset_dB = offset_dB;
v = obj.connect();
for ch = 1:obj.numChannels
if obj.active(ch)
writeline(v, ...
sprintf("SENS%d:CORR:LOSS:INP:MAGN %gDB", ch, obj.offset_dB(ch)));
end
end
delete(v);
end
function offset_dB = getOffsetCorrection(obj)
v = obj.connect();
offset_dB = NaN(1,obj.numChannels);
for ch = 1:obj.numChannels
if obj.active(ch)
writeline(v, sprintf("SENS%d:CORR:LOSS:INP:MAGN?", ch));
offset_dB(ch) = str2double(readline(v));
end
end
delete(v);
obj.offset_dB = offset_dB;
end
function setAvgTime(obj, avgTime_s)
if any(avgTime_s < obj.avgTime_min_s) || any(avgTime_s > obj.avgTime_max_s)
oldv = avgTime_s;
avgTime_s = max(avgTime_s, obj.avgTime_min_s);
avgTime_s = min(avgTime_s, obj.avgTime_max_s);
fprintf('[HPPowerMeter8153A] Averaging time clipped:\n');
for ch = 1:obj.numChannels
if oldv(ch) ~= avgTime_s(ch)
fprintf(' CH%d: %.3g s %.3g s (allowed %.3g %.3g s)\n', ...
ch, oldv(ch), avgTime_s(ch), ...
obj.avgTime_min_s, obj.avgTime_max_s);
end
end
end
obj.avgTime_s = avgTime_s;
v = obj.connect();
for ch = 1:obj.numChannels
if obj.active(ch) && ~isnan(obj.avgTime_s(ch))
writeline(v, ...
sprintf("SENS%d:POW:ATIME %g", ch, obj.avgTime_s(ch)));
end
end
delete(v);
end
function avgTime_s = getAvgTime(obj)
v = obj.connect();
avgTime_s = NaN(1,obj.numChannels);
for ch = 1:obj.numChannels
if obj.active(ch)
writeline(v, sprintf("SENS%d:POW:ATIME?", ch));
avgTime_s(ch) = str2double(readline(v));
end
end
delete(v);
obj.avgTime_s = avgTime_s;
end
end
methods (Access = protected)
function v = connect(obj)
v = visadev(obj.visaAddress);
end
end
end

View File

@@ -20,7 +20,7 @@ classdef Thor_PDFA < handle
function obj = Thor_PDFA(options) function obj = Thor_PDFA(options)
arguments arguments
options.serialport_number = 'COM12' options.serialport_number = 'COM18'
options.safety_mode = 1; options.safety_mode = 1;
end end
@@ -37,7 +37,7 @@ classdef Thor_PDFA < handle
end end
function o = connectSerial(obj) function o = connectSerial(obj)
o = [];
try try
% Connect to the PDFA % Connect to the PDFA
o = serialport(obj.serialport_number, 9600); o = serialport(obj.serialport_number, 9600);
@@ -62,10 +62,14 @@ classdef Thor_PDFA < handle
function getStatus(obj) function getStatus(obj)
o = obj.connectSerial(); o = obj.connectSerial();
if ~isempty(o)
obj.getStatus_(o); obj.getStatus_(o);
if obj.safety_mode if obj.safety_mode
obj obj
end end
end
end end

View File

@@ -51,7 +51,7 @@ classdef DBHandler < handle
"Vendor", "MySQL", ... "Vendor", "MySQL", ...
"Server", "134.245.243.254", ... "Server", "134.245.243.254", ...
"PortNumber", 3306, ... "PortNumber", 3306, ...
"JDBCDriverLocation", "C:\Users\Silas\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar"); "JDBCDriverLocation", "C:\Users\sioe\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar");
end end
catch e catch e
@@ -588,14 +588,13 @@ classdef DBHandler < handle
return return
catch ME catch ME
if attempt < maxFast if attempt < maxFast
pause(1) pause(0.1)
else else
pause(10) pause(1)
end end
lastErr = ME; lastErr = ME;
end end
end end
pause(60)
error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport()) error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport())
end end

View File

@@ -16,9 +16,9 @@ classdef Metricstruct
SNR (1,1) double {mustBeNumeric} = NaN SNR (1,1) double {mustBeNumeric} = NaN
SNR_level (:,1) double {mustBeNumeric} = [] SNR_level (:,1) double {mustBeNumeric} = []
STD (1,1) double {mustBeNumeric} = NaN STD (1,1) double {mustBeNumeric} = NaN
STD_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] STD_level (:,1) double = []
STDrx (1,1) double {mustBeNumeric} = NaN STDrx (1,1) double {mustBeNumeric} = NaN
STDrx_level (:,1) double {mustBeNumeric, mustBeNonnegative} = [] STDrx_level (:,1) double = []
EVM (1,1) double {mustBeNumeric} = NaN EVM (1,1) double {mustBeNumeric} = NaN
EVM_level (:,1) double {mustBeNumeric} = [] EVM_level (:,1) double {mustBeNumeric} = []
@@ -71,16 +71,22 @@ classdef Metricstruct
end end
end end
function print(obj) function print(obj,options)
% Print method to display key metrics in a formatted way % Print method to display key metrics in a formatted way
arguments
obj
options.description = '';
end
% Define the width for formatting % Define the width for formatting
nameWidth = 15; % Width for parameter names nameWidth = 15; % Width for parameter names
valueWidth = 12; % Width for values valueWidth = 12; % Width for values
% Print header % Print header
fprintf('\n%s\n', repmat('=', 1, nameWidth + valueWidth)); fprintf('\n%s\n', repmat('=', 1, nameWidth + valueWidth));
fprintf(' Results \n'); fprintf([char(options.description),' Results \n']);
fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth)); fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth));
% Function to format numbers with appropriate precision % Function to format numbers with appropriate precision

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

@@ -137,7 +137,21 @@ classdef DataStorage < handle
if ~isempty(tmp) if ~isempty(tmp)
if isa(tmp,'double') if isa(tmp,'double')
value(i) = tmp ; try
value(i,:) = tmp ;
catch
a = size(value,2);
b = size(tmp,2);
if a > b
value(i,:) =[tmp,NaN(1,a-b)] ;
elseif a < b
value(i,:) = tmp(1:size(value,2)) ;
else
error('unknwon case...')
end
end
elseif isa(tmp,'Signal') || isa(tmp,'struct') || isa(tmp,'Exfo_laser') || isa(tmp,'DC_supply') elseif isa(tmp,'Signal') || isa(tmp,'struct') || isa(tmp,'Exfo_laser') || isa(tmp,'DC_supply')
if i == 1 if i == 1
value = {}; value = {};

View File

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

View File

@@ -5,27 +5,37 @@
wh = load([path filesep file]); wh = load([path filesep file]);
wh = wh.wh; 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(); plotJob = struct();
width = 350; width = 350;
height = 200; height = 200;
plotJob.Position = [100 100 width 100+height]; plotJob.Position = [100 100 width 100+height];
cols = cbrewer2("paired",12); cols = cbrewer2("paired",12);
plotJob.color = cols(1,:); plotJob.color = cols(1,:);
plotJob.l = 2; plotJob.l = 10;
plotJob.ch = 16; plotJob.ch = 16;
plotJob.d = 0; plotJob.d = 0;
plotJob.sgm = 0; plotJob.sgm = 0;
plotJob.pol = "copolarized"; plotJob.pol = "copolarized";
plotJob.p_in = 3; plotJob.p_in = 3;
plotJob.gamma = 0; plotJob.gamma = 0.0023;
plotJob.pmd = 0; plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9; plotJob.channelspacing = 400e9;
plotJob.randzdw = 0; plotJob.randzdw = 1;
plotJob.plot_ber_curve = 1; plotJob.plot_ber_curve = 0;
plotJob.plot_3dber_curve = 0; plotJob.plot_3dber_curve = 0;
plotJob.plot_violin = 0; plotJob.plot_violin = 1;
plotJob.plot_wavelength_sweep = 0; plotJob.plot_wavelength_sweep = 0;
plotJob.plot_wavelength_sweep_failure_rate = 0; plotJob.plot_wavelength_sweep_failure_rate = 0;
@@ -34,13 +44,20 @@ plotJob.dataStatArg = 'Lineplot with quartiles';
plotJob.plotTypeArg = 'Lines'; plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'a'; plotJob.displayname = 'a';
plotJob.title = 'title'; plotJob.title = 'title';
plotJob.figName = '16 Chann__'; plotJob.figName = '16 Chann';
plotJob.xAxisLabel = 'ROP per Channel in dBm'; plotJob.xAxisLabel = 'ROP per Channel in dBm';
plotJob.yAxisLabel = 'BER'; plotJob.yAxisLabel = 'BER';
% createbercurves(wh,plotJob) %%
createviolinplots(wh,plotJob);
% createsweepplots(wh,plotJob); % createbercurves(wh,plotJob)
P = [3];
for i = 1:2
plotJob.p_in = P(i);
createviolinplots(wh,plotJob);
end
% createsweepplots(wh,plotJob);
%% 1 %% 1
@@ -64,7 +81,7 @@ D = [0,0,0,3,0,0,0,3];
Sgm = [0,0,0,1,0,0,0,1]; Sgm = [0,0,0,1,0,0,0,1];
colidx = [4,8,6]; colidx = [4,8,6];
P_launch = [0,3,6]; P_launch = [3,6];
fig = figure('Name',plotJob.figName); fig = figure('Name',plotJob.figName);
fig.Position = plotJob.Position; fig.Position = plotJob.Position;
@@ -81,7 +98,7 @@ for idx = 1:(numRows * numCols)
plotJob.sgm = Sgm(idx); plotJob.sgm = Sgm(idx);
plotJob.randzdw = 1; plotJob.randzdw = 1;
for i = 1:3 for i = 1:length(P_launch)
plotJob.p_in = P_launch(i); plotJob.p_in = P_launch(i);
plotJob.color = cols(colidx(i),:); plotJob.color = cols(colidx(i),:);
@@ -199,6 +216,7 @@ end
%% 2 %% 2
function createviolinplots(wh,plotJob) function createviolinplots(wh,plotJob)
width = 350; width = 350;
height = 200; height = 200;
s = 100; s = 100;
@@ -208,30 +226,34 @@ cols = cbrewer2("paired",12);
numRows = 1; numRows = 1;
numCols = 4; numCols = 4;
plotJob.ch = 16; plotJob.ch = 16;
plotJob.p_in = 3; plotJob.randzdw = 1;
plotJob.randzdw = 0;
Pol = ["copolarized","copolarized","alternated","paired",]; Pol = ["copolarized","copolarized","alternated","paired",];
Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."]; Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."];
D = [0,3,0,0]; D = [0,3,0,0];
Sgm = [0,1,0,0]; Sgm = [0,1,0,0];
colidx = [2]; colidx = [4];
Len = [2]; Len = plotJob.l;
plotJob.figName = ['_Violin',num2str(plotJob.ch),' Channels; ',num2str(plotJob.channelspacing*1e-9),' GHz; ',num2str(plotJob.p_in),' dBm; randomized: ', num2str(plotJob.randzdw)]; fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
fig = figure('Name',plotJob.figName); if isvalid(fig)
fig.Position = plotJob.Position; figure(fig)
fig.Units = "centimeters"; % fig = get(fig);
fig.Position = [0 0 18 7]; 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
t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact');
for idx = 1:(numRows * numCols) for idx = 1:(numRows * numCols)
% Create subplot % Create subplot
%subplot(numRows, numCols, idx); subplot(numRows, numCols, idx);
nexttile;
plotJob.pol = Pol(idx); plotJob.pol = Pol(idx);
plotJob.d = D(idx); plotJob.d = D(idx);
@@ -295,7 +317,6 @@ annotation(fig,'textbox',...
'FontSize',8,... 'FontSize',8,...
'FitBoxToText','off'); 'FitBoxToText','off');
copygraphics(t,'BackgroundColor','none');
% lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','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.NumColumns = 3;
% lgd.Layout.Tile = 'south'; % lgd.Layout.Tile = 'south';

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

@@ -6,7 +6,7 @@ fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig) if isvalid(fig)
figure(fig) figure(fig)
fig = get(fig); % fig = get(fig);
AxesMain = fig.CurrentAxes; AxesMain = fig.CurrentAxes;
hold on hold on
else else
@@ -28,32 +28,39 @@ realization = wh.parameter.realization.values(1:end);
% get all xAxis values % get all xAxis values
xAxis = wh.parameter.p_out.values; xAxis = wh.parameter.p_out.values;
markerstyle = 'o';
linestyle = '-';
% Fetch Data from Warehouse % Fetch Data from Warehouse
for xl = 1:numel(xAxis) for xl = 1:numel(xAxis)
p_out = xAxis(xl); p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst" 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 = 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); temp = removeZeros(temp);
ber(xl) = max(temp,[],'all'); ber(xl) = quantile(temp,0.9,"all");
% ber(xl) = max(temp,[],'all');
linew = 1.0; linew = 1.0;
markersz = 3; markersz = plotJob.markersize;
linestyle = '-'; markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "AVG" 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 = 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); temp = removeZeros(temp);
ber(xl) = mean(temp,'all'); ber(xl) = mean(temp,'all');
linew = 1.0; linew = 1.0;
markersz = 3; markersz = plotJob.markersize;
linestyle = '--'; markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "Best" 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 = 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); temp = removeZeros(temp);
ber(xl) = min(temp,[],'all'); ber(xl) = min(temp,[],'all');
linew = 1.0; linew = 1.0;
markersz = 3; markersz = plotJob.markersize;
linestyle = '-'; markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)" 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 = 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);
@@ -63,7 +70,8 @@ for xl = 1:numel(xAxis)
linew = 1; linew = 1;
markersz = 1; markersz = 1;
linestyle = '-'; markerstyle = plotJob.markerstyle;
linestyle = plotJob.linestyle;
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles" elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
@@ -75,14 +83,16 @@ for xl = 1:numel(xAxis)
upperq(xl) = quantile(temp,0.99,"all"); upperq(xl) = quantile(temp,0.99,"all");
lowerq(xl) = quantile(temp,0.04,"all"); lowerq(xl) = quantile(temp,0.04,"all");
if lowerq(xl) == 0
lowerq(xl) = lowerq(xl-1);
end
% upperq(xl) = 0.5*std(tmp,1,'all','omitnan'); % upperq(xl) = 0.5*std(tmp,1,'all','omitnan');
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan'); % lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
% upperq(xl) = max(dataNoNans); upperq(xl) = max(temp(:));
% lowerq(xl) = min(dataNoNans); 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))); % 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))); % lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
@@ -149,6 +159,7 @@ elseif string(plotJob.plotTypeArg) == "Lines"
% [xAxis,ber] = interpCurve(xAxis, ber); % [xAxis,ber] = interpCurve(xAxis, ber);
% end % end
cols = cbrewer2('RdBu',size(ber,1));
for rlz = 1: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)") if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
@@ -160,6 +171,7 @@ elseif string(plotJob.plotTypeArg) == "Lines"
if rlz < size(ber,1) 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 = plot(xAxis,ber(rlz,:),linestyle,'Marker',"none",'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off');
@@ -169,22 +181,25 @@ elseif string(plotJob.plotTypeArg) == "Lines"
s.DataTipTemplate.DataTipRows(1); s.DataTipTemplate.DataTipRows(1);
s.DataTipTemplate.DataTipRows(2) = []; s.DataTipTemplate.DataTipRows(2) = [];
else else
if string(plotJob.dataStatArg) == "Lineplot with quartiles" 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.1,'linewidth',0.7);
[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.MarkerFaceColor = col;
hl.MarkerSize = 3; hl.MarkerSize = 2;
set(hp,'HandleVisibility','off'); set(hp,'HandleVisibility','off');
%hp.LineWidth = 1.2; %hp.LineWidth = 1.2;
ho = outlinebounds(hl,hp); ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.5); set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.6);
set(ho,'HandleVisibility','off'); set(ho,'HandleVisibility','off');
% errorbar(xAxis,ber(rlz,:),ber(rlz,:)-lowerq(rlz,:),upperq(rlz,:)-ber(rlz,:),'-o','Color',col,'linewidth',0.7);
else else
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]); 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.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: "; s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
@@ -245,9 +260,10 @@ grid minor
fontsize(AxesMain,8,"points") fontsize(AxesMain,8,"points")
% fontname(AxesMain,"Arial") % fontname(AxesMain,"Arial")
fig.Position = plotJob.Position;
fig.Units = "centimeters"; % fig.Position = plotJob.Position;
fig.Position = [2 2 8.5 7]; % fig.Units = "centimeters";
% fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex') set(AxesMain,'TickLabelInterpreter','latex')
@@ -257,7 +273,7 @@ set(AxesMain.Legend,'Interpreter','latex')
ylim([1e-5,0.3]); ylim([1e-5,0.3]);
xlim([min(xAxis),-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") % annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points")

View File

@@ -155,7 +155,7 @@ ylabel('Penalty in dB');
xlabel('Channel Number'); xlabel('Channel Number');
ylim([-9.3,-3]); ylim([-9.3,-3]);
xlim([0,plotJob.ch+1]); xlim([0,plotJob.ch+1]);
grid minor; % grid minor;
set(gca, 'color', 'none'); set(gca, 'color', 'none');
legend = []; legend = [];

View File

@@ -49,22 +49,5 @@ classdef clr
hold off; hold off;
end end
function showSet(colorStruct)
% Show colors from a structure in a bar plot
names = fieldnames(colorStruct);
colors = cell2mat(struct2cell(colorStruct)');
figure;
hold on;
for i = 1:size(colors, 1)
fill([0 1 1 0], [i-1 i-1 i i], colors(i, :), 'EdgeColor', 'k');
text(1.1, i-0.5, names{i}, 'FontSize', 12, 'Interpreter', 'none');
end
ylim([0, size(colors, 1)]);
xlim([0, 1.5]);
axis off;
title('Color Preview');
hold off;
end
end end
end end

View File

@@ -8,6 +8,7 @@ classdef equalizer_structure < int32
% db_precoded (3) % db_precoded (3)
vnle_db_mlse (4) vnle_db_mlse (4)
db_encoded (5) db_encoded (5)
ml_mlse (6)
end end
end end

View File

@@ -13,14 +13,13 @@ arguments
end end
try try
% Initialize output structures % Initialize output structures
output.ffe_package = {}; output.ffe_package = {};
output.mlse_package = {}; output.mlse_package = {};
output.vnle_package = {}; output.vnle_package = {};
output.dbtgt_package = {}; output.dbtgt_package = {};
output.dbenc_package = {}; output.dbenc_package = {};
output.mlmlse_package = {};
if options.mode == "load_run_id" || options.append_to_db if options.mode == "load_run_id" || options.append_to_db
% Initialize database connection % Initialize database connection
@@ -96,11 +95,12 @@ try
adaption= 1; adaption= 1;
use_dd_mode = 1; use_dd_mode = 1;
use_ffe = 1; use_ffe = 0;
use_dfe = 1; use_dfe = 0;
use_vnle_mlse = 1; use_vnle_mlse = 0;
use_dbtgt = 1; use_dbtgt = 0;
use_dbenc = 0; use_dbenc = 0;
use_ml_mlse = 1;
addProcessingResultToDatabase = 0; addProcessingResultToDatabase = 0;
@@ -130,7 +130,7 @@ try
mlse_ = MLSE("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",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); 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) % Duobinary signaling (db encoded)
mlse_db_enc = MLSE("DIR", [1,1], "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);
eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ... eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ...
@@ -140,7 +140,12 @@ try
% Preprocess signal % Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym); Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym);
% Scpe_sig.spectrum("fignum",2223,"normalizeTo0dB",1,"displayname",'Rx'); 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.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx');
% Scpe_sig.eye(fsym,M,"fignum",1024); % Scpe_sig.eye(fsym,M,"fignum",1024);
@@ -194,14 +199,26 @@ try
pf_ncoeffs = 1; pf_ncoeffs = 1;
ffe_order = [50, 5, 5]; 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); 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); pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
useviterbi = 0; useviterbi = 0;
if useviterbi if useviterbi
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
else else
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
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 end
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
@@ -222,38 +239,38 @@ try
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config); database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
end end
% pf_ncoeffs = 2; end
% 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);
% pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); if use_ml_mlse
% % 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); %ML-based MLSE (L=2)
% mu_ml = 0.01; training_epochs = 100;
% [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
% "precode_mode", duob_mode,... "len_tr",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
% 'showAnalysis', 0, ... "traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0);
% "postFFE", [],...
% "eth_style_symbol_mapping", 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;
% ffe_results.metrics.print;
% mlse_results.metrics.print; if options.append_to_db
% database.addProcessingResult(run_id, ml_mlse_results.metrics, ml_mlse_results.config);
% output.mlse_package{r} = mlse_results; end
% 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 end
if use_dbtgt if use_dbtgt
useviterbi = 0; useviterbi = 0;
if useviterbi if useviterbi
mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
else else
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); 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 end
ffe_order = [50, 5, 5]; 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); 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);
@@ -276,6 +293,9 @@ try
if duob_mode == db_mode.db_encoded 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",[]); 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; output.dbenc_package{r} = db_results;
if options.append_to_db if options.append_to_db

View File

@@ -35,9 +35,28 @@ if ~isempty(options.postFFE)
[eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols); [eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols);
end end
% Process through MLSE if isa(mlse_,'MLSE_viterbi')
% [mlse_signal] = mlse_.process(eq_signal); [mlse_signal] = mlse_.process(eq_signal);
[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); else
% Aufpassen mit welcher Sequenz man hier vergleicht für LLR stuff...
% gespeichtere "Symbols" sind schon DB codiert, das wollen wir hier
% nicht! Sondern die precoded aber nicht db-encoded müssen als ref in
% die LLR berechnung gehen!
ref_sym = PAMmapper(M,0).map(tx_bits); %ist klar
ref_sym_dpc = Duobinary().precode(ref_sym); % precoded
% ref_sym_dbenc = Duobinary().encode(ref_sym_dpc); %encoded - das wurde gesendet!
% ref_sym_dec = Duobinary().decode(ref_sym_dbenc); %ref_sym wieder zurück!
mlse_.trellis_states = PAMmapper(M,0).levels;
mlse_.trellis_state_mode = 1;
[mlse_signal,LLR,GMI_MLSE] = mlse_.process(eq_signal,ref_sym_dpc);
end
% tx_symbols_ = Duobinary().decode(tx_symbols);
% [mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
% Apply duobinary encoding and decoding % Apply duobinary encoding and decoding
mlse_signal = Duobinary().encode(mlse_signal); mlse_signal = Duobinary().encode(mlse_signal);

View File

@@ -130,6 +130,10 @@ db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse);
db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)';
if options.showAnalysis if options.showAnalysis
eq_signal.eye(eq_signal.fs,M,"fignum",249);
eq_noise = eq_noise - mean(eq_noise.signal); eq_noise = eq_noise - mean(eq_noise.signal);
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum");

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

@@ -45,7 +45,26 @@ end
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
% Process through postfilter and MLSE % Process through postfilter and MLSE
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
if 0 %tx_symbols.fs > 190e9
if pf_.ncoeff == 1
if pf_.coefficients(2) < 0
% coeff is negative for too high/ bad VNLE convergence
pf_.coefficients(2) = 0.9;
end
else
%long memory / pf respinse - not sure what to set here in a worst
%case :-)
end
%do it again:
pf_.useBurg = 0;
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
end
mlse_.DIR = pf_.coefficients; mlse_.DIR = pf_.coefficients;
GMI_MLSE = NaN; GMI_MLSE = NaN;
@@ -249,7 +268,8 @@ figure(336);
hold on; hold on;
eq_signal_sd.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0); eq_signal_sd.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0);
eq_noise.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0); eq_noise.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0);
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
showEQNoisePSD(eq_noise, "fignum", 338, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
for t = 1:4 for t = 1:4
pf_.ncoeff = t; pf_.ncoeff = t;
@@ -263,6 +283,7 @@ if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
end end
showEQcoefficients('n1', eq_.e,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2); showEQfilter(eq_.e, eq_signal_sd.fs.*2);
figure(340); clf; figure(340); clf;

View File

@@ -22,7 +22,6 @@ end
% Ensure the figure is ready before calling spectrum % Ensure the figure is ready before calling spectrum
eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color); eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color);
title('EEN')
if ~isnan(options.postfilter_taps) if ~isnan(options.postfilter_taps)
% Hold on to the figure for further plotting % Hold on to the figure for further plotting
@@ -43,6 +42,6 @@ end
end end
xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]); xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]);
ylim([-15, 0]); % ylim([-15, 0]);
end end

View File

@@ -52,6 +52,8 @@ end
legend legend
grid on grid on
% view([90 -90]);
end end

View File

@@ -1,40 +0,0 @@
function [eq_, pf_, mlse_, mlse_db_, eq_post] = configureEqualizers(M, len_tr, vnle_order, dfe_order, mu_dc, mu_ffe, mu_dfe, pf_ncoeffs)
% CONFIGUREEQUALIZERS Creates and configures equalizer objects
%
% Inputs:
% M - PAM level
% len_tr - Training length
% vnle_order - Array with orders for VNLE [order1, order2, order3]
% dfe_order - Array with orders for DFE
% mu_dc - DC adaptation rate
% mu_ffe - Array with adaptation rates for FFE [mu1, mu2, mu3]
% mu_dfe - Adaptation rate for DFE
% pf_ncoeffs - Number of coefficients for postfilter
%
% Outputs:
% eq_ - Configured EQ object
% pf_ - Configured Postfilter object
% mlse_ - Configured MLSE_viterbi object
% mlse_db_ - Configured MLSE_viterbi object for duobinary
% eq_post - Configured FFE object for post-processing
% Configure main equalizer
eq_ = EQ("Ne", vnle_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);
% Configure postfilter
pf_ = Postfilter("ncoeff", pf_ncoeffs, "useBurg", 1);
% Configure MLSE objects
mlse_ = MLSE_viterbi("duobinary_output", 0, 'M', M, ...
'trellis_states', PAMmapper(M,0).levels);
mlse_db_ = MLSE_viterbi("DIR", [1,1], "duobinary_output", 0, ...
"M", M, "trellis_states", PAMmapper(M,0).levels);
% Configure post-FFE
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);
end

View File

@@ -16,11 +16,17 @@ Scpe_sig = Scpe_sig.resample("fs_out", 2*fsym);
[Scpe_sig, ~] = Scpe_sig.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0); [Scpe_sig, ~] = Scpe_sig.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
% Apply Gaussian filter % Apply Gaussian filter
% Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ... if 1
% "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*0.52, ...
% "active", true).process(Scpe_sig); "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 %Remove DC offset
Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); Scpe_sig = Scpe_sig - mean(Scpe_sig.signal);
end end

View File

@@ -209,6 +209,7 @@ wh = submit_options.wh;
wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex); wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex);
wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex); wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex);
wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex); wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex);
wh.addValueToStorageByLinIdx(val.mlmlse_package,'mlmlse_package',jobIndex);
end end
end end
function p = setupParallelPool(numWorkers, idleTimeout) function p = setupParallelPool(numWorkers, idleTimeout)

View File

@@ -1,5 +1,5 @@
% Define the precomp path % Define the precomp path
precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\"; precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp";
% Step 1: Find all valid files (assume .mat files for ChannelFreqResp) % Step 1: Find all valid files (assume .mat files for ChannelFreqResp)
fileList = dir(fullfile(precomp_path, '*.mat')); fileList = dir(fullfile(precomp_path, '*.mat'));

View File

@@ -1,20 +1,50 @@
function waitUntilClick() function waitUntilClick(options)
% Create the UI figure
fig = uifigure('Name', 'Pause Execution', 'Position', [100 100 300 150]);
% Create a label to inform the user % use like this to stop any computation and wait for user to press ENTER or
uilabel(fig, 'Position', [50 80 200 40], 'Text', 'Click "Continue" to proceed', ... % Continue button. I used this in the lab to wait until smth settled or I
'FontSize', 14, 'HorizontalAlignment', 'center'); % set a device to a certain operating point...
% Create the "Continue" button % USAGE:
continueButton = uibutton(fig, 'push', 'Text', 'Continue', 'Position', [100 20 100 40], ... % waitUntilClick;
% OPTIONAL: provide input text that is prompted :-)
% usrcmd = sprintf('Laser to %d dBm',p);
% waitUntilClick('Text',usrcmd);
arguments
options.Text string = "Click ""Continue"" to proceed"
end
% Create the UI figure
fig = uifigure('Name', 'Pause Execution', ...
'Position', [100 100 300 150], ...
'KeyPressFcn', @(src,event) keyHandler(event));
% Create a label with custom text
uilabel(fig, ...
'Position', [30 70 240 60], ...
'Text', options.Text, ...
'FontSize', 14, ...
'HorizontalAlignment', 'center', ...
'WordWrap','on');
% Create the "Continue" button
uibutton(fig, 'push', ...
'Text', 'Continue', ...
'Position', [100 20 100 40], ...
'ButtonPushedFcn', @(src, event) closeWindow()); 'ButtonPushedFcn', @(src, event) closeWindow());
% Function to close the window when "Continue" is clicked
function closeWindow() function closeWindow()
delete(fig); % Close the figure window delete(fig);
end end
% Wait until the figure is closed to resume execution function keyHandler(event)
waitfor(fig); if strcmp(event.Key,'return') || strcmp(event.Key,'enter')
closeWindow();
end
end
% Wait until the figure is closed
waitfor(fig);
end end

View File

@@ -0,0 +1,20 @@
function NF_dB = noiseFigureFromOSNR(Pin_dBm, OSNR_dB, lambda_nm, Bref_nm)
if nargin < 4
Bref_nm = 0.1; % OSNR reference bandwidth
end
h = 6.62607015e-34; % Planck [J*s]
c = 299792458; % speed of light [m/s]
lambda = lambda_nm * 1e-9;
nu = c / lambda;
% convert reference bandwidth from nm to Hz
Bref_Hz = Bref_nm*1e-9 * c / lambda^2;
% ASE noise power density in dBm
Pn_dBm = 10*log10(h*nu*Bref_Hz) + 30;
NF_dB = Pin_dBm - OSNR_dB - Pn_dBm;
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

@@ -1,51 +0,0 @@
figure(2)
tiledlayout(1,3)
cols = linspecer(5);
cnt = 1;
for m = [4,6,8]
M = m;
fsym = 112e9;
fdac = 256e9;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",0,"pulseformer",NaN,...
"randkey",33,...
"db_precode",1,"db_encode",0,...
"mrds_code",0,"mrds_blocklength",512).process();
Symbols_pre = Duobinary().precode(Symbols);
Symbols_db = Duobinary().encode(Symbols_pre);
if M == 4
Symbols_db.signal = Symbols_db.signal .*sqrt(2.5);
elseif M == 6
Symbols_db.signal = Symbols_db.signal .*sqrt(5.8);
elseif M == 8
Symbols_db.signal = Symbols_db.signal .*sqrt(10.5);
end
% figure(1)
% hold on
% histogram(Symbols_db.signal,"EdgeAlpha",0.3,"Normalization","probability");
% figure(1)
nexttile
hold on
bar(unique(Symbols_db.signal),histcounts(int32(Symbols_db.signal),"Normalization","probability"),"FaceColor",cols(cnt,:),"FaceAlpha",0.6,"BarWidth",1-(0.2*cnt),"LineWidth",0.5,"EdgeColor",'black','DisplayName',['Duobinary PAM-',num2str(M)]);
xticks(unique(Symbols_db.signal));
ylim([0 0.26]);
xlabel("Symbol")
cnt = cnt+1,
end

View File

@@ -1,18 +0,0 @@
% Define the filter taps
h_ = {[1],[1 1],[1 2 1],[1 3 3 1]};
for i = 1:length(h_)
h = h_{i};
[H, w] = freqz(h, 1, 1024, 1);
figure(1);
hold on
plot(w, 10*log10(abs(H)), 'LineWidth', 2,'DisplayName',['$(1+D)^2$']); %todo
xlabel('Normalized Frequency');
ylabel('Amplitude in dB');
grid on;
ylim([-20,10])
end

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

@@ -1,14 +1,15 @@
% Gitter für lambda0 und S0 % Gitter für lambda0 und S0
lambda0_vec = linspace(1300,1320,200); lambda0_vec = linspace(1260,1360,200);
S0_vec = linspace(0.06,0.1,200); S0_vec = linspace(0.06,0.1,200);
[Lambda0, S0] = meshgrid(lambda0_vec, S0_vec); [Lambda0, S0] = meshgrid(lambda0_vec, S0_vec);
% Festen Betriebsparameter % Festen Betriebsparameter
lambda = 1293; % nm lambda = 1293; % nm
L = 10; % km L = 1; % km
% Dispersion berechnen (lineare Näherung) % Dispersion berechnen (lineare Näherung)
D = S0 .* ( lambda - Lambda0 ) * L; D = S0 .* ( lambda - Lambda0 ) * L;
% D = (S0./4) .* ( lambda - (Lambda0.^4)./(lambda^3) ) * L;
%% 2D-Konturplot nur mit Linien und Text %% 2D-Konturplot nur mit Linien und Text
figure('Color','w'); figure('Color','w');

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

@@ -1,37 +1,113 @@
function beautifyBERplot() function beautifyBERplot(options)
% BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures. % BEAUTIFYBERPLOT Enhances BER-style plots for publication-quality figures.
% Supports automatic smoothing and trend-line overlay.
%
% Usage examples:
% beautifyBERplot; % default
% beautifyBERplot("polyfit",1); % add polynomial fit
% beautifyBERplot("polyfit",1,"fitmethod","pchip") % piecewise cubic fit
%
% Supported fitmethod options: 'polyfit', 'smoothingspline', 'loess', 'pchip'
% Set line properties for all current plot lines arguments
lines = findall(gca, 'Type', 'Line'); options.logscale (1,1) logical = 1
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles options.setmarkers (1,1) logical = 1;
num_markers = length(markers); options.polyfit (1,1) logical = 0
options.polyorder (1,1) double = 2
options.fitmethod (1,1) string = "polyfit" % choose fit type
end
% --- find all line objects in current axes
lines = findall(gca, 'Type', 'Line');
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'};
num_markers = length(markers);
% --- style all lines consistently
for i = 1:length(lines)
lines(i).LineWidth = 1.2;
% lines(i).LineStyle = '-';
if options.setmarkers == 1
if string(lines(i).Marker) == "none"
lines(i).Marker = markers{mod(i-1, num_markers) + 1};
end
lines(i).MarkerSize = 4;
lines(i).MarkerFaceColor = lines(i).Color;
end
end
% --- optional smoothing/fitting overlay
if options.polyfit
hold on
for i = 1:length(lines) for i = 1:length(lines)
lines(i).LineWidth = 1.3; % Thicker line width x = lines(i).XData;
%lines(i).LineStyle = '-'; % Solid lines for simplicity y = lines(i).YData;
% if string(lines(i).Marker) == "none" valid = isfinite(x) & isfinite(y);
% lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically if sum(valid) < options.polyorder + 1
% end continue;
lines(i).MarkerSize = 4; % Marker size
lines(i).MarkerFaceColor = 'auto'; % Use line color for marker face
end end
% Change all text interpreters to LaTeX xf = linspace(min(x(valid)), max(x(valid)), 200);
set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex');
% Set figure background to white % ----- choose fitting method -----
set(gcf, 'Color', 'w'); switch lower(options.fitmethod)
case "polyfit"
p = polyfit(x(valid), y(valid), options.polyorder);
yf = polyval(p, xf);
case "smoothingspline"
try
f = fit(x(valid)', y(valid)', 'smoothingspline');
yf = feval(f, xf);
catch
yf = interp1(x(valid), y(valid), xf, 'pchip');
end
% Set logarithmic scale for y-axis, but only if it makes sense. case "loess"
% If this is not always desired, you could condition this on the presence of lines or data. yf = smooth(x(valid), y(valid), 0.2, 'loess');
set(gca, 'YScale', 'log'); yf = interp1(x(valid), yf, xf, 'linear', 'extrap');
case "pchip"
yf = interp1(x(valid), y(valid), xf, 'pchip');
otherwise
warning('Unknown fitmethod "%s". Using polyfit.', options.fitmethod);
p = polyfit(x(valid), y(valid), options.polyorder);
yf = polyval(p, xf);
end
% --- lightened color for fit overlay
lightcol = lines(i).Color + 0.4 * (1 - lines(i).Color);
lightcol(lightcol > 1) = 1;
plot(xf, yf, '-', 'Color', lightcol, ...
'LineWidth', 0.7, 'Marker', 'none', ...
'HandleVisibility','off');
end
hold off
end
% --- axis scaling and cosmetics
if options.logscale
set(gca, 'YScale', 'log');
end
% --- Figure size in centimeters ---
% width_pt = 500;
% height_pt = 300;
%
% pt2cm = 0.03514598; % TeX point cm
% width_cm = width_pt * pt2cm; % = 8.85 cm
% height_cm = height_pt * pt2cm; % = 2.81 cm
%
% set(gcf, 'Units', 'centimeters', 'Position', [2 2 width_cm height_cm]);
% set(gcf, 'PaperUnits', 'centimeters', 'PaperPosition', [0 0 width_cm height_cm]);
% --- Formatting ---
set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex');
set(gcf, 'Color', 'w');
set(gca, 'Box', 'on', 'LineWidth', 0.8);
grid on;
set(gca, 'FontSize', 10, 'FontName', 'Latin Modern Roman');
% Customize grid and box appearance
set(gca, 'Box', 'on', 'LineWidth', 0.8); % Thicker border
grid on;
% grid minor;
% Adjust font size and style for better readability
set(gca, 'FontSize', 10, 'FontName', 'Times New Roman');
end 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

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

BIN
projects.mat Normal file

Binary file not shown.

View File

@@ -243,7 +243,6 @@ for occ = 1:record_realizations
fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER); fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER);
end end

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

@@ -1,34 +1,20 @@
db = DBHandler("type","mysql","dataBase",'labor');
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 4;
fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'symbolrate','EQUALS', 112e9);
fp.where('Runs', 'fiber_length','EQUALS', 0);
fp.where('Runs', 'is_mpi','EQUALS', 1);
fp.where('Runs', 'interference_path_length','EQUALS', 70);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
fp.where('Runs', 'sir','EQUALS',20);
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; [dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs'));
% 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', 4, ...
'interference_path_length', 300, ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', [], ...
'v_bias', [] ...
);
selectedFields = {'Runs.run_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', 'Measurements.power_mpi_interference'};
[dataTable,sql_query] = db.queryDB(filterParams, selectedFields);
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
@@ -55,7 +41,7 @@ for i = 1:size(dataTable,1)
Scpe_sig_raw = load([savePath, char(dataTable_.rx_raw_path(1))]); Scpe_sig_raw = load([savePath, char(dataTable_.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
% Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0); Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0);
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym); Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym);

View File

@@ -8,14 +8,14 @@ end
precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\"; precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\";
precomp_fn = "precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2"; precomp_fn = "precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2";
precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
run_lab_automation = 1; run_lab_automation = 0;
%% Configuration %% Configuration
conf = confRequest; conf = confRequest;
referenceFields = fieldnames(db.tables.Configurations); referenceFields = fieldnames(db.tables.Configurations);
assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!'); % assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!');
%% Lab Automation %% Lab Automation
@@ -28,7 +28,6 @@ if run_lab_automation
[v_bias_meas,i_bias_meas]=dcs.readVals(); [v_bias_meas,i_bias_meas]=dcs.readVals();
end end
% Laser % Laser
if (any(confPrev.laser_power ~= conf.laser_power)) || (~exist('laser','var')) || (isempty(confPrev.laser_power)) if (any(confPrev.laser_power ~= conf.laser_power)) || (~exist('laser','var')) || (isempty(confPrev.laser_power))
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
laser.setWavelength(conf.wavelength); laser.setWavelength(conf.wavelength);
@@ -42,7 +41,7 @@ if run_lab_automation
end end
voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]); voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]);
if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -38 if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -37
holdAndShowValue holdAndShowValue
end end
@@ -51,15 +50,56 @@ if run_lab_automation
pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15'); pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15');
end end
% Construct AWG and Scope Modules %%%%%%
fdac = 224e9;
fadc = 160e9;
Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1);
Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]);
A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0);
end end
% if conf.symbolrate == 72e9
% if conf.pam_level == 2
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% elseif conf.pam_level == 4
% conf.v_awg = 0.55;
% conf.pulsef_alpha =0.6;
% elseif conf.pam_level == 6
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% elseif conf.pam_level == 8
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% end
% elseif conf.symbolrate == 96e9
% if conf.pam_level == 2
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 4
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 6
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 8
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% end
% elseif conf.symbolrate == 112e9
% if conf.pam_level == 2
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 4
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 6
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 8
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% end
% end
awg_upload_required = 1;
if any(confPrev.pam_level == conf.pam_level) && any(confPrev.symbolrate == conf.symbolrate)
awg_upload_required = 0;
end
%% Signal generation and transmission %% Signal generation and transmission
if awg_upload_required || isempty(loop_id) if awg_upload_required || isempty(loop_id)
@@ -70,6 +110,13 @@ if awg_upload_required || isempty(loop_id)
sequence_order = min(18,sequence_order); sequence_order = min(18,sequence_order);
end end
% Construct AWG and Scope Modules %%%%%%
fdac = 224e9;
fadc = 160e9;
Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1);
Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]);
A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0);
Pamsource = PAMsource(... Pamsource = PAMsource(...
"fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,... "fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,...
"fs_out",fdac,... "fs_out",fdac,...
@@ -80,11 +127,11 @@ if awg_upload_required || isempty(loop_id)
"randkey",1,... "randkey",1,...
"duobinary_mode", conf.db_mode); "duobinary_mode", conf.db_mode);
conf.pam_source = Pamsource;
confPrev = conf;
[Digi_sig,Symbols,Bits] = Pamsource.process(); [Digi_sig,Symbols,Bits] = Pamsource.process();
figure(10);clf;
Digi_sig.spectrum("displayname",'Tx Pulse Shaped','fignum',10,'normalizeTo0dB',1);
%%%%% Precompensation Routine %%%%%% %%%%% Precompensation Routine %%%%%%
if precomp_mode == 1 % measure channel if precomp_mode == 1 % measure channel
@@ -95,11 +142,13 @@ if awg_upload_required || isempty(loop_id)
Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn); Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn);
end end
% Digi_sig.spectrum("displayname",'Tx Spectrum','fignum',10,'normalizeTo0dB',0); Digi_sig.spectrum("displayname",'Tx Pre-Emphasized','fignum',10,'normalizeTo0dB',1);
%%%%% Resample to DAC rate %%%%%% %%%%% Resample to DAC rate %%%%%%
Digi_sig = Digi_sig.resample("fs_out",Awg.fdac); Digi_sig = Digi_sig.resample("fs_out",Awg.fdac);
Digi_sig.spectrum("displayname",'Tx Resampled','fignum',10,'normalizeTo0dB',1);
% [~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig); % [~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig);
Awg.upload("signal2",Digi_sig); Awg.upload("signal2",Digi_sig);
@@ -114,6 +163,8 @@ if precomp_mode == 1
return; % End routine if precomp mode is active return; % End routine if precomp mode is active
end end
confPrev = conf;
%% Save static TX data (only once) %% Save static TX data (only once)
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss'); currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
timeStr = char(currentTime); timeStr = char(currentTime);
@@ -143,7 +194,7 @@ for recIdx = 1:n_recording
end end
% Plot spectrum and time domain (optional) % Plot spectrum and time domain (optional)
% Scpe_sig_raw.spectrum("displayname", 'Rx Spectrum', 'fignum', 10, 'normalizeTo0dB', 0); Scpe_sig_raw.spectrum("displayname", 'Rx Spectrum', 'fignum', 10, 'normalizeTo0dB', 0);
Scpe_sig_raw.plot("clear", 1, "displayname", 'Rx Signal', 'fignum', 11); Scpe_sig_raw.plot("clear", 1, "displayname", 'Rx Signal', 'fignum', 11);
%% Save Routine %% Save Routine
@@ -154,7 +205,6 @@ for recIdx = 1:n_recording
rx_raw_path = [filesep, current_folder, filesep, filename, '_rx_signal_raw']; rx_raw_path = [filesep, current_folder, filesep, filename, '_rx_signal_raw'];
save([savePath, rx_raw_path], "Scpe_sig_raw"); save([savePath, rx_raw_path], "Scpe_sig_raw");
% Check if loop_id is already created % Check if loop_id is already created
if isempty(loop_id) if isempty(loop_id)
newLoop = db.tables.LoopControl; newLoop = db.tables.LoopControl;
@@ -166,7 +216,6 @@ for recIdx = 1:n_recording
fprintf('LoopControl entry created: loop_id = %d\n', loop_id); fprintf('LoopControl entry created: loop_id = %d\n', loop_id);
end end
%% Append to Runs Table %% Append to Runs Table
newRun = db.tables.Runs; newRun = db.tables.Runs;
newRun.run_id = NaN; newRun.run_id = NaN;
@@ -184,9 +233,12 @@ for recIdx = 1:n_recording
%% Append to Configurations Table %% Append to Configurations Table
conf.configuration_id = NaN; conf.configuration_id = NaN;
conf.run_id = run_id; conf.run_id = run_id;
conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10;
conf.pam_source = Pamsource;
db.appendToTable('Configurations', conf); db.appendToTable('Configurations', conf);
%% Append to Measurements Table %% Append to Measurements Table
laser.getLaserInfo;
meas = db.tables.Measurements; meas = db.tables.Measurements;
meas.measurement_id = NaN; meas.measurement_id = NaN;
meas.run_id = run_id; meas.run_id = run_id;
@@ -204,12 +256,17 @@ for recIdx = 1:n_recording
db.appendToTable('Measurements', meas); db.appendToTable('Measurements', meas);
%% Submit DSP Routine %% Submit DSP Routine
if recIdx == 1
if subimt_DSP if subimt_DSP
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ... [out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
"parallel", parallel_dsp, "max_occurences", max_occurences); "parallel", parallel_dsp, "max_occurences", max_occurences);
end end
if awg_upload_required
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
"parallel", 0, "max_occurences", 1);
end
end
end end

View File

@@ -2,7 +2,7 @@
basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025.db'; database_name = 'ecoc2025.db';
database = DBHandler("pathToDB", [basePath, database_name]); database = DBHandler("pathToDB", [basePath, database_name],"type",'mysql');
filterParams = database.tables; filterParams = database.tables;
filterParams.Configurations = struct( ... filterParams.Configurations = struct( ...

View File

@@ -1,49 +1,58 @@
% basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
% database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("type",'mysql','dataBase','labor');
filterParams = database.tables; % dsp_options.database_type = 'mysql';
filterParams.Runs.loop_id = 209; % dsp_options.dataBase = 'labor';
% filterParams.Configurations = struct( ... % dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\';
% 'symbolrate', 112e9, ... %[224,336,360,390,420,448] % database = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
% 'fiber_length', 0, ... % filterParams = database.tables;
% 'db_mode', '"no_db"', ... % filterParams.Runs.loop_id = 209;
% 'interference_attenuation', [], ... % % filterParams.Configurations = struct( ...
% 'interference_path_length', 1000, ... % % 'symbolrate', 112e9, ... %[224,336,360,390,420,448]
% 'is_mpi', 1, ... % % 'fiber_length', 0, ...
% 'pam_level', 4, ... % % 'db_mode', '"no_db"', ...
% 'wavelength', 1310, ... % % 'interference_attenuation', [], ...
% 'precomp_amp', [], ... % % 'interference_path_length', 1000, ...
% 'signal_attenuation', [], ... % % 'is_mpi', 1, ...
% 'v_awg', [], ... % % 'pam_level', 4, ...
% 'v_bias', [] ... % % 'wavelength', 1310, ...
% ); % % 'precomp_amp', [], ...
% % 'signal_attenuation', [], ...
% % 'v_awg', [], ...
% % 'v_bias', [] ...
% % );
%
% % if 1
% % % filterParams.EqualizerParameters.dc_buffer_len = 1;
% % filterParams.EqualizerParameters.ffe_buffer_len = 1;
% % filterParams.EqualizerParameters.smoothing_buffer_len = 4096;
% % filterParams.EqualizerParameters.smoothing_buffer_update = 224;
% % filterParams.EqualizerParameters.DCmu = 0;
% % end
% a = database.getTableFieldNames('Runs');
% b = database.getTableFieldNames('Results');
% c = database.getTableFieldNames('EqualizerParameters');
% d = [a;b;c];
%
% [dataTable,~] = database.queryDB(filterParams, d);
%
% selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'...
% 'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ...
% 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ...
% 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
% if 1 db = DBHandler("type","mysql","dataBase",'labor');
% % filterParams.EqualizerParameters.dc_buffer_len = 1;
% filterParams.EqualizerParameters.ffe_buffer_len = 1;
% filterParams.EqualizerParameters.smoothing_buffer_len = 4096;
% filterParams.EqualizerParameters.smoothing_buffer_update = 224;
% filterParams.EqualizerParameters.DCmu = 0;
% end
a = database.getTableFieldNames('Runs');
b = database.getTableFieldNames('Results');
c = database.getTableFieldNames('EqualizerParameters');
d = [a;b;c];
[dataTable,~] = database.queryDB(filterParams, d); fp = QueryFilter();
% fp.where('mpi_superview', 'loop_id','EQUALS', 209);
fp.where('mpi_superview', 'symbolrate','EQUALS', 112e9);
fp.where('mpi_superview', 'pam_level','EQUALS', 4);
fn = [db.getTableFieldNames('mpi_superview')];
[dataTable,sql_query] = db.queryDB(fp,fn);
selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'...
'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ...
'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ...
'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
[dataTable_raw,sql_query] = database.queryDB(filterParams, selectedFields);
%% %%
dataTable_clean = dataTable_raw; dataTable_clean = dataTable;
dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference); dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference);
dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level); dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level);
dataTable_clean = cleanUpTable(dataTable_clean); dataTable_clean = cleanUpTable(dataTable_clean);
@@ -66,7 +75,7 @@ for int_len = [0,50,300,1000]
mode = 4; mode = 4;
for mode = [1,4] for mode = [1,2]
hold on; hold on;
dataTable = dataTable_clean; dataTable = dataTable_clean;
@@ -124,7 +133,7 @@ for int_len = [0,50,300,1000]
method = 'ideal dc tracking'; method = 'ideal dc tracking';
end end
dataTable(dataTable.eq_id==0,:) = []; % dataTable(dataTable.eq_id==0,:) = [];
dataTable(dataTable.equalizer_structure~=1,:) = []; dataTable(dataTable.equalizer_structure~=1,:) = [];
% Modify values in 'interference_path_length' where the condition is met % Modify values in 'interference_path_length' where the condition is met
@@ -182,6 +191,10 @@ for int_len = [0,50,300,1000]
dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); dataTableGrpd_min = groupIt(fixedVars, dataTable, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); dataTableGrpd_max = groupIt(fixedVars, dataTable, @max);
% dataTableGrpd_mean(dataTableGrpd_mean.nRows<50,:) = [];
% dataTableGrpd_min(dataTableGrpd_min.nRows<50,:) = [];
% dataTableGrpd_max(dataTableGrpd_max.nRows<50,:) = [];
% Create a new figure % Create a new figure
hold on hold on
@@ -237,6 +250,8 @@ for int_len = [0,50,300,1000]
% Hide patch (shaded area) from legend % Hide patch (shaded area) from legend
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
% Fit a 4th-order polynomial to log10(BER) % Fit a 4th-order polynomial to log10(BER)
p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed
@@ -286,9 +301,10 @@ for int_len = [0,50,300,1000]
'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname)); 'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname));
pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)}; pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)};
pair_two = {'Rate', dataTable.bitrate(loopFiltSingle, :) * 1e-9}; pair_two = {'Baud', dataTable.symbolrate(loopFiltSingle, :) * 1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)}; pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three); pair_four = {'#bits', round(dataTable.numBits(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three,pair_four);
end end
end end

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

@@ -19,7 +19,7 @@ if options.parallel
% Check if a pool exists % Check if a pool exists
pool = gcp('nocreate'); pool = gcp('nocreate');
if isempty(pool) if isempty(pool)
parpool; parpool(4);
end end
% Submit the DSP function asynchronously % Submit the DSP function asynchronously

View File

@@ -1,13 +1,13 @@
% 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" % 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 %% Parameters
df = 1e6;%150e3; % Laser linewidth [Hz] df = 1e6; % Laser linewidth [Hz]
SIR_dB = 20; % Interference attenuation [dB] SIR_dB = 20; % Interference attenuation [dB]
alpha = 10^(-SIR_dB/20); % Interference attenuation [linear] alpha = 10^(-SIR_dB/20); % Interference attenuation [linear]
n_fiber = 1.467; % Refractive index n_fiber = 1.467; % Refractive index
c = physconst('lightspeed'); % [m/s] c = physconst('lightspeed'); % [m/s]
L = linspace(0,400,40); % Interference delay [m] L = linspace(0,250,50); % Interference delay [m]
tau = n_fiber./c.*L; % Interference time (= tau) [s] tau = n_fiber./c.*L; % Interference time (= tau) [s]
tau_c = 1/(pi*df); % laser coherence time [s] tau_c = 1/(pi*df); % laser coherence time [s]
@@ -22,7 +22,7 @@ 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) 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 phase_noise_std = sqrt(2*pi*df/fs); % standard dev. phase noise
num_realizations = 10; % number of parallel runs num_realizations = 50; % number of parallel runs
monte_carlo_variance = zeros(num_realizations, length(L)); monte_carlo_variance = zeros(num_realizations, length(L));
parfor r = 1:num_realizations parfor r = 1:num_realizations
@@ -48,7 +48,9 @@ avg_of_mc_variances = mean(monte_carlo_variance, 1);
std_of_mc_variances = std(monte_carlo_variance, 0, 1); std_of_mc_variances = std(monte_carlo_variance, 0, 1);
%% Analytic variance %% Analytic variance
analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau)).^2; 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 %% Plot
cols = [0.3467 0.5360 0.6907 cols = [0.3467 0.5360 0.6907
@@ -62,20 +64,21 @@ hold on;
plot(L, avg_of_mc_variances, 'LineWidth',2, 'DisplayName','Simulation','Color',cols(1,:),'LineStyle','-'); 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'); 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','-'); plot(L_, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-');
xticks(coherence_length_multiples.*L_c); xticks(coherence_length_multiples.*L_c);
xticklabels(round(coherence_length_multiples.*L_c)); xticklabels(round(coherence_length_multiples.*L_c,1));
norm_to_coherence_len = 1; norm_to_coherence_len = 1;
if norm_to_coherence_len if norm_to_coherence_len
xticklabels(coherence_length_multiples); xticklabels(coherence_length_multiples);
xlabel('$\tau_c$', 'FontSize',12); xlabel('$n \cdot L_c$', 'FontSize',12);
else
xlabel('Interference Delay [m]', 'FontSize',12);
end end
xline(L_c.*coherence_length_multiples, 'LineWidth',1.5, 'DisplayName','Coh. Length','HandleVisibility','off','Color',[0.7,0.7,0.7],'LineStyle','-'); 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)]); xlim([0,L(end)]);
yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$'); yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$');
xlabel('Interference Delay [m]', 'FontSize',12);
grid on; grid on;
ylabel('Intensity Variance', 'FontSize',12); ylabel('Intensity Variance', 'FontSize',12);
title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14); title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14);

View File

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

View File

@@ -20,7 +20,7 @@ fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info'); fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped')]; fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')];
[dataTable,~] = db.queryDB(fp, fields); [dataTable,~] = db.queryDB(fp, fields);
eqstructures = unique(dataTable.equalizer_structure); eqstructures = unique(dataTable.equalizer_structure);

View File

@@ -5,7 +5,7 @@ db = DBHandler("dataBase", [dataBase], "type", database_type);
fp = QueryFilter(); fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987); % fp.where('Runs', 'run_id','EQUALS', 987);
M = 8; M = 6;
fp.where('Runs', 'pam_level','EQUALS', M); fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','LESS_THAN', 310e9); % fp.where('Runs', 'bitrate','LESS_THAN', 310e9);
fp.where('Runs', 'fiber_length','EQUALS', 2); fp.where('Runs', 'fiber_length','EQUALS', 2);
@@ -13,12 +13,12 @@ fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000); % fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11); % fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18); % fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1293); fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 0); % fp.where('Runs', 'db_mode','EQUALS', 0);
fp.where('Runs', 'rop_attenuation','EQUALS', 0); fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info'); fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')]; fields = [fields; db.getTableFieldNames('dashboard_ungrouped_aug_nov_2025')];
[dataTable,~] = db.queryDB(fp, fields); [dataTable,~] = db.queryDB(fp, fields);
eqstructures = unique(dataTable.equalizer_structure); eqstructures = unique(dataTable.equalizer_structure);
@@ -29,7 +29,7 @@ show_bitrate = true;
figure(5); figure(5);
hold on hold on
for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse] for eqs = [equalizer_structure.vnle]
% figure('Name',string([char(eqs),''])); % figure('Name',string([char(eqs),'']));
% hold on % hold on
@@ -148,13 +148,14 @@ for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse]
xticks(xraw); xticks(xraw);
if show_bitrate if show_bitrate
xticks(200:25:500); % xticks(200:25:500);
xlim([350 500]); % xlim([350 500]);
xlim([min(xraw), max(xraw)]);
else else
xlim([min(xraw), max(xraw)]); xlim([min(xraw), max(xraw)]);
end end
ylim([5e-4, 0.3]); ylim([1e-4, 0.5]);
end end
yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off'); yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off');

View File

@@ -17,7 +17,7 @@ fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 1); % fp.where('Runs', 'db_mode','EQUALS', 1);
fp.where('Runs', 'rop_attenuation','EQUALS', 0); fp.where('Runs', 'rop_attenuation','EQUALS', 0);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard')); [dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard_ungrouped_after_nov_2025'));
eqstructures = unique(dataTable.equalizer_structure); eqstructures = unique(dataTable.equalizer_structure);

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

View File

@@ -0,0 +1,124 @@
%% ============================================================
% LOAD DATA
% ============================================================
database_type = 'mysql';
db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
M = 4; % PAM level for this analysis
fp = QueryFilter();
fp.where('Runs', 'pam_level', 'EQUALS', M);
fp.where('Runs', 'fiber_length', 'EQUALS', 10);
fp.where('Runs', 'bitrate', 'EQUALS', 360e9);
fp.where('Runs', 'is_mpi', 'EQUALS', 0);
fields = [
db.getTableFieldNames('power_state_info');
db.getTableFieldNames('dashboard_ungrouped_alltime')
];
[dataTable, ~] = db.queryDB(fp, fields);
%% ============================================================
% COMMON CONFIGURATION FOR ALL SUBPLOTS
% ============================================================
%% ============================================================
% DEFINE DSP ALGORITHMS FOR THE 4 SUBPLOTS
% ============================================================
curves = struct;
curves(1).name = 'VNLE';
curves(1).eq = equalizer_structure.vnle;
curves(1).pre = 0;
curves(1).color = clr.Paired.red;
curves(2).name = 'PF + MLSE';
curves(2).eq = equalizer_structure.vnle_pf_mlse;
curves(2).pre = 0;
curves(2).color = clr.Paired.green;
curves(3).name = 'DB-target + MLSE';
curves(3).eq = equalizer_structure.vnle_db_mlse;
curves(3).pre = 0;
curves(3).color = clr.Paired.blue;
curves(4).name = 'ML-based MLSE';
curves(4).eq = equalizer_structure.ml_mlse;
if M == 4
curves(4).pre = 0;
else
curves(4).pre = 1;
end
curves(4).color = clr.Paired.purple;
%% ============================================================
% ANALYSIS ENGINE NO PLOTTING
% ============================================================
results = struct;
for k = 1:numel(curves)
%% ---- 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', 360e9, ...
'fiber_length', 10, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', curves(k).pre);
%% ---- GET BER ----
cfg.y_axis = 'BER';
A = analyze_measurements_gpt(dataTable, cfg);
results(k).wavelength = A.group{1}.x;
if curves(k).eq == equalizer_structure.vnle_db_mlse || ...
curves(k).eq == equalizer_structure.ml_mlse
% DB and ML-based need precoded BER
results(k).ber = A.group{1}.y_precoded;
else
results(k).ber = A.group{1}.y;
end
end
%% ============================================================
% 1×4 TILED BER-vs-WAVELENGTH FIGURE
% ============================================================
fig=figure(901);
tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
lw = 1.8; % line width
ms = 6; % marker size
for k = 1:numel(curves)
nexttile; hold on;
plot(results(k).wavelength, results(k).ber, ...
'-o', ...
'Color', curves(k).color, ...
'MarkerFaceColor', curves(k).color, ...
'MarkerSize', ms, ...
'LineWidth', lw);
set(gca,'YScale','log');
grid on;
xlabel('wavelength');
ylabel('BER');
title(curves(k).name);
ylim([1e-4, 0.1])
beautifyBERplot();
end
pos = 1e3.*[0.1070 0.5497 1.4113 0.3253];
set(fig, 'Position', pos);

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