BCJR implementation

WDM code added (Pol Cont., Opt MUX/DEMUX, Opt Atten, DP_Fiber) -> the codebase is not optimized to always work with dp signals!
This commit is contained in:
Silas Oettinghaus
2025-09-17 13:58:58 +02:00
parent f4a22d23a2
commit 4099f6820f
37 changed files with 3643 additions and 593 deletions

View File

@@ -6,10 +6,10 @@ Symbols = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_symbols.mat");S
eq_ = FFE_DCremoval("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",1e-5,"mu_tr",0,"order",50,"sps",2,"decide",0,"mu_dc",0.005,"dc_buffer_len",1);
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("type","mysql");
% savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
% databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
% database_name = 'ecoc2025_loops.db';
% db = DBHandler("type","mysql");
params = (logspace(-6,-2,20));
params = floor((logspace(2,3,20)));
@@ -19,16 +19,16 @@ for i = 1:length(params)
eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",0.005,"dc_buffer_len",224, ...
"mu_dc",0.005,"dc_buffer_len",1, ...
"ffe_buffer_len",1,...
"smoothing_buffer_length",0,...
"smoothing_buffer_update",224);
"smoothing_buffer_update",1);
result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
ber_ffe(i) = result.ber_vnle;
result = ffe(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
ber_ffe(i) = result.metrics;
fprintf(" FFE Results: %.2e\n", ber_ffe(i));
db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
% db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
% eq_ = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0003,"mu_tr",0,"order",50,"sps",2,"decide",1,"buffer_length",params(i));

View File

@@ -1,25 +1,24 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
% basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
% database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("type",'mysql');
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', [], ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', [], ...
'v_bias', [] ...
);
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;
@@ -28,9 +27,15 @@ filterParams.Configurations = struct( ...
% 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];
selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'...
'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.interference_path_length' 'Configurations.signal_attenuation' ...
[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'};

View File

@@ -0,0 +1,82 @@
% 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]
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]
tau = n_fiber./c.*L; % Interference time (= tau) [s]
tau_c = 1/(pi*df); % laser coherence time [s]
L_c = (c/n_fiber)*tau_c; % laser coherence length [m]
var_sat = 2*alpha^2; % Analytical saturation of variance
%% MonteCarlo Simulation
fs = 100e9; % sampling rate [Hz]
Tsim = 50e-6; % sim duration [s]
N = round(Tsim*fs); % number of samples for each realization
max_delay_samples = round(max(tau)*fs); % largest delay that is evaluated (based on max. Interference delay)
phase_noise_std = sqrt(2*pi*df/fs); % standard dev. phase noise
num_realizations = 10; % number of parallel runs
monte_carlo_variance = zeros(num_realizations, length(L));
parfor r = 1:num_realizations
% generate a realization of phase noise random walk
dphi = phase_noise_std * randn(1, N + max_delay_samples); % matlab randn process has std = 1
phi = cumsum(dphi);
phi_direct = phi(max_delay_samples+1 : max_delay_samples+N);
var_k = zeros(1, length(L));
for t = 1:length(tau)
nd = round( tau(t)*fs ); % delay in samples for current interference time
phi_delayed = phi(max_delay_samples+1-nd : max_delay_samples+N-nd); %cut out interfering signal part (was earlier)
E = exp(1j*phi_direct) + alpha*exp(1j*phi_delayed); % E-fields combined
I = abs(E).^2; % photo current as magnitude square of E-field
var_k(t) = var(I);
end
monte_carlo_variance(r, :) = var_k;
end
avg_of_mc_variances = mean(monte_carlo_variance, 1);
std_of_mc_variances = std(monte_carlo_variance, 0, 1);
%% Analytic variance
analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau)).^2;
%% Plot
cols = [0.3467 0.5360 0.6907
0.9153 0.2816 0.2878
0.4416 0.7490 0.4322];
coherence_length_multiples = 0.5:0.5:ceil(L(end)/L_c);
figure();
hold on;
plot(L, avg_of_mc_variances, 'LineWidth',2, 'DisplayName','Simulation','Color',cols(1,:),'LineStyle','-');
errorbar(L, avg_of_mc_variances,std_of_mc_variances, 'LineWidth',0.7,'LineStyle','none', 'DisplayName','Simulation','Color',cols(1,:),'HandleVisibility','off');
plot(L, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-');
xticks(coherence_length_multiples.*L_c);
xticklabels(round(coherence_length_multiples.*L_c));
norm_to_coherence_len = 1;
if norm_to_coherence_len
xticklabels(coherence_length_multiples);
xlabel('$\tau_c$', '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);
legend('Location','southeast');

View File

@@ -0,0 +1,32 @@
%% Parameters
df = linspace(100e3,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]
%% Plot
figure('Color','w');
loglog(df/1e6, L_c, 'LineWidth',2,'LineStyle','-'); % linewidth in MHz
xticks([0.1, 1, 10, 50]);
yticks([1, 10, 100, 1000]);
yticklabels({'1','10','100','1000'})
grid on; box on;
xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','none');
ylabel('Coherence length [m]','FontSize',12,'Interpreter','none');
title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','none');
%% Annotate some key points
hold on;
freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz]
for f = freqs
x = f/1e6;
y = (c/n_fiber) * (1/(pi*f));
scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ...
'FontSize',10,'HorizontalAlignment','left');
end

View File

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

View File

@@ -1,6 +1,6 @@
% === SETTINGS ===
dsp_options.append_to_db = 0;
dsp_options.max_occurences = 1;
dsp_options.append_to_db = 1;
dsp_options.max_occurences = 15;
experiment = "highspeed_2024";
dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files'
@@ -39,29 +39,23 @@ end
% === Get Run ID's ===
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
fp.where('Runs', 'pam_level','EQUALS', 4);
fp.where('Runs', 'bitrate','EQUALS', 360e9);
fp.where('Runs', 'fiber_length','EQUALS', 2);
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 6;
% fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','EQUALS', 480e9);
% fp.where('Runs', 'symbolrate','EQUALS', 162e9);
fp.where('Runs', 'fiber_length','EQUALS', 1);
fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', 0);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fp.where('Runs', 'wavelength','LESS_THAN', 1311);
% fp.where('Runs', 'db_mode','EQUALS', 0);
% fp.where('Runs', 'rop_attenuation','NOT_EQUAL', 0);
% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
% Keep only the rows corresponding to the first occurrence of each 'sir' value
% [~, unique_indices] = unique(dataTable.sir, 'first');
% dataTable = dataTable(unique_indices, :);
% dataTable = dataTable(1,:);
% === Set LOOPS & Initialize DataStorage ===
dsp_options.parameters = struct();
% dsp_options.parameters.pf_ncoeffs = [1,2];%[0,logspace(-4,0,10)];
@@ -75,150 +69,281 @@ wh.addStorage("dbenc_package");
% === RUN IT ===
[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true);
[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true);
results_db = results(dataTable.db_mode==1);
results_nodb = results(dataTable.db_mode==0);
for i = 1:numel(results_db)
% VNLE (from results_nodb)
gmi_v = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.vnle_package);
ber_v = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.vnle_package);
air_v = cellfun(@(c) c.metrics.AIR, results_nodb{1,i}.vnle_package);
snr_v = cellfun(@(c) c.metrics.SNR, results_nodb{1,i}.vnle_package);
[BER_VNLE(i), idx_ber] = min(ber_v);
GMI_VNLE(i) = gmi_v(idx_ber);
AIR_VNLE(i) = air_v(idx_ber);
SNR_VNLE(i) = max(snr_v);
idx_gmi_min_vnle(i) = find(gmi_v == min(gmi_v), 1);
idx_air_max_vnle(i) = find(air_v == max(air_v), 1);
% MLSE (from results_db)
gmi_m = cellfun(@(c) c.metrics.GMI, results_nodb{1,i}.mlse_package);
ber_m = cellfun(@(c) c.metrics.BER, results_nodb{1,i}.mlse_package);
air_m = cellfun(@(c) c.metrics.AIR, results_nodb{1,i}.mlse_package);
[BER_MLSE(i), idx_ber] = min(ber_m);
GMI_MLSE(i) = gmi_m(idx_ber);
AIR_MLSE(i) = air_m(idx_ber);
idx_gmi_min_mlse(i) = find(gmi_m == min(gmi_m), 1);
idx_air_max_mlse(i) = find(air_m == max(air_m), 1);
% DB (from results_db, BER_precoded)
gmi_db = cellfun(@(c) c.metrics.GMI, results_db{1,i}.dbtgt_package);
ber_db = cellfun(@(c) c.metrics.BER, results_db{1,i}.dbtgt_package);
ber_db_prec = cellfun(@(c) c.metrics.BER_precoded, results_db{1,i}.dbtgt_package);
air_db = cellfun(@(c) c.metrics.AIR, results_db{1,i}.dbtgt_package);
[BER_DB(i), idx_ber] = min(ber_db);
[BER_DB_PREC(i), idx_ber] = min(ber_db_prec);
GMI_DB(i) = gmi_db(idx_ber);
AIR_DB(i) = air_db(idx_ber);
idx_gmi_min_db(i) = find(gmi_db == min(gmi_db), 1);
idx_air_max_db(i) = find(air_db == max(air_db), 1);
% metadata
bitrate(i) = dataTable.bitrate(i);
baudrate(i) = dataTable.symbolrate(i);
end
STYLE_BASE = 2; % adjust this single number to scale markers & lines
MARKER_SIZE = STYLE_BASE; % marker size (MATLAB MarkerSize)
LINE_WIDTH = max(2, STYLE_BASE/3); % line width (keeps lines reasonable when STYLE_BASE large)
% --- color map / method -> color assignment (keeps colors consistent) ---
cols = cbrewer2('Paired',8);
cols = linspecer(6);
d = 0;
cm.VNLE = cols(1 + d, :);
cm.MLSE = cols(2 + d, :);
cm.DB_precode = cols(3 + d, :);
cm.DB = cols(4 + d, :); % duobinary
% prepare x values in GBd
xGHz = baudrate .* 1e-9;
xticks_vals = xGHz;
xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false);
% common marker settings (filled, same face+edge color)
mk.VNLE = {'Marker','o','MarkerFaceColor',cm.VNLE,'MarkerEdgeColor',cm.VNLE,'MarkerSize',MARKER_SIZE};
mk.MLSE = {'Marker','*','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.MLSE,'MarkerSize',MARKER_SIZE};
mk.DB_precode = {'Marker','^','MarkerFaceColor',cm.DB_precode,'MarkerEdgeColor',cm.DB_precode,'MarkerSize',MARKER_SIZE};
mk.DB = {'Marker','d','MarkerFaceColor',cm.DB,'MarkerEdgeColor',cm.DB,'MarkerSize',MARKER_SIZE};
% wh.getStoValue('ffe_package',0.005);
% wh.getStoValue('mlse_package',0.005);
% ---------------- FIGURE : BER ----------------
figure(112+M); clf; hold on;
plot(xGHz, BER_VNLE, ...
'DisplayName','VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, BER_MLSE, ...
'DisplayName','MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, BER_DB, ...
'DisplayName','DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
plot(xGHz, BER_DB_PREC, ...
'DisplayName','Diff. Precode + DB tgt.', ...
mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB);
yline(4.85e-3,'LineWidth',1,'HandleVisibility','off');
yline(2.2e-4,'LineWidth',1,'HandleVisibility','off');
xlabel('Baudrate in GBd');
ylabel('BER');
set(gca, 'yscale', 'log');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% [dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]);
% ---------------- FIGURE 15 : GMI ----------------
figure(113+M); clf; hold on;
plot(xGHz, GMI_VNLE, ...
'DisplayName','VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, GMI_MLSE, ...
'DisplayName','MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, GMI_DB, ...
'DisplayName','DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('GMI');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% ---------------- FIGURE 15 : AIR ----------------
m = floor(log2(M)*10)/10;
figure(114+M); clf; hold on;
plot(xGHz, GMI_VNLE.*xGHz, ...
'DisplayName','AIR VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
% duobinary has only one GMI curve (DB output)
plot(xGHz, GMI_MLSE.*xGHz, ...
'DisplayName','AIR MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
% MLSE symbol-wise (if present)
plot(xGHz, GMI_DB.*xGHz, ...
'DisplayName','AIR DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
% ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('AIR in Gbps');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% ---------------- FIGURE 15 : Information Rates ----------------
tp = TransmissionPerformance;
m = floor(log2(M)*10)/10;
figure(213+M); clf; hold on;
netrates_vnle = tp.calculateNetRate(baudrate.* m, ...
'NGMI', GMI_VNLE./m, ...
'BER', BER_VNLE);
%
% dataTable = cleanUpTable(dataTable);
% wh_analyze = wh_adap;
% % wh_analyze = wh_dcremoval_old2;
% % wh_analyze = wh_dcremoval_old2;
% plot(xGHz, GMI_VNLE.*xGHz, ...
% 'DisplayName','GMI*R VNLE', ...
% mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
%
% res = cell(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length);
% ber_mean = zeros(wh_analyze.parameter.mu_dc.length,wh_analyze.parameter.dc_buffer_len.length);
% for m = 1:wh_analyze.parameter.mu_dc.length
% for b = 1:wh_analyze.parameter.dc_buffer_len.length
% res{m,b}=wh_analyze.getStoValue("ffe_package",wh_analyze.parameter.mu_dc.values(m),wh_analyze.parameter.dc_buffer_len.values(b));
% try
% cells = res{m,b}{1};
% idx = cellfun(@(c) ~isempty(c), cells);
% ber = cellfun(@(c) c.metrics.BER, cells(idx));
% ber_mean(m,b) = mean(ber);
% catch
% ber_mean(m,b) = NaN;
% end
% end
% end
%
% figure()
% hold on
% ber_fix = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_fix);
% ber_adap_m2 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results_adap);
% ber_adap_method1 = cellfun(@(c) c.ffe_package{1}.metrics.BER, results);
% plot(dataTable.sir,ber_fix,'LineWidth',1,'DisplayName',sprintf('DCt; fix mu = 0.5; p=1024'),'Marker','.','MarkerSize',10);
% plot(dataTable.sir,ber_adap_method1,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 1; p=1024'),'Marker','.','MarkerSize',10);
% plot(dataTable.sir,ber_adap_m2,'LineWidth',1,'DisplayName',sprintf('DCt; adap mu 2; p=1024'),'Marker','.','MarkerSize',10);
% xlabel('BER');
% xlabel('SIR');
% yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
% % ylim([9e-4, 0.5]);
% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
% legend('show', 'Location', 'best');
% grid on;
% plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ...
% 'DisplayName','SD+HD VNLE', ...
% mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
% plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ...
% 'DisplayName','Staircase VNLE', ...
% mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
%
%
% %
% % MLSE symbol-wise (if present)
% plot(xGHz, GMI_MLSE.*xGHz, ...
% 'DisplayName','GMI*R MLSE', ...
% mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
%
%
% figure; clf
%
% % Create meshgrid for contourf
% [X, Y] = meshgrid(dsp_options.parameters.mu_dc, dsp_options.parameters.dc_buffer_len);
%
% % Create contour plot
% contourf(X, Y, ber_mean', 20); % 20 contour levels, adjust as needed
%
% % Set axes to logarithmic scale
% set(gca, 'XScale', 'log', 'YScale', 'log');
%
% colormap("parula");
% c = colorbar;
% c.Label.String = 'BER';
%
% xlabel('\mu_{dc}');
% ylabel('dc\_buffer\_len');
% title('BER Optimization over \mu_{dc} and dc\_buffer\_len');
%
% % Make plot prettier
% grid on
% set(gca, 'Layer', 'top'); % Put grid lines on top of contours
%
%
% % === Look at it ===
% y_var = 'BER_precoded';
% x_var = 'bitrate';
% fixedVars = {'equalizer_structure', x_var};
%
% [dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
%
% % --- Group and aggregate ---
% dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean);
% dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min);
% dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max);
%
% % Choose a color map
% cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure)));
%
% figure;
% hold on;
%
% % Get unique equalizer structures for grouping
% unique_eq = unique(dataTableGrpd_mean.equalizer_structure);
%
% for i = 1:numel(unique_eq)
% eq_val = unique_eq(i);
%
% % Filter grouped data for this equalizer structure
% filt = dataTableGrpd_mean.equalizer_structure == eq_val;
%
% x = dataTableGrpd_mean.(x_var)(filt);
% y_mean = dataTableGrpd_mean.(y_var)(filt);
% y_min = dataTableGrpd_min.(y_var)(filt);
% y_max = dataTableGrpd_max.(y_var)(filt);
%
% % Bounds for boundedline (distance from mean)
% y_lower = y_mean - y_min;
% y_upper = y_max - y_mean;
% y_bounds = [y_lower, y_upper];
%
% % --- Bounded line (mean ± min/max) ---
% if exist('boundedline', 'file')
% [hl, hp] = boundedline(x, y_mean, y_bounds, ...
% 'alpha', 'transparency', 0.1, ...
% 'cmap', cols(i,:), ...
% 'nan', 'fill', ...
% 'orientation', 'vert');
% set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val));
% set(hp, 'HandleVisibility', 'off');
% else
% % If boundedline is not available, use errorbar
% errorbar(x, y_mean, y_lower, y_upper, ...
% 'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ...
% 'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off');
% end
%
% % --- Normal line (mean only) ---
% plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ...
% 'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off');
%
% % --- Scatter plot for individual points (from original data) ---
% % Filter original data for this group
% orig_filt = dataTableClean.equalizer_structure == eq_val;
% x_scatter = dataTableClean.(x_var)(orig_filt);
% y_scatter = dataTableClean.(y_var)(orig_filt);
%
% scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ...
% 'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off');
% end
%
% yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
% set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
% xlabel(x_var, 'Interpreter', 'none');
% ylabel(y_var, 'Interpreter', 'none');
% legend('show', 'Location', 'best');
% grid on;
% title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none');
% hold off;
% netrates_mlse = tp.calculateNetRate(baudrate.* m, ...
% 'NGMI', GMI_MLSE./m, ...
% 'BER', BER_MLSE);
% plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ...
% 'DisplayName','SD+HD MLSE', ...
% mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
% plot(xGHz, netrates_mlse.HD.NetRate.*1e-9, ...
% 'DisplayName','Staircase MLSE', ...
% mk.MLSE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
% duobinary has only one GMI curve (DB output)
figure(1111); clf; hold on;
plot(xGHz, GMI_DB.*xGHz, ...
'DisplayName','GMI*R DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
netrates_db = tp.calculateNetRate(baudrate.* m, ...
'NGMI', GMI_DB./m, ...
'BER', BER_DB_PREC);
plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD DB', ...
mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB);
plot(xGHz, netrates_db.STAIR.NetRate.*1e-9, ...
'DisplayName','Staircase DB', ...
mk.DB_precode{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.DB_precode);
plot(xGHz, netrates_db.O_FEC.NetRate.*1e-9, ...
'DisplayName','O-FEC DB', ...
mk.DB_precode{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.DB_precode);
plot(xGHz, netrates_db.KP4_hamming.NetRate.*1e-9, ...
'DisplayName','KP4 Hamming DB', ...
mk.DB_precode{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB_precode);
% ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('AIR in Gbps');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% xlim([1, 256])
figure(2222); clf; hold on;
plot(xGHz, GMI_MLSE.*xGHz, ...
'DisplayName','GMI*R DB tgt.', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
netrates_mlse = tp.calculateNetRate(baudrate.* m, ...
'NGMI', GMI_MLSE./m, ...
'BER', BER_MLSE);
plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD DB', ...
mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, netrates_mlse.STAIR.NetRate.*1e-9, ...
'DisplayName','Staircase DB', ...
mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_mlse.O_FEC.NetRate.*1e-9, ...
'DisplayName','O-FEC DB', ...
mk.VNLE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_mlse.KP4_hamming.NetRate.*1e-9, ...
'DisplayName','KP4 Hamming DB', ...
mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
% ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('AIR in Gbps');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% xlim([1, 256])
figure(3333); clf; hold on;
plot(xGHz, GMI_VNLE.*xGHz, ...
'DisplayName','GMI*R DB tgt.', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
netrates_vnle = tp.calculateNetRate(baudrate.* m, ...
'NGMI', GMI_VNLE./m, ...
'BER', BER_VNLE);
plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD DB', ...
mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, netrates_vnle.STAIR.NetRate.*1e-9, ...
'DisplayName','Staircase DB', ...
mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_vnle.O_FEC.NetRate.*1e-9, ...
'DisplayName','O-FEC DB', ...
mk.VNLE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_vnle.KP4_hamming.NetRate.*1e-9, ...
'DisplayName','KP4 Hamming DB', ...
mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
% ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('AIR in Gbps');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% xlim([1, 256])

View File

@@ -1,7 +1,8 @@
%%% Run parameters
% TX
M = 6;
fsym = 112e9;
m = floor(log2(M)*10)/10;
fsym = 224e9;
apply_pulsef = 1;
fdac = 256e9;
@@ -9,20 +10,20 @@ fadc = 256e9;
random_key = 2;
rcalpha = 0.05;
kover = 16;
kover = 8;
vbias_rel = 0.5;
u_pi = 2.9;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1290;
laser_linewidth = 0;
laser_wavelength = 1310;
laser_linewidth = 1e6;
% Channel
link_length = 10000;
link_length = 0;
vnle_order1 = 50;
vnle_order2 = 3;
vnle_order3 = 3;
vnle_order2 = 0;
vnle_order3 = 0;
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dfe_order = [0 0 0];
@@ -43,20 +44,22 @@ mu_dfe = 0.0004;
dfe_ = sum(dfe_order)>0;
doub_mode = db_mode.no_db;
rop = [-5];
cols = linspecer(6);
rop = [-6];
bwl = [0.5:0.1:1.5];
fsym = [72:8:170].*1e9;
ber_vnle = [];
fsym = [120:8:256].*1e9;
fsym =150e9;
ber_vnle = [];
ber_mlse = [];
ber_viterbi = [];
ber_db = [];
ber_db_diff_precoded = [];
gmi_vnle = [];
gmi_vnle_bitwise = [];
gmi_mlse = [];
gmi_mlse_db = [];
parfor r = 1:length(fsym)
for r = 1:length(fsym)
Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha);
@@ -74,29 +77,94 @@ parfor r = 1:length(fsym)
"db_precode",db_precode,"db_encode",db_encode,...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process();
El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig);
% El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig);
El_sig = M8199B("kover",kover).process(Digi_sig);
% AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig);
%%%%% Low-pass el. components %%%%%%
tx_bwl = 50e9;
El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
% tx_bwl = 100e9;
% El_sig = Filter('filtdegree',3,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = El_sig.normalize("mode","oneone");
% El_sig = El_sig.setPower(1,"dBm");
% figure;histogram(El_sig.signal);
%%%%% MODULATE E/O CONVERSION %%%%%%
%%%%% MODULATE E/O CONVERSION %%%%%
u_pi = 3.2;
vbias = -u_pi*0.5;
[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig);
figure(15);
hold on
scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF')
xlabel('Input in V')
ylabel('abs(Eopt)2 in mW','Interpreter','latex')
ylim([0 2]);
xlim([-3.2 0]);
Opt_sig.eye(fsym(r),M,"fignum",103837);
%%%%%% Fiber %%%%%%
Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig);
%%%%%% ROP %%%%%%
Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig);
% Opt_sig.eye(fsym(r),M,"fignum",103838);
% % Opt_sig.signal = Opt_sig.signal + 5*abs(mean(Opt_sig.signal));
% Opt_sig.move_it_spectrum("displayname",'Opt Sig after Amp','fignum',1223323);
% Pc = abs(mean(Opt_sig.signal)).^2; % carrier power
% Ptot = mean(abs(Opt_sig.signal).^2); % total power
% Ps = max(Ptot - Pc, eps);
% Pcdb = 10*log10(Pc);
% Psdb = 10*log10(Ps);
%
% cspr_dB = 10*log10(Pc / Ps);
%
% % Minimal in-place CSPR set (real, nonnegative field constraint)
% E = Opt_sig.signal; % real field samples
% target_cspr_dB = 20; % <-- set your target CSPR (dB)
%
% % Decompose into DC + zero-mean waveform
% m = mean(E);
% x0 = E - m; % zero-mean modulation
% Ps0 = mean(x0.^2); % sideband power (fixed if shape kept)
%
% % Current CSPR (for reference)
% Pc_cur = m^2;
% Ptot_cur = mean(E.^2);
% Ps_cur = max(Ptot_cur - Pc_cur, eps);
% cspr_in = 10*log10(Pc_cur / Ps_cur);
%
% % Bias needed for target CSPR, and minimal bias to keep E>=0
% R_tgt = 10^(target_cspr_dB/10); % Pc/Ps
% a_req = sqrt(R_tgt * Ps0); % required DC bias
% a_min = -min(x0); % to avoid negatives everywhere
% a = max(a_req, a_min); % if infeasible, lands at CSPR_min
%
% % Apply bias (preserves waveform shape)
% E_new = a + x0;
%
% % Achieved CSPR
% Pc_new = mean(E_new)^2;
% Ptot_new = mean(E_new.^2);
% Ps_new = max(Ptot_new - Pc_new, eps);
% cspr_out = 10*log10(Pc_new / Ps_new);
%
% % (Optional) show feasibility info
% cspr_min = 10*log10((a_min^2)/max(Ps0,eps));
% disp(table(cspr_in, target_cspr_dB, cspr_min, cspr_out));
%
% % Use E_new as your adjusted field
% Opt_sig.signal = E_new;
%%%%%% PD Square Law %%%%%%
PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig);
%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%%
rx_bwl = 50e9;
rx_bwl = 70e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig);
% %%%%%% Low-pass Scope %%%%%%
@@ -113,21 +181,29 @@ parfor r = 1:length(fsym)
[~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0);
Rx_sig = Scpe_cell{1};
Rx_sig = Rx_sig.normalize("mode","rms");
if 1
if 0
%Duobinary Targeting
eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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);
mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels);
db_ref_sequence = Duobinary().encode(Symbols);
db_ref_constellation = unique(db_ref_sequence.signal);
[eq_signal, eq_noise] = eq_.process(Rx_sig,db_ref_sequence);
mlse_.DIR = [1,1];
[mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols);
viterbi = 0;
if viterbi
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_.DIR = [1,1];
[mlse_sig_sd] = mlse_.process(eq_signal);
else
mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling);
mlse_.DIR = [1,1];
[mlse_sig_sd,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols);
end
mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(mlse_sig_sd);
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M);
@@ -138,48 +214,84 @@ parfor r = 1:length(fsym)
tx_bits_precoded = PAMmapper(M,0,"eth_style",0).demap(tx_symbols_precoded);
rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_precoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded(r),~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[~,errors_db_diff_precoded,ber_db_diff_precoded(r),a] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db_pre(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal);
%B) Just determine BER
rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd);
[bits_mlse,errors_mlse,ber_db(r),~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[bits_mlse,errors_db,ber_db(r),a] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal);
fprintf('BER ber_db_diff_precoded: %.2e \n',ber_db_diff_precoded(r));
fprintf('BER Vber_dbNLE: %.2e \n',ber_db(r));
% figure();hold on;stem(1:15,burst_db(r,:),'LineWidth',1,'Color',cols(1,:));stem(1:15,burst_db_pre(r,:),'LineWidth',1,'Color',cols(2,:));set(gca, 'yscale', 'log');
end
% FFE or VNLE
eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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",[vnle_order1,vnle_order2,vnle_order3],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.00,"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,"order",[50,2,2],"sps",2,"decide",0);
[eq_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols);
[gmi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100);
[gmi_vnle(r)] = calc_ngmi(eq_signal_sd,Symbols);
[gmi_bitwise(r)] = calc_gmi_bitwise(eq_signal_sd,Symbols);
snr_vnle(r) = calc_snr(Symbols, eq_signal_sd-Symbols);
eq_signal_sd.plot("displayname",'bla','fignum',118);
showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ');
[mi_gomez(r)] = calc_air(eq_signal_sd, Symbols, "skip_front", 100, "skip_end", 100);
[gmi_vnle_bitwise(r)] = calc_ngmi(eq_signal_sd,Symbols);
[gmi_bitwise_2(r)] = calc_gmi_bitwise(eq_signal_sd,Symbols);
snr_vnle(r) = calc_snr(Symbols, eq_signal_sd-Symbols);
eq_signal_sd.plot("displayname",'bla','fignum',199);
eq_signal_sd.eye(fsym(r),M,"fignum",103837);
% Hard decision on VNLE output
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd);
[~,~,ber_vnle(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[~,tot_err,ber_vnle(r),a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_vnle(r,:) = count_error_bursts(a, 10)./tot_err;
showLevelConfusionMatrix(eq_signal_hd,Symbols,"M",M,"fignum",200,"displayname",'bla');
showLevelScatter(eq_signal_sd,Symbols,"displayname",'VNLE Out','f_sym',fsym(r),'fignum',201);
show2Dconstellation(eq_signal_sd,Symbols,"displayname",'VNLE Out','fignum',2241);
fprintf('BER VNLE: %.2e \n',ber_vnle(r));
fprintf('NGMI VNLE: %.2f \n',gmi_vnle(r)./log2(M));
fprintf('NGMI VNLE: %.2f \n',gmi_vnle_bitwise(r)./m);
if 1
% Process through postfilter and MLSE
pf_ncoeffs = 1;
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
if fsym(r) < 200e9
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.1]);
else
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.85]);
end
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling);
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
mlse_.DIR = pf_.coefficients;
alpha(r) = pf_.coefficients(2);
[signalclass_hd,LLR,gmi_mlse(r)] = mlse_.process(mlse_sig_sd,Symbols);
mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd);
rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd);
[~,~,ber_mlse(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('BER: %.2e \n',ber_mlse(r));
fprintf('GMI MLSE: %.5f \n',gmi_mlse(r));
[~,tot_err,ber_mlse(r),a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_mlse(r,:) = count_error_bursts(a, 10);
showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla');
fprintf('BER MLSE: %.2e \n',ber_mlse(r));
fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m);
levels = sort(unique(Symbols.signal(:)).'); % 1×6
pairs = reshape(mlse_sig_hd.signal,2,[]).';
isedge = ismember(pairs, [levels(1) levels(end)]);
isforbidden = sum(isedge,2)==2;
fprintf('Found %d forbidden transitions (evenodd edges).\n', nnz(isforbidden));
% Process through postfilter and MLSE
pf_ncoeffs = 1;
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
@@ -198,36 +310,166 @@ parfor r = 1:length(fsym)
end
cols = cbrewer2('Paired',8);
d = 1;
figure(11);hold on
plot(fsym.*1e-9,alpha,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:));
% --- style control (one variable controls both marker size and linewidth) ---
STYLE_BASE = 2; % adjust this single number to scale markers & lines
MARKER_SIZE = STYLE_BASE; % marker size (MATLAB MarkerSize)
LINE_WIDTH = max(1.5, STYLE_BASE/3); % line width (keeps lines reasonable when STYLE_BASE large)
% --- color map / method -> color assignment (keeps colors consistent) ---
cols = cbrewer2('Paired',8);
cols = linspecer(6);
d = 0;
cm.VNLE = cols(1 + d, :);
cm.MLSE = cols(2 + d, :);
cm.DB_precode = cols(3 + d, :);
cm.DB = cols(4 + d, :); % duobinary
% prepare x values in GBd
xGHz = fsym .* 1e-9;
xticks_vals = xGHz;
xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false);
% common marker settings (filled, same face+edge color)
mk.VNLE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.VNLE,'MarkerSize',MARKER_SIZE};
mk.MLSE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.MLSE,'MarkerSize',MARKER_SIZE};
mk.DB_precode = {'Marker','none','MarkerFaceColor',cm.DB_precode,'MarkerEdgeColor',cm.DB_precode,'MarkerSize',MARKER_SIZE};
mk.DB = {'Marker','none','MarkerFaceColor',cm.DB,'MarkerEdgeColor',cm.DB,'MarkerSize',MARKER_SIZE};
% ---------------- FIGURE 11 : alpha (VNLE) ----------------
figure(110+M); clf; hold on;
plot(xGHz, alpha, ...
'DisplayName','VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
xlabel('Baudrate in GBd');
ylabel('alpha');
set(gca, 'XTick', xticks_vals, 'XTickLabel', xtick_labels);
grid on;
legend('Location','best');
figure(15);hold on
plot(fsym.*1e-9,gmi_gomez,'DisplayName','gomez','Marker','x','LineStyle','-','Color',cols(1+d,:));
plot(fsym.*1e-9,gmi_vnle,'DisplayName','vnle other','Marker','x','LineStyle','-','Color',cols(3+d,:));
% plot(fsym.*1e-9,gmi_bitwise,'DisplayName','bitwise','Marker','x','LineStyle','--','Color',cols(5+d,:));
plot(fsym.*1e-9,gmi_mlse,'DisplayName','MLSE','Marker','*');
plot(fsym.*1e-9,gmi_mlse_db,'DisplayName','DB Output','Marker','*');
ylim([log2(M)-1 log2(M)]);
% ---------------- FIGURE 15 : GMI ----------------
figure(111+M); clf; hold on;
plot(xGHz, mi_gomez, ...
'DisplayName','MI VNLE', ...
mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, gmi_vnle_bitwise, ...
'DisplayName','GMI VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
% duobinary has only one GMI curve (DB output)
plot(xGHz, gmi_mlse_db, ...
'DisplayName','GMI DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
% MLSE symbol-wise (if present)
plot(xGHz, gmi_mlse, ...
'DisplayName','GMI MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('GMI');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% xlim([184, 256])
% ---------------- FIGURE 13 : BER ----------------
figure(312+M); hold on;
plot(xGHz, ber_vnle, ...
'DisplayName','VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, ber_mlse, ...
'DisplayName','MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, ber_viterbi, ...
'DisplayName','Viterbi', ...
mk.MLSE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
yline(4.85e-3,'LineWidth',1,'HandleVisibility','off');
yline(2.2e-4,'LineWidth',1,'HandleVisibility','off');
plot(xGHz, ber_db, ...
'DisplayName','DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
plot(xGHz, ber_db_diff_precoded, ...
'DisplayName','Prec. + DB tgt.', ...
mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB);
figure(13);hold on
plot(fsym.*1e-9,ber_vnle,'DisplayName','VNLE','Marker','x','LineStyle','-','Color',cols(1+d,:));
plot(fsym.*1e-9,ber_mlse,'DisplayName','MLSE','Marker','x','LineStyle','-','Color',cols(3+d,:));
plot(fsym.*1e-9,ber_viterbi,'DisplayName','Viterbi','Marker','x','LineStyle','--','Color',cols(5+d,:));
plot(fsym.*1e-9,ber_db_diff_precoded,'DisplayName','MLSE db diff','Marker','.','MarkerSize',15,'LineStyle','-');
plot(fsym.*1e-9,ber_db,'DisplayName','MLSE db','Marker','.','MarkerSize',15,'LineStyle','-');
xlabel('Baudrate in GBd');
ylabel('BER');
set(gca, 'yscale', 'log');
% ylim([1e-6 0.1]);
legend
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
% xlim([184, 256])
% ---------------- FIGURE 15 : Information Rates ----------------
tp = TransmissionPerformance;
m = floor(log2(M)*10)/10;
figure(113+M); clf; hold on;
netrates_vnle = tp.calculateNetRate(fsym.* m, ...
'NGMI', gmi_vnle_bitwise./m, ...
'BER', ber_vnle);
%
plot(xGHz, gmi_vnle_bitwise.*xGHz, ...
'DisplayName','GMI*R VNLE', ...
mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD VNLE', ...
mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ...
'DisplayName','Staircase VNLE', ...
mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
%
% MLSE symbol-wise (if present)
plot(xGHz, gmi_mlse.*xGHz, ...
'DisplayName','GMI*R MLSE', ...
mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
netrates_mlse = tp.calculateNetRate(fsym.* m, ...
'NGMI', gmi_mlse./m, ...
'BER', ber_mlse);
plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD MLSE', ...
mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, netrates_mlse.HD.NetRate.*1e-9, ...
'DisplayName','Staircase MLSE', ...
mk.MLSE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
% duobinary has only one GMI curve (DB output)
plot(xGHz, gmi_mlse_db.*xGHz, ...
'DisplayName','GMI*R DB tgt.', ...
mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB);
netrates_db = tp.calculateNetRate(fsym.* m, ...
'NGMI', gmi_mlse_db./m, ...
'BER', ber_db);
plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ...
'DisplayName','SD+HD DB', ...
mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB);
plot(xGHz, netrates_db.HD.NetRate.*1e-9, ...
'DisplayName','Staircase DB', ...
mk.DB{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.DB);
% ylim([log2(M)-1, log2(M)]);
xlabel('Baudrate in GBd');
ylabel('AIR in Gbps');
set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end));
grid on;
legend('Location','best');
xlim([184, 256])
% Auxiliary nested helper for numerically stable log-sum-exp
function s = logsumexp(a)

268
projects/WDM/WDM_model.m Normal file
View File

@@ -0,0 +1,268 @@
%%% Run parameters
% TX
M = 4;
m = floor(log2(M)*10)/10;
fsym = 224e9;
apply_pulsef = 1;
fdac = 2*fsym;
fadc = 2*fsym;
random_key = 2;
rcalpha = 0.05;
kover = 8;
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_linewidth = 0e6;
% Channel
link_length = 2;
rop = -5;
vnle_order1 = 50;
vnle_order2 = 0;
vnle_order3 = 0;
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dfe_order = [0 0 0];
alpha = 0;
len_tr = 4096*2;
mu_ffe1 = 0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dc = 0.005;
% mu_dc = 0;
mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
mu_dfe = 0.0004;
dfe_ = sum(dfe_order)>0;
doub_mode = db_mode.no_db;
cols = linspecer(6);
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 0;
% AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig);
%%%%% Low-pass el. components %%%%%%
% tx_bwl = 100e9;
% El_sig = Filter('filtdegree',3,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
wavelengthplan = calcWavelengthPlan(16,400e9,1310);
wavelengthplan = [1295,1305,1315,1325];
N = numel(wavelengthplan);
f_plan = physconst('lightspeed')./(wavelengthplan.*1e-9);
margin = 25e12; % some THz left and right
f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
f_nyq = f_span/2;
upsample_required = f_nyq./(fdac*kover/2);
upsample_pow = 2^nextpow2(upsample_required);
upsample_ceil = ceil(upsample_required);
f_opt = fdac*kover*upsample_pow;
f_opt_nyq = f_opt/2;
signal_cell = {};
Symbols = {};
Tx_bits = {};
rop = linspace(-11,0,6); %12 workers when parallel
num_realiz = 10;
gmi_vnle_bitwise = NaN(length(wavelengthplan),length(rop),num_realiz);
snr_vnle= NaN(length(wavelengthplan),length(rop),num_realiz);
ber_vnle= NaN(length(wavelengthplan),length(rop),num_realiz);
for realiz = 1:num_realiz
for l = 1:N
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(...
"fsym",fsym,"M",M,"order",18,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",random_key+l+realiz,...
"db_precode",db_precode,"db_encode",db_encode,...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process();
% Digi_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0);
Lp_awg = Filter('filtdegree',3,"f_cutoff",100e9,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0,"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
% El_sig = M8199B("kover",kover).process(Digi_sig);
% El_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = El_sig.normalize("mode","oneone");
% El_sig = El_sig.setPower(1,"dBm");
% figure;histogram(El_sig.signal);
%%%%% MODULATE E/O CONVERSION %%%%%
Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",wavelengthplan(l),"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+l+realiz).process(El_sig);
signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",30).process(Eml_out);
end
Opt_sig_wdm = Optical_Multiplex("fs_in",signal_cell{l}.fs,"fs_out",upsample_pow*Eml_out.fs,...
"lambda_center",1310,"random_key",0,"filtype",1,"B",200e9).process(signal_cell);
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",3+10*log10(N)).process(Opt_sig_wdm);
% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0);
% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2);
%%%%%% Fiber %%%%%%
Opt_sig_wdm_fib=Opt_sig_wdm;
nSegments = 2;
zdw = 1310;
D_local = 0; %if ~=0, simulation uses "segmented fiber with d+,d-)
randomize_D = true;
Dvec = getDispersionVector(nSegments, D_local, zdw, randomize_D, random_key+realiz);
for s = 1:nSegments
Opt_sig_wdm_fib = DP_Fiber("L",link_length/nSegments,"D",Dvec(s),"Dpmd",0.1,"Ds",0.06,...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0,...
"gamma",0.0023,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib);
end
Opt_sig_wdm_fib.spectrum("fignum",realiz,"displayname",'bla','lambda0_nm',1310,'useWavelengthAxis',0);
% Opt_sig_wdm_fib.move_it_spectrum("fignum",100212,"displayname",'bla');
% Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig)
parfor ri = 1:length(rop)
%%%%%% ROP %%%%%%
Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop(ri)+10*log10(N)).process(Opt_sig_wdm_fib);
Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/upsample_pow,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx);
PD_cell = {};
for l = 1:N
%%%%%% PD Square Law %%%%%%
assert(fdac*kover==Opt_sig_wdm_demux{l}.fs,'Sampling Frequencies do not match! Check previous steps');
PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key+l+realiz).process(Opt_sig_wdm_demux{l});
PD_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1);
%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%%
rx_bwl = 100e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig);
% %%%%%% Low-pass Scope %%%%%%
Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
%%%%%% Scope %%%%%%
Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc,...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth,...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,...
"adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(PD_sig);
Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
% Scpe_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1);
[~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 1);
Rx_sig = Scpe_cell{1};
Rx_sig = Rx_sig.normalize("mode","rms");
% FFE or VNLE
eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.00,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
[eq_signal_sd, eq_noise] = eq_.process(Rx_sig, Symbols{l});
showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ');
[mi_gomez] = calc_air(eq_signal_sd, Symbols{l}, "skip_front", 100, "skip_end", 100);
[gmi_vnle_bitwise(l,ri,realiz)] = calc_ngmi(eq_signal_sd,Symbols{l});
% [gmi_bitwise_2] = calc_gmi_bitwise(eq_signal_sd,Symbols{l});
snr_vnle(l,ri,realiz) = calc_snr(Symbols{l}, eq_signal_sd-Symbols{l});
% eq_signal_sd.plot("displayname",'bla','fignum',199);
% eq_signal_sd.eye(fsym,M,"fignum",103837);
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd);
[~,tot_err,ber_vnle(l,ri,realiz),a] = calc_ber(rx_bits.signal,Tx_bits{l}.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_vnle = count_error_bursts(a, 10)./tot_err;
% showLevelConfusionMatrix(eq_signal_hd,Symbols{l},"M",M,"fignum",200,"displayname",'bla');
% showLevelScatter(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','f_sym',fsym,'fignum',201);
% show2Dconstellation(eq_signal_sd,Symbols{l},"displayname",'VNLE Out','fignum',2241);
fprintf('CH %d :BER VNLE: %.2e \n',l,ber_vnle(l,ri,realiz));
fprintf('CH %d :NGMI VNLE: %.2f \n',l,gmi_vnle_bitwise(l,ri,realiz)./m);
end
end
end
figure();hold on;
cols = linspecer(N);
for l = 1:N
% plot(rop,mean(squeeze(ber_vnle(l,:,:)),2,'omitnan'),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)))
plot(rop,squeeze(ber_vnle(l,:,:)),'Marker','*','DisplayName',sprintf('Ch: %d',wavelengthplan(l)),'Color',cols(l,:),'HandleVisibility','on')
end
yline([3.8e-3,2.2e-4],'HandleVisibility','off');
ylabel('BER');
xlabel('ROP')
title('BER vs. ROP');
set(gca, 'XScale', 'linear', ...
'YScale', 'log', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey)
% MATLAB version of the Python generator shown above.
% Returns an N×1 vector (ps/(nm·km)).
%
% D is the nominal dispersion magnitude. For D>0 the link is segmented with
% alternating sign (+D, -D, +D, ). For D==0 it is flat (0) except for
% ZDW randomization. The ZDW detuning is ~N(0, 2 nm) around 1310 nm and is
% converted to dispersion via 0.09 ps/(nm·km) per nm.
% constants (matching the Python code)
meanLambda_nm = 1310; % center wavelength
sigma_nm = 2; % ZDW sigma
Dslope = 0.09; % ps/(nm·km) per nm detuning
% random ZDW-induced dispersion offset
if randomize_ZDW
rng(randomkey, 'twister');
rand_zdws_nm = meanLambda_nm + sigma_nm .* randn(N,1);
rand_D = (rand_zdws_nm - ref_zdw) .* Dslope; % ps/(nm·km)
else
rand_D = zeros(N,1);
end
% nominal segmented pattern (match Python intent; keep length N)
if D > 0
base = (-1) .^ ((0:N-1).'); % +1,-1,+1,-1,...
else % D == 0 (or anything else)
base = ones(N,1);
end
dispersion_vector = base .* D + rand_D; % ps/(nm·km)
end