Merge branch 'main' of cau-git.rz.uni-kiel.de:nt/mitarbeiter/silas/imdd_simulation
This commit is contained in:
@@ -344,10 +344,13 @@ classdef Signal
|
||||
|
||||
arguments
|
||||
obj
|
||||
options.fignum
|
||||
options.fignum = 2025
|
||||
options.displayname = "";
|
||||
options.color = [];
|
||||
options.linestyle = '-';
|
||||
options.normalizeToNyquist = 0;
|
||||
options.addDCoffset = 0;
|
||||
options.normalizeToDC = 0;
|
||||
options.normalizeTo0dB = 0;
|
||||
options.max_num_lines = []; % Leave empty or omit to disable line rotation
|
||||
options.fft_length = [];
|
||||
@@ -360,6 +363,8 @@ classdef Signal
|
||||
options.fft_length = 2^(nextpow2(length(obj.signal))-9);
|
||||
end
|
||||
|
||||
|
||||
|
||||
if options.normalizeToNyquist == 0
|
||||
[p_lin,f_Hz] = pwelch(obj.signal, hanning(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.
|
||||
end
|
||||
|
||||
% p_lin = movmean(p_lin,4);
|
||||
|
||||
if options.normalizeTo0dB
|
||||
p_lin = p_lin ./ max(p_lin);
|
||||
p_dbm = 10*log10(p_lin); % normalized to 0 dB
|
||||
@@ -415,11 +422,20 @@ classdef Signal
|
||||
ax = gca;
|
||||
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))
|
||||
if isempty(options.color)
|
||||
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1);
|
||||
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
|
||||
|
||||
@@ -449,21 +465,25 @@ classdef Signal
|
||||
|
||||
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
|
||||
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]);
|
||||
catch
|
||||
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
|
||||
yticks(round(linspace(y_min, y_max, min(10, max(4, ceil(y_range/10))))));
|
||||
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;
|
||||
legend
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -961,8 +981,8 @@ classdef Signal
|
||||
maxA = max(sig(100:end-100))*1.3;
|
||||
minA = min(sig(100:end-100))*1.3;
|
||||
|
||||
% maxA = 0.0015;
|
||||
% minA = 0;
|
||||
maxA = 0.12;
|
||||
minA = -0.08;
|
||||
|
||||
difference= maxA-minA;
|
||||
|
||||
@@ -1012,7 +1032,7 @@ classdef Signal
|
||||
|
||||
% add information
|
||||
|
||||
if 1
|
||||
if 0
|
||||
|
||||
pwr_dbm = round(obj.power,3);
|
||||
pwr_lin = obj.power("unit",power_notation.W);
|
||||
@@ -1121,20 +1141,20 @@ classdef Signal
|
||||
|
||||
end
|
||||
|
||||
yticks(linspace(0,histpoints,6));
|
||||
y_tickstring = sprintfc('%.2f', y_tickstring);
|
||||
yticklabels(y_tickstring);
|
||||
|
||||
xticks(linspace(0,histpoints_horizontal,6))
|
||||
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
|
||||
xticklabels(x_tickstring);
|
||||
|
||||
grid off
|
||||
|
||||
end
|
||||
|
||||
|
||||
yticks(linspace(0,histpoints,6));
|
||||
y_tickstring = sprintfc('%.2f', y_tickstring);
|
||||
yticklabels(y_tickstring);
|
||||
|
||||
xticks(linspace(0,histpoints_horizontal,6))
|
||||
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
|
||||
xticklabels(x_tickstring);
|
||||
|
||||
%
|
||||
end
|
||||
|
||||
% disp('h');
|
||||
|
||||
@@ -139,6 +139,8 @@ classdef ChannelFreqResp < handle
|
||||
fnew = linspace(0,fstarget/2,length(Target)/2+1);
|
||||
fnew = fnew(2:end-1);
|
||||
|
||||
|
||||
|
||||
% 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
|
||||
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)
|
||||
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
|
||||
nH = find(~isnan(iH),1,'first');
|
||||
iH(1:nH)=iH(nH);
|
||||
@@ -171,6 +177,8 @@ classdef ChannelFreqResp < handle
|
||||
% five frequencies -> should be the vaue at f=0=DC component?
|
||||
iH = iH./mean(abs(iH)); %why 1:5??
|
||||
|
||||
dc = 20*log10(abs(mean(abs(iH(1:5)))));
|
||||
|
||||
% set maximum amplification
|
||||
% set als values higher than hmax to hmax and keep the
|
||||
% phase information by multiplication with respective
|
||||
@@ -206,7 +214,7 @@ classdef ChannelFreqResp < handle
|
||||
|
||||
function plot(obj)
|
||||
|
||||
figure(55);
|
||||
figure();
|
||||
clf;
|
||||
|
||||
Havg = obj.H;
|
||||
@@ -225,7 +233,7 @@ classdef ChannelFreqResp < handle
|
||||
xlim([0.2 .5*max(obj.faxis)*1e-9]);
|
||||
grid on;
|
||||
|
||||
figure(56);
|
||||
figure();
|
||||
clf;
|
||||
|
||||
|
||||
@@ -246,7 +254,7 @@ classdef ChannelFreqResp < handle
|
||||
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
|
||||
|
||||
%%% plot for publication
|
||||
figure(1234);
|
||||
figure(101);
|
||||
hold all;
|
||||
box on;
|
||||
title('Magnitude Freq. Response');
|
||||
@@ -269,6 +277,25 @@ classdef ChannelFreqResp < handle
|
||||
legend('Interpreter','latex')
|
||||
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
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ classdef FFE < handle
|
||||
mask = ones(obj.order,1);
|
||||
maincursor_pos=ceil(length(obj.e)/2);
|
||||
always_ideal_decision = 0;
|
||||
save_debug = 1;
|
||||
save_debug = 0;
|
||||
grad =0;
|
||||
weight = 0;
|
||||
update = 0;
|
||||
|
||||
233
Classes/04_DSP/Equalizer/FFE_MLSE.m
Normal file
233
Classes/04_DSP/Equalizer/FFE_MLSE.m
Normal 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
|
||||
355
Classes/04_DSP/Equalizer/ML_MLSE.m
Normal file
355
Classes/04_DSP/Equalizer/ML_MLSE.m
Normal 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
|
||||
@@ -6,6 +6,10 @@ classdef MLSE < handle
|
||||
DIR
|
||||
trellis_states
|
||||
duobinary_output
|
||||
trellis_state_mode
|
||||
trellis_exclusion
|
||||
debug
|
||||
scale_mode
|
||||
end
|
||||
|
||||
methods (Access=public)
|
||||
@@ -19,7 +23,10 @@ classdef MLSE < handle
|
||||
options.DIR double = [1];
|
||||
options.trellis_states double = [-3 -1 1 3];
|
||||
options.duobinary_output logical = false;
|
||||
|
||||
options.trellis_state_mode = 2;
|
||||
options.trellis_exclusion = 0;
|
||||
options.scale_mode = 2;
|
||||
options.debug = 0;
|
||||
end
|
||||
|
||||
%
|
||||
@@ -33,6 +40,12 @@ classdef MLSE < handle
|
||||
|
||||
function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass)
|
||||
|
||||
arguments
|
||||
obj
|
||||
signalclass
|
||||
ref_symbolclass
|
||||
end
|
||||
|
||||
data_in = signalclass.signal;
|
||||
shape_in = size(data_in);
|
||||
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)
|
||||
|
||||
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);
|
||||
% 1 = normalize to = 1 rms;
|
||||
% 2 = use target symbols;
|
||||
% 3 = use statistical levels
|
||||
% 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,
|
||||
% 1 = RMS→scale MODEL,
|
||||
% 2 = MMSE/time-corr→scale 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)
|
||||
|
||||
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
|
||||
|
||||
@@ -257,14 +277,107 @@ classdef MLSE < handle
|
||||
|
||||
|
||||
if debug
|
||||
alpha_ = alpha - min(alpha) + eps;
|
||||
figure();hold on;
|
||||
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(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');
|
||||
yticks(obj.trellis_states);
|
||||
ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]);
|
||||
% alpha_ = alpha - min(alpha) + eps;
|
||||
% figure();hold on;
|
||||
% 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(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');
|
||||
% yticks(obj.trellis_states);
|
||||
% 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
|
||||
|
||||
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
|
||||
@@ -544,11 +657,6 @@ classdef MLSE < handle
|
||||
|
||||
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
|
||||
|
||||
@@ -12,6 +12,7 @@ classdef Exfo_laser < handle
|
||||
safety_mode
|
||||
end
|
||||
|
||||
%% Any user can call these methods to alter the laser state
|
||||
methods (Access=public)
|
||||
|
||||
function obj = Exfo_laser(options)
|
||||
@@ -169,6 +170,10 @@ classdef Exfo_laser < handle
|
||||
end
|
||||
|
||||
end %public mehtods
|
||||
|
||||
|
||||
% DAU must not be able to call these funcitons outside of the class!
|
||||
% Hence they are private! :-)
|
||||
methods (Access=private)
|
||||
|
||||
function v = connectLaser_(obj)
|
||||
@@ -180,7 +185,7 @@ classdef Exfo_laser < handle
|
||||
configureTerminator(v, "CR", "CR"); % Set the terminator to carriage return (CR)
|
||||
elseif obj.lab_interface == lab_interface.gpib
|
||||
% Connect via GPIB adress
|
||||
visastring = ['GPIB0::',obj.connection_id,'::INSTR'];
|
||||
visastring = ['GPIB1::',obj.connection_id,'::INSTR'];
|
||||
v = visadev(visastring);
|
||||
else
|
||||
error('Interface not supported');
|
||||
@@ -342,8 +347,13 @@ classdef Exfo_laser < handle
|
||||
command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)];
|
||||
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
|
||||
pause(0.2);
|
||||
pause(1.5);
|
||||
response = readline(serialObj);
|
||||
success = contains(response, 'OK');
|
||||
end
|
||||
|
||||
83
Classes/05_Lab/LabDeviceTemplate.m
Normal file
83
Classes/05_Lab/LabDeviceTemplate.m
Normal 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
|
||||
327
Classes/05_Lab/OSA_Advantest.m
Normal file
327
Classes/05_Lab/OSA_Advantest.m
Normal 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
|
||||
146
Classes/05_Lab/OptLaserN7714A.m
Normal file
146
Classes/05_Lab/OptLaserN7714A.m
Normal 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
|
||||
168
Classes/05_Lab/OptPowerMeter8153A.m
Normal file
168
Classes/05_Lab/OptPowerMeter8153A.m
Normal 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
|
||||
@@ -20,7 +20,7 @@ classdef Thor_PDFA < handle
|
||||
function obj = Thor_PDFA(options)
|
||||
|
||||
arguments
|
||||
options.serialport_number = 'COM12'
|
||||
options.serialport_number = 'COM18'
|
||||
options.safety_mode = 1;
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ classdef Thor_PDFA < handle
|
||||
end
|
||||
|
||||
function o = connectSerial(obj)
|
||||
|
||||
o = [];
|
||||
try
|
||||
% Connect to the PDFA
|
||||
o = serialport(obj.serialport_number, 9600);
|
||||
@@ -62,11 +62,15 @@ classdef Thor_PDFA < handle
|
||||
function getStatus(obj)
|
||||
|
||||
o = obj.connectSerial();
|
||||
obj.getStatus_(o);
|
||||
if obj.safety_mode
|
||||
obj
|
||||
|
||||
if ~isempty(o)
|
||||
obj.getStatus_(o);
|
||||
if obj.safety_mode
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function enablePDFA(obj)
|
||||
|
||||
@@ -51,7 +51,7 @@ classdef DBHandler < handle
|
||||
"Vendor", "MySQL", ...
|
||||
"Server", "134.245.243.254", ...
|
||||
"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
|
||||
|
||||
catch e
|
||||
@@ -588,14 +588,13 @@ classdef DBHandler < handle
|
||||
return
|
||||
catch ME
|
||||
if attempt < maxFast
|
||||
pause(1)
|
||||
pause(0.1)
|
||||
else
|
||||
pause(10)
|
||||
pause(1)
|
||||
end
|
||||
lastErr = ME;
|
||||
end
|
||||
end
|
||||
pause(60)
|
||||
error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport())
|
||||
end
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ classdef Metricstruct
|
||||
SNR (1,1) double {mustBeNumeric} = NaN
|
||||
SNR_level (:,1) double {mustBeNumeric} = []
|
||||
STD (1,1) double {mustBeNumeric} = NaN
|
||||
STD_level (:,1) double {mustBeNumeric, mustBeNonnegative} = []
|
||||
STD_level (:,1) double = []
|
||||
STDrx (1,1) double {mustBeNumeric} = NaN
|
||||
STDrx_level (:,1) double {mustBeNumeric, mustBeNonnegative} = []
|
||||
STDrx_level (:,1) double = []
|
||||
EVM (1,1) double {mustBeNumeric} = NaN
|
||||
EVM_level (:,1) double {mustBeNumeric} = []
|
||||
|
||||
@@ -71,16 +71,22 @@ classdef Metricstruct
|
||||
end
|
||||
end
|
||||
|
||||
function print(obj)
|
||||
function print(obj,options)
|
||||
% Print method to display key metrics in a formatted way
|
||||
arguments
|
||||
obj
|
||||
options.description = '';
|
||||
end
|
||||
|
||||
|
||||
% Define the width for formatting
|
||||
nameWidth = 15; % Width for parameter names
|
||||
valueWidth = 12; % Width for values
|
||||
|
||||
% Print header
|
||||
|
||||
fprintf('\n%s\n', repmat('=', 1, nameWidth + valueWidth));
|
||||
fprintf(' Results \n');
|
||||
fprintf([char(options.description),' Results \n']);
|
||||
fprintf('%s\n', repmat('=', 1, nameWidth + valueWidth));
|
||||
|
||||
% Function to format numbers with appropriate precision
|
||||
|
||||
146
Classes/GifWriter.m
Normal file
146
Classes/GifWriter.m
Normal 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
|
||||
@@ -137,7 +137,21 @@ classdef DataStorage < handle
|
||||
if ~isempty(tmp)
|
||||
|
||||
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')
|
||||
if i == 1
|
||||
value = {};
|
||||
|
||||
7
Classes/Warehouse_class/classes/Parameter.m
Normal file
7
Classes/Warehouse_class/classes/Parameter.m
Normal file
@@ -0,0 +1,7 @@
|
||||
classdef Parameter < StorageParameter
|
||||
methods
|
||||
function obj = Parameter(varargin)
|
||||
obj@StorageParameter(varargin{:});
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,27 +5,37 @@
|
||||
wh = load([path filesep file]);
|
||||
wh = wh.wh;
|
||||
|
||||
% fields = fieldnames(wh.parameter);
|
||||
% for k = 1:numel(fields)
|
||||
% oldParam = wh.parameter.(fields{k});
|
||||
% % copy over the properties to your new class
|
||||
% wh.parameter.(fields{k}) = StorageParameter(...
|
||||
% oldParam.Name, oldParam.values);
|
||||
% end
|
||||
%
|
||||
|
||||
|
||||
plotJob = struct();
|
||||
width = 350;
|
||||
height = 200;
|
||||
plotJob.Position = [100 100 width 100+height];
|
||||
cols = cbrewer2("paired",12);
|
||||
plotJob.color = cols(1,:);
|
||||
plotJob.l = 2;
|
||||
plotJob.l = 10;
|
||||
plotJob.ch = 16;
|
||||
plotJob.d = 0;
|
||||
plotJob.sgm = 0;
|
||||
plotJob.pol = "copolarized";
|
||||
plotJob.p_in = 3;
|
||||
plotJob.gamma = 0;
|
||||
plotJob.pmd = 0;
|
||||
plotJob.gamma = 0.0023;
|
||||
plotJob.pmd = 0.1;
|
||||
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_violin = 0;
|
||||
plotJob.plot_violin = 1;
|
||||
plotJob.plot_wavelength_sweep = 0;
|
||||
plotJob.plot_wavelength_sweep_failure_rate = 0;
|
||||
|
||||
@@ -34,13 +44,20 @@ plotJob.dataStatArg = 'Lineplot with quartiles';
|
||||
plotJob.plotTypeArg = 'Lines';
|
||||
plotJob.displayname = 'a';
|
||||
plotJob.title = 'title';
|
||||
plotJob.figName = '16 Chann__';
|
||||
plotJob.figName = '16 Chann';
|
||||
plotJob.xAxisLabel = 'ROP per Channel in dBm';
|
||||
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
|
||||
@@ -64,7 +81,7 @@ D = [0,0,0,3,0,0,0,3];
|
||||
Sgm = [0,0,0,1,0,0,0,1];
|
||||
|
||||
colidx = [4,8,6];
|
||||
P_launch = [0,3,6];
|
||||
P_launch = [3,6];
|
||||
|
||||
fig = figure('Name',plotJob.figName);
|
||||
fig.Position = plotJob.Position;
|
||||
@@ -81,7 +98,7 @@ for idx = 1:(numRows * numCols)
|
||||
plotJob.sgm = Sgm(idx);
|
||||
plotJob.randzdw = 1;
|
||||
|
||||
for i = 1:3
|
||||
for i = 1:length(P_launch)
|
||||
|
||||
plotJob.p_in = P_launch(i);
|
||||
plotJob.color = cols(colidx(i),:);
|
||||
@@ -199,6 +216,7 @@ end
|
||||
|
||||
%% 2
|
||||
function createviolinplots(wh,plotJob)
|
||||
|
||||
width = 350;
|
||||
height = 200;
|
||||
s = 100;
|
||||
@@ -208,30 +226,34 @@ cols = cbrewer2("paired",12);
|
||||
numRows = 1;
|
||||
numCols = 4;
|
||||
|
||||
|
||||
plotJob.ch = 16;
|
||||
plotJob.p_in = 3;
|
||||
plotJob.randzdw = 0;
|
||||
plotJob.randzdw = 1;
|
||||
|
||||
Pol = ["copolarized","copolarized","alternated","paired",];
|
||||
Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."];
|
||||
D = [0,3,0,0];
|
||||
Sgm = [0,1,0,0];
|
||||
|
||||
colidx = [2];
|
||||
Len = [2];
|
||||
colidx = [4];
|
||||
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 = figure('Name',plotJob.figName);
|
||||
fig.Position = plotJob.Position;
|
||||
fig.Units = "centimeters";
|
||||
fig.Position = [0 0 18 7];
|
||||
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
|
||||
|
||||
t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact');
|
||||
for idx = 1:(numRows * numCols)
|
||||
% Create subplot
|
||||
%subplot(numRows, numCols, idx);
|
||||
nexttile;
|
||||
subplot(numRows, numCols, idx);
|
||||
|
||||
plotJob.pol = Pol(idx);
|
||||
plotJob.d = D(idx);
|
||||
@@ -295,7 +317,6 @@ annotation(fig,'textbox',...
|
||||
'FontSize',8,...
|
||||
'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.NumColumns = 3;
|
||||
% lgd.Layout.Tile = 'south';
|
||||
@@ -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');
|
||||
@@ -6,7 +6,7 @@ fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
|
||||
|
||||
if isvalid(fig)
|
||||
figure(fig)
|
||||
fig = get(fig);
|
||||
% fig = get(fig);
|
||||
AxesMain = fig.CurrentAxes;
|
||||
hold on
|
||||
else
|
||||
@@ -28,32 +28,39 @@ realization = wh.parameter.realization.values(1:end);
|
||||
% get all xAxis values
|
||||
xAxis = wh.parameter.p_out.values;
|
||||
|
||||
markerstyle = 'o';
|
||||
linestyle = '-';
|
||||
|
||||
% Fetch Data from Warehouse
|
||||
for xl = 1:numel(xAxis)
|
||||
p_out = xAxis(xl);
|
||||
if string(plotJob.dataStatArg) == "Worst"
|
||||
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
|
||||
temp = removeZeros(temp);
|
||||
ber(xl) = max(temp,[],'all');
|
||||
ber(xl) = quantile(temp,0.9,"all");
|
||||
% ber(xl) = max(temp,[],'all');
|
||||
linew = 1.0;
|
||||
markersz = 3;
|
||||
linestyle = '-';
|
||||
markersz = plotJob.markersize;
|
||||
markerstyle = plotJob.markerstyle;
|
||||
linestyle = plotJob.linestyle;
|
||||
|
||||
elseif string(plotJob.dataStatArg) == "AVG"
|
||||
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
|
||||
temp = removeZeros(temp);
|
||||
ber(xl) = mean(temp,'all');
|
||||
linew = 1.0;
|
||||
markersz = 3;
|
||||
linestyle = '--';
|
||||
markersz = plotJob.markersize;
|
||||
markerstyle = plotJob.markerstyle;
|
||||
linestyle = plotJob.linestyle;
|
||||
|
||||
elseif string(plotJob.dataStatArg) == "Best"
|
||||
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
|
||||
temp = removeZeros(temp);
|
||||
ber(xl) = min(temp,[],'all');
|
||||
linew = 1.0;
|
||||
markersz = 3;
|
||||
linestyle = '-';
|
||||
markersz = plotJob.markersize;
|
||||
markerstyle = plotJob.markerstyle;
|
||||
linestyle = plotJob.linestyle;
|
||||
|
||||
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
|
||||
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
|
||||
@@ -63,7 +70,8 @@ for xl = 1:numel(xAxis)
|
||||
|
||||
linew = 1;
|
||||
markersz = 1;
|
||||
linestyle = '-';
|
||||
markerstyle = plotJob.markerstyle;
|
||||
linestyle = plotJob.linestyle;
|
||||
|
||||
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
|
||||
|
||||
@@ -75,14 +83,16 @@ for xl = 1:numel(xAxis)
|
||||
upperq(xl) = quantile(temp,0.99,"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');
|
||||
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
|
||||
|
||||
% upperq(xl) = max(dataNoNans);
|
||||
% lowerq(xl) = min(dataNoNans);
|
||||
upperq(xl) = max(temp(:));
|
||||
lowerq(xl) = min(temp(:));
|
||||
|
||||
if lowerq(xl) == 0
|
||||
lowerq(xl) = 1e-8;
|
||||
end
|
||||
|
||||
% upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
|
||||
% lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
|
||||
@@ -149,6 +159,7 @@ elseif string(plotJob.plotTypeArg) == "Lines"
|
||||
% [xAxis,ber] = interpCurve(xAxis, ber);
|
||||
% end
|
||||
|
||||
cols = cbrewer2('RdBu',size(ber,1));
|
||||
for rlz = 1:size(ber,1)
|
||||
%
|
||||
if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
|
||||
@@ -160,6 +171,7 @@ elseif string(plotJob.plotTypeArg) == "Lines"
|
||||
|
||||
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');
|
||||
|
||||
@@ -169,22 +181,25 @@ elseif string(plotJob.plotTypeArg) == "Lines"
|
||||
s.DataTipTemplate.DataTipRows(1);
|
||||
s.DataTipTemplate.DataTipRows(2) = [];
|
||||
|
||||
|
||||
else
|
||||
|
||||
if string(plotJob.dataStatArg) == "Lineplot with quartiles"
|
||||
[hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-o','alpha','Color',col,'transparency', 0.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.MarkerSize = 3;
|
||||
hl.MarkerSize = 2;
|
||||
set(hp,'HandleVisibility','off');
|
||||
%hp.LineWidth = 1.2;
|
||||
|
||||
ho = outlinebounds(hl,hp);
|
||||
set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.5);
|
||||
set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.6);
|
||||
set(ho,'HandleVisibility','off');
|
||||
|
||||
% errorbar(xAxis,ber(rlz,:),ber(rlz,:)-lowerq(rlz,:),upperq(rlz,:)-ber(rlz,:),'-o','Color',col,'linewidth',0.7);
|
||||
|
||||
else
|
||||
|
||||
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"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.DataTipRows(1).Label = "Ch: ";
|
||||
@@ -245,9 +260,10 @@ grid minor
|
||||
fontsize(AxesMain,8,"points")
|
||||
% fontname(AxesMain,"Arial")
|
||||
|
||||
fig.Position = plotJob.Position;
|
||||
fig.Units = "centimeters";
|
||||
fig.Position = [2 2 8.5 7];
|
||||
|
||||
% fig.Position = plotJob.Position;
|
||||
% fig.Units = "centimeters";
|
||||
% fig.Position = [2 2 8.5 7];
|
||||
|
||||
set(AxesMain,'TickLabelInterpreter','latex')
|
||||
|
||||
@@ -257,7 +273,7 @@ set(AxesMain.Legend,'Interpreter','latex')
|
||||
|
||||
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")
|
||||
@@ -155,7 +155,7 @@ ylabel('Penalty in dB');
|
||||
xlabel('Channel Number');
|
||||
ylim([-9.3,-3]);
|
||||
xlim([0,plotJob.ch+1]);
|
||||
grid minor;
|
||||
% grid minor;
|
||||
set(gca, 'color', 'none');
|
||||
legend = [];
|
||||
|
||||
@@ -49,22 +49,5 @@ classdef clr
|
||||
hold off;
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@ classdef equalizer_structure < int32
|
||||
% db_precoded (3)
|
||||
vnle_db_mlse (4)
|
||||
db_encoded (5)
|
||||
ml_mlse (6)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -13,14 +13,13 @@ arguments
|
||||
end
|
||||
|
||||
try
|
||||
|
||||
|
||||
% Initialize output structures
|
||||
output.ffe_package = {};
|
||||
output.mlse_package = {};
|
||||
output.vnle_package = {};
|
||||
output.dbtgt_package = {};
|
||||
output.dbenc_package = {};
|
||||
output.mlmlse_package = {};
|
||||
|
||||
if options.mode == "load_run_id" || options.append_to_db
|
||||
% Initialize database connection
|
||||
@@ -96,11 +95,12 @@ try
|
||||
adaption= 1;
|
||||
use_dd_mode = 1;
|
||||
|
||||
use_ffe = 1;
|
||||
use_dfe = 1;
|
||||
use_vnle_mlse = 1;
|
||||
use_dbtgt = 1;
|
||||
use_ffe = 0;
|
||||
use_dfe = 0;
|
||||
use_vnle_mlse = 0;
|
||||
use_dbtgt = 0;
|
||||
use_dbenc = 0;
|
||||
use_ml_mlse = 1;
|
||||
|
||||
addProcessingResultToDatabase = 0;
|
||||
|
||||
@@ -130,7 +130,7 @@ try
|
||||
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
|
||||
eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0,"adaption_technique","lms");
|
||||
eq_post =FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
|
||||
eq_post = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
|
||||
% Duobinary signaling (db encoded)
|
||||
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
|
||||
eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ...
|
||||
@@ -140,7 +140,12 @@ try
|
||||
% Preprocess signal
|
||||
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.eye(fsym,M,"fignum",1024);
|
||||
|
||||
@@ -194,14 +199,26 @@ try
|
||||
|
||||
pf_ncoeffs = 1;
|
||||
ffe_order = [50, 5, 5];
|
||||
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",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);
|
||||
|
||||
useviterbi = 0;
|
||||
if useviterbi
|
||||
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
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
|
||||
|
||||
[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);
|
||||
end
|
||||
|
||||
% pf_ncoeffs = 2;
|
||||
% 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);
|
||||
% % 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);
|
||||
%
|
||||
% [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
|
||||
% "precode_mode", duob_mode,...
|
||||
% 'showAnalysis', 0, ...
|
||||
% "postFFE", [],...
|
||||
% "eth_style_symbol_mapping", 0);
|
||||
%
|
||||
% ffe_results.metrics.print;
|
||||
% mlse_results.metrics.print;
|
||||
%
|
||||
% output.mlse_package{r} = mlse_results;
|
||||
% output.vnle_package{r} = ffe_results;
|
||||
%
|
||||
% if options.append_to_db
|
||||
% database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config);
|
||||
% database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
|
||||
% end
|
||||
end
|
||||
|
||||
if use_ml_mlse
|
||||
|
||||
%ML-based MLSE (L=2)
|
||||
mu_ml = 0.01; training_epochs = 100;
|
||||
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
|
||||
"len_tr",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
|
||||
"traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0);
|
||||
|
||||
[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode);
|
||||
output.mlmlse_package{r} = ml_mlse_results;
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, ml_mlse_results.metrics, ml_mlse_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if use_dbtgt
|
||||
|
||||
useviterbi = 0;
|
||||
if useviterbi
|
||||
mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
else
|
||||
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
|
||||
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);
|
||||
@@ -276,6 +293,9 @@ try
|
||||
|
||||
if duob_mode == db_mode.db_encoded
|
||||
|
||||
mlse_db_enc = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
|
||||
|
||||
db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, M, Scpe_sig, Symbols, Tx_bits, "precode_mode",duob_mode, "showAnalysis",0,"postFFE",[]);
|
||||
output.dbenc_package{r} = db_results;
|
||||
if options.append_to_db
|
||||
|
||||
@@ -35,9 +35,28 @@ if ~isempty(options.postFFE)
|
||||
[eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols);
|
||||
end
|
||||
|
||||
% Process through MLSE
|
||||
% [mlse_signal] = mlse_.process(eq_signal);
|
||||
[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
|
||||
if isa(mlse_,'MLSE_viterbi')
|
||||
[mlse_signal] = mlse_.process(eq_signal);
|
||||
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
|
||||
mlse_signal = Duobinary().encode(mlse_signal);
|
||||
|
||||
@@ -130,6 +130,10 @@ db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse);
|
||||
db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)';
|
||||
|
||||
if options.showAnalysis
|
||||
|
||||
eq_signal.eye(eq_signal.fs,M,"fignum",249);
|
||||
|
||||
|
||||
eq_noise = eq_noise - mean(eq_noise.signal);
|
||||
|
||||
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum");
|
||||
|
||||
140
Functions/EQ_structures/ml_mlse.m
Normal file
140
Functions/EQ_structures/ml_mlse.m
Normal 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
|
||||
@@ -45,7 +45,26 @@ end
|
||||
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
|
||||
|
||||
% Process through postfilter and MLSE
|
||||
|
||||
[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;
|
||||
|
||||
GMI_MLSE = NaN;
|
||||
@@ -249,7 +268,8 @@ figure(336);
|
||||
hold on;
|
||||
eq_signal_sd.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
|
||||
pf_.ncoeff = t;
|
||||
@@ -263,6 +283,7 @@ if ~isempty(postFFE)
|
||||
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
|
||||
end
|
||||
|
||||
showEQcoefficients('n1', eq_.e,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339);
|
||||
showEQfilter(eq_.e, eq_signal_sd.fs.*2);
|
||||
|
||||
figure(340); clf;
|
||||
|
||||
@@ -22,7 +22,6 @@ end
|
||||
|
||||
% Ensure the figure is ready before calling spectrum
|
||||
eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color);
|
||||
title('EEN')
|
||||
|
||||
if ~isnan(options.postfilter_taps)
|
||||
% Hold on to the figure for further plotting
|
||||
@@ -43,6 +42,6 @@ end
|
||||
end
|
||||
|
||||
xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]);
|
||||
ylim([-15, 0]);
|
||||
% ylim([-15, 0]);
|
||||
|
||||
end
|
||||
|
||||
@@ -52,6 +52,8 @@ end
|
||||
legend
|
||||
grid on
|
||||
|
||||
% view([90 -90]);
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
% Apply Gaussian filter
|
||||
% Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ...
|
||||
% "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ...
|
||||
% "active", true).process(Scpe_sig);
|
||||
if 1
|
||||
Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*0.52, ...
|
||||
"fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ...
|
||||
"active", true).process(Scpe_sig);
|
||||
else
|
||||
Scpe_sig = Filter('filtdegree', 4, "f_cutoff", Symbols.fs.*0.6, ...
|
||||
"fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ...
|
||||
"active", true).process(Scpe_sig);
|
||||
end
|
||||
|
||||
% Remove DC offset
|
||||
%Remove DC offset
|
||||
Scpe_sig = Scpe_sig - mean(Scpe_sig.signal);
|
||||
|
||||
end
|
||||
@@ -209,6 +209,7 @@ wh = submit_options.wh;
|
||||
wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex);
|
||||
wh.addValueToStorageByLinIdx(val.dbtgt_package,'dbtgt_package',jobIndex);
|
||||
wh.addValueToStorageByLinIdx(val.dbenc_package,'dbenc_package',jobIndex);
|
||||
wh.addValueToStorageByLinIdx(val.mlmlse_package,'mlmlse_package',jobIndex);
|
||||
end
|
||||
end
|
||||
function p = setupParallelPool(numWorkers, idleTimeout)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
% 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)
|
||||
fileList = dir(fullfile(precomp_path, '*.mat'));
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
function waitUntilClick()
|
||||
% Create the UI figure
|
||||
fig = uifigure('Name', 'Pause Execution', 'Position', [100 100 300 150]);
|
||||
function waitUntilClick(options)
|
||||
|
||||
% Create a label to inform the user
|
||||
uilabel(fig, 'Position', [50 80 200 40], 'Text', 'Click "Continue" to proceed', ...
|
||||
'FontSize', 14, 'HorizontalAlignment', 'center');
|
||||
% use like this to stop any computation and wait for user to press ENTER or
|
||||
% Continue button. I used this in the lab to wait until smth settled or I
|
||||
% set a device to a certain operating point...
|
||||
|
||||
% Create the "Continue" button
|
||||
continueButton = uibutton(fig, 'push', 'Text', 'Continue', 'Position', [100 20 100 40], ...
|
||||
'ButtonPushedFcn', @(src, event) closeWindow());
|
||||
% USAGE:
|
||||
% 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());
|
||||
|
||||
% Function to close the window when "Continue" is clicked
|
||||
function closeWindow()
|
||||
delete(fig); % Close the figure window
|
||||
delete(fig);
|
||||
end
|
||||
|
||||
% Wait until the figure is closed to resume execution
|
||||
waitfor(fig);
|
||||
function keyHandler(event)
|
||||
if strcmp(event.Key,'return') || strcmp(event.Key,'enter')
|
||||
closeWindow();
|
||||
end
|
||||
end
|
||||
|
||||
% Wait until the figure is closed
|
||||
waitfor(fig);
|
||||
end
|
||||
20
Functions/Metrics/noiseFigureFromOSNR.m
Normal file
20
Functions/Metrics/noiseFigureFromOSNR.m
Normal 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
|
||||
27
Functions/Theory/CCDM/ccdm_gpt_example.m
Normal file
27
Functions/Theory/CCDM/ccdm_gpt_example.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
89
Functions/Theory/dispersion_10km.m
Normal file
89
Functions/Theory/dispersion_10km.m
Normal 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
|
||||
@@ -1,14 +1,15 @@
|
||||
% 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);
|
||||
[Lambda0, S0] = meshgrid(lambda0_vec, S0_vec);
|
||||
|
||||
% Festen Betriebsparameter
|
||||
lambda = 1293; % nm
|
||||
L = 10; % km
|
||||
L = 1; % km
|
||||
|
||||
% Dispersion berechnen (lineare Näherung)
|
||||
D = S0 .* ( lambda - Lambda0 ) * L;
|
||||
% D = (S0./4) .* ( lambda - (Lambda0.^4)./(lambda^3) ) * L;
|
||||
|
||||
%% 2D-Konturplot nur mit Linien und Text
|
||||
figure('Color','w');
|
||||
|
||||
146
Functions/Theory/dispersion_contour_bandwidth_lambda.m
Normal file
146
Functions/Theory/dispersion_contour_bandwidth_lambda.m
Normal 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 (1260–1360 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
|
||||
38
Functions/Theory/dispersion_first_notch_10km.m
Normal file
38
Functions/Theory/dispersion_first_notch_10km.m
Normal 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');
|
||||
165
Functions/Theory/dispersion_power_fading.m
Normal file
165
Functions/Theory/dispersion_power_fading.m
Normal 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 (1260–1360 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
|
||||
32
Functions/Theory/dispersion_wavelength_notch.m
Normal file
32
Functions/Theory/dispersion_wavelength_notch.m
Normal 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])
|
||||
111
Functions/Theory/dispersion_wdm.m
Normal file
111
Functions/Theory/dispersion_wdm.m
Normal 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
|
||||
120
Functions/Theory/matched_filter_rrc.m
Normal file
120
Functions/Theory/matched_filter_rrc.m
Normal 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');
|
||||
40
Functions/Theory/power_fading.m
Normal file
40
Functions/Theory/power_fading.m
Normal 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');
|
||||
@@ -1,37 +1,113 @@
|
||||
function beautifyBERplot()
|
||||
% BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures.
|
||||
function beautifyBERplot(options)
|
||||
% 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'
|
||||
|
||||
arguments
|
||||
options.logscale (1,1) logical = 1
|
||||
options.setmarkers (1,1) logical = 1;
|
||||
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)
|
||||
x = lines(i).XData;
|
||||
y = lines(i).YData;
|
||||
valid = isfinite(x) & isfinite(y);
|
||||
if sum(valid) < options.polyorder + 1
|
||||
continue;
|
||||
end
|
||||
|
||||
xf = linspace(min(x(valid)), max(x(valid)), 200);
|
||||
|
||||
% ----- choose fitting method -----
|
||||
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
|
||||
|
||||
case "loess"
|
||||
yf = smooth(x(valid), y(valid), 0.2, 'loess');
|
||||
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');
|
||||
|
||||
|
||||
% Set line properties for all current plot lines
|
||||
lines = findall(gca, 'Type', 'Line');
|
||||
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles
|
||||
num_markers = length(markers);
|
||||
|
||||
for i = 1:length(lines)
|
||||
lines(i).LineWidth = 1.3; % Thicker line width
|
||||
%lines(i).LineStyle = '-'; % Solid lines for simplicity
|
||||
% if string(lines(i).Marker) == "none"
|
||||
% lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically
|
||||
% end
|
||||
lines(i).MarkerSize = 4; % Marker size
|
||||
lines(i).MarkerFaceColor = 'auto'; % Use line color for marker face
|
||||
end
|
||||
|
||||
% Change all text interpreters to LaTeX
|
||||
set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex');
|
||||
|
||||
% Set figure background to white
|
||||
set(gcf, 'Color', 'w');
|
||||
|
||||
|
||||
% Set logarithmic scale for y-axis, but only if it makes sense.
|
||||
% If this is not always desired, you could condition this on the presence of lines or data.
|
||||
set(gca, 'YScale', 'log');
|
||||
|
||||
% 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
|
||||
|
||||
62
Functions/convert_freq_lambda.m
Normal file
62
Functions/convert_freq_lambda.m
Normal file
@@ -0,0 +1,62 @@
|
||||
%% ============================================================
|
||||
% Wavelength–Frequency 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
|
||||
133
Libs/wesanderson_colors/WesPalette.m
Normal file
133
Libs/wesanderson_colors/WesPalette.m
Normal 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 [0–1]
|
||||
|
||||
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
|
||||
20
Libs/wesanderson_colors/hex2rgb.m
Normal file
20
Libs/wesanderson_colors/hex2rgb.m
Normal file
@@ -0,0 +1,20 @@
|
||||
function rgb = hex2rgb(hex)
|
||||
% HEX2RGB Convert HEX color codes to RGB [0–1]
|
||||
|
||||
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
|
||||
64
Libs/wesanderson_colors/minimal_example_wespalette.m
Normal file
64
Libs/wesanderson_colors/minimal_example_wespalette.m
Normal 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}',...
|
||||
});
|
||||
79
Libs/wesanderson_colors/wes_palettes.m
Normal file
79
Libs/wesanderson_colors/wes_palettes.m
Normal 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
BIN
projects.mat
Normal file
Binary file not shown.
@@ -243,7 +243,6 @@ for occ = 1:record_realizations
|
||||
|
||||
fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER);
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
BIN
projects/ECOC_2025/ecoc2025.db
Normal file
BIN
projects/ECOC_2025/ecoc2025.db
Normal file
Binary file not shown.
1
projects/ECOC_2025/ecoc2025.sqbpro
Normal file
1
projects/ECOC_2025/ecoc2025.sqbpro
Normal file
File diff suppressed because one or more lines are too long
BIN
projects/ECOC_2025/ecoc2025_einmessung.db
Normal file
BIN
projects/ECOC_2025/ecoc2025_einmessung.db
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/ecoc2025_fail.db
Normal file
BIN
projects/ECOC_2025/ecoc2025_fail.db
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/ecoc2025_loops - Kopie.db
Normal file
BIN
projects/ECOC_2025/ecoc2025_loops - Kopie.db
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/ecoc2025_loops.db
Normal file
BIN
projects/ECOC_2025/ecoc2025_loops.db
Normal file
Binary file not shown.
@@ -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;
|
||||
% 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);
|
||||
[dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs'));
|
||||
|
||||
[~, 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 = 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);
|
||||
|
||||
@@ -8,14 +8,14 @@ end
|
||||
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_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
|
||||
run_lab_automation = 1;
|
||||
run_lab_automation = 0;
|
||||
|
||||
%% Configuration
|
||||
|
||||
conf = confRequest;
|
||||
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
|
||||
|
||||
@@ -28,7 +28,6 @@ if run_lab_automation
|
||||
[v_bias_meas,i_bias_meas]=dcs.readVals();
|
||||
end
|
||||
% Laser
|
||||
|
||||
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.setWavelength(conf.wavelength);
|
||||
@@ -42,7 +41,7 @@ if run_lab_automation
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
@@ -51,15 +50,56 @@ if run_lab_automation
|
||||
pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15');
|
||||
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
|
||||
|
||||
% 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
|
||||
if awg_upload_required || isempty(loop_id)
|
||||
@@ -70,6 +110,13 @@ if awg_upload_required || isempty(loop_id)
|
||||
sequence_order = min(18,sequence_order);
|
||||
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(...
|
||||
"fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,...
|
||||
"fs_out",fdac,...
|
||||
@@ -80,11 +127,11 @@ if awg_upload_required || isempty(loop_id)
|
||||
"randkey",1,...
|
||||
"duobinary_mode", conf.db_mode);
|
||||
|
||||
conf.pam_source = Pamsource;
|
||||
confPrev = conf;
|
||||
|
||||
[Digi_sig,Symbols,Bits] = Pamsource.process();
|
||||
|
||||
figure(10);clf;
|
||||
Digi_sig.spectrum("displayname",'Tx Pulse Shaped','fignum',10,'normalizeTo0dB',1);
|
||||
|
||||
|
||||
%%%%% Precompensation Routine %%%%%%
|
||||
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);
|
||||
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 %%%%%%
|
||||
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);
|
||||
|
||||
Awg.upload("signal2",Digi_sig);
|
||||
@@ -114,6 +163,8 @@ if precomp_mode == 1
|
||||
return; % End routine if precomp mode is active
|
||||
end
|
||||
|
||||
confPrev = conf;
|
||||
|
||||
%% Save static TX data (only once)
|
||||
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
|
||||
timeStr = char(currentTime);
|
||||
@@ -143,7 +194,7 @@ for recIdx = 1:n_recording
|
||||
end
|
||||
|
||||
% 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);
|
||||
|
||||
%% Save Routine
|
||||
@@ -154,7 +205,6 @@ for recIdx = 1:n_recording
|
||||
rx_raw_path = [filesep, current_folder, filesep, filename, '_rx_signal_raw'];
|
||||
save([savePath, rx_raw_path], "Scpe_sig_raw");
|
||||
|
||||
|
||||
% Check if loop_id is already created
|
||||
if isempty(loop_id)
|
||||
newLoop = db.tables.LoopControl;
|
||||
@@ -166,7 +216,6 @@ for recIdx = 1:n_recording
|
||||
fprintf('LoopControl entry created: loop_id = %d\n', loop_id);
|
||||
end
|
||||
|
||||
|
||||
%% Append to Runs Table
|
||||
newRun = db.tables.Runs;
|
||||
newRun.run_id = NaN;
|
||||
@@ -184,9 +233,12 @@ for recIdx = 1:n_recording
|
||||
%% Append to Configurations Table
|
||||
conf.configuration_id = NaN;
|
||||
conf.run_id = run_id;
|
||||
conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10;
|
||||
conf.pam_source = Pamsource;
|
||||
db.appendToTable('Configurations', conf);
|
||||
|
||||
%% Append to Measurements Table
|
||||
laser.getLaserInfo;
|
||||
meas = db.tables.Measurements;
|
||||
meas.measurement_id = NaN;
|
||||
meas.run_id = run_id;
|
||||
@@ -204,12 +256,17 @@ for recIdx = 1:n_recording
|
||||
|
||||
db.appendToTable('Measurements', meas);
|
||||
|
||||
|
||||
%% Submit DSP Routine
|
||||
|
||||
if subimt_DSP
|
||||
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
|
||||
"parallel", parallel_dsp, "max_occurences", max_occurences);
|
||||
if recIdx == 1
|
||||
if subimt_DSP
|
||||
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
|
||||
"parallel", parallel_dsp, "max_occurences", max_occurences);
|
||||
end
|
||||
if awg_upload_required
|
||||
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
|
||||
"parallel", 0, "max_occurences", 1);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
|
||||
database_name = 'ecoc2025.db';
|
||||
database = DBHandler("pathToDB", [basePath, database_name]);
|
||||
database = DBHandler("pathToDB", [basePath, database_name],"type",'mysql');
|
||||
|
||||
filterParams = database.tables;
|
||||
filterParams.Configurations = struct( ...
|
||||
|
||||
@@ -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;
|
||||
filterParams.Runs.loop_id = 209;
|
||||
% filterParams.Configurations = struct( ...
|
||||
% 'symbolrate', 112e9, ... %[224,336,360,390,420,448]
|
||||
% 'fiber_length', 0, ...
|
||||
% 'db_mode', '"no_db"', ...
|
||||
% 'interference_attenuation', [], ...
|
||||
% 'interference_path_length', 1000, ...
|
||||
% 'is_mpi', 1, ...
|
||||
% 'pam_level', 4, ...
|
||||
% 'wavelength', 1310, ...
|
||||
% 'precomp_amp', [], ...
|
||||
% 'signal_attenuation', [], ...
|
||||
% 'v_awg', [], ...
|
||||
% 'v_bias', [] ...
|
||||
% );
|
||||
% dsp_options.database_type = 'mysql';
|
||||
% dsp_options.dataBase = 'labor';
|
||||
% dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\';
|
||||
% database = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
|
||||
% filterParams = database.tables;
|
||||
% filterParams.Runs.loop_id = 209;
|
||||
% % filterParams.Configurations = struct( ...
|
||||
% % 'symbolrate', 112e9, ... %[224,336,360,390,420,448]
|
||||
% % 'fiber_length', 0, ...
|
||||
% % 'db_mode', '"no_db"', ...
|
||||
% % 'interference_attenuation', [], ...
|
||||
% % 'interference_path_length', 1000, ...
|
||||
% % 'is_mpi', 1, ...
|
||||
% % 'pam_level', 4, ...
|
||||
% % '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
|
||||
% % 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];
|
||||
db = DBHandler("type","mysql","dataBase",'labor');
|
||||
|
||||
[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.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level);
|
||||
dataTable_clean = cleanUpTable(dataTable_clean);
|
||||
@@ -66,7 +75,7 @@ for int_len = [0,50,300,1000]
|
||||
mode = 4;
|
||||
|
||||
|
||||
for mode = [1,4]
|
||||
for mode = [1,2]
|
||||
|
||||
hold on;
|
||||
dataTable = dataTable_clean;
|
||||
@@ -124,7 +133,7 @@ for int_len = [0,50,300,1000]
|
||||
method = 'ideal dc tracking';
|
||||
end
|
||||
|
||||
dataTable(dataTable.eq_id==0,:) = [];
|
||||
% dataTable(dataTable.eq_id==0,:) = [];
|
||||
dataTable(dataTable.equalizer_structure~=1,:) = [];
|
||||
|
||||
% 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_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
|
||||
|
||||
hold on
|
||||
@@ -237,6 +250,8 @@ for int_len = [0,50,300,1000]
|
||||
% Hide patch (shaded area) from legend
|
||||
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
|
||||
|
||||
|
||||
|
||||
% Fit a 4th-order polynomial to log10(BER)
|
||||
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));
|
||||
|
||||
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)};
|
||||
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
|
||||
|
||||
|
||||
BIN
projects/ECOC_2025/precomp_1km.mat
Normal file
BIN
projects/ECOC_2025/precomp_1km.mat
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/precomp_1km_1mm_cable.mat
Normal file
BIN
projects/ECOC_2025/precomp_1km_1mm_cable.mat
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_100ghz_pd.mat
Normal file
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_100ghz_pd.mat
Normal file
Binary file not shown.
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd.mat
Normal file
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd.mat
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shot2.mat
Normal file
BIN
projects/ECOC_2025/precomp_1km_1mm_cable_70ghz_pd_shot2.mat
Normal file
Binary file not shown.
163
projects/ECOC_2025/sqlite_sequence.sql
Normal file
163
projects/ECOC_2025/sqlite_sequence.sql
Normal 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;
|
||||
@@ -19,7 +19,7 @@ if options.parallel
|
||||
% Check if a pool exists
|
||||
pool = gcp('nocreate');
|
||||
if isempty(pool)
|
||||
parpool;
|
||||
parpool(4);
|
||||
end
|
||||
|
||||
% Submit the DSP function asynchronously
|
||||
|
||||
@@ -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"
|
||||
|
||||
%% Parameters
|
||||
df = 1e6;%150e3; % Laser linewidth [Hz]
|
||||
df = 1e6; % Laser linewidth [Hz]
|
||||
SIR_dB = 20; % Interference attenuation [dB]
|
||||
alpha = 10^(-SIR_dB/20); % Interference attenuation [linear]
|
||||
n_fiber = 1.467; % Refractive index
|
||||
c = physconst('lightspeed'); % [m/s]
|
||||
|
||||
L = linspace(0,400,40); % Interference delay [m]
|
||||
L = linspace(0,250,50); % Interference delay [m]
|
||||
tau = n_fiber./c.*L; % Interference time (= tau) [s]
|
||||
|
||||
tau_c = 1/(pi*df); % laser coherence time [s]
|
||||
@@ -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)
|
||||
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));
|
||||
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);
|
||||
|
||||
%% 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
|
||||
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','-');
|
||||
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);
|
||||
xticklabels(round(coherence_length_multiples.*L_c));
|
||||
xticklabels(round(coherence_length_multiples.*L_c,1));
|
||||
|
||||
norm_to_coherence_len = 1;
|
||||
if norm_to_coherence_len
|
||||
xticklabels(coherence_length_multiples);
|
||||
xlabel('$\tau_c$', 'FontSize',12);
|
||||
xlabel('$n \cdot L_c$', 'FontSize',12);
|
||||
else
|
||||
xlabel('Interference Delay [m]', 'FontSize',12);
|
||||
end
|
||||
|
||||
xline(L_c.*coherence_length_multiples, 'LineWidth',1.5, 'DisplayName','Coh. Length','HandleVisibility','off','Color',[0.7,0.7,0.7],'LineStyle','-');
|
||||
xlim([0,L(end)]);
|
||||
yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$');
|
||||
xlabel('Interference Delay [m]', 'FontSize',12);
|
||||
grid on;
|
||||
ylabel('Intensity Variance', 'FontSize',12);
|
||||
title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14);
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
%% 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
|
||||
c = 3e8; % Speed of light [m/s]
|
||||
|
||||
% Compute coherence length (1/e of mean-fringe decay)
|
||||
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
|
||||
figure('Color','w');
|
||||
loglog(df/1e6, L_c, 'LineWidth',2,'LineStyle','-'); % linewidth in MHz
|
||||
xticks([0.1, 1, 10, 50]);
|
||||
yticks([1, 10, 100, 1000]);
|
||||
yticklabels({'1','10','100','1000'})
|
||||
% xticks([0.1, 1, 10, 50]);
|
||||
% yticks([1, 10, 100, 1000]);
|
||||
% yticklabels({'1','10','100','1000'})
|
||||
grid on; box on;
|
||||
xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','none');
|
||||
ylabel('Coherence length [m]','FontSize',12,'Interpreter','none');
|
||||
title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','none');
|
||||
xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','latex');
|
||||
ylabel('Coherence length [m]','FontSize',12,'Interpreter','latex');
|
||||
title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','latex');
|
||||
|
||||
%% Annotate some key points
|
||||
hold on;
|
||||
freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz]
|
||||
for f = freqs
|
||||
x = f/1e6;
|
||||
y = (c/n_fiber) * (1/(pi*f));
|
||||
scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
|
||||
|
||||
text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ...
|
||||
'FontSize',10,'HorizontalAlignment','left');
|
||||
|
||||
end
|
||||
% hold on;
|
||||
% freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz]
|
||||
% for f = freqs
|
||||
% x = f/1e6;
|
||||
% y = (c/n_fiber) * (1/(pi*f));
|
||||
% scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
|
||||
%
|
||||
% text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ...
|
||||
% 'FontSize',10,'HorizontalAlignment','left');
|
||||
%
|
||||
% end
|
||||
|
||||
@@ -20,7 +20,7 @@ fp.where('Runs', 'rop_attenuation','EQUALS', 0);
|
||||
|
||||
|
||||
fields = db.getTableFieldNames('power_state_info');
|
||||
fields = [fields; db.getTableFieldNames('dashboard_ungrouped')];
|
||||
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')];
|
||||
[dataTable,~] = db.queryDB(fp, fields);
|
||||
|
||||
eqstructures = unique(dataTable.equalizer_structure);
|
||||
|
||||
@@ -5,7 +5,7 @@ db = DBHandler("dataBase", [dataBase], "type", database_type);
|
||||
|
||||
fp = QueryFilter();
|
||||
% fp.where('Runs', 'run_id','EQUALS', 987);
|
||||
M = 8;
|
||||
M = 6;
|
||||
fp.where('Runs', 'pam_level','EQUALS', M);
|
||||
% fp.where('Runs', 'bitrate','LESS_THAN', 310e9);
|
||||
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', 'loop_id','GREATER_THAN', 11);
|
||||
% 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', 'rop_attenuation','EQUALS', 0);
|
||||
|
||||
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);
|
||||
|
||||
eqstructures = unique(dataTable.equalizer_structure);
|
||||
@@ -29,7 +29,7 @@ show_bitrate = true;
|
||||
figure(5);
|
||||
hold on
|
||||
|
||||
for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse]
|
||||
for eqs = [equalizer_structure.vnle]
|
||||
|
||||
% figure('Name',string([char(eqs),'']));
|
||||
% hold on
|
||||
@@ -148,13 +148,14 @@ for eqs = [equalizer_structure.vnle, equalizer_structure.vnle_pf_mlse]
|
||||
|
||||
xticks(xraw);
|
||||
if show_bitrate
|
||||
xticks(200:25:500);
|
||||
xlim([350 500]);
|
||||
% xticks(200:25:500);
|
||||
% xlim([350 500]);
|
||||
xlim([min(xraw), max(xraw)]);
|
||||
else
|
||||
xlim([min(xraw), max(xraw)]);
|
||||
end
|
||||
|
||||
ylim([5e-4, 0.3]);
|
||||
ylim([1e-4, 0.5]);
|
||||
|
||||
end
|
||||
yline([2.2e-4, 4.85e-3, 2e-2],'LineWidth',1,'LineStyle','--','HandleVisibility','off');
|
||||
|
||||
@@ -17,7 +17,7 @@ fp.where('Runs', 'wavelength','EQUALS', 1310);
|
||||
% fp.where('Runs', 'db_mode','EQUALS', 1);
|
||||
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
|
||||
|
||||
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard'));
|
||||
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('dashboard_ungrouped_after_nov_2025'));
|
||||
|
||||
eqstructures = unique(dataTable.equalizer_structure);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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' ...
|
||||
% });
|
||||
@@ -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];
|
||||
@@ -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}',...
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
% =======================================================================
|
||||
% (2–4) 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
|
||||
@@ -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
|
||||
@@ -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 ===
|
||||
@@ -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
Reference in New Issue
Block a user