halfway merged and pulled?!

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,160 @@
%% ============================================================
% PARAMETERS
% ============================================================
database_type = 'mysql';
db = DBHandler("dataBase", "labor_highspeed", "type", database_type);
pam_level = 4; % FIXED for this figure
baudrates = [300e9 330e9 360e9 390e9];
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 results(b, k): b = baudrate index, k = DSP scheme index
% ============================================================
results = struct;
for b = 1:length(baudrates)
Rb = baudrates(b);
% --- query matching runs ---
fp = QueryFilter();
fp.where('Runs','pam_level','EQUALS', pam_level);
fp.where('Runs','fiber_length','EQUALS', fiberL);
fp.where('Runs','bitrate','EQUALS', Rb);
fp.where('Runs','is_mpi','EQUALS', 0);
[dataTable, ~] = db.queryDB(fp, fields);
for k = 1:numel(curves)
%% ---- DECIDE PRE-EMPH & PRECoded RULES for PAM-4 ----
pre_emph = decide_preemph(pam_level, curves(k).eq);
use_precoded = decide_precoded(pam_level, curves(k).eq);
%% ---- SETUP ANALYSIS 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', pam_level, ...
'is_mpi', 0, ...
'bitrate', Rb, ...
'fiber_length', fiberL, ...
'equalizer_structure', curves(k).eq, ...
'pre_emph', pre_emph);
%% ---- RUN ANALYSIS ----
A = analyze_measurements_gpt(dataTable, cfg);
results(b,k).wavelength = A.group{1}.x;
if use_precoded
results(b,k).ber = A.group{1}.y_precoded;
else
results(b,k).ber = A.group{1}.y;
end
end
end
%% ============================================================
% PLOT 1×4 (one tile per baudrate)
% ============================================================
fig = figure(); clf;
tiledlayout(1,4,'TileSpacing','compact','Padding','compact');
lw = 1.8;
ms = 6;
for b = 1:length(baudrates)
nexttile; hold on;
for k = 1:numel(curves)
plot(results(b,k).wavelength, results(b,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('Wavelength [nm]');
ylabel('BER');
ylim([1e-4 0.1]);
title(sprintf('PAM-%d @ %.0f GBd',pam_level, baudrates(b)/1e9));
legend('Location','best');
beautifyBERplot();
end
% Optional figure size
pos = 1e3.*[0.1 0.55 1.4 0.32];
set(fig, 'Position', pos);
%% ============================================================
% DECISION LOGIC (INLINE FUNCTIONS)
% ============================================================
function pe = decide_preemph(M, eq)
% PRE-EMPH RULES:
switch M
case 4
if eq == equalizer_structure.vnle
pe = 1; % PAM4: VNLE pre-emph on
else
pe = 0; % PAM4: all others off
end
case {6,8}
pe = 1; % PAM6/8: all pre-emph on
otherwise
pe = 0;
end
end
function flag = decide_precoded(M, eq)
% PRE-CODE RULES:
if eq == equalizer_structure.vnle_db_mlse
flag = 1; % Always for DB-target
elseif eq == equalizer_structure.ml_mlse && M == 4
flag = 1; % PAM4: ML-based precoded
else
flag = 0;
end
end

View File

@@ -0,0 +1,88 @@
%% ============================================================
% PLOT
% ============================================================
figure; hold on;
ms = 32; % scatter size
lw = 0.8; % line width
for k = 1:4 % PAM-2/4/6/8
M = pam_list(k);
idxPam = (Mvals == M);
% Extract for this PAM
x = baud(idxPam);
y = netrate(idxPam);
n = names(idxPam);
% Get color for this PAM format
col = colors(k,:);
% ----- LEGEND FLAG (only add one entry per PAM) -----
firstLegend = true;
% ---- PLOT ALL POINTS (marker based on publication) ----
for i = 1:sum(idxPam)
% marker selection by publication
pubIdx = find(pub_list == n(i), 1);
marker = markerlist{mod(pubIdx-1, nMarkers) + 1};
if firstLegend
h = scatter(x(i), y(i), ms, ...
'Marker', marker, ...
'MarkerEdgeColor', col, ...
'MarkerFaceColor', col, ...
'DisplayName', sprintf('PAM-%d', M));
firstLegend = false;
else
h = scatter(x(i), y(i), ms, ...
'Marker', marker, ...
'MarkerEdgeColor', col, ...
'MarkerFaceColor', col, ...
'HandleVisibility','off');
end
% ====== CUSTOM DATATIP CONTENT ======
dt = h.DataTipTemplate;
dt.DataTipRows(1).Label = 'Baud rate';
dt.DataTipRows(2).Label = 'Net rate';
% Add publication name
dt.DataTipRows(end+1) = dataTipTextRow('Publication', n(i));
end
% ---- Fit (PAM-specific) ----
valid = ~isnan(x) & ~isnan(y);
if sum(valid) >= 3
p = polyfit(x(valid), y(valid), 2);
xfit = linspace(min(x(valid)), max(x(valid)), 200);
yfit = polyval(p, xfit);
plot(xfit, yfit, ':', ...
'LineWidth', lw, ...
'Color', col, ...
'HandleVisibility', 'off'); % do NOT add to legend
end
end
grid on;
xlabel('Baud rate [GBd]');
ylabel('Net rate [Gb/s]');
legend('Location','northwest');
set(gca,'FontSize',11);
%% === EXPORT ===
outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\highspeedresults.tikz';
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'legend columns=1' ...
});

View File

@@ -0,0 +1,334 @@
function [M, cfg] = analyze_measurements_gpt(T, cfg)
% ANALYZE_MEASUREMENTS_GPT
% Filter, compute X/Y, group and aggregate measurements from table T.
% No plotting here.
%
% Usage:
% [M, cfg] = analyze_measurements_gpt(dataTable, cfg);
%
% Typical result (single group):
% M.x -> aggregated x-values (e.g., grossrate)
% M.y -> aggregated y-values (e.g., BER or NGMI)
% M.y_precoded -> aggregated precoded BER (if available)
%
% For multiple groups:
% M.group(g).x, M.group(g).y, M.group(g).label, ...
%% ---- Defaults (non-plot) ----
if nargin < 2, cfg = struct; end
defaults = struct( ...
'x_axis' , 'symbolrate', ...
'y_axis' , 'BER', ...
'y_scale' , 'auto', ...
'group_by' , {{'equalizer_structure','pre_emph'}}, ...
'filters' , struct, ...
'agg' , 'mean', ...
'outlier' , 'auto', ...
'mad_z' , 4, ...
'pct_limits' , [2.5 97.5], ...
'min_pts_x' , 3, ...
'show_raw' , true, ...
'show_precoded', [], ...
'show_spread' , 'none', ...
'fec_lines' , [], ...
'plot' , struct() ... % plot settings handled in plot function
);
cfg = filldefaults(cfg, defaults);
%% ---- Derived/prep columns ----
if ~ismember('pre_emph', T.Properties.VariableNames)
if ~ismember('db_mode', T.Properties.VariableNames)
error('Missing column "db_mode" for pre_emph derivation.');
end
T.pre_emph = T.db_mode == 0;
end
if ~ismember(cfg.y_axis, T.Properties.VariableNames)
error('y_axis "%s" not found in table.', cfg.y_axis);
end
isBER = startsWith(cfg.y_axis, "BER", 'IgnoreCase', true);
M.isBER = isBER;
if strcmpi(cfg.y_scale,'auto')
cfg.y_scale = tern(isBER, 'log', 'linear');
end
if strcmpi(cfg.outlier,'auto')
cfg.outlier = tern(isBER, 'mad', 'none');
end
if isempty(cfg.show_precoded)
cfg.show_precoded = isBER && ismember('BER_precoded', T.Properties.VariableNames);
end
%% ---- Filters & core X/Y extraction ----
T = applyFilters(T, cfg.filters);
[x_raw, x_label] = computeX(T, cfg.x_axis);
y_raw = T.(cfg.y_axis);
validXY = isfinite(x_raw) & isfinite(y_raw);
T = T(validXY, :);
x_raw = x_raw(validXY);
y_raw = y_raw(validXY);
% Degiga if needed
if mean(abs(y_raw)) > 1e8
y_raw = y_raw .* 1e-9;
end
if cfg.show_precoded && ismember('BER_precoded', T.Properties.VariableNames)
y_raw_p = T.BER_precoded(validXY);
else
y_raw_p = [];
end
%% ---- Grouping ----
group_by = cfg.group_by;
if ~all(ismember(group_by, T.Properties.VariableNames))
error('Some group_by columns are missing in table.');
end
[G, grpTbl] = findgroups(T(:, group_by));
nG = max(G);
%% ---- Aggregation per group ----
M = struct;
M.cfg = cfg;
M.x_label = x_label;
M.y_axis = cfg.y_axis;
M.x_axis = cfg.x_axis;
M.nGroups = nG;
% raw (filtered) data
M.raw = struct;
M.raw.x = x_raw;
M.raw.y = y_raw;
M.raw.y_precoded = y_raw_p;
M.raw.T = T;
M.group = cell(nG,1);
useLog = strcmpi(cfg.y_scale,'log');
for gi = 1:nG
idx = (G == gi);
Ti = T(idx,:);
xi = x_raw(idx);
yi = y_raw(idx);
[xu, ~, iu] = unique(xi);
yu = nan(size(xu));
ylo = nan(size(xu));
yhi = nan(size(xu));
for k = 1:numel(xu)
bin = (iu==k);
yy = yi(bin);
yy = yy(isfinite(yy));
if isempty(yy), continue; end
km = outlierMask(yy, cfg, useLog);
if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end
yy = yy(km);
switch lower(cfg.agg)
case 'median'
yu(k) = median(yy,'omitnan');
case 'mean'
yu(k) = mean(yy,'omitnan');
case 'min'
yu(k) = min(yy);
case 'max'
yu(k) = max(yy);
otherwise
error('Unknown agg mode "%s".', cfg.agg);
end
if strcmpi(cfg.show_spread,'iqr')
q = prctile(yy,[25 75]);
ylo(k) = max(yu(k)-q(1), eps);
yhi(k) = max(q(2)-yu(k), eps);
elseif strcmpi(cfg.show_spread,'minmax')
ylo(k) = min(yy);
yhi(k) = max(yy);
end
end
% sort by x
[xu, ord] = sort(xu);
yu = yu(ord);
ylo = ylo(ord);
yhi = yhi(ord);
g = struct;
g.label = buildLabel(grpTbl(gi,:), group_by);
g.idx = find(G==gi);
g.T = Ti;
g.x_raw = xi;
g.y_raw = yi;
g.x = xu;
g.y = yu;
g.y_lo = ylo;
g.y_hi = yhi;
g.y_precoded = [];
g.y_precoded_lo = [];
g.y_precoded_hi = [];
% Precoded aggregation (if requested & available)
if cfg.show_precoded && ~isempty(y_raw_p) && strcmpi(cfg.y_axis,'BER')
ypi = y_raw_p(idx);
ypu = nan(size(xu));
for k = 1:numel(xu)
bin = (iu==k);
yy = ypi(bin);
yy = yy(isfinite(yy));
if isempty(yy), continue; end
km = outlierMask(yy, cfg, true);
if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end
yy = yy(km);
switch lower(cfg.agg)
case 'median'
ypu(k) = median(yy,'omitnan');
case 'mean'
ypu(k) = mean(yy,'omitnan');
case 'min'
ypu(k) = min(yy);
case 'max'
ypu(k) = max(yy);
end
end
g.y_precoded = ypu(ord);
end
M.group{gi} = g;
end
% Convenience flatten for single-group case
if nG == 1
g = M.group{1};
M.x = g.x;
M.y = g.y;
M.y_precoded = g.y_precoded;
end
end % ===== main =====
%% ===================== Helpers =====================
function cfg = filldefaults(cfg, defs)
fn = fieldnames(defs);
for i = 1:numel(fn)
f = fn{i};
if ~isfield(cfg, f) || isempty(cfg.(f))
cfg.(f) = defs.(f);
elseif isstruct(defs.(f)) && isstruct(cfg.(f))
cfg.(f) = filldefaults(cfg.(f), defs.(f)); % recursive
end
end
end
function out = tern(cond, a, b)
if cond
out = a;
else
out = b;
end
end
function T2 = applyFilters(T, filters)
if isempty(filters), T2 = T; return; end
keep = true(height(T),1);
fns = fieldnames(filters);
for i = 1:numel(fns)
name = fns{i};
if ~ismember(name, T.Properties.VariableNames)
warning('Filter column "%s" not found. Ignored.', name);
continue
end
val = filters.(name);
col = T.(name);
if isa(val,'function_handle')
m = val(col);
if ~islogical(m) || ~isequal(size(m), size(col))
error('Filter for %s must return logical mask of same size.', name);
end
keep = keep & m;
else
keep = keep & ismember(col, val);
end
end
T2 = T(keep,:);
end
function [x, label] = computeX(T, whichX)
switch lower(whichX)
case {'symbolrate','baudrate'}
x = T.symbolrate * 1e-9;
label = 'Symbol rate [GBd]';
case 'bitrate'
if ~ismember('pam_level', T.Properties.VariableNames)
error('bitrate requires "pam_level" column.');
end
bits = floor(log2(double(T.pam_level))*10)/10;
x = (T.symbolrate .* bits) * 1e-9;
label = 'Grossrate [Gb/s]';
case 'grossrate'
x = T.grossrate * 1e-9;
label = 'Grossrate [Gb/s]';
otherwise
if ~ismember(whichX, T.Properties.VariableNames)
error('x_axis "%s" not found in table.', whichX);
end
x = T.(whichX);
label = whichX;
end
x = double(x(:));
end
function keep = outlierMask(y, cfg, useLog)
if isempty(y), keep = false(size(y)); return; end
y = y(:);
switch lower(cfg.outlier)
case 'none'
keep = true(size(y)); return
case 'mad'
z = tern(useLog, log10(y), y);
med = median(z,'omitnan');
madv = median(abs(z-med),'omitnan');
if ~(isfinite(madv) && madv>0)
keep = true(size(y)); return
end
sigma = 1.4826*madv;
zz = tern(useLog, log10(y), y);
keep = abs(zz - med) <= cfg.mad_z*sigma;
case 'pctl'
pr = prctile(y, cfg.pct_limits);
keep = (y >= pr(1)) & (y <= pr(2));
otherwise
error('Unknown outlier mode "%s".', cfg.outlier);
end
end
function s = buildLabel(grpRow, group_by)
parts = strings(1, numel(group_by));
for i = 1:numel(group_by)
key = group_by{i};
val = grpRow.(key);
if iscell(val), val = val{1}; end
if islogical(val), val = tern(val,'w/','w/o'); end
if key == "equalizer_structure"
key = '';
val = upper(val);
val = strrep(val,'_',' ');
end
if key == "pre_emph"
val = [val, ' pre-emph.'];
key = '';
end
parts(i) = sprintf('%s %s', key, string(val));
end
s = strjoin(parts, ', ');
end

View File

@@ -0,0 +1,240 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';
db = DBHandler("dataBase", [dataBase], "type", database_type);
% M = 4;
fp = QueryFilter();
% fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'wavelength','LESS_THAN', 1312);
% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025
[dataTable,~] = db.queryDB(fp, fields);
%%
cfg = struct;
cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate
cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ...
cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise
cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available
cfg.show_raw = false;
cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax
cfg.agg = 'min'; % or 'median'
cfg.show_precoded = 0;
% cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional
cfg.fec_lines = [];
cfg.plot.custom_colors = [
clr.Paired.red;
clr.Paired.blue;
clr.Paired.green;
clr.Paired.orange;
clr.Paired.purple
];
cfg.plot.custom_colors_scatter = [
clr.Paired.lightred;
clr.Paired.lightblue;
clr.Paired.lightgreen;
clr.Paired.lightorange;
clr.Paired.lightpurple
];
% New styling knobs
cfg.plot.use_cbrewer2 = true;
cfg.plot.colormap = 'Paired';
cfg.plot.paired_dark_first = false; % dark for lines, light for scatter
cfg.plot.lineWidth = 2.0;
cfg.plot.errWidth = 1.2;
cfg.plot.scatterAlpha = 0.35;
cfg.plot.legendLocation = 'best';
cfg.plot.fecLineWidth = 2.4; % thicker FEC limits
cfg.plot.lineStyle_pre_emph_on = '-';
cfg.plot.lineStyle_pre_emph_off = '-';
%% PLOT NGMI
% Common cfg
cfg.show_precoded = 1;
cfg.group_by = {'equalizer_structure','pre_emph'};
cfg.x_axis = 'grossrate';
cfg.y_axis = 'NGMI'; % 'BER' | 'GMI' | 'AIR' | ...
cfg.y_scale = 'lin';
cfg.plot.custom_colors_scatter = [];
cfg.plot.use_cbrewer2 = false;
cfg.fec_lines = [];
cfg.agg = 'max';
cfg.figure_number = 45;
% fig = figure(cfg.figure_number);
lambda = 1310;
% PAM 4
cfg.plot.custom_colors = clr.Paired.red;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',4, ...
'equalizer_structure',equalizer_structure.vnle_db_mlse, ...
'pre_emph',0,'wavelength',lambda);
cfg.show_precoded = 1;
a = plot_measurements_gpt(dataTable, cfg);
ngmi_pam4 = a.lines(1).YData;
grossrates = a.lines(1).XData;
tp = TransmissionPerformance;
netrates_vnle = tp.calculateNetRate(grossrates, ...
'NGMI', ngmi_pam4, ...
'BER', BER_VNLE);
cfg.plot.custom_colors = clr.Paired.red;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',4, ...
'equalizer_structure',equalizer_structure.vnle_db_mlse, ...
'pre_emph',0,'wavelength',1293);
cfg.show_precoded = 1;
plot_measurements_gpt(dataTable, cfg);
% PAM 6
cfg.plot.custom_colors = clr.Paired.blue;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',6, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',lambda);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
cfg.plot.custom_colors = clr.Paired.blue;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',6, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',1293);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
% PAM 8
cfg.plot.custom_colors = clr.Paired.green;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',8, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',lambda);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
cfg.plot.custom_colors = clr.Paired.green;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',8, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',1293);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
beautifyBERplot
ylim([0.87,1.01]);
% xlim([290,480]);
%% PLOT AIR
% Common cfg
cfg.show_precoded = 1;
cfg.group_by = {'equalizer_structure','pre_emph'};
cfg.x_axis = 'grossrate';
cfg.y_axis = 'AIR'; % 'BER' | 'GMI' | 'AIR' | ...
cfg.y_scale = 'lin';
cfg.plot.custom_colors_scatter = [];
cfg.plot.use_cbrewer2 = false;
cfg.fec_lines = [];
cfg.agg = 'max';
cfg.figure_number = 47;
lambda = 1310;
% cfg = struct;
cfg.filters = struct('is_mpi',0,'pam_level',4, ...
'equalizer_structure',equalizer_structure.vnle_db_mlse, ...
'pre_emph',0,'wavelength',lambda);
cfg.x_axis = 'grossrate';
cfg.y_axis = 'NGMI'; % or 'NGMI', etc.
cfg.show_precoded = 1;
[M, cfg] = analyze_measurements_gpt(dataTable, cfg);
grossrates = M.x; % aggregated X
ber = M.y; % aggregated Y (BER or NGMI)
ber_prec = M.y_precoded; % precoded BER (if available)
[h, M] = plot_measurements_gpt(dataTable, cfg);
% PAM 4
cfg.plot.custom_colors = clr.Paired.red;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',4, ...
'equalizer_structure',equalizer_structure.vnle_db_mlse, ...
'pre_emph',0,'wavelength',lambda);
cfg.show_precoded = 1;
plot_measurements_gpt(dataTable, cfg);
cfg.plot.custom_colors = clr.Paired.red;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',4, ...
'equalizer_structure',equalizer_structure.vnle_db_mlse, ...
'pre_emph',0,'wavelength',1293);
cfg.show_precoded = 1;
plot_measurements_gpt(dataTable, cfg);
% PAM 6
cfg.plot.custom_colors = clr.Paired.blue;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',6, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',lambda);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
cfg.plot.custom_colors = clr.Paired.blue;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',6, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',1293);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
% PAM 8
cfg.plot.custom_colors = clr.Paired.green;
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',8, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',lambda);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
cfg.plot.custom_colors = clr.Paired.green;
cfg.plot.custom_linetypes = {'--'};
cfg.filters = struct('is_mpi',0,'pam_level',8, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',1,'wavelength',1293);
cfg.show_precoded = 0;
plot_measurements_gpt(dataTable, cfg);
ax = gca;
beautifyBERplot
ylim([275,435]);
xlim([290,480]);
%%

View File

@@ -0,0 +1,112 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';
db = DBHandler("dataBase", [dataBase], "type", database_type);
M = 4;
fp = QueryFilter();
fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_aug_nov_2025')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025
[dataTable,~] = db.queryDB(fp, fields);
%%
cfg = struct;
cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate
cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ...
cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise
cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available
cfg.show_raw = false;
cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax
cfg.agg = 'min'; % or 'median'
cfg.show_precoded = 0;
% cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional
cfg.fec_lines = [];
cfg.plot.custom_colors = [
clr.Paired.red;
clr.Paired.blue;
clr.Paired.green;
clr.Paired.orange;
clr.Paired.purple
];
cfg.plot.custom_colors_scatter = [
clr.Paired.lightred;
clr.Paired.lightblue;
clr.Paired.lightgreen;
clr.Paired.lightorange;
clr.Paired.lightpurple
];
% New styling knobs
cfg.plot.use_cbrewer2 = true;
cfg.plot.colormap = 'Paired';
cfg.plot.paired_dark_first = false; % dark for lines, light for scatter
cfg.plot.lineWidth = 2.0;
cfg.plot.errWidth = 1.2;
cfg.plot.scatterAlpha = 0.35;
cfg.plot.legendLocation = 'best';
cfg.plot.fecLineWidth = 2.4; % thicker FEC limits
cfg.plot.lineStyle_pre_emph_on = '-';
cfg.plot.lineStyle_pre_emph_off = '-';
%%
cfg.figure_number = 42;
% ---- VNLE, no pre-emph (solid red) ----
cfg.plot.custom_colors = [clr.Paired.red];
cfg.plot.custom_linetypes = {'-'};
cfg.filters = struct('is_mpi',0,'pam_level',M, ...
'equalizer_structure',equalizer_structure.vnle, ...
'pre_emph',0);
plot_measurements_gpt(dataTable, cfg);
% ---- VNLE, with pre-emph (dashed red) ----
cfg.plot.custom_colors = [clr.Paired.red];
cfg.plot.custom_linetypes = {'--'};
cfg.filters.pre_emph = 1;
plot_measurements_gpt(dataTable, cfg);
% ---- VNLE PF MLSE, no pre-emph (solid green) ----
cfg.plot.custom_colors = [clr.Paired.green];
cfg.plot.custom_linetypes = {'-'};
cfg.filters.equalizer_structure = equalizer_structure.vnle_pf_mlse;
cfg.filters.pre_emph = 0;
plot_measurements_gpt(dataTable, cfg);
% ---- VNLE PF MLSE, with pre-emph (dashed green) ----
cfg.plot.custom_colors = [clr.Paired.green];
cfg.plot.custom_linetypes = {'--'};
cfg.filters.pre_emph = 1;
plot_measurements_gpt(dataTable, cfg);
% === FEC LINES (no legend) ===
yline([2.2e-4 4.85e-3 2e-2], ...
'LineWidth',1.5,'Color',[0.4 0.4 0.4], ...
'LineStyle',':','HandleVisibility','off');
% === BEAUTIFY ===
% beautifyBERplot; % your function
%% === EXPORT TO TIKZ ===
outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\compare_pre_emphasis.tikz';
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'legend columns=1' ...
} );

View File

@@ -0,0 +1,92 @@
% ============================================================
% MINIMAL EXAMPLE: Query Analyze Plot Extract X/Y data
% ============================================================
%% === Load from database ===
db = DBHandler("dataBase","labor_highspeed","type","mysql");
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS',2);
% fp.where('Runs','pam_level','EQUALS',6); % PAM-4
% fp.where('Runs','db_mode','EQUALS',0); % w/o pre-emph
fields = db.getTableFieldNames('dashboard_ungrouped_alltime');
[dataTable,~] = db.queryDB(fp, fields);
%% === Define config ===
for m = [4,6,8]
cfg = struct;
cfg.x_axis = 'symbolrate';
cfg.y_axis = 'Alpha';
cfg.group_by = {'equalizer_structure','pre_emph'};
cfg.filters = struct('is_mpi',0,'pam_level',m, ...
'equalizer_structure',equalizer_structure.vnle_pf_mlse, ...
'pre_emph',0);
cfg.agg = 'max';
cfg.outlier = 'mad';
cfg.show_raw = false;
cfg.show_precoded = 0;
% Plot cosmetics (minimal)
cfg.plot = struct;
cfg.plot.custom_colors = linspecer(8);
cfg.plot.custom_linetypes = {'-'};
cfg.plot.lineWidth = 2;
% ============================================================
% === ANALYSIS ONLY (no plotting) =============================
% ============================================================
A = analyze_measurements_gpt(dataTable, cfg);
% Now you have:
% A.raw.x = raw x-values
% A.raw.y = raw BER values
% A.group{1}.x = unique sorted x-values
% A.group{1}.y = aggregated BER for each x
x_values = A.group{1}.x;
y_values = A.group{1}.y;
% ============================================================
% === PLOT ====================================================
% ============================================================
figure(10);hold on
cfg.ax = gca; % optional: plot into existing axes
plot(x_values,y_values,...
'LineWidth', 2, ...
'Color', clr.Set1.red, ...
'MarkerSize', 5, ...
'MarkerFaceColor', clr.Set1.red,...
'Marker','o');
% [h, ~] = plot_measurements_gpt(dataTable, cfg);
title('Minimal VNLE BER Example')
xlabel('Grossrate [Gb/s]')
ylabel('BER')
xticks(100:30:220)
xlim([100,220]);
ylim([0,1]);
end
% outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\alphas.tikz';
% matlab2tikz(outfile, ...
% 'width','\fwidth', ...
% 'height','\fheight', ...
% 'showInfo',false, ...
% 'extraAxisOptions',{ ...
% 'legend style={font=\footnotesize}', ...
% 'legend columns=1' ...
% 'every axis/.append style={font=\scriptsize}',...
% 'minor grid style={line width=0.2pt, solid, color=black!10}',...
% 'grid style={line width=0.4pt, solid, color=black!20}',...
% 'grid style={dashed}',...
% });

View File

@@ -0,0 +1,111 @@
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1;
database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
rate = 390e9;
%% 1 - PAM 4 with preemphasis
fp = QueryFilter();
M = 4;
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', 0);
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);
% 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);
if rate == 390e9
Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-5.8);
elseif rate == 300e9
Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-4.7);
end
ylim([-30,3]);
xlim([-5,100]);
Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx');
%% 1 - PAM 4 without preemphasis
fp = QueryFilter();
M = 4;
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', 1);
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);
% 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.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',0);
ylim([-30,3]);
xlim([-5,100]);
Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx');
if 1
%% show freuqncy response of filter
measure = 1;
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9);
%
Digi_sig = freqresp.buildOFDM();
% Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]);
Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig);
Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.bessel_inp,"active",true).process(Digi_sig);
freqresp.estimate(Digi_sig,"fileName",'','save',false);
freqresp.plot()
a = gca;
a.YTick = [-30,-20,-10,0];
%% system frex
precomp_filename ='lab_high_speed';
precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp";
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9);
freqresp.load('loadPath', precomp_path, 'fileName', precomp_filename);
fprintf('Plotting: %s\n', precomp_filename);
freqresp.plot();
outfile = 'C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\spectrum_2.tikz';
matlab2tikz(outfile, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'legend columns=1' ...
});
end

View File

@@ -0,0 +1,222 @@
function [h, M, cfg] = plot_measurements_gpt(T, cfg)
% PLOT_MEASUREMENTS_GPT
% Plot measurements, using analyze_measurements_gpt for data handling.
%
% Usage:
% h = plot_measurements_gpt(dataTable, cfg);
% [h, M] = plot_measurements_gpt(dataTable, cfg);
%
% For values only, without plotting, call:
% [M, cfg] = analyze_measurements_gpt(dataTable, cfg);
if nargin < 2, cfg = struct; end
% --- First: run analysis (filtering, grouping, aggregation) ---
[M, cfg] = analyze_measurements_gpt(T, cfg);
nG = M.nGroups;
%% ---- Plot defaults ----
plotdefs = struct( ...
'use_cbrewer2' , true, ...
'colormap' , 'Paired', ...
'paired_dark_first' , true, ...
'lineWidth' , 1.8, ...
'errWidth' , 1.0, ...
'scatterSize' , 14, ...
'scatterAlpha' , 0.35, ...
'marker' , 'o', ...
'marker_precoded' , 's', ...
'legendLocation' , 'best', ...
'fecLineWidth' , 2.2, ...
'fecColor' , [0.25 0.25 0.25], ...
'capSize' , 6, ...
'lineStyle_default' , '-', ...
'custom_colors' , [], ...
'custom_colors_scatter' , [] ...
);
if ~isfield(cfg,'plot') || isempty(cfg.plot)
cfg.plot = struct;
end
cfg.plot = filldefaults(cfg.plot, plotdefs);
%% ---- Colors ----
[cols_line, cols_scatter] = buildGroupColors(nG, cfg.plot);
%% ---- Axes / Figure handling ----
if isfield(cfg,'ax') && ~isempty(cfg.ax) && isgraphics(cfg.ax,'axes')
ax = cfg.ax;
set(gcf,'CurrentAxes',ax);
else
if isfield(cfg,'figure_number') && ~isempty(cfg.figure_number)
figure(cfg.figure_number);
else
figure;
end
ax = gca;
end
hold(ax,'on');
grid(ax,'on');
h.lines = gobjects(nG,1);
h.err = gobjects(nG,1);
h.scat = gobjects(nG,1);
h.lines_p = gobjects(nG,1);
%% ---- Plot each group ----
for gi = 1:nG
g = M.group{gi};
xu = g.x;
yu = g.y;
ylo = g.y_lo;
yhi = g.y_hi;
% --- Linestyle selection ---
ls = cfg.plot.lineStyle_default;
if isfield(cfg.plot,'custom_linetypes') && ~isempty(cfg.plot.custom_linetypes)
L = cfg.plot.custom_linetypes;
ls = L{ mod(gi-1, numel(L)) + 1 };
end
colL = cols_line(gi,:);
lbl = g.label;
% Main line
h.lines(gi) = plot(ax, xu, yu, ...
'LineWidth', cfg.plot.lineWidth, ...
'Marker', cfg.plot.marker, 'MarkerSize', 3, ...
'Color', colL, 'LineStyle', ls, ...
'DisplayName', char(lbl));
% Spread
if any(isfinite(ylo)) && any(isfinite(yhi))
h.err(gi) = errorbar(ax, xu, yu, ylo, yhi, 'LineStyle','none', ...
'Color', colL, 'CapSize', cfg.plot.capSize, 'HandleVisibility','off');
h.err(gi).LineWidth = cfg.plot.errWidth;
end
% Raw scatter
if cfg.show_raw
% reuse stored raw data (no extra filtering)
xi = g.x_raw;
yi = g.y_raw;
colS = cols_scatter(gi,:);
scatter(ax, xi, yi, cfg.plot.scatterSize, colS, 'filled', ...
'MarkerFaceAlpha', cfg.plot.scatterAlpha, ...
'MarkerEdgeAlpha', cfg.plot.scatterAlpha, ...
'HandleVisibility','off');
end
% Precoded overlay
if cfg.show_precoded && ~isempty(g.y_precoded) && strcmpi(M.y_axis,'BER')
ypu = g.y_precoded;
h.lines_p(gi) = plot(ax, xu, ypu, ...
'LineWidth', max(1.2, cfg.plot.lineWidth-0.2), ...
'Marker', cfg.plot.marker_precoded, 'MarkerSize', 3, ...
'Color', colL, 'LineStyle', ':', ...
'DisplayName', [char(lbl) ' (precoded)']);
end
end
%% ---- Axes / Labels / FEC ----
ylabel(ax, M.y_axis, 'Interpreter','none');
xlabel(ax, M.x_label, 'Interpreter','none');
set(ax, 'YScale', cfg.y_scale, 'FontSize', 11);
% X ticks/limits using all group x-values
allX = cellfun(@(g) g.x(:), M.group, 'UniformOutput', false);
allX = unique(vertcat(allX{:}));
if ~isempty(allX)
xticks(ax, allX);
xticklabels(cellstr(num2str(round(allX,1), '%.4f')))
xlim(ax, [min(allX), max(allX)]);
end
if startsWith(M.y_axis,"BER",'IgnoreCase',true)
for v = cfg.fec_lines
yline(ax, v, '--', 'Color', cfg.plot.fecColor, ...
'LineWidth', cfg.plot.fecLineWidth, 'HandleVisibility','off');
end
ylim(ax, [1e-5, 0.5]);
yticks(ax, [1e-5, 1e-4, 1e-3, 1e-2, 1e-1]);
end
% legend(ax, 'Location', cfg.plot.legendLocation); % if you want legends
box(ax,'on');
end % ===== main =====
%% ===================== Helpers =====================
function cfg = filldefaults(cfg, defs)
fn = fieldnames(defs);
for i = 1:numel(fn)
f = fn{i};
if ~isfield(cfg, f) || isempty(cfg.(f))
cfg.(f) = defs.(f);
elseif isstruct(defs.(f)) && isstruct(cfg.(f))
cfg.(f) = filldefaults(cfg.(f), defs.(f));
end
end
end
function [cols_line, cols_scatter] = buildGroupColors(nG, plotcfg)
% 1) User-provided custom colors
if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors)
C = plotcfg.custom_colors;
if size(C,1) < nG
error('custom_colors must have at least nG=%d rows.', nG);
end
cols_line = C(1:nG, :);
if isfield(plotcfg,'custom_colors_scatter') && ~isempty(plotcfg.custom_colors_scatter)
Cs = plotcfg.custom_colors_scatter;
if size(Cs,1) < nG
error('custom_colors_scatter must have at least nG=%d rows.', nG);
end
cols_scatter = Cs(1:nG, :);
else
cols_scatter = zeros(nG,3);
for i = 1:nG
cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.40);
end
end
return;
end
% 2) Standard behavior
useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2;
if useBrewer
N = max(2*nG, 12);
C = cbrewer2(plotcfg.colormap, N);
cols_line = zeros(nG,3);
cols_scatter = zeros(nG,3);
for i = 1:nG
if plotcfg.paired_dark_first
dark = C(2*i-1, :);
light = C(2*i, :);
else
light = C(2*i-1, :);
dark = C(2*i, :);
end
cols_line(i,:) = dark;
cols_scatter(i,:) = light;
end
else
C = lines(max(nG,7));
cols_line = C(1:nG,:);
cols_scatter = zeros(nG,3);
for i = 1:nG
cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.50);
end
end
end
function c2 = lightenColor(c, fracTowardWhite)
c = c(:).';
c2 = (1-fracTowardWhite)*c + fracTowardWhite*1;
end

View File

@@ -0,0 +1,436 @@
function h = plot_measurements_gpt_old(T, cfg)
% Versatile plotting from your DB table (with cbrewer2 'Paired' palette).
%
% Usage:
% h = plot_measurements_flex(dataTable, cfg)
%% ---- Defaults
if nargin < 2, cfg = struct; end
defaults = struct( ...
'x_axis' , 'symbolrate', ...
'y_axis' , 'BER', ...
'y_scale' , 'auto', ...
'group_by' , {{'equalizer_structure','pre_emph'}}, ...
'filters' , struct, ...
'agg' , 'mean', ...
'outlier' , 'auto', ...
'mad_z' , 4, ...
'pct_limits' , [2.5 97.5], ...
'min_pts_x' , 3, ...
'show_raw' , true, ...
'show_precoded', [], ...
'show_spread' , 'none', ...
'fec_lines' , [], ...
'plot', struct() ...
);
cfg = filldefaults(cfg, defaults);
% ---- Plot defaults (new)
plotdefs = struct( ...
'use_cbrewer2' , true, ...
'colormap' , 'Paired', ... % ColorBrewer 'Paired'
'paired_dark_first' , true, ... % dark for lines, light for scatter
'lineWidth' , 1.8, ...
'errWidth' , 1.0, ...
'scatterSize' , 14, ...
'scatterAlpha' , 0.35, ...
'marker' , 'o', ...
'marker_precoded' , 's', ...
'lineStyle_pre_emph_on' , '--', ...
'lineStyle_pre_emph_off', '-', ...
'legendLocation' , 'best', ...
'fecLineWidth' , 2.2, ... % thicker FEC limits
'fecColor' , [0.25 0.25 0.25], ...
'capSize' , 6, ...
'lineStyle_default' , '-', ...
'use_pre_emph_styling' , true ...
);
cfg.plot = filldefaults(cfg.plot, plotdefs);
%% ---- Derived/prep columns
if ~ismember('pre_emph', T.Properties.VariableNames)
if ~ismember('db_mode', T.Properties.VariableNames)
error('Missing column "db_mode" for pre_emph derivation.');
end
T.pre_emph = T.db_mode == 0;
end
if ~ismember(cfg.y_axis, T.Properties.VariableNames)
error('y_axis "%s" not found in table.', cfg.y_axis);
end
isBER = startsWith(cfg.y_axis, "BER", 'IgnoreCase', true);
if strcmpi(cfg.y_scale,'auto'), cfg.y_scale = tern(isBER, 'log', 'linear'); end
if strcmpi(cfg.outlier,'auto'), cfg.outlier = tern(isBER, 'mad', 'none'); end
if isempty(cfg.show_precoded)
cfg.show_precoded = isBER && ismember('BER_precoded', T.Properties.VariableNames);
end
%% ---- Filters
T = applyFilters(T, cfg.filters);
[x_raw, x_label] = computeX(T, cfg.x_axis);
y_raw = T.(cfg.y_axis);
validXY = isfinite(x_raw) & isfinite(y_raw);
T = T(validXY, :);
x_raw = x_raw(validXY);
y_raw = y_raw(validXY);
if mean(abs(y_raw)) > 1e8
%giga values
y_raw = y_raw.*1e-9;
end
if cfg.show_precoded && ismember('BER_precoded', T.Properties.VariableNames)
y_raw_p = T.BER_precoded(validXY);
else
y_raw_p = [];
end
%% ---- Grouping
group_by = cfg.group_by;
if ~all(ismember(group_by, T.Properties.VariableNames))
error('Some group_by columns are missing in table.');
end
[G, grpTbl] = findgroups(T(:, group_by));
nG = max(G);
% ==== Colors (cbrewer2 'Paired' with dark/ light pairs) ====
[cols_line, cols_scatter] = buildGroupColors(nG, cfg.plot);
%% ---- Axes / Figure handling (new unified logic)
% Priority:
% 1) cfg.ax use existing axes (subplots/tiles)
% 2) cfg.figure_number select/create figure
% 3) fallback: create new figure
if isfield(cfg,'ax') && ~isempty(cfg.ax) && isgraphics(cfg.ax,'axes')
ax = cfg.ax; % use caller-provided axes
set(gcf,'CurrentAxes',ax);
else
if isfield(cfg,'figure_number') && ~isempty(cfg.figure_number)
figure(cfg.figure_number);
else
figure;
end
ax = gca; % active axes
end
hold(ax,'on');
grid(ax,'on');
h.lines = gobjects(nG,1);
h.err = gobjects(nG,1);
h.scat = gobjects(nG,1);
h.lines_p = gobjects(nG,1);
for gi = 1:nG
idx = (G==gi);
Ti = T(idx,:);
xi = x_raw(idx);
yi = y_raw(idx);
% Aggregate per unique x
[xu, ia, iu] = unique(xi);
yu = nan(size(xu));
ylo = nan(size(xu));
yhi = nan(size(xu));
for k = 1:numel(xu)
bin = (iu==k);
yy = yi(bin);
yy = yy(isfinite(yy));
if isempty(yy), continue; end
km = outlierMask(yy, cfg, strcmpi(cfg.y_scale,'log'));
if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end
yy = yy(km);
if strcmpi(cfg.agg,'median'), yu(k)=median(yy,'omitnan'); elseif strcmpi(cfg.agg,'mean'), yu(k)=mean(yy,'omitnan'); elseif strcmpi(cfg.agg,'min'), yu(k)=min(yy); elseif strcmpi(cfg.agg,'max'), yu(k)=max(yy); end
if strcmpi(cfg.show_spread,'iqr')
q = prctile(yy,[25 75]);
ylo(k) = max(yu(k)-q(1), eps);
yhi(k) = max(q(2)-yu(k), eps);
elseif strcmpi(cfg.show_spread,'minmax')
ylo(k) = min(yy);
yhi(k) = max(yy);
end
end
% sort
[xu, ord] = sort(xu);
yu = yu(ord);
ylo = ylo(ord);
yhi = yhi(ord);
% Styles
% Decide if we style by pre_emph
% --- LINE TYPE SELECTION (no pre-emphasis logic) ---
ls = cfg.plot.lineStyle_default;
% User-defined override (cycled)
if isfield(cfg.plot,'custom_linetypes') && ~isempty(cfg.plot.custom_linetypes)
L = cfg.plot.custom_linetypes;
ls = L{ mod(gi-1, numel(L)) + 1 };
end
lbl = buildLabel(grpTbl(gi,:), group_by);
% Main line (dark)
colL = cols_line(gi,:);
h.lines(gi) = plot(xu, yu, ...
'LineWidth', cfg.plot.lineWidth, ...
'Marker', cfg.plot.marker, 'MarkerSize', 3, ...
'Color', colL, 'LineStyle', ls, ...
'DisplayName', char(lbl));
% Spread (IQR) in line color
if any(isfinite(ylo)) && any(isfinite(yhi))
h.err(gi) = errorbar(xu, yu, ylo, yhi, 'LineStyle','none', ...
'Color', colL, 'CapSize', cfg.plot.capSize, 'HandleVisibility','off');
h.err(gi).LineWidth = cfg.plot.errWidth;
end
% Raw kept scatter (light)
if cfg.show_raw
keep_all = false(size(yi));
for k = 1:numel(xu)
bin = (iu==k);
yy = yi(bin);
km = outlierMask(yy, cfg, strcmpi(cfg.y_scale,'log'));
if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end
keep_all(bin) = km;
end
colS = cols_scatter(gi,:);
scatter(xi(keep_all), yi(keep_all), cfg.plot.scatterSize, colS, 'filled', ...
'MarkerFaceAlpha', cfg.plot.scatterAlpha, 'MarkerEdgeAlpha', cfg.plot.scatterAlpha, ...
'HandleVisibility','off');
end
% Precoded overlay (dotted, squares), in line color
if cfg.show_precoded && ~isempty(y_raw_p) && strcmpi(cfg.y_axis,'BER')
ypi = y_raw_p(idx);
ypu = nan(size(xu));
for k = 1:numel(xu)
bin = (iu==k);
yy = ypi(bin);
yy = yy(isfinite(yy));
if isempty(yy), continue; end
km = outlierMask(yy, cfg, true);
if nnz(km) < cfg.min_pts_x, km = true(size(yy)); end
yy = yy(km);
if strcmpi(cfg.agg,'median'), ypu(k)=median(yy,'omitnan'); elseif strcmpi(cfg.agg,'mean'), ypu(k)=mean(yy,'omitnan'); elseif strcmpi(cfg.agg,'min'), ypu(k)=min(yy); elseif strcmpi(cfg.agg,'max'), ypu(k)=max(yy); end
end
h.lines_p(gi) = plot(xu, ypu, ...
'LineWidth', max(1.2, cfg.plot.lineWidth-0.2), ...
'Marker', cfg.plot.marker_precoded, 'MarkerSize', 3, ...
'Color', colL, 'LineStyle', ':', ...
'DisplayName', [char(lbl) ' (precoded)']);
end
end
%% ---- Axes / Labels / FEC
ylabel(cfg.y_axis, 'Interpreter','none');
xlabel(x_label, 'Interpreter','none');
set(gca, 'YScale', cfg.y_scale, 'FontSize', 11);
% legend('Location', cfg.plot.legendLocation); box on;
xticks(floor(xu));
xlim([min(xu), max(xu)])
if startsWith(cfg.y_axis,"BER",'IgnoreCase',true)
for v = cfg.fec_lines
yline(v, '--', 'Color', cfg.plot.fecColor, ...
'LineWidth', cfg.plot.fecLineWidth, 'HandleVisibility','off');
end
ylim([1e-5, 0.5]);
yticks([1e-5, 1e-4, 1e-3, 1e-2, 1e-1]);
end
end % ===== main =====
%% ===================== Helpers =====================
function cfg = filldefaults(cfg, defs)
fn = fieldnames(defs);
for i = 1:numel(fn)
f = fn{i};
if ~isfield(cfg, f) || isempty(cfg.(f))
cfg.(f) = defs.(f);
elseif isstruct(defs.(f)) && isstruct(cfg.(f))
cfg.(f) = filldefaults(cfg.(f), defs.(f)); % recursive for structs
end
end
end
function out = tern(cond, a, b)
if cond
out = a;
else
out = b;
end
end
function T2 = applyFilters(T, filters)
if isempty(filters), T2 = T; return; end
keep = true(height(T),1);
fns = fieldnames(filters);
for i = 1:numel(fns)
name = fns{i};
if ~ismember(name, T.Properties.VariableNames)
warning('Filter column "%s" not found. Ignored.', name); %#ok<*WNTAG>
continue
end
val = filters.(name);
col = T.(name);
if isa(val,'function_handle')
m = val(col);
if ~islogical(m) || ~isequal(size(m), size(col))
error('Filter for %s must return logical mask of same size.', name);
end
keep = keep & m;
else
keep = keep & ismember(col, val);
end
end
T2 = T(keep,:);
end
function [x, label] = computeX(T, whichX)
switch lower(whichX)
case {'symbolrate','baudrate'}
x = T.symbolrate * 1e-9;
label = 'Symbol rate [GBd]';
case 'bitrate'
if ~ismember('pam_level', T.Properties.VariableNames)
error('bitrate requires "pam_level" column.');
end
bits = floor(log2(double(T.pam_level))*10)/10;
x = (T.symbolrate .* bits) * 1e-9;
label = 'Grossrate [Gb/s]';
case 'grossrate'
x = (T.grossrate) * 1e-9;
label = 'Grossrate [Gb/s]';
otherwise
if ~ismember(whichX, T.Properties.VariableNames)
error('x_axis "%s" not found in table.', whichX);
end
x = T.(whichX);
label = whichX;
end
x = double(x(:));
end
function keep = outlierMask(y, cfg, useLog)
if isempty(y), keep = false(size(y)); return; end
y = y(:);
switch lower(cfg.outlier)
case 'none'
keep = true(size(y)); return
case 'mad'
z = tern(useLog, log10(y), y);
med = median(z,'omitnan');
madv = median(abs(z-med),'omitnan');
if ~(isfinite(madv) && madv>0)
keep = true(size(y)); return
end
sigma = 1.4826*madv;
zz = tern(useLog, log10(y), y);
keep = abs(zz - med) <= cfg.mad_z*sigma;
case 'pctl'
pr = prctile(y, cfg.pct_limits);
keep = (y >= pr(1)) & (y <= pr(2));
otherwise
error('Unknown outlier mode "%s".', cfg.outlier);
end
end
function s = buildLabel(grpRow, group_by)
parts = strings(1, numel(group_by));
for i = 1:numel(group_by)
key = group_by{i};
val = grpRow.(key);
if iscell(val), val = val{1}; end
if islogical(val), val = tern(val,'w/','w/o'); end
if key == "equalizer_structure"
key = '';
val = upper(val);
val = strrep(val,'_',' ');
end
if key == "pre_emph"
% key = strrep(key,'_','-');
val = [val, ' pre-emph.'];
key = '';
end
parts(i) = sprintf('%s %s', key, string(val));
end
s = strjoin(parts, ', ');
end
function [cols_line, cols_scatter] = buildGroupColors(nG, plotcfg)
% --- 1) User-provided custom colors -------------------------------
if isfield(plotcfg,'custom_colors') && ~isempty(plotcfg.custom_colors)
C = plotcfg.custom_colors;
if size(C,1) < nG
error('custom_colors must have at least nG=%d rows.', nG);
end
cols_line = C(1:nG, :);
% Scatter colors: either user-provided or lightened
if isfield(plotcfg,'custom_colors_scatter') && ~isempty(plotcfg.custom_colors_scatter)
Cs = plotcfg.custom_colors_scatter;
if size(Cs,1) < nG
error('custom_colors_scatter must have at least nG=%d rows.', nG);
end
cols_scatter = Cs(1:nG, :);
else
% auto-lighten scatter colors
cols_scatter = zeros(nG,3);
for i = 1:nG
cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.40);
end
end
return;
end
% --- 2) Standard behavior (using cbrewer2 or fallback) ------------
useBrewer = plotcfg.use_cbrewer2 && exist('cbrewer2','file')==2;
if useBrewer
N = max(2*nG, 12);
C = cbrewer2(plotcfg.colormap, N);
cols_line = zeros(nG,3);
cols_scatter = zeros(nG,3);
for i = 1:nG
if plotcfg.paired_dark_first
dark = C(2*i-1, :);
light = C(2*i, :);
else
light = C(2*i-1, :);
dark = C(2*i, :);
end
cols_line(i,:) = dark;
cols_scatter(i,:) = light;
end
else
C = lines(max(nG,7));
cols_line = C(1:nG,:);
cols_scatter = zeros(nG,3);
for i = 1:nG
cols_scatter(i,:) = lightenColor(cols_line(i,:), 0.50);
end
end
end
function c2 = lightenColor(c, fracTowardWhite)
c = c(:).';
c2 = (1-fracTowardWhite)*c + fracTowardWhite*1;
end

View File

@@ -0,0 +1,38 @@
X-Werte;PAM-2;PAM-4;PAM-6;PAM-8;PAM-12
224;204;;;;
205,5;193,4;;;;
205,1;189,7;;;;
192;168;;;;
240;;427,4;;;
225;;420,5;;;
210;;336;;;
184;;332;;;
192;;320;;;
176;;306,0869565;;;
190;;304;;;
168;;294;;;
156,2;;287,1;;;
160,8;;286,9;;;
170;;272;;;
132;;250,9505703;;;
112;;209,3457944;;;
172;;337;;;
216;;;474,6;;
147,2;;;329,9;;
132;;;319,7891753;;
143,1;;;318;;
160;;;377;;
225;;;;562,5;
200;;;;510;
160;;;;438;
180;;;;432;
180;;;;432;
144;;;;384;
143,7;;;;363,4;
144;;;;360;
136;;;;353,859497;
136;;;;342,7995295;
128;;;;329,0488432;
129,7;;;;311,2;
160;;;;413;
160;;;;;481,2
1 X-Werte PAM-2 PAM-4 PAM-6 PAM-8 PAM-12
2 224 204
3 205,5 193,4
4 205,1 189,7
5 192 168
6 240 427,4
7 225 420,5
8 210 336
9 184 332
10 192 320
11 176 306,0869565
12 190 304
13 168 294
14 156,2 287,1
15 160,8 286,9
16 170 272
17 132 250,9505703
18 112 209,3457944
19 172 337
20 216 474,6
21 147,2 329,9
22 132 319,7891753
23 143,1 318
24 160 377
25 225 562,5
26 200 510
27 160 438
28 180 432
29 180 432
30 144 384
31 143,7 363,4
32 144 360
33 136 353,859497
34 136 342,7995295
35 128 329,0488432
36 129,7 311,2
37 160 413
38 160 481,2

View File

@@ -0,0 +1,76 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
M = 8;
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'symbolrate','EQUALS', 165e9); %150, 165, 180, 195, 210, 225, 240
fp.where('Runs', 'fiber_length','EQUALS', 2);
% fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis
% fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_alltime')]; %dashboard_ungrouped_after_nov_2025 dashboard_ungrouped_aug_nov_2025
[dataTable,~] = db.queryDB(fp, fields);
%%
cfg = struct;
cfg.x_axis = 'grossrate'; % 'symbol rate' | 'bitrate' | 'wavelength' grossrate
cfg.y_axis = 'BER'; % 'BER' | 'GMI' | 'AIR' | ...
cfg.group_by = {'equalizer_structure','pre_emph'};
cfg.filters = struct('is_mpi',0,'pam_level',M,'equalizer_structure',[equalizer_structure.ml_mlse]);%,equalizer_structure.vnle_pf_mlse,equalizer_structure.vnle]);
cfg.y_scale = 'auto'; % auto -> log for BER*, linear otherwise
cfg.outlier = 'mad'; % simple, robust; 'none' or 'pctl' also available
cfg.show_raw = false;
cfg.show_spread = 'none'; % 'none' or 'iqr' or minmax
cfg.agg = 'min'; % or 'median'
cfg.show_precoded = 0;
cfg.fec_lines = [2.2e-4 4.85e-3 2e-2]; % optional
cfg.figure_number = 42;
cfg.plot.custom_colors = [
clr.Paired.red;
clr.Paired.blue;
clr.Paired.green;
clr.Paired.orange;
clr.Paired.purple
];
cfg.plot.custom_colors_scatter = [
clr.Paired.lightred;
clr.Paired.lightblue;
clr.Paired.lightgreen;
clr.Paired.lightorange;
clr.Paired.lightpurple
];
% New styling knobs
cfg.plot.use_cbrewer2 = true;
cfg.plot.colormap = 'Paired';
cfg.plot.paired_dark_first = false; % dark for lines, light for scatter
cfg.plot.lineWidth = 2.0;
cfg.plot.errWidth = 1.2;
cfg.plot.scatterAlpha = 0.35;
cfg.plot.legendLocation = 'best';
cfg.plot.fecLineWidth = 2.4; % thicker FEC limits
plot_measurements_gpt(dataTable, cfg);
% beautifyBERplot()
%% FIG PRE EMPHASIS

View File

@@ -0,0 +1,87 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
M = 8;
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'symbolrate','EQUALS', 165e9);
fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18);
fp.where('Runs', 'wavelength','EQUALS', 1310);
% fp.where('Runs', 'db_mode','EQUALS', 1); % 0 == high preemphasis // 1 == low preemphasis
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
fields = [fields; db.getTableFieldNames('dashboard_ungrouped_new')];
[dataTable,~] = db.queryDB(fp, fields);
eqstructures = unique(dataTable.equalizer_structure);
% Create the figure
figure(18);
hold on
for pre_emph = [0,1]
dbmode_filtered = dataTable(dataTable.db_mode == ~pre_emph,:);
for eqs = [equalizer_structure.vnle]
eq_choice = equalizer_structure(eqs);
if sum(eqstructures == eq_choice)~=1
disp(eq_choice)
continue
end
eq_filtered = dbmode_filtered(dbmode_filtered.equalizer_structure == eq_choice,:);
if eqs ==equalizer_structure.vnle_pf_mlse
eq_filtered = eq_filtered(eq_filtered.DIR == "1",:);
end
symbolrate_sorted = sortrows(eq_filtered,{'symbolrate'}, 'ascend');
% Example data (replace these with your real vectors)
symbolrate = symbolrate_sorted.symbolrate.*1e-9; % in baud
bitrate = symbolrate * 2;
gmi = symbolrate_sorted.GMI; % BER
snr = symbolrate_sorted.SNR; % BER
cols = cbrewer2('Paired',12);
dname = [char(eq_choice)];
dname = strrep(dname,'_','+');
if pre_emph
dname = [dname,'; w/ pre-emph.'];
else
dname = [dname,'; w/o pre-emph.'];
end
plot(symbolrate, snr, '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]);
grid on;
% Axis labels and title
xlabel('Bit Rate Gbps', 'FontSize', 12);
ylabel('GMI', 'FontSize', 12);
title('GMI vs. Bit Rate', 'FontSize', 14, 'FontWeight', 'bold');
% Improve tick formatting
set(gca, 'XScale', 'linear', ...
'YScale', 'linear', ...
'TickLabelInterpreter', 'none', ...
'FontSize', 11);
legend
xticks(symbolrate);
% Optional: tighten axis limits
xlim([min(symbolrate), max(symbolrate)]);
% ylim([log2(M)-1, log2(M)]);
end
end

View File

@@ -0,0 +1,96 @@
database_type = 'mysql';
dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
db = DBHandler("dataBase", [dataBase], "type", database_type);
fp = QueryFilter();
fp.where('power_state_info', 'pam_level','EQUALS', 4);
fp.where('power_state_info', 'db_mode','EQUALS', 1);
% fp.where('power_state_info', 'fiber_length','EQUALS', 1);
fp.where('power_state_info', 'is_mpi','EQUALS', 0);
fields = db.getTableFieldNames('power_state_info');
% [dataTable,~] = db.queryDB(fp, fields);
fiber_len = unique(dataTable.fiber_length);
cnt = 0;
y_variable = 'power_mzm';
x_variable = "wavelength";
f = figure(3);
clf
hold on
for fl = 1:numel(fiber_len)
fl_filtered = dataTable(dataTable.fiber_length == fiber_len(fl),:);
[~, ia] = unique(fl_filtered.run_id, 'first');
fl_filtered = fl_filtered(ia, :);
fl_filtered_ = groupsummary( ...
fl_filtered, ... % input table
x_variable, ... % grouping variable
"mean", ... % which summary statistic
y_variable); % which column to average
wavelength_sorted = sortrows(fl_filtered, {'wavelength'}, 'ascend');
% pull out your vectors
lambda = wavelength_sorted.wavelength;
power_laser = wavelength_sorted.power_laser;
power_mzm = wavelength_sorted.power_mzm;
power_rop = wavelength_sorted.power_rop;
power_pd = wavelength_sorted.power_pd_in;
voa = wavelength_sorted.voa_atten;
len = wavelength_sorted.fiber_length;
run_ids = wavelength_sorted.run_id; % <-- this is what we want in the datatip
cols = linspecer(8);
% plot the two curves and capture their Line handles
% h1 = plot(lambda, power_laser,'LineWidth', 0.5, 'MarkerSize', 4,'Marker','o','LineStyle','none','Color',cols(fl,:),'MarkerFaceColor',cols(fl,:),'DisplayName','Laser Output');
% % Add run_id as a datatip row
% % For each line, tell the datatip template where to find the run_id:
% h1.DataTipTemplate.DataTipRows(end+1) = ...
% dataTipTextRow('run\_id', run_ids);
% h1.DataTipTemplate.DataTipRows(end+1) = ...
% dataTipTextRow('len', run_ids);
% h1.DataTipTemplate.DataTipRows(end+1) = ...
% dataTipTextRow('voaatten', voa);
%
dname = sprintf('%s; %d km',y_variable, fiber_len(fl));
h2 = plot(fl_filtered_.(x_variable), fl_filtered_.(['mean_',y_variable]), 'LineWidth', 1, 'MarkerSize', 4,'Marker','o','LineStyle','-','Color',cols(fl,:),'MarkerFaceColor',cols(fl,:),'DisplayName',dname);
h2.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('run\_id', run_ids);
h2.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('len', len);
h2.DataTipTemplate.DataTipRows(end+1) = ...
dataTipTextRow('voaatten', voa);
grid on;
xticks(sort(unique(lambda)));
xticklabels(sort(unique(lambda)));
% Labels, scales, legend, etc.
xlabel('Wavelength in nm','FontSize',12);
ylabel('Power in dB','FontSize',12);
title('Power ','FontSize',14,'FontWeight','bold');
set(gca, 'XScale','linear','YScale','linear','FontSize',11);
legend
xlim([min(lambda)-2, max(lambda)+2]);
ylim([floor(min(fl_filtered_.(['mean_',y_variable])))-1 12]);
ylim([-12 12]);
cnt = cnt+1;
yline(8,'HandleVisibility','off');
end
yline([4.85e-3, 2e-2],'--','LineWidth',1,'HandleVisibility','off');
posH = get(f, 'Position'); % [left, bottom, width, height]
newPos = [posH(1), posH(2), 750, 300];
set(f, 'Position', newPos);

View File

@@ -0,0 +1,379 @@
% === SETTINGS ===
dsp_options.append_to_db = 1;
dsp_options.max_occurences = 1;
experiment = "highspeed_2024";
dsp_options.mode = "load_run_id"; % 'simulate' & 'load_files'
dsp_options.load_file_path = struct();
if dsp_options.mode == "load_run_id"
if experiment == "highspeed_2024"
dsp_options.database_type = 'mysql';
dsp_options.dataBase = 'labor_highspeed';%'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\silas_labor_newdsp_newstructure.db';
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
elseif experiment == "mpi_ecoc_2025"
dsp_options.database_type = 'mysql';
dsp_options.dataBase = 'labor';
dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\';
db = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
end
elseif dsp_options.mode == "load_files"
dsp_options.load_file_path.tx_bits_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_bits.mat"';
dsp_options.load_file_path.tx_symbols_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091513_PAM_4_R_112_symbols.mat"';
dsp_options.load_file_path.rx_raw_path = "Z:\2025\ECOC Silas\ecoc_2025\mpi_opti_1000m_pam2 4 6 8\20250417_091525_PAM_4_R_112_rec01_rx_signal_raw.mat"';
elseif dsp_options.mode == "simulate"
error('Not yet implemented')
end
% === Get Run ID's ===
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 2776);
M = 6;
fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390
% fp.where('Runs', 'symbolrate','EQUALS', 195e9);
fp.where('Runs', 'fiber_length','EQUALS', 10);
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','EQUAL', 0);
% fp.where('Runs', 'power_pd_in','LESS_THAN', 7);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
% === Set LOOPS & Initialize DataStorage ===
dsp_options.parameters = struct();
% dsp_options.parameters.pf_ncoeffs = [1,2];%s[0,logspace(-4,0,10)];
wh = DataStorage(dsp_options.parameters);
wh.addStorage("ffe_package");
wh.addStorage("mlse_package");
wh.addStorage("vnle_package");
wh.addStorage("dbtgt_package");
wh.addStorage("dbenc_package");
wh.addStorage("mlmlse_package");
%% === RUN IT ===
[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true);
%% =========================================================================
% LOAD METADATA
% =========================================================================
[dataTable, ~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
results = results(:).'; % ensure row vector
N = numel(results);
% =========================================================================
% PREALLOCATE METRIC ARRAYS
% =========================================================================
BER_VNLE = nan(1,N);
BER_MLSE = nan(1,N);
BER_DB = nan(1,N);
BER_DB_PREC = nan(1,N);
BER_MLMLSE = nan(1,N);
BER_MLMLSE_PREC = nan(1,N);
% =========================================================================
% EXTRACT METRICS (ONE LOOP, ROBUST)
% =========================================================================
for i = 1:N
r = results{i};
% ---- VNLE (no-DB mode) ----
if isfield(r, 'vnle_package') && ~isempty(r.vnle_package)
pkg = r.vnle_package;
BER_VNLE(i) = min(cellfun(@(c) c.metrics.BER, pkg));
end
% ---- Classical MLSE (DB mode) ----
if isfield(r, 'mlse_package') && ~isempty(r.mlse_package)
pkg = r.mlse_package;
BER_MLSE(i) = min(cellfun(@(c) c.metrics.BER, pkg));
BER_MLSE_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg));
end
% ---- DB Target (DB mode) ----
if isfield(r, 'dbtgt_package') && ~isempty(r.dbtgt_package)
pkg = r.dbtgt_package;
BER_DB(i) = min(cellfun(@(c) c.metrics.BER, pkg));
BER_DB_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg));
end
% ---- ML-based MLSE (both modes) ----
if isfield(r, 'mlmlse_package') && ~isempty(r.mlmlse_package)
pkg = r.mlmlse_package;
% raw BER
BER_MLMLSE(i) = min(cellfun(@(c) c.metrics.BER, pkg));
% precoded BER
if isfield(pkg{1}.metrics, 'BER_precoded')
BER_MLMLSE_PREC(i) = min(cellfun(@(c) c.metrics.BER_precoded, pkg));
end
end
end
%% =========================================================================
% METADATA (ALWAYS INDEX-ALIGNED WITH RESULTS)
% =========================================================================
bitrate = dataTable.bitrate(:).';
baudrate = dataTable.symbolrate(:).';
rop_atten = dataTable.rop_attenuation(2:2:end).';
rop_pre = dataTable.power_rop(1:2:end).';
rop = dataTable.power_rop(2:2:end).';
% =========================================================================
% PLOT STYLE
% =========================================================================
STYLE_BASE = 2;
MARKER_SIZE = STYLE_BASE;
LINE_WIDTH = max(2, STYLE_BASE/3);
cols = cbrewer2('Paired', 8);
cm.VNLE = cols(1,:);
cm.MLSE = cols(2,:);
cm.DB_PREC = cols(3,:);
cm.DB = cols(4,:);
cm.ML_MLSE = cols(6,:);
mk = @(col,shape) {'Marker',shape,'MarkerFaceColor',col,'MarkerEdgeColor',col,'MarkerSize',MARKER_SIZE};
% =========================================================================
% FIGURE 1: BER vs BAUDRATE
% =========================================================================
figure(112+M); clf; hold on;
xGHz = baudrate * 1e-9;
plot(xGHz, BER_VNLE, 'DisplayName','VNLE', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE);
plot(xGHz, BER_MLSE, 'DisplayName','MLSE', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
% plot(xGHz, BER_MLSE_PREC, 'DisplayName','MLSE', 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE);
plot(xGHz, BER_DB_PREC, 'DisplayName','Diff. Precode + DB', 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB_PREC);
plot(xGHz, BER_MLMLSE_PREC, 'DisplayName','ML-based MLSE', 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.ML_MLSE);
yline(2e-2,'LineWidth',1,'HandleVisibility','off');
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'); grid on; legend('Location','best');
% beautifyBERplot;
%% ---------------- 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);
%
% plot(xGHz, GMI_VNLE.*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(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

@@ -0,0 +1,236 @@
precomp_mode = 0;
precomp_path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT";
precomp_fn = "precomp_simulated.mat";
% TX
M = 4;
fsym = 72e9;
f_nyquist = fsym/2;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
% fdac = 2*fsym;
% fadc = 2*fsym;
random_key = 1;
duob_mode = db_mode.no_db;
tx_bwl = 0.8.*f_nyquist;
rx_bwl = 0.8.*f_nyquist;
rcalpha = 0.05;
kover = 16;
vbias_rel = 0.5;
u_pi = 2.9;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1293;
laser_linewidth = 0;
% Channel
link_length = 60000;
% RX
rop = -8;
% EQ
eq_mode = equalizer_structure.vnle_pf_mlse;
ffe_order=[50,0,0];
vnle_order=[50,5,5];
dfe_order = [0 0 0];
len_tr = 4096*2;
mu_ffe = [0.0004 0.0004 0.0004];
mu_dfe = 0.0004;
mu_dc = 0.00;
dfe_ = sum(dfe_order)>0;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
[Digi_sig,Symbols,Tx_bits] = 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,...
"duobinary_mode",duob_mode).process();
if precomp_mode == 1 % measure channel
precomp_est = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',fdac);
Digi_sig = precomp_est.buildOFDM();
elseif precomp_mode == 2 % apply precomp
precomp_est = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',Digi_sig.fs);
Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',-50,'loadPath',precomp_path,'fileName',precomp_fn);
end
% Symbols.spectrum("displayname",'Tx Symbols','fignum',10,'normalizeTo0dB',1);
Digi_sig.eye(fsym,M,"fignum",1234567);
%%%%% AWG
%El_sig = M8199B("kover",kover).process(Digi_sig);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig);
% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0);
% El_sig = El_sig.setPower(0,"dBm");
El_sig.spectrum("displayname",'Tx Signal','fignum',10,'normalizeTo0dB',0);
%%%%% Low-pass el. components %%%%%%
El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true).process(El_sig);
% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig);
El_sig = El_sig.normalize("mode","oneone");
%%%%% MODULATE E/O CONVERSION %%%%%%
[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);
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 %%%%%%
Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig);
%%%%%% PD Square Law %%%%%%
Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig);
%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%%
Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.bessel_inp,"active",true).process(Rx_sig);
% %%%%%% Low-pass Scope %%%%%%
Lp_scpe = Filter('filtdegree',4,"f_cutoff",35e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1);
%%%%%% 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(Rx_sig);
Scpe_sig.spectrum("displayname",'Rx Signal','fignum',10,'normalizeTo0dB',1);
Scpe_sig.eye(fsym,M,"fignum",1973763)
%%%%% Precompensation Routine %%%%%%
if precomp_mode == 1
Scpe_sig_resampled = Scpe_sig.resample("fs_in",fadc,"fs_out",2*fsym);
precomp_est.estimate(Scpe_sig_resampled,"save",false,"savePath",precomp_path,"fileName",precomp_fn);
precomp_est.plot();
precomp_est.save();
end
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym);
Scpe_sig.signal = Scpe_sig.signal(1:2*Symbols.length);
use_ffe = 0;
use_dfe = 0;
use_vnle_mlse = 1;
use_dbtgt = 1;
use_dbenc = 1;
if duob_mode ~= db_mode.db_encoded
if use_ffe
ffe_order = [50, 0, 0];
eq_dfe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',1,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
disp('FFE:')
ffe_results.metrics.print;
end
if use_dfe
ffe_order = [50, 5, 5];
eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
disp('DFE:')
dfe_results.metrics.print;
end
if use_vnle_mlse
if 0
pf_ncoeffs = 1;
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
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', 1, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
disp('VNLE:')
ffe_results.metrics.print;
disp('MLSE:')
mlse_results.metrics.print;
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', 1, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
disp('VNLE:')
ffe_results.metrics.print;
disp('MLSE:')
mlse_results.metrics.print;
end
if use_dbtgt
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);
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels);
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, ...
'showAnalysis',1 ,...
"postFFE", []);
disp('DB:')
dbt_results.metrics.print;
end
end
if duob_mode == db_mode.db_encoded
eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ...
"training_loops", 5, "dd_loops", 5, "K", 2, "DCmu", mu_dc, ...
"DDmu", [mu_ffe mu_dfe], "DFEmu", 0.005, "FFEmu", 0, "plotfinal", 0, "ideal_dfe", 1);
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",1,"postFFE",[]);
db_results.metrics.print;
end

View File

@@ -0,0 +1,118 @@
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1;
db = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 2);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','bitrate','EQUALS', 300e9);
fp.where('Runs','pam_level','EQUALS', 4);
fp.where('Runs','rop_attenuation','EQUALS', 0);
fp.where('Runs','is_mpi','EQUALS', 0);
fp.where('Runs', 'db_mode','EQUALS', 0);
fields = db.getTableFieldNames('Runs');
[dataTable,~] = db.queryDB(fp, fields);
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);
% Show spectrum
Scpe_sig.spectrum("fignum",1,"displayname",'Rx')
%% simple FFE
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
ffe_order = [50, 0, 0];
eq_dfe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
ffe_results.metrics.print("description",'FFE');
ffe_results.config.equalizer_structure = "ffe";
%% a) VNLE // b) concatenated VNLE + MLSE
pf_ncoeffs = 1;
ffe_order = [50, 5, 5];
dfe_order = [0,0,0];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",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
%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);
[vnle_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);
vnle_results.metrics.print("description",'VNLE');
mlse_results.metrics.print("description",'VNLE + PF + MLSE');
%% Duobinary Equalization
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);
ffe_order = [50, 5, 5];
dfe_order = [0,0,0];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",4096,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0,...
"postFFE", []);
dbt_results.metrics.print("description",'Duobinary EQ');
%% Ml based Viterbi
%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);
ml_mlse_results.metrics.print("description",'ML pre Eq. + Viterbi')

View File

@@ -0,0 +1,181 @@
%%% Run parameters
% TX
M = 4;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 2;
rcalpha = 0.05;
kover = 8;
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1300;
laser_linewidth = logspace(0,6.2,24);
% Channel
link_length = 10;
alpha = 0;
doub_mode = db_mode.no_db;
cols = linspecer(6);
rop = [-6];
bwl = [0.5:0.1:1.5];
fsym = [200:16:256].*1e9;
% nonlin_mod = [0.5:0.01:0.75];
fsym = ones(size(laser_linewidth)).*fsym(1);
nonlin_mod = ones(size(laser_linewidth)).*0.5;
ffe_results = {};
mlse_results_lin= {};
parfor r = 1:length(laser_linewidth)
Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 1;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym(r),"M",M,"order",18,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"db_precode",db_precode,"db_encode",db_encode,...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process();
El_sig = M8199B("kover",kover).process(Digi_sig);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = El_sig.normalize("mode","oneone");
%%%%% MODULATE E/O CONVERSION %%%%%
u_pi = 3.2;
vbias = -u_pi*nonlin_mod(r);
[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(r),"randomkey",random_key+1).process(El_sig);
%%%%%% Fiber %%%%%%
mpi = 1;
if mpi
Combined_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);
else
% 2) ping pong fiber propagation
mpi_path = 00;
Interference_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",mpi_path*2,"alpha",0,"D",0,"lambda0",1310,"gamma",0).process(Opt_sig);
Interference_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",-30).process(Interference_sig);
[Main_sig,dly] = Opt_sig.delay("delay_meter",mpi_path*2);
% Add
Combined_sig = Main_sig + Interference_sig;
% Cut (due to the delays there is a jump in the signals)
if dly == 0;dly = 1;end
Combined_sig.signal = Combined_sig.signal(ceil(dly):end);
% Fiber
Combined_sig = Fiber("fsimu",Combined_sig.fs,"fiber_length",2,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.08).process(Combined_sig);
end
%%%%%% ROP %%%%%%
Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Combined_sig);
%%%%%% 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 = 70e9;
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(r));
% Symbols.signal = Symbols.signal(1:Scpe_sig_2sps.length/2);
% 2sps
[~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0);
Rx_sig_2sps = Scpe_cell{1};
Rx_sig_2sps = Rx_sig_2sps.normalize("mode","rms");
% 1sps
Scpe_sig_1sps = Scpe_sig.resample("fs_out",1*fsym(r));
[~, Scpe_cell_1sps, ~, found_sync] = Scpe_sig_1sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0);
Rx_sig_1sps = Scpe_cell_1sps{1};
Rx_sig_1sps = Rx_sig_1sps.normalize("mode","rms");
%% RUN DSP
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_ffe2 mu_ffe3];
mu_dfe = 0.0004;
pf_ncoeffs = 1;
ffe_order = [50, 3, 3];
mu_lms = 0.0005;
eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms");
% eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",1,"DCmu",0.00,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0);
[ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
ffe_results{r}.metrics.print;
mlse_results_lin{r}.metrics.print;
end
figure(1);hold on;
plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, ffe_results),'DisplayName','VNLE')
plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, mlse_results_lin),'DisplayName','VNLE+MLSE')
xlabel('Linewidth [GHz]');
ylabel('BER')
set(gca,'YScale','log');
legend;
ylim([1e-5 1e-1]);
beautifyBERplot;
figure();hold on;
plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, ffe_results),'DisplayName','FFE')
plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, mlse_results_lin),'DisplayName','FFE+MLSE')
xlabel('Linewidth [GHz]');
ylabel('AIR [GBd]')
% set(gca,'YScale','log');
legend;
beautifyBERplot("logscale",0);
figure(); hold on
stem(calcWavelengthPlan(16,400e9,1310),ones(16,1),'DisplayName','16x400','Marker','.','LineWidth',1);
stem(calcWavelengthPlan(8,800e9,1310),ones(8,1),'DisplayName','8x800','Marker','.','LineWidth',1);
stem(calcWavelengthPlan(16,800e9,1310),ones(16,1),'DisplayName','16x800','Marker','.','LineWidth',1);
ylim([0,1.2]);
ylabel('wavelength [nm]');
xlim([1270, 1350])

View File

@@ -0,0 +1,569 @@
%%% Run parameters
% TX
M = 4;
m = floor(log2(M)*10)/10;
fsym = 224e9;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 2;
rcalpha = 0.05;
kover = 8;
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1310;
laser_linewidth = 1e6;
% Channel
link_length = 0;
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);
rop = [-6];
bwl = [0.5:0.1:1.5];
fsym = [192:16:256].*1e9;
fsym = 208e9;
ber_vnle = [];
ber_mlse = [];
ber_mlse_burg = [];
ber_viterbi = [];
ber_db = [];
ber_db_diff_precoded = [];
gmi_vnle_bitwise = [];
gmi_mlse = [];
gmi_mlse_db = [];
for r = 1:length(fsym)
Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 1;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym(r),"M",M,"order",19,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"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 = 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 = 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 %%%%%
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);
if 0
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);
end
%%%%%% 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 = 70e9;
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(r));
% Scpe_sig_resampled.signal = Scpe_sig_resampled.signal(1:2*length(Symbols));
[~, 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
%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);
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);
viterbi = 0;
if viterbi
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_.DIR = [1,1];
[eq_signal_whitened] = 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];
[eq_signal_whitened,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols);
end
mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(eq_signal_whitened);
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M);
tx_symbols_precoded = Duobinary().encode(Symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
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),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_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",[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_fullresp, eq_noise] = eq_.process(Rx_sig, Symbols);
showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ');
[mi_gomez(r)] = calc_air(eq_signal_fullresp, Symbols, "skip_front", 100, "skip_end", 100);
[gmi_vnle_bitwise(r)] = calc_ngmi(eq_signal_fullresp,Symbols);
[gmi_bitwise_2(r)] = calc_gmi_bitwise(eq_signal_fullresp,Symbols);
snr_vnle(r) = calc_snr(Symbols, eq_signal_fullresp-Symbols);
% eq_signal_fullresp.plot("displayname",'bla','fignum',199);
% eq_signal_fullresp.eye(fsym(r),M,"fignum",103837);
% Hard decision on VNLE output
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_fullresp);
rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd);
[~,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_fullresp,Symbols,"displayname",'VNLE Out','f_sym',fsym(r),'fignum',201);
% show2Dconstellation(eq_signal_fullresp,Symbols,"displayname",'VNLE Out','fignum',2241);
fprintf('BER VNLE: %.2e \n',ber_vnle(r));
fprintf('NGMI VNLE: %.2f \n',gmi_vnle_bitwise(r)./m);
if 1
% Process through postfilter and MLSE
pf_ncoeffs = 1;
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
% showEQNoisePSD(eq_noise,"postfilter_taps",pf_.coefficients,"displayname",'Postfilter Burg based');
alpha(r) = pf_.coefficients(2);
alpha_vec = max(0,round(alpha(r),2)-0.2):0.025:round(alpha(r),2)+0.4;
alpha_vec = unique(sort([alpha_vec, 1, alpha(r)]));
gmi_mlse_ = zeros(size(alpha_vec));
ber_mlse_ = zeros(size(alpha_vec));
parfor a=1:numel(alpha_vec)
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)]);
pf_ = Postfilter("ncoeff",1,"useBurg",0,"coefficients",[1,alpha_vec(a)]);
[eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise);
[signalclass_hd,LLR,gmi_mlse_(a)] = mlse_.process(eq_signal_whitened,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);
[~,tot_err,ber_mlse_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% burst_mlse(r,:) = count_error_bursts(errpos, 10);
% if 0
% fprintf('BER MLSE: %.2e \n',ber_mlse(r));
% fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m);
%
% showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla');
%
% 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);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
% [eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise);
% mlse_.DIR = pf_.coefficients;
% mlse_output = mlse_.process(eq_signal_whitened);
% mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_output);
% rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd);
% [~,~,ber_viterbi(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('Viterbi BER: %.2e \n',ber_viterbi(r));
% end
end
[ber_mlse(r),idx] = min(ber_mlse_);
gmi_mlse(r) = gmi_mlse_(idx);
ber_mlse_burg(r) = ber_mlse_(alpha_vec==alpha(r));
best_alpha(r) = alpha_vec(idx);
end
% IR target in EQ
if 1
% alpha_vec = max(0,round(alpha(r),2)-0.1):0.01:min(1,round(alpha(r),2)+0.1);
plot_stuff = 0;
gmi_mlse_pr_tgt_ = zeros(size(alpha_vec));
ber_mlse_pr_tgt_ = zeros(size(alpha_vec));
for a = 1:numel(alpha_vec)
alpha_vec(a) = 0.9;
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);
Symbols_filt = Symbols.filter([1,alpha_vec(a)],1);
[eq_signal_prtgt, eq_noise] = eq_.process(Rx_sig, Symbols_filt);
showLevelHistogram(eq_signal_prtgt,Symbols_filt,"displayname",'VNLE Out','fignum',201);
if plot_stuff
% Plot the response for respective EQ targets
Symbols_filt.spectrum("displayname",'IDEAL Filtered Reference','fignum',240587);
eq_signal_whitened.spectrum("displayname",'Full tgt. EQ + PF','fignum',240587);
eq_signal_prtgt.spectrum("displayname",'Partial Resp. Target EQ','fignum',240587);
noise_pf_out = Symbols_filt-eq_signal_whitened;
noise_pr_tgt = Symbols_filt-eq_signal_prtgt;
noise_pf_out.spectrum("displayname",'Ideal PR - Whitening Out','fignum',240588);
noise_pr_tgt.spectrum("displayname",'Ideal PR - PR Target Out','fignum',240588);
end
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)],'debug',0);
[signalclass_hd,LLR,gmi_mlse_pr_tgt_(a)] = mlse_.process(eq_signal_prtgt,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);
[~,tot_err,ber_mlse_pr_tgt_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% burst_mlse_(a,:) = count_error_bursts(errpos, 10);
% fprintf('BER MLSE: %.2e \n',ber_mlse_pr_tgt_(a));
% fprintf('NGMI MLSE: %.5f \n',gmi_mlse_pr_tgt_(a)./m);
end
[ber_mlse_pr_tgt(r),idx] = min(ber_mlse_pr_tgt_);
gmi_mlse_pr_tgt(r) = gmi_mlse_pr_tgt_(idx);
best_alpha_pr_tgt(r) = alpha_vec(idx);
end
cols = cbrewer2('paired',8);
figure(); hold on
title(sprintf('%d GBd',fsym(r).*1e-9));
scatter(alpha_vec,ber_mlse_,15,'Marker','o','LineWidth',1,'DisplayName','MLSE','MarkerEdgeColor',cols(1,:));
scatter(best_alpha(r),ber_mlse(r),15,'Marker','o','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:));
scatter(alpha(r),ber_mlse_burg(r),25,'Marker','+','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:));
scatter(1,ber_db_diff_precoded(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:));
scatter(1,ber_db(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:));
scatter(alpha_vec,ber_mlse_pr_tgt_,15,'Marker','x','LineWidth',1,'DisplayName','MLSE Partial Resp tgt','MarkerEdgeColor',cols(5,:));
scatter(best_alpha_pr_tgt(r),ber_mlse_pr_tgt(r),25,'Marker','x','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(6,:));
set(gca,"YScale","log");
% ylim([1e-6 0.5]);
% xlim([0.1 1]);
drawnow;
end
% --- 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 : 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);
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');
% 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)
% LOGSUMEXP Compute log(sum(exp(a))) in a numerically stable way
m = max(a);
s = m + log(sum(exp(a - m)));
end

View File

@@ -0,0 +1,192 @@
%%% Run parameters
% TX
M = 4;
fsym = 180e9;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 1;
db_precode = 0;
db_encode = 0;
rcalpha = 0.05;
kover = 16;
vbias_rel = 0.5;
u_pi = 2.9;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1310;
laser_linewidth = 0;
tx_bw_nyquist = 1;
% Channel
link_length = 1;
% RX
rop = 0;
rx_bw_nyquist = 0.99;
vnle_order1 = 50;
vnle_order2 = 3;
vnle_order3 = 3;
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dfe_order = [0 0 0];
pf_ncoeffs = 1;
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;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.db_precoded;
apply_pulsef = 1;
[Digi_sig,Symbols,Tx_bits] = 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,...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process();
Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1);
%% proof of concept
Symbols_db = Duobinary().encode(Symbols);
mim_decoded = Duobinary().decode(Symbols_db,"M",M);
rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded);
rx_bits_mim_decoded_.signal = circshift(rx_bits_mim_decoded.signal,0);
[~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('BER mim: %.2e \n',ber_mim_decode);
%%
%%%%% AWG
% El_sig = M8199A("kover",kover).process(Digi_sig);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig);
% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0);
% El_sig = El_sig.setPower(0,"dBm");
%%%%% Low-pass el. components %%%%%%
f_nyquist = fsym/2;
tx_bwl = tx_bw_nyquist.*f_nyquist;
% tx_bwl = 80e9;
El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig);
El_sig = El_sig.normalize("mode","oneone");
%%%%% MODULATE E/O CONVERSION %%%%%%
[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);
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 %%%%%%
Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig);
%%%%%% PD Square Law %%%%%%
Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig);
%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%%
rx_bwl = rx_bw_nyquist.*f_nyquist;
% rx_bwl = 80e9;
Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig);
% %%%%%% Low-pass Scope %%%%%%
Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1);
%%%%%% 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(Rx_sig);
Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym);
[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
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);
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);
if duob_mode == db_mode.no_db
% FFE or VNLE
[eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols);
% Hard decision on VNLE output
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);
mlse_.DIR = pf_.coefficients;
mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols);
% BER
rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd);
[~,tot_err,ber_vnle,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_sd);
[~,tot_err,ber_mlse,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
elseif duob_mode == db_mode.db_precoded
%%
[EQ_sig, Noi] = eq_.process(Scpe_cell{1},Duobinary().encode(Symbols));
showLevelHistogram(EQ_sig,Duobinary().encode(Symbols),"displayname",101);
mim_decoded = Duobinary().decode(EQ_sig,"M",M);
showLevelHistogram(mim_decoded,Symbols,"displayname",101);
rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded);
rx_bits_mim_decoded_.signal = circshift(rx_bits_mim_decoded.signal,0);
[~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('BER mim: %.2e \n',ber_mim_decode);
%%
mlse_sig_hd = MLSE("DIR",[1,1],"duobinary_output",1,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig,Symbols);
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M);
rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_decoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
%%
end

View File

@@ -0,0 +1,164 @@
%% analyze_filter_length.m
clear; clc;
M = 4;
randkey = 1;
% --- Parameter sweep
order_range = 2:3:11; % FFE order
delta_range = 0:2:4; % delta
SNR_dB = 20;
% --- Prepare bit sequence
order_bits = 19;
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
N = 2^(order_bits-1);
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
Bits = Informationsignal(bitpattern);
Symbols = PAMmapper(M,0).map(Bits);
Symbols.fs = 200e9;
% --- Channel (minimal ISI + AWGN)
h = [0.3 0.9 0.3]; h = h/norm(h);
symbols_filt = Symbols.filter(h,1);
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal,SNR_dB,'measured');
% --- Generate all parameter pairs
[O,D] = ndgrid(order_range, delta_range);
pairs = [O(:), D(:)];
training_len = 100;
ber_vec = nan(size(pairs,1),1); % initialize with NaN
ber_training = nan(size(pairs,1),training_len);
ce_vec = nan(size(pairs,1),1);
ce_training = nan(size(pairs,1),training_len);
% --- Parallel loop over parameter pairs
parfor k = 1:size(pairs,1)
order_k = pairs(k,1);
delta_k = pairs(k,2);
% Skip invalid combinations (delay cannot exceed filter length)
if abs(delta_k) >= order_k
fprintf('Skip: order=%d, delta=%d (invalid)\n', order_k, delta_k);
continue;
end
try
ml = ML_MLSE("epochs_tr",training_len,"epochs_dd",1,"len_tr",2^15, ...
"mu_dd",0.1,"mu_tr",0.1,"order",order_k,"sps",1, ...
"traceback_depth",128,"L",3,"delta",delta_k,"adaptive_mu",0);
[y_ml,y_ref] = ml.process(symbols_noi,Symbols);
ref_bits = PAMmapper(M,0).demap(y_ref);
eq_bits = PAMmapper(M,0).demap(y_ml);
ber_training(k,:) = ml.ber;
ce_training(k,:) = ml.ce;
[~,~,ber_vec(k)] = calc_ber(eq_bits.signal, ref_bits.signal, ...
"skip_front",10,"skip_end",10);
L = min(length(ml.ce),30);
ce_vec(k) = mean(ml.ce(end-L+1:end));
fprintf('order=%d, delta=%d BER=%.2e, CE=%.3f\n', ...
order_k, delta_k, ber_vec(k), ce_vec(k));
catch ME
fprintf('Error at order=%d, delta=%d: %s\n', ...
order_k, delta_k, ME.message);
ber_vec(k) = NaN;
ce_vec(k) = NaN;
end
end
% --- reshape to 2D matrices
ber_mat = reshape(ber_vec, numel(order_range), numel(delta_range));
ce_mat = reshape(ce_vec, numel(order_range), numel(delta_range));
%% --- Plot BER
figure; hold on
cols = cbrewer2('Set1',10);
for i = 1:numel(delta_range)
plot(order_range,ber_mat(:,i),'DisplayName',sprintf('delta: %d',delta_range(i)),'Color',cols(i,:))
end
beautifyBERplot
ylabel('BER'); xlabel('Filter Order [N]');
title('BER vs. Filter order');
ylim([1e-4, 0.1]);
yline(3.8e-3,'HandleVisibility','off');
yline(2.2e-4,'HandleVisibility','off');
%% --- Plot Cross-Entropy
figure; hold on
for i = 1:numel(delta_range)
plot(order_range,ce_mat(:,i),'DisplayName',sprintf('delta: %d',delta_range(i)))
end
% beautifyBERplot
ylabel('BER'); xlabel('Filter Order [N]');
title('BER vs. Filter order');
%% --- Training Curves: BER and CE per combination
figure('Name','Training Convergence'); hold on
cols = cbrewer2('Set1', 10); % one color per delta
[O, D] = ndgrid(order_range, delta_range);
for i = 1:size(ber_training,1)
ord = O(i);
del = D(i);
if ord <= del
continue;
end
% --- show only order 2 and 10
if ord == 2
lnst = '-';
elseif ord == 5
lnst = ':';
elseif ord == 8
lnst = '--';
elseif ord == 11
lnst = '-.';
end
b = ber_training(i,:);
plot_label = sprintf('order=%d, delta=%d', ord, del);
plot(1:length(b), b, 'Color', cols(del+1, :), ...
'DisplayName', plot_label,'LineStyle',lnst);
end
set(gca,'YScale','log');
xlabel('Epoch');
ylabel('BER');
title('Training Convergence (BER)');
legend('show');
grid on;
%% --- Cross-Entropy curves
figure('Name','Cross-Entropy'); hold on
cols = cbrewer2('Set1',size(ce_training,1));
for i = 1:size(ce_training,1)
[O, D] = ndgrid(order_range, delta_range);
plot_label = sprintf('order=%d, delta=%d', O(i), D(i));
c = ce_training(i,:);
c(~isfinite(c) | c==0) = NaN;
if all(isnan(c)), continue; end
plot(1:length(c), c, 'Color', cols(D(i)+1,:), ...
'DisplayName', plot_label);
end
set(gca,'YScale','log');
xlabel('Epoch');
ylabel('Cross-Entropy');
title('Training Convergence (CE)');
legend('show');
grid on;

View File

@@ -0,0 +1,113 @@
M = 4;
order = 19;
randkey = 1;
bitpattern = [];
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
N = 2^(order-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Bits = Informationsignal(bitpattern);
Symbols = PAMmapper(M,0).map(Bits);
Symbols.fs = 200e9;
Bits_ = PAMmapper(M, 0, "eth_style", 0).demap(Symbols);
% --- Channel: minimal ISI response + AWGN ---
h = [0.3 0.9 0.3]; % impulse response (normalized later if desired)
h = h / norm(h); % optional normalization for unit energy
symbols_filt = Symbols.filter(h,1);
%% SHOW Loss during training
mu = logspace(-3,-0.8,12);
ber_ml_mlse = zeros(size(mu));
ber_training = [];
ce_training = [];
parfor i = 1:numel(mu)
symbols_noi = symbols_filt;
SNR_dB = 20;
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB, 'measured'); % AWGN with given SNR
ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",2^15,...
"mu_dd",mu(i),"mu_tr",mu(i),"order",5,"sps",1,...
"traceback_depth",128,"L",3,"delta",0,'adaptive_mu',0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(symbols_noi,Symbols);
ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml_mlse(i), errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse(i));
ber_training(i,:) = ml_mlse_equalizer.ber;
ce_training(i,:) = ml_mlse_equalizer.ce;
end
%%
symbols_noi = symbols_filt;
SNR_dB = 20;
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB, 'measured'); % AWGN with given SNR
ml_mlse_equalizer_adap = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",2^16,...
"mu_dd",1,"mu_tr",1,"order",5,"sps",1,...
"traceback_depth",128,"L",3,"delta",0,"adaptive_mu",1);
[y_ml_mlse,y_ref] = ml_mlse_equalizer_adap.process(symbols_noi,Symbols);
ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml_mlse_, errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_);
%%
figure();hold on
plot(mu,ber_ml_mlse,'DisplayName','ML-based MLSE');
beautifyBERplot;
xlim([mu(1), mu(end)]);
xlabel('mu');
ylabel('BER');
title('PAM-4; M=3; AWGN Channel');
ylim([1e-5 0.1]);
%%
figure()
hold on;
cols = cbrewer2('Spectral',12);
for i = 1:12
plot(1:200,ber_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:));
end
set(gca,'YScale','log');
xlabel('Epoch');
ylabel('BER');
title('PAM-4; L=3; SNR=20; AWGN Channel');
plot(1:200,ml_mlse_equalizer_adap.ber,'DisplayName','Adaptive mu');
%%
figure()
hold on;
cols = cbrewer2('Spectral',12);
for i = 1:12
plot(1:200,ce_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:));
end
set(gca,'YScale','log');
xlabel('Epoch');
ylabel('Cross-Entropy');
title('PAM-4; L=3; SNR=20; AWGN Channel');
plot(1:200,ml_mlse_equalizer_adap.ce,'DisplayName','Adaptive mu');
%% SUPER LONG EPOCHS

View File

@@ -0,0 +1,163 @@
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1;
database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
run_id = 2776;
dataTable = queryRunid(run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
% if database.checkIfRunExists('Results','run_id',run_id)
% disp(['Already got at least one reulst for run id: ',num2str(run_id),' '])
% return
% end
% Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options);
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym);
Scpe_sig.spectrum("fignum",1,"displayname",'Rx')
%%
ffe_order = [50, 5, 5];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^14,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"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,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3);
%Duobinary Targeting
db_ref_sequence = Duobinary().encode(Symbols);
db_ref_constellation = unique(db_ref_sequence.signal);
[eq_signal, eq_noise] = eq_.process(Scpe_sig,db_ref_sequence);
%%
if 1
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,Symbols);
else
% Ml MLSE
ml_mlse_equalizer = ML_MLSE("epochs_tr",20,"epochs_dd",1,"len_tr",length(eq_signal),...
"mu_dd",0.01,"mu_tr",0.01,"order",11,"sps",2,...
"traceback_depth",128,"L",1,"delta",4,'adaptive_mu',0);
[mlse_sig_sd,ref_sig] = ml_mlse_equalizer.process(Scpe_sig,db_ref_sequence);
end
%%
mlse_sig_sd_decoded = Duobinary().decode(mlse_sig_sd,"M",M);
ref_sig_decoded = Duobinary().decode(db_ref_sequence,"M",M);
mlse_sig_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_sd_decoded);
ref_sig_bits = PAMmapper(M,0,"eth_style",0).demap(ref_sig_decoded);
err = sum(ref_sig_decoded.signal ~= mlse_sig_sd_decoded.signal);
[bits_db,errors_db,ber_db,a] = calc_ber(mlse_sig_bits.signal,ref_sig_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
%%
switch duob_mode
case db_mode.no_db
% TX Data is not precoded:
% A) Emulate diff precoding
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M);
tx_symbols_precoded = Duobinary().encode(Symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
%B) Just determine BER
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
[bits_mlse,errors_mlse,ber_db,~] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
case db_mode.db_precoded
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
% A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here!
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M);
rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_decoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db_precoded = count_error_bursts(a, 40);
% B) Omit the Coding by comparing with demapped TX symbol sequence
Tx_bits_ = PAMmapper(M,0,"eth_style",0).demap(Symbols);
rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd);
[bits_db,errors_db,ber_db,a] = calc_ber(rx_bits_mlse.signal,Tx_bits_.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db = count_error_bursts(a, 40);
cols = linspecer(8);
figure();hold on;
stem(1:40,burst_db,'LineWidth',1,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder');
stem(1:40,burst_db_precoded,'LineWidth',1,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder');
xlabel('Bit Error Burst Length')
ylabel('Occurence')
set(gca, 'yscale', 'log');
end
%% SHOW Loss during training
mu = logspace(-3,-0.8,12);
ber_ml_mlse = zeros(size(mu));
ber_training = [];
ce_training = [];
parfor i = 1:numel(mu)
ml_mlse_equalizer = ML_MLSE("epochs_tr",200,"epochs_dd",1,"len_tr",length(Scpe_sig),...
"mu_dd",mu(i),"mu_tr",mu(i),"order",11,"sps",2,...
"traceback_depth",128,"L",2,"delta",4,'adaptive_mu',0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Scpe_sig,Symbols);
ref_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ref);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml_mlse(i), errpos] = calc_ber(ml_mlse_bits.signal, ref_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse(i));
ber_training(i,:) = ml_mlse_equalizer.ber;
ce_training(i,:) = ml_mlse_equalizer.ce;
end
%%
figure();hold on
plot(mu,ber_ml_mlse,'DisplayName','ML-based MLSE');
beautifyBERplot;
xlim([mu(1), mu(end)]);
xlabel('mu');
ylabel('BER');
title('PAM-4; M=3; AWGN Channel');
ylim([1e-5 0.1]);
%%
figure()
hold on;
cols = cbrewer2('Spectral',12);
for i = 1:12
plot(1:200,ber_training(i,:),'DisplayName', sprintf('mu=%.3f ',mu(i)),'Color',cols(i,:));
end
set(gca,'YScale','log');
xlabel('Epoch');
ylabel('BER');
title('PAM-4; L=3; SNR=20; AWGN Channel');
plot(1:200,ml_mlse_equalizer_adap.ber,'DisplayName','Adaptive mu');

View File

@@ -0,0 +1,13 @@
function rop_fec = interp_fec_cross(rops, ber, fec_thr)
if all(~isfinite(ber))
rop_fec = NaN; return;
end
idx = find(ber < fec_thr, 1, 'first');
if isempty(idx) || idx == 1
rop_fec = NaN; return; % no crossing
end
% linear interpolation between the two nearest points
x1 = rops(idx-1); x2 = rops(idx);
y1 = ber(idx-1); y2 = ber(idx);
rop_fec = interp1([y1 y2], [x1 x2], fec_thr, 'linear', NaN);
end

Binary file not shown.

View File

@@ -0,0 +1,555 @@
classdef bcjr_pam < handle
%MLSE calculates the most probable sequence for an input signal with given/ known channel impulse response of any length
properties(Access=public)
M %PAM-M
DIR
trellis_states
duobinary_output
end
methods (Access=public)
function obj = bcjr_pam(options)
%NAME Construct an instance of this class
% Detailed explanation goes here
arguments
options.M double = 4;
options.DIR double = [1];
options.trellis_states double = [-3 -1 1 3];
options.duobinary_output logical = false;
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
end
function [VITERBI_ESTIMATION_SYMBOLS,LLR_exact,GMI] = process(obj,data_in,data_ref,tx_bits,bit_mapping)
debug = 0;
% States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target)
trellis_state_mode = 2;
% 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 = 1; % PAM-6 only (only if data is NOT precoded!)
% Additional scaling between states, expected output (noiseless_received) and the noisy, filtered input signal
scale_mode = 2; % scale_mode:
% 0 = no scaling,
% 1 = use RMS to scale MODEL,
% 2 = use MMSE/time-corr to scale MODEL, -> This best to get the GMI right -> sometimes the LLP's are not centered around zero...
% 3 = use RMS to scale DATA,
% 4 = use MMSE/time-corr to scale DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% PREPARATIONS %%%%%%%%
% remove unnecessary zeros at start of impulse response to keep
% number of trellis states minimal
DIR_nonzero = find(obj.DIR ~= 0);
if DIR_nonzero(1) > 1
obj.DIR(1:DIR_nonzero(1)-1) = [];
end
if isscalar(obj.DIR)
obj.DIR = [0 obj.DIR];
end
% impulse respnse to remove from signal
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
% Trellis States
obj.trellis_states = reshape(obj.trellis_states,1,[]);
if trellis_state_mode == 1 % Normalize the Trellis states to =1 RMS
obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states);
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));
elseif trellis_state_mode == 3 %use_statistical_levels
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
constellation = unique(data_ref);
% find actual levels from rx signal
symbols_for_lvl = NaN(numel(constellation),length(data_ref));
for l = 1:numel(constellation)
level_amplitude = constellation(l);
symbols_for_lvl(l,data_ref==level_amplitude) = data_in(data_ref==level_amplitude);
end
%replace the trellis states
avg_levels = mean(symbols_for_lvl,2,'omitnan');
obj.trellis_states = sort(avg_levels)';
%also replace the whole ref signal (PAM-M) levels
[~, idx] = ismember(data_ref, unique(data_ref));
data_ref = avg_levels(idx);
end
% seems to be the only way to use combvec for a flexible amount
% of vectors. 'combs' contains all trellis states
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1);
pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2));
combs = fliplr(combvec(pre_comb_cell{:}).');
first_sym = combs(:,1); % das ist das älteste/ trailing Symbol aus der sequenz
last_sym = combs(:,end); %hiermit wird entschieden/ das ist das cursor symbol am ende der sequenz
nStates = length(last_sym);
% % Calculate all possible input symbols for the desired impulse
% % response. Row number is the index of the previous state,
% % column number is the index of the next state
% % noise free received == branch metrics
% assumes: last_sym = combs(:,end); % already defined earlier
levels = sort(unique(obj.trellis_states(:)).');
edges = [levels(1) levels(end)]; % edge levels (0 and 5 in PAM6)
noise_free_received = inf(nStates,nStates); % rows: to, cols: from
edge_edge_mask = false(nStates,nStates); % rows: to, cols: from
for from = 1:nStates
for to = 1:nStates
% valid transition if shift-register overlap holds
if all(combs(to,2:end) == combs(from,1:end-1))
% noiseless sample for the 'to' state reached from 'from'
noise_free_received(to,from) = ...
dot(combs(to,:), obj.DIR(end:-1:2)) + last_sym(from)*obj.DIR(1);
% mark edgeedge candidate (to be excluded only on evenodd steps)
edge_edge_mask(to,from) = ...
(last_sym(from)==edges(1) || last_sym(from)==edges(2)) && ...
(last_sym(to) ==edges(1) || last_sym(to) ==edges(2));
end
end
end
h = flip(obj.DIR(:)).';
data_in = data_in(:);
y_ideal = conv(data_ref(:), h, "same");
switch scale_mode
case 0
g = 1; b = 0;
case 1 % RMS: scale model to data
g = rms(data_in)/rms(y_ideal); b = mean(data_in) - g*mean(y_ideal);
case 2 % MMSE/time-corr: scale states to data
[c,lags] = xcorr(data_in(:), y_ideal, 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal, lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:)-mu_y;
yi_c = y_ideal-mu_i;
g = (yi_c'*y_c)/(yi_c'*yi_c);
b = mu_y - g*mu_i;
case 3 % RMS flipped: scale data to model
gd = rms(y_ideal)/rms(data_in); bd = mean(y_ideal) - gd*mean(data_in);
data_in = gd*data_in + bd;
g = 1; b = 0;
case 4 % MMSE/time-corr flipped: scale data to states
[c,lags] = xcorr(data_in(:), y_ideal(:), 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal(:), lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:) - mu_y; % data_in centered
yi_c = y_ideal - mu_i; % ideal centered
g = (y_c' * yi_c) / (y_c' * y_c);
b = mu_i - g * mu_y;
data_in = g * data_in(:) + b;
g = 1; b = 0;
end
% apply (g,b) to states/ expected values
noise_free_received = g*noise_free_received + b;
last_sym = g*last_sym + b;
% calculate noise power
sigma2 = mean(abs(data_in - (g*y_ideal + b)).^2); %noise = mean(abs((RX Signal - IDEAL Signal)))^2
inv2s2 = 1/(2*sigma2);
if debug
figure(100); clf; hold on
obj.showLevelScatter_(data_in, data_ref);
yline(noise_free_received(:), 'DisplayName','Transition States','Color','red','HandleVisibility','off');
yline(obj.trellis_states(:), 'DisplayName','Transition States','Color','green','LineWidth',2,'HandleVisibility','off')
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
% Initialize the output vector
pm = zeros(nStates,nStates);
bm_fw = zeros(nStates,nStates,length(data_in));
% first start is evaluated without ISI/ wihout the full Impulse response
% so simply use the constellation here
bm = -(data_in(1) - last_sym).^2 * inv2s2;
pm = pm + bm;
[alpha(:,1),pm_survivor_fw_idx(:,1)] = max(pm,[],2);
pm = repmat(alpha(:,1).',nStates,1);
bm_fw(:,:,1) = pm;
% Forward Recursion (FSM Computation)
for n = 2:length(data_in)
bm = -(data_in(n) - noise_free_received).^2 * inv2s2;
% exclude edge to edge transitions only for even->odd steps && PAM-6
if mod(n,2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
pm = pm + bm;
[alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state (get min distance for all state transitions towards a new state)
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_fw(:,:,n) = bm;
end
% we can now get the best path as min
viterbi_path = NaN(1,length(data_in));
% find ideal trellis path by going through the trellis backwards
[~,viterbi_path(length(data_in))] = max(alpha(:,length(data_in)));
for n = length(data_in):-1:2
viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n);
end
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]);
end
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
VITERBI_ESTIMATION_SYMBOLS = reshape(VITERBI_ESTIMATION_SYMBOLS,size(data_in));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% BACKWARD (Beta's) %%%%%
% Initialize the output vector
pm = zeros(nStates,nStates);
beta = zeros(nStates,length(data_in));
pm_survivor_bw_idx = zeros(nStates,length(data_in));
bm_bw = zeros(nStates,nStates,length(data_in));
% starting with the state that has the lowest sum path
% metric, follow the stored information about the
% predecessor
for h = length(data_in)-1:-1:1
bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2;
% exclude edge to edge transitions for even->odd steps && PAM-6
if mod(h+1, 2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
pm = pm + bm.';
[beta(:,h),pm_survivor_bw_idx(:,h)] = max(pm,[],2); % choose lowest path metric as new state
pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_bw(:,:,h) = bm;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD (Combine Alpha and Beta to yield LLP's) %%%%%
%calc the log probabilities (llp's)
for k = 1:length(data_in)
if k == 1
alpha_ = repmat(alpha(:,k)',[nStates,1])';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + beta_,[],2);
else
alpha_ = repmat(alpha(:,k-1)',[nStates,1])';
gamma_ = bm_fw(:,:,k)';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_';
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% Calc LLR's %%%%%
% These are interchangeable...
nml_LLP = LLP - max(LLP); %subtract highest value for better numerical stability, LLP's are not always close to zero
expLLP = exp(nml_LLP);
state_prob = expLLP ./ sum(expLLP); % sums to one (or numerically close to one)
% compute symbolposteriors from LLP in the logdomain:
amax = max(LLP,[],1);
logZ = amax + log(sum(exp(LLP - amax), 1));
logPstate = LLP - logZ; % still in logdomain
state_prob = exp(logPstate); % exact, sums to 1
if obj.M == 6
num_bits = 5;
% all possible transitions (for now 36, including the "edges"
% of the QAM 32 constellation)
states = [-5 -3 -1 1 3 5];
pam6transitions = combvec(states,states)'; % pam6transitions =
% [-5 -5;
% -3 -5;
% -1 -5; ...
[~, idx_sym_1] = ismember(pam6transitions(:,1), states);
[~, idx_sym_2] = ismember(pam6transitions(:,2), states);
pam6ind = [idx_sym_1, idx_sym_2];
numPairs = floor(size(LLP,2)/2);
LLR_exact = zeros(numPairs,5);
LLR_maxlogmap = zeros(numPairs,5);
for k = 1:numPairs
symbol1 = 2*k-1;
symbol2 = 2*k;
LLP1 = LLP(:,symbol1);
LLP2 = LLP(:,symbol2);
prob1 = state_prob(:,symbol1);
prob2 = state_prob(:,symbol2);
% All 36 Combinations: M = LLP Symbol 1 + LLP Symbol 2
Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2));
pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2));
% for each of the 5 bits sum exact-probs or max-log
for b = 1:num_bits
idx_sym_1 = bit_mapping(:,b)==1;
idx_bit_1 = bit_mapping(:,b)==0;
% exact LLR from probabilities
P1 = sum(pij(idx_sym_1)); %prob that bit == 1
P0 = sum(pij(idx_bit_1));
LLR_exact(k,b) = log(P1./P0); %ratio by multiplication
% max-log:
LLR_maxlogmap(k,b) = max( Mij(idx_sym_1) ) - max( Mij(idx_bit_1) ); % ratio by subtraction
end
end
% GMI calc includes the Tx-bitstream
tx_bits_pam6_reshaped = reshape(tx_bits',5,[])'; % N x 5
MI = zeros(1, num_bits);
for k = 1:num_bits
idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en
idx_sym_1 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_1,k);
llr1 = LLR_exact(idx_sym_1,k);
% Calculate mutual information for bit position k
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
MI(k) = 1 - 0.5 * (I0 + I1);
end
GMI = sum(MI); % Total mutual information per symbol
GMI = GMI/2; % GMI per single symbol not per two symbols
else
% Number of symbols and bits per symbol
num_bits = log2(length(obj.trellis_states)); % 2 bits per symbol
% bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).showBitMapping;
% Initialize LLR storage
LLR_maxlogmap = zeros(length(data_in),num_bits);
LLR_exact = zeros(length(data_in),num_bits);
% Compute bit-wise LLRs
for bit_idx = 1:num_bits
% Find indices where bit is 0 and where it is 1
idx_bit_0 = bit_mapping(:,bit_idx) == 0;
idx_bit_1 = bit_mapping(:,bit_idx) == 1;
% Sum over log-probabilities
% Max-Log approximation uses the single max LLP value
% instead of sum over all LLP's
LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1);
% Sum probabilities over states for which the bit is 1 and 0, respectively.
P0 = sum(state_prob(idx_bit_0, :),1);
P1 = sum(state_prob(idx_bit_1, :),1);
LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% CALC NGMI %%%%%
MI = zeros(1, num_bits);
for k = 1:num_bits
idx_bit_0 = (tx_bits(:,k) == 0); %wo sind die 1en
idx_bit_1 = (tx_bits(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_0,k);
llr1 = LLR_exact(idx_bit_1,k);
% mutual information for bit position k
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
MI(k) = 1 - 0.5 * (I0 + I1); % assumes equally distributed ones and zeros
end
GMI = sum(MI); % Total bitwise mutual information
end
if debug
%%% DEBUG PLOT LIKELIHOOD RATIOS %%%
figure(115);clf
subplot(2,1,1)
for bit = 1:num_bits
hold on;
histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
subplot(2,1,2)
for bit = 1:num_bits
hold on;
histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
if obj.M == 6
pairs = reshape(VITERBI_ESTIMATION_SYMBOLS,2,[]).';
levels = sort(unique(VITERBI_ESTIMATION_SYMBOLS));
isedge = ismember(pairs, [levels(1) levels(end)]);
isforbidden = sum(isedge,2)==2;
fprintf('Found %d forbidden transitions (even -> odd ; edge -> edge).\n', nnz(isforbidden));
end
end
end
function [symbols_for_lvl,avg_for_lvl] = showLevelScatter_(~,eq_signal,ref_symbols)
figure()
rx_symbols = eq_signal; %./ rms(eq_signal);
correct_symbols = ref_symbols;
% col = cbrewer2('Paired',numel(unique(correct_symbols))*2);
col = ...
[0.6510 0.8078 0.8902; ...
0.1216 0.4706 0.7059; ...
0.6980 0.8745 0.5412; ...
0.2000 0.6275 0.1725; ...
0.9843 0.6039 0.6000; ...
0.8902 0.1020 0.1098; ...
0.9922 0.7490 0.4353; ...
1.0000 0.4980 0; ...
0.7922 0.6980 0.8392; ...
0.4157 0.2392 0.6039; ...
1.0000 1.0000 0.6000; ...
0.6941 0.3490 0.1569; ...
0.6510 0.8078 0.8902; ...
0.1216 0.4706 0.7059; ...
0.6980 0.8745 0.5412; ...
0.2000 0.6275 0.1725];
ccnt = -1;
levels = unique(correct_symbols);
symbols_for_lvl = NaN(numel(levels),length(correct_symbols));
start = 1;
ende = length(correct_symbols);
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan');
xax = 1:length(correct_symbols);
scatter(xax(start:ende),symbols_for_lvl(l,start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
hold on;
end
std_lvl = round(std_lvl,2);
ccnt = 0;
avg_for_lvl = NaN(numel(levels),length(correct_symbols));
% Add the windowed/ smoothed curves
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
L = 500;
movmean = 1/L .* movsum(rx_symbols(correct_symbols==level_amplitude),[L/2,L/2], 'Endpoints', 'fill');
avg_for_lvl(l,correct_symbols==level_amplitude) = movmean;
nanx = isnan(avg_for_lvl(l,:));
t = 1:numel(avg_for_lvl(l,:));
avg_for_lvl(l,nanx) = interp1(t(~nanx), avg_for_lvl(l,~nanx), t(nanx));
plot(xax(start:ende),avg_for_lvl(l,start:ende),'Color',col(ccnt,:));
hold on
end
% yline(levels);
xlabel('Samples');
ylabel('Amplitude');
ylim([-3 3]);
end
end
end

View File

@@ -0,0 +1,193 @@
if 0
% A) RUN FULL LOOP
M_format = [2,4,6,8];
snr = 10:25;
else
% B) RUN FOR DEBUG AND TEST
M_format = 4;
snr = 20;
end
for m = 1:length(M_format)
% --- Parameters ---
M = M_format(m); % PAM order (e.g., 2,4,8)
Nsym = 1e5; % number of symbols
h = [1, 0.5, 0.2]; % Impulse response to remove
b = log2(M);
if M == 6 b = 5; end
rng(1);
bits_tx = logical(randi([0 1], Nsym, b, 'uint8'));
tx_symbols = pammap(bits_tx,M);
if M == 6
states = unique(tx_symbols);
pam6transitions = combvec(states',states')'; % pam6transitions =
bitmapping = pamdemap(reshape(pam6transitions',1,[])',M);
else
bitmapping = pamdemap(unique(tx_symbols),M);
end
scaling = sqrt(sum(unique(tx_symbols).^2)/numel(unique(tx_symbols)));
tx_symbols = tx_symbols ./ scaling;
% apply impulse response to signal
y_filt = filter(h, 1, tx_symbols);
for s = 1:length(snr)
% apply noise
y = awgn(y_filt,snr(s),"measured",1);
% apply ml-MLSE
adaptive_mu = 0;
mu_lms = 0.15;
ml_mlse_equalizer = ml_mlse_pam("epochs_tr",50,"epochs_dd",1,"len_tr",length(y)/2,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
"L",2,"delta",4,"adaptive_mu",adaptive_mu);
[ml_mlse_estimate,~] = ml_mlse_equalizer.process(y,tx_symbols);
rx_symbols = ml_mlse_estimate .* scaling;
bits_rx = pamdemap(rx_symbols,M);
BER_ml(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx);
fprintf('BER = %.2e \n', BER_ml(m,s));
% apply bcjr
BCJR = bcjr_pam("DIR",h,"duobinary_output",0,"M",M,"trellis_states",unique(tx_symbols));
[viterbi_estimate,LLR,GMI(m,s)] = BCJR.process(y,tx_symbols,bits_tx,bitmapping);
% decode LLR's
bits_LLR = LLR > 0;
% demap viterbi symbols sequence
rx_symbols = viterbi_estimate .* scaling;
bits_rx = pamdemap(rx_symbols,M);
% BER calc
BER_vit(m,s) = nnz(bits_tx ~= bits_LLR) / numel(bits_tx);
fprintf('BER LLR = %.2e \n', BER_vit(m,s));
BER_llr(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx);
fprintf('BER = %.2e \n', BER_llr(m,s));
end
end
%%
figure();hold on
for m = 1:length(M_format)
p=plot(snr,BER_llr(m,:),'DisplayName',sprintf('Viterbi: PAM %d',M_format(m)));
plot(snr,BER_ml(m,:),'DisplayName',sprintf('ML-Based: PAM %d',M_format(m)),'LineStyle',':','Color',p.Color);
end
ylabel('BER');
xlabel('SNR')
title('BER vs. SNR');
set(gca, 'XScale', 'linear', ...
'YScale', 'log', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
%%
figure();hold on
for m = 1:length(M_format)
plot(snr,GMI(m,:),'DisplayName',sprintf('GMI PAM %d',M_format(m)))
end
ylabel('GMI');
xlabel('SNR')
title('GMI vs. SNR');
set(gca, 'XScale', 'linear', ...
'YScale', 'linear', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
function symbols = pammap(bits,M)
bits = logical(bits);
if M == 2
symbols = bits;
elseif M == 4
symbols= 2*bits(:,1) + (bits(:,1)==bits(:,2));
symbols=2*symbols-3;
elseif M == 6
m = 1;
if size(bits,2)>size(bits,1)
bits = bits'; %vector aufrecht stellen
end
bits = reshape(bits',1,[])';
thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
% LUT based mapping
for k = 1:5:fix(length(bits)/5)*5
symbols(m:m+1,1) = thres(bin2dec(int2str(bits(k:k+4)'))+1,:);
m = m+2;
end
elseif M == 8
x1 = bits(:,1);
x2 = (bits(:,1)==bits(:,3));
x3 = x2~=bits(:,2);
symbols = 4*x1 + 2*x2 + x3;
symbols=2*symbols-7;
end
end
function bits = pamdemap(symbols,M)
if M == 2
thres=0;
elseif M == 4
thres=[-2,0,2];
elseif M == 6
thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
elseif M == 8
thres=-6:2:6;
end
if M ~= 6
symbols = symbols';
a = squeeze(repmat(real(symbols),[1 1 length(thres)])); %Eingangssignal in 3 spalten
b = squeeze(repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1])); %Threshold in 3 Spalten
comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3
comp_real=repmat(real(symbols),[1 1 length(thres)]) > repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1]);
s1=size(comp_real,1);
s2=size(comp_real,2);
end
if M == 2
data_out=abs(comp_real(:,:,1));
elseif M == 4
data_out=[comp_real(:,:,2); ones(s1,s2) - comp_real(:,:,1) + comp_real(:,:,3)];
elseif M == 6
if size(symbols,2) > 1
symbols = symbols.';
end
if length(symbols)/2 ~= round(length(symbols)/2)
symbols = [symbols;0];
end
m = 1;
for n = 1:2:length(symbols)
dist = sqrt((symbols(n)-thres(:,1)).^2+(symbols(n+1)-thres(:,2)).^2);
[~,dd_idx] = min(dist);
% dec_out(n:n+1) = LUT(dd_idx,:);
data_out(m:m+4) = bitget(dd_idx-1,5:-1:1);
m = m+5;
end
data_out = reshape(data_out',5,[]);
elseif M == 8
data_out=[comp_real(:,:,4);
comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7);
1-comp_real(:,:,2)+comp_real(:,:,6)];
end
bits = data_out';
end

View File

@@ -0,0 +1,473 @@
classdef ml_mlse_pam < handle
% ALGORITHM DESCRIBED IN:
% W. Lanneer and Y. Lefevre, Machine Learning-Based Pre-Equalizers for
% Maximum Likelihood Sequence Estimation in High-Speed PONs,
% in 2023 31st European Signal Processing Conference
% Further ML Refs:
% https://machinelearningmastery.com/cross-entropy-for-machine-learning/
% https://docs.pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html
% The central idea is to overcome the (white-) noise assumption within the previously described
% Viterbi algorithm, more precisely a closed-loop optimization is proposed that finds a suitable
% filter-set to directly compute the branch metrics c_k (s,s^' ). These can directly be used to
% carry out the conventional Viterbi algorithm. The system consists of S^L S=F linear FIR filters,
% combined with one bias coefficient respectively. These filters take the received input samples to
% compute the branch metrics estimates (c_k ) ̂(s,s^' ) according toThe central idea is to overcome
% the (white-) noise assumption within the previously described Viterbi algorithm, more precisely
% a closed-loop optimization is proposed that finds a suitable filter-set to directly compute the
% branch metrics c_k (s,s^' ). These can directly be used to carry out the conventional Viterbi
% algorithm. The system consists of S^L S=F linear FIR filters, combined with one bias coefficient
% respectively. These filters take the received input samples to compute the branch metrics
% estimates. Finally, the usual Viterbi is carried out...
% Recommended Settings and some findings:
% Requires many training epochs. According to ML people, 100,200 or
% even up to 1000 epochs are normal for ML-convergence
% The mu parameter _can_ be adaptive - using the cross entropy and when
% analyzing the isolated training it looks very promisig. However, is
% later use I found this is not as stable as a fixed learning rate.
% mu = 0.1 worked good for me
% Longer orders/ filter length are not always better. For me order=11
% was good.
% Delay factor (delta) is good when the order is also increased. With
% order = 11, a delta of =4 shows good results
properties
sps % usually 2
order
e
e_tr
error
len_tr
mu_tr
epochs_tr
% dd_mode -> not implemented here!
mu_dd %weight update in dd mode
epochs_dd
adaptive_mu
constellation
L %viterbi memory length
alpha
DIR
DIR_flip
trellis_states
traceback_depth
S
Nf
delta
nStates
nFeasible
combs
first_sym
last_sym
valid
valid_to_idx
valid_from_idx
w
nbiasTerms
true_to_state_idx
state_dict % containers.Map: key(sequence)->state index
key_fmt = '%.8g_'; % key format for sequence strings
nSym % |constellation|
ber = []
ce = ones(1,1);
end
methods
function obj = ml_mlse_pam(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.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
function [x_viterbi,x_ref] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
X = X./rms(X);
% Use sorted constellation for deterministic mapping
obj.constellation = sort(unique(D),'ascend');
obj.nSym = numel(obj.constellation);
if length(X)/length(D) ~= obj.sps
warning('Signal length does not fit to reference!');
end
% ==============================================================
% INITIALIZATION
% ==============================================================
% --- Parameters
obj.S = numel(obj.constellation); % Num of Symbols
obj.Nf = obj.order*obj.sps; % filter length (auto adapt for n-SPS...)
obj.nStates = obj.S^obj.L; % S^L states
obj.nFeasible = obj.nStates*obj.S; % S^(L+1) feasible states
% --- Trellis mapping
obj.trellis_states = reshape(obj.constellation,1,[]); % make row vector
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{:}).'); % rows: states, columns: [x_k, x_{k-1}, ...]
obj.first_sym = obj.combs(:,1);
obj.last_sym = obj.combs(:,end);
obj.nStates = size(obj.combs,1);
% --- Valid transitions; adapted from the old Viterbi in
% Move-It where the "noise free received" states are calculated
% using the same loop and clause
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);
% Allocate vectors and weights
% !! IF SHAPE FIT, then we already have smth there an we want
% to start with the existing filter-set (saves comp. time/ or to test fixed filter on new data)
obj.nbiasTerms = 1;
if isempty(obj.w) || any(size(obj.w) ~= [obj.Nf+obj.nbiasTerms,obj.nFeasible])
obj.w = zeros(obj.Nf+obj.nbiasTerms,obj.nFeasible); % filter weights per transition + bias tap
% obj.w = randn(obj.Nf+obj.nbiasTerms,obj.nFeasible);
end
% This is a weird workaround - but it works and is much faster
% than findig the state indices every time:
% Precompute dictionary for fast state lookup (sequence -> state)
keys = cell(obj.nStates,1);
for i = 1:obj.nStates
keys{i} = obj.seq_key(obj.combs(i,:)); % combs row is already [x_k, x_{k-1}, ...]
end
obj.state_dict = containers.Map(keys, 1:obj.nStates);
% ==============================================================
% TRAINING
% ==============================================================
n = obj.len_tr;
training = 1;
obj.equalize(X, D,obj.mu_tr,obj.epochs_tr,n,training);
obj.e_tr = obj.e;
% ==============================================================
% Testing; Fixed Mode
% ==============================================================
n = length(X);
training = 0;
obj.mu_dd = obj.mu_tr; %For now no DD mode is implemented...
[x_viterbi,x_ref]=obj.equalize(X, D,obj.mu_dd,obj.epochs_dd,n,training);
end
function [y,y_ref] = equalize(obj,x,d,mu,epochs,N,training)
% ==============================================================
% ML-Based Branch Metric Estimation + Viterbi
% ==============================================================
debug = 1;
showPlots = 1;
nSymbols = ceil(N/obj.sps);
for epoch = 1:epochs
% state metrics (log-domain costs): keep as column [nStatesx1]
pm = zeros(obj.nStates,1);
v_tilde = zeros(1,obj.nFeasible);
pred = zeros(nSymbols, obj.nStates);
pm_sto = nan(obj.nStates, nSymbols);
CE_accum = 0;
% START IDX can be randomized during training, but this
% requires some testing - it is not better, maybe a
% solutiuon is to use the same window for 10-20 epochs
% and then switch to another window
% for now: simply use the first parts of the signal for
% training and also for testing... not "the
randomize_training_window = 0;
if randomize_training_window && training
max_start = length(x) - ( (ceil(N/obj.sps)-1)*obj.sps + 1 );
max_start = max(1, max_start); % safety
start_sample = randi([1, max_start], 1); %rnd training; not really good
else
start_sample = 1;
end
end_sample = start_sample + (ceil(N/obj.sps)-1)*obj.sps;
start_symbol = 1 + floor((start_sample - 1)/obj.sps); % ABSOLUTE symbol index
symbol = 0;
for sample = start_sample:obj.sps:end_sample
symbol = symbol + 1;
k = symbol;
sym_idx = start_symbol + (symbol - 1);
% input signal window y_k; delayed by 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)]; % Nfx1
yk = [yk;ones( obj.nbiasTerms,1)];
% Apply Filter; Predict branch metrics for all feasible transitions: c_hat
% Formula (8)
c_hat = (yk.' * obj.w); % [1xnFeasible]
c_hat = c_hat.'; % [nFeasiblex1]
% Extended path metrics: v_tilde = pm(from) + c_hat
v_tilde = pm(obj.valid_from_idx) + c_hat; % [nFeasiblex1]
% ===== Cross Entropy Loss Update =====
if 1 %training
% --- allocate storage once
if epoch == 1 && symbol == 1
obj.true_to_state_idx = ones(ceil(N/obj.sps),1,'uint32');
end
% --- previous "to" becomes current "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
% only compute in first epoch
if sym_idx >= obj.L
key_to = obj.seq_key(flip(d(sym_idx-obj.L+1 : sym_idx)));
if isKey(obj.state_dict, key_to)
obj.true_to_state_idx(symbol) = obj.state_dict(key_to);
else
obj.true_to_state_idx(symbol) = true_from_state_idx;
end
else
obj.true_to_state_idx(symbol) = true_from_state_idx;
end
end
% --- ensure valid (from,to)
dirac = zeros(obj.nFeasible,1);
mask = obj.valid_from_idx==true_from_state_idx & ...
obj.valid_to_idx == obj.true_to_state_idx(symbol);
if any(mask)
dirac(mask) = 1;
else
idx = find(obj.valid_from_idx==true_from_state_idx,1,'first');
dirac(idx) = 1;
obj.true_to_state_idx(symbol) = obj.valid_to_idx(idx);
end
% softmax over -v_tilde (numerically safe shift)
v_shift = -(v_tilde - min(v_tilde)); % shift to small positive numbers
v_shift = min(v_shift, 100); % clamp exponent argument to avoid extreme numbers/ overflow (exp(50)=5e21)
expv = exp(v_shift);
p = expv ./ (sum(expv) + eps);
% Cross entropy
CE_symbol(symbol) = -log(p(dirac==1) + eps);
if sym_idx > obj.L
CE_smooth(symbol) = 0.01*CE_symbol(symbol) + 0.99*CE_smooth(symbol-1);
else
if epoch > 1
CE_smooth(symbol) = obj.ce(end); %stitch together ce from last epoch? or =1 for very first round?!
else
CE_smooth(symbol) = CE_symbol(symbol);
end
end
CE_accum = CE_symbol(symbol) + CE_accum;
% Formula (10)
% gradient term (t - p)
dmp = (dirac - p)'; % 1xnFeasible
% Formula (10)
dL_Dw = (yk) .* dmp;
% Start updates only when the symbol index has L history
if sym_idx >= obj.L
if obj.adaptive_mu
mu_eff = CE_smooth(sym_idx);
mu_eff = max(min(mu_eff, 0.2), 1e-4);
else
mu_eff = mu;
end
% see Algorithm 1 in paper
obj.w = obj.w - mu_eff .* dL_Dw; % (Nf+1)xnFeasible
end
% if debug && epoch > 2
% figure(100);
% subplot(4,1,1);
% heatmap(p');
% title('Probs')
% subplot(4,1,2);
% heatmap(dmp);
% title('Update')
% subplot(4,1,3);
% heatmap(dL_Dw);
% title('Update')
% subplot(4,1,4);
% heatmap(bj.w);
% title('Update')
%
% end
end
% Compare-Select
v_tilde_mat = inf(obj.nStates, obj.nStates);
v_tilde_mat(obj.valid) = v_tilde; %reshapes to usual (from x to) matrix
[pm_next, pred(k,:)] = min(v_tilde_mat, [], 2); %here, calc min for each column
% re-center, otherwise it will overflow
pm_next = pm_next - min(pm_next);
pm = pm_next;
pm_sto(:,symbol) = pm;
end
% Traceback
[~, s_end] = min(pm);
viterbi_path = zeros(symbol,1);
viterbi_path(symbol) = s_end;
for n = symbol:-1:2
viterbi_path(n-1) = pred(n, viterbi_path(n));
end
% cut here to have the same indices when shuffling/
% starting the start_symbol indx != 1
y_ref = d(start_symbol:end);
y = obj.first_sym(viterbi_path);
% Debug and Plots
if debug && training
sym_start = start_symbol;
sym_end = start_symbol + symbol - 1;
ref_slice = d(sym_start : sym_end);
err = sum(y ~= ref_slice(1:numel(y)));
try %works with demapper, not provided in Deliverable
ref_bits = PAMmapper(obj.S,0).demap(ref_slice);
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: %.1e \n',epoch, ber);
obj.ber(epoch) = ber;
berlabel = 'BER';
catch %fallback ser
ser = err./length(y);
fprintf('Epoch: %d - SER: %.1e \n',epoch, ser);
obj.ber(epoch) = ser;
berlabel = 'BER';
end
obj.ce(epoch) = CE_accum./symbol;
if showPlots
figure(10);clf
subplot(3,2,1:2);
heatmap(obj.w);
title('Filter')
subplot(3,2,3);
v_tildemat = NaN(obj.nStates, obj.nStates);
v_tildemat(obj.valid) = v_tilde; % log-domain scores
heatmap(v_tildemat);
title('Extended Path Metrics v-tilde')
subplot(3,2,4);
scatter(1:symbol,pm_sto,1,'.')
title('Path Metric Winners v')
subplot(3,2,5);hold on
scatter(1:symbol,CE_symbol,1,'.');
scatter(1:symbol,CE_smooth,1,'.')
title('Cross Entropy')
ylabel('Cross Entropy')
xlabel('Symbols')
subplot(3,2,6); hold on
% Left y-axis: Cross Entropy
yyaxis left
scatter(1:length(obj.ce), obj.ce, 10, 's', 'filled')
ylabel('Cross Entropy')
% Right y-axis: BER
yyaxis right
scatter(1:length(obj.ber), obj.ber, 10, 'd', 'filled')
set(gca, 'YScale', 'log')
ylabel(berlabel)
xlim([1, epochs])
xlabel('Epoch')
title('Cross Entropy // BER')
grid on
drawnow
end
end
end
end
end
methods (Access=private)
function k = seq_key(obj, seq)
% Build a stable key string for a sequence row vector in the *same order as combs rows* ([x_k, x_{k-1}, ...])
% Use rounding via sprintf to avoid floating-point issues.
% seq must be a row vector.
k = sprintf(obj.key_fmt, seq);
end
end
end

View File

@@ -0,0 +1,222 @@
%%% Run parameters
% TX
M = 4;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 2;
rcalpha = 0.05;
kover = 8;
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1310;
laser_linewidth = 1e6;
% Channel
link_length = 0;
doub_mode = db_mode.no_db;
cols = linspecer(6);
rop = [-8];
bwl = [0.5:0.1:1.5];
fsym = [160:16:256].*1e9;
% nonlin_mod = [0.5:0.01:0.75];
nonlin_mod = ones(size(fsym)).*0.5;
ffe_results = {};
mlse_results_lin= {};
for r = 1:length(fsym)
Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 1;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym(r),"M",M,"order",18,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"db_precode",db_precode,"db_encode",db_encode,...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process();
El_sig = M8199B("kover",kover).process(Digi_sig);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = El_sig.normalize("mode","oneone");
%%%%% MODULATE E/O CONVERSION %%%%%
u_pi = 3.2;
vbias = -u_pi*nonlin_mod(r);
[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);
%%%%%% 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);
%%%%%% 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 = 70e9;
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(r));
% 2sps
[~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1);
Rx_sig_2sps = Scpe_cell{1};
Rx_sig_2sps = Rx_sig_2sps.normalize("mode","rms");
% 1sps
Scpe_sig_1sps = Scpe_sig.resample("fs_out",1*fsym(r));
[~, Scpe_cell_1sps, ~, found_sync] = Scpe_sig_1sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 1);
Rx_sig_1sps = Scpe_cell_1sps{1};
Rx_sig_1sps = Rx_sig_1sps.normalize("mode","rms");
showLevelHistogram(Rx_sig_1sps,Symbols,"displayname",'ffe','fignum',111);
%%
mu_lms = 0.0005;
pf_ncoeffs = 2;
eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms");
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% FFE
[y_ffe, ffe_noise] = eq_.process(Rx_sig_2sps, Symbols);
Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
[~, errors, ber_ffe, ~] = calc_ber(Eq_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
fprintf('FFE: %.2e \n',ber_ffe);
% Postfilter
[y_white,whitened_noise] = pf_.process(y_ffe, ffe_noise);
% Sequence Est
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
[y_mlse] = mlse_.process(y_white,Symbols);
mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
[~, errors, ber_mlse_normal, errpos] = calc_ber(mlse_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('MLSE: %.2e \n',ber_mlse_normal);
showLevelHistogram(y_ffe,Symbols,"displayname",'ffe','fignum',111);
bursts = count_error_bursts(errpos, 10);
e = zeros(size(mlse_bits.signal));
e(errpos) = 1;
figure(8)
stem(e)
%% RUN ML-Based MLSE
mu_lms = 0.15;
ml_mlse_equalizer = ML_MLSE("epochs_tr",50,"epochs_dd",10,"len_tr",Rx_sig_2sps.length-100,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",15,"sps",2,...
"traceback_depth",128,"L",3,"delta",5);
%%
ml_mlse_equalizer.epochs_tr = 50;
ml_mlse_equalizer.epochs_dd = 1;
[y_ml_mlse,Vit_signal] = ml_mlse_equalizer.process(Rx_sig_2sps,Symbols);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber, errpos] = calc_ber(ml_mlse_bits.signal, Tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber);
bursts = count_error_bursts(errpos, 10);
e = zeros(size(ml_mlse_bits.signal));
e(errpos) = 1;
figure(8)
stem(e)
figure()
plot(ml_mlse_equalizer.ber)
beautifyBERplot
%% optimize delta
deltas = [-1:4];
ber = zeros(1,length(deltas));
parfor m = 1:numel(deltas)
mu_lms = 0.2;
ml_mlse_equalizer = ML_MLSE("epochs_tr",2,"epochs_dd",5,"len_tr",2^13,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",1,...
"traceback_depth",128,"L",3,"delta",deltas(m));
[y_ml_mlse,Vit_signal] = ml_mlse_equalizer.process(y_ffe,Symbols);
y_ml_mlse_ = y_ml_mlse;
y_ml_mlse_.signal = circshift(y_ml_mlse.signal,0);
mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse_);
[~, errors, ber(m), errpos] = calc_ber(mlse_bits.signal, Tx_bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
fprintf('ML MLSE: %.2e \n',ber(m));
end
figure(7); hold on
title('ML MLSE')
plot(deltas,ber,'DisplayName','BER');
yline(ber_ffe,'DisplayName','BER FFE');
yline(ber_mlse_normal,'DisplayName','BER MLSE');
xlabel('deltas')
beautifyBERplot
legend
ylim([1e-5, 1e-1]);
set(gca,'YScale','log');
%% RUN Comparison
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_ffe2 mu_ffe3];
mu_dfe = 0.0004;
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
mu_lms = 0.0005;
eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms");
% eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",1,"DCmu",0.00,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0);
[ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
mlse_results_lin{r}.metrics.print;
ffe_results{r}.metrics.print;
end

View File

@@ -0,0 +1,116 @@
ber_ffe = [];
ber_mlse = [];
ber_dbtgt = [];
ber_ml = [];
mlse = 1;
dbtgt = 0;
duob_mode = db_mode.no_db;
baudrates = [136:8:224].*1e9;
for i = 1:length(baudrates)
rop = -8;
M = 4;
[Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model("M",M,"fsym",baudrates(i),"rop",rop,"laser_linewidth",1310,"link_length_m",0,"random_key",1,"apply_pulsef",1);
% [Rx_sig_2sps_v2, Symbols_v2, Tx_bits_v2] = standard_link_model("M",M,"fsym",200e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",2);
% [Rx_sig_2sps_v3, Symbols_v3, Tx_bits_v3] = standard_link_model("M",M,"fsym",200e9,"rop",rop,"laser_linewidth",1300,"link_length_m",0,"random_key",3);
%% FFE + MLSE
if mlse
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
ber_ffe(i) = ffe_results.metrics.BER;
ber_mlse(i) = mlse_results.metrics.BER;
fprintf('BER FFE: %.2e \n',ber_ffe(i));
fprintf('BER MLSE: %.2e \n',ber_mlse(i));
end
%% FFE DB tgt. + MLSE
if dbtgt
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3);
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0,...
"postFFE", []);
ber_dbtgt(i) = dbt_results.metrics.BER;
end
%%
mu_lms = 0.0005;
pf_ncoeffs = 2;
eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms");
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% FFE
[y_ffe, ffe_noise] = eq_.process(Rx_sig_2sps_v1, Symbols_v1);
% Postfilter
[y_white,whitened_noise] = pf_.process(y_ffe, ffe_noise);
%% RUN ML-Based MLSE
mu_lms = 0.15;
ml_mlse_equalizer = ML_MLSE("epochs_tr",50,"epochs_dd",1,"len_tr",2^16,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",4,"sps",2,...
"traceback_depth",128,"L",2,"delta",0);
ml_mlse_equalizer.mu_tr = 0.005;
ml_mlse_equalizer.epochs_tr = 2;
ml_mlse_equalizer.epochs_dd = 1;
[y_ml_mlse,~] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml(i), errpos] = calc_ber(ml_mlse_bits.signal, Tx_bits_v1.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml(i));
% figure(11);hold on
% plot(1:numel(ml_mlse_equalizer.ber),ml_mlse_equalizer.ber);
% beautifyBERplot;
% xlim([1,numel(ml_mlse_equalizer.ber)])
end
%%
figure(6); hold on;
if mlse
plot(baudrates,ber_ffe,'DisplayName','FFE');
plot(baudrates,ber_mlse,'DisplayName','MLSE');
end
if dbtgt
plot(baudrates,ber_dbtgt,'DisplayName','DB tgt');
end
plot(baudrates,ber_ml,'DisplayName','ML-MLSE');
beautifyBERplot;
legend

View File

@@ -0,0 +1,30 @@
%% read_wpd_csv.m
% Minimal importer for WebPlotDigitizer multi-curve CSV
filename = 'wpd_datasets.csv'; % <-- set your file path here
T = readtable(filename);
% Read header row manually
fid = fopen(filename);
hdr1 = strsplit(strrep(fgetl(fid), '"', ''), ','); % curve names
hdr2 = strsplit(strrep(fgetl(fid), '"', ''), ','); % X/Y header row
fclose(fid);
% Extract unique curve names
names = hdr1(~cellfun('isempty',hdr1));
% Create struct for each curve
mii = struct();
for i = 1:numel(names)
base = matlab.lang.makeValidName(strrep(names{i},' ','_'));
xi = 2*(i-1)+1; % X column
yi = xi+1; % Y column
mii.(base).X = T{:,xi};
mii.(base).Y = T{:,yi};
% also create workspace variable "name_wpd"
assignin('base',[base '_wpd'], mii.(base));
end
disp('Imported datasets:');
disp(fieldnames(mii));

View File

@@ -0,0 +1,139 @@
clear; clc;
M = 4;
randkey = 1;
duob_mode = db_mode.no_db;
mlse = 1;
dbtgt = 1;
baudrates = 180e9:2e9:220e9; % outer loop
rops = linspace(-10,0,12); % inner sweep
FEC_thr = 3.8e-3; % BER target
% --- allocate results
reqROP_FFE = nan(size(baudrates));
reqROP_MLSE = nan(size(baudrates));
reqROP_DBTGT = nan(size(baudrates));
reqROP_ML_MLSE2 = nan(size(baudrates));
reqROP_ML_MLSE3 = nan(size(baudrates));
%% ====================== OUTER LOOP ======================
for b = 1:numel(baudrates)
baudrate = baudrates(b);
fprintf('\n=== %.0f GBd ===\n', baudrate/1e9);
ber_ffe = nan(size(rops));
ber_mlse = nan(size(rops));
ber_dbtgt = nan(size(rops));
ber_ml2 = nan(size(rops));
ber_ml3 = nan(size(rops));
%% -------- inner ROP loop --------
for i = 1:length(rops)
rop = rops(i);
[Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model( ...
"M",M,"fsym",baudrate,"rop",rop,"laser_linewidth",1310, ...
"link_length_m",0,"random_key",1);
%% FFE + MLSE
if mlse
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ...
"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ...
"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ...
"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M, ...
'trellis_states',PAMmapper(M,0).levels,'scale_mode',2, ...
'trellis_exclusion',0,'trellis_state_mode',2,'debug',0, ...
'DIR',pf_.coefficients);
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, ...
Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ...
"precode_mode", duob_mode,'showAnalysis', 0, "postFFE", [], ...
"eth_style_symbol_mapping", 0);
ber_ffe(i) = ffe_results.metrics.BER;
ber_mlse(i) = mlse_results.metrics.BER;
end
%% FFE + duobinary target MLSE
if dbtgt
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M, ...
"trellis_states",PAMmapper(M,0).levels,'scale_mode',2, ...
'trellis_exclusion',0,'trellis_state_mode',3);
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ...
"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ...
"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ...
"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, ...
Symbols_v1, Tx_bits_v1, "precode_mode", duob_mode, ...
'showAnalysis', 0, "postFFE", []);
ber_dbtgt(i) = dbt_results.metrics.BER;
end
%% ML-based MLSE (L=2)
mu_ml = 0.1; training_epochs = 100;
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",2,"delta",4,"adaptive_mu",0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1);
ref_bits = PAMmapper(M,0).demap(y_ref);
ml_bits = PAMmapper(M,0).demap(y_ml_mlse);
[~,~,ber_ml2(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ...
"skip_front",10,"skip_end",10);
%% ML-based MLSE (L=3)
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1);
ref_bits = PAMmapper(M,0).demap(y_ref);
ml_bits = PAMmapper(M,0).demap(y_ml_mlse);
[~,~,ber_ml3(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ...
"skip_front",10,"skip_end",10);
end % ROP loop
%% --- find required ROP (FEC crossing)
reqROP_FFE(b) = interp_fec_cross(rops, ber_ffe, FEC_thr);
reqROP_MLSE(b) = interp_fec_cross(rops, ber_mlse, FEC_thr);
reqROP_DBTGT(b) = interp_fec_cross(rops, ber_dbtgt, FEC_thr);
reqROP_ML_MLSE2(b) = interp_fec_cross(rops, ber_ml2, FEC_thr);
reqROP_ML_MLSE3(b) = interp_fec_cross(rops, ber_ml3, FEC_thr);
% --- diagnostic
fprintf('Baud %.0f GBd: FFE %.1f, MLSE %.1f, DB %.1f, ML2 %.1f, ML3 %.1f\n', ...
baudrate/1e9, reqROP_FFE(b), reqROP_MLSE(b), reqROP_DBTGT(b), ...
reqROP_ML_MLSE2(b), reqROP_ML_MLSE3(b));
end
%% ====================== PLOT REQUIRED ROP ======================
cols = cbrewer2('Set1',8);
colFFE = cols(1,:);
colMLSE = cols(2,:);
colDBTGT = cols(4,:);
colML_MLSE = cols(3,:);
figure(); hold on
plot(baudrates/1e9, reqROP_FFE, '-o','Color',colFFE, 'DisplayName','FFE');
plot(baudrates/1e9, reqROP_MLSE, '-s','Color',colMLSE, 'DisplayName','FFE+PF+MLSE');
plot(baudrates/1e9, reqROP_DBTGT, '--^','Color',colDBTGT, 'DisplayName','DB tgt. MLSE');
plot(baudrates/1e9, reqROP_ML_MLSE2, '-v','Color',colML_MLSE, 'DisplayName','ML-based MLSE (L=2)');
plot(baudrates/1e9, reqROP_ML_MLSE3, '-d','Color',colML_MLSE*0.8,'DisplayName','ML-based MLSE (L=3)');
xlabel('Baud rate [GBd]');
ylabel('Required ROP [dBm]');
title('ROP required for FEC threshold');
grid on; legend('Location','northwest');
beautifyBERplot("logscale",0,"polyfit",1,"polyorder",4,"fitmethod",'polyfit');

View File

@@ -0,0 +1,140 @@
clear; clc;
M = 4;
randkey = 1;
duob_mode = db_mode.no_db;
mlse = 1;
dbtgt = 1;
link_lengths = 0:1:8; % [m] --- outer loop
rops = linspace(-10, 0, 12); % [dBm] --- inner sweep
FEC_thr = 3.8e-3; % BER target
baudrate = 200e9;
% --- allocate results
reqROP_FFE = nan(size(link_lengths));
reqROP_MLSE = nan(size(link_lengths));
reqROP_DBTGT = nan(size(link_lengths));
reqROP_ML_MLSE2 = nan(size(link_lengths));
reqROP_ML_MLSE3 = nan(size(link_lengths));
%% ====================== OUTER LOOP ======================
for L = 1:numel(link_lengths)
link_length_m = link_lengths(L);
fprintf('\n=== %.0f m fiber length ===\n', link_length_m);
ber_ffe = nan(size(rops));
ber_mlse = nan(size(rops));
ber_dbtgt = nan(size(rops));
ber_ml2 = nan(size(rops));
ber_ml3 = nan(size(rops));
%% -------- inner ROP loop --------
parfor i = 1:length(rops)
rop = rops(i);
[Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1] = standard_link_model( ...
"M",M,"fsym",baudrate,"rop",rop,"laser_wavelength",1290, ...
"link_length_km",link_length_m,"random_key",1);
Rx_sig_2sps_v1.spectrum("displayname",'Rx Sig','normalizeTo0dB',1);
%% FFE + MLSE
if mlse
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ...
"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ...
"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ...
"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M, ...
'trellis_states',PAMmapper(M,0).levels,'scale_mode',2, ...
'trellis_exclusion',0,'trellis_state_mode',2,'debug',0, ...
'DIR',pf_.coefficients);
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, ...
Rx_sig_2sps_v1, Symbols_v1, Tx_bits_v1, ...
"precode_mode", duob_mode,'showAnalysis', 0, "postFFE", [], ...
"eth_style_symbol_mapping", 0);
ber_ffe(i) = ffe_results.metrics.BER;
ber_mlse(i) = mlse_results.metrics.BER;
end
%% FFE + duobinary target MLSE
if dbtgt
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M, ...
"trellis_states",PAMmapper(M,0).levels,'scale_mode',2, ...
'trellis_exclusion',0,'trellis_state_mode',3);
ffe_order = [50, 0, 0];
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",2^13, ...
"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.005, ...
"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0, ...
"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, M, Rx_sig_2sps_v1, ...
Symbols_v1, Tx_bits_v1, "precode_mode", duob_mode, ...
'showAnalysis', 0, "postFFE", []);
ber_dbtgt(i) = dbt_results.metrics.BER;
end
%% ML-based MLSE (L=2)
mu_ml = 0.1; training_epochs = 100;
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",2,"delta",4,"adaptive_mu",0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1);
ref_bits = PAMmapper(M,0).demap(y_ref);
ml_bits = PAMmapper(M,0).demap(y_ml_mlse);
[~,~,ber_ml2(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ...
"skip_front",10,"skip_end",10);
%% ML-based MLSE (L=3)
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",2^16,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
[y_ml_mlse,y_ref] = ml_mlse_equalizer.process(Rx_sig_2sps_v1,Symbols_v1);
ref_bits = PAMmapper(M,0).demap(y_ref);
ml_bits = PAMmapper(M,0).demap(y_ml_mlse);
[~,~,ber_ml3(i)] = calc_ber(ml_bits.signal, ref_bits.signal, ...
"skip_front",10,"skip_end",10);
end % ROP loop
%% --- find required ROP (FEC crossing)
reqROP_FFE(L) = interp_fec_cross(rops, ber_ffe, FEC_thr);
reqROP_MLSE(L) = interp_fec_cross(rops, ber_mlse, FEC_thr);
reqROP_DBTGT(L) = interp_fec_cross(rops, ber_dbtgt, FEC_thr);
reqROP_ML_MLSE2(L) = interp_fec_cross(rops, ber_ml2, FEC_thr);
reqROP_ML_MLSE3(L) = interp_fec_cross(rops, ber_ml3, FEC_thr);
fprintf('Length %.0f m: FFE %.1f, MLSE %.1f, DB %.1f, ML2 %.1f, ML3 %.1f\n', ...
link_length_m, reqROP_FFE(L), reqROP_MLSE(L), reqROP_DBTGT(L), ...
reqROP_ML_MLSE2(L), reqROP_ML_MLSE3(L));
end
%% ====================== PLOT REQUIRED ROP ======================
cols = cbrewer2('Set1',8);
colFFE = cols(1,:);
colMLSE = cols(2,:);
colDBTGT = cols(4,:);
colML_MLSE = cols(3,:);
figure(); hold on
plot(link_lengths, reqROP_FFE, '-o','Color',colFFE, 'DisplayName','FFE');
plot(link_lengths, reqROP_MLSE, '-s','Color',colMLSE, 'DisplayName','FFE+PF+MLSE');
plot(link_lengths, reqROP_DBTGT, '--^','Color',colDBTGT, 'DisplayName','DB tgt. MLSE');
plot(link_lengths, reqROP_ML_MLSE2, '-v','Color',colML_MLSE, 'DisplayName','ML-based MLSE (L=2)');
plot(link_lengths, reqROP_ML_MLSE3, '-d','Color',colML_MLSE*0.8,'DisplayName','ML-based MLSE (L=3)');
xlabel('Link length [km]');
ylabel('Required ROP [dBm]');
title(sprintf('Required ROP the reach FEC threshold (3.8e-3); %.0f GBd PAM-%d', baudrate.*1e-9, M));
legend('Location','northwest');
grid on;
beautifyBERplot("logscale",0,"polyfit",1,"polyorder",3,"fitmethod",'smoothingspline');

View File

@@ -0,0 +1,99 @@
function [Rx_sig_2sps,Symbols,Tx_bits] = standard_link_model(options)
% STANDARD_LINK_MODEL Basic IM/DD link simulation
% Rx_sig_2sps = standard_link_model(...optional args...)
%
% All arguments are optional and default to standard parameters
% if not provided.
arguments
% --- Transmitter settings ---
options.M (1,1) double = 4
options.apply_pulsef (1,1) logical = true
options.fdac (1,1) double = 256e9
options.fadc (1,1) double = 256e9
options.random_key (1,1) double = 2
options.rcalpha (1,1) double = 0.05
options.kover (1,1) double = 8
options.vbias_rel (1,1) double = 0.5
options.u_pi (1,1) double = 3.2
options.laser_wavelength (1,1) double = 1310
options.laser_linewidth (1,1) double = 1e6
% --- Channel parameters ---
options.link_length_km (1,1) double = 0
options.rop (1,:) double = -5
options.fsym (1,:) double = (212:16:256)*1e9
options.doub_mode (1,1) db_mode = db_mode.no_db
% --- Debug ---
options.debug (1,1) logical = false
end
% --- Pulse former ---
Pform = Pulseformer("fsym",options.fsym,"fdac",4*options.fsym, ...
"pulse","rc","pulselength",16,"alpha",options.rcalpha);
% --- Transmitter source ---
[Digi_sig,Symbols,Tx_bits] = PAMsource( ...
"fsym",options.fsym,"M",options.M,"order",18,"useprbs",0, ...
"fs_out",options.fdac,"applyclipping",0,"clipfactor",1.5, ...
"applypulseform",options.apply_pulsef,"pulseformer",Pform, ...
"randkey",options.random_key,"db_precode",0,"db_encode",0, ...
"mrds_code",0,"mrds_blocklength",512, ...
"duobinary_mode",options.doub_mode).process();
% --- AWG driver ---
El_sig = M8199B("kover",options.kover).process(Digi_sig);
El_sig = El_sig.normalize("mode","oneone");
% --- E/O Modulation ---
vbias = -options.vbias_rel*options.u_pi;
Opt_sig = EML("mode",eml_mode.im_cosinus,"power",3, ...
"fsimu",El_sig.fs,"lambda",options.laser_wavelength, ...
"bias",vbias,"u_pi",options.u_pi,"linewidth",options.laser_linewidth, ...
"randomkey",options.random_key+1).process(El_sig);
% --- Fiber ---
Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",options.link_length_km, ...
"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig);
% --- Amplifier (ROP set) ---
Opt_sig = Amplifier("amp_mode","ideal_no_noise", ...
"gain_mode","output_power","amplification_db",options.rop).process(Opt_sig);
% --- Photodiode ---
PD_sig = Photodiode("fsimu",options.fdac*options.kover,"dark_current",2e-8, ...
"responsivity",1,"temperature",20,"nep",1.8e-11, ...
"randomkey",options.random_key).process(Opt_sig);
% --- Electrical LPF (receiver frontend) ---
rx_bwl = 70e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl, ...
"fs",options.fdac*options.kover,"filterType",filtertypes.butterworth, ...
"active",true).process(PD_sig);
% --- Scope low-pass and sampling ---
Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",options.fadc, ...
"filterType",filtertypes.butterworth,"active",true);
Scpe_sig = Scope("fsimu",options.fdac*options.kover,"fadc",options.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);
% --- Downsample to 2 sps ---
Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*options.fsym);
[~,Scpe_cell,~,found_sync] = Scpe_sig_2sps.tsynch( ...
"reference",Symbols,"fs_ref",options.fsym,"debug_plots",0);
try
Rx_sig_2sps = Scpe_cell{1}.normalize("mode","rms");
catch
Rx_sig_2sps = Scpe_sig_2sps.normalize("mode","rms");
end
end

View File

@@ -0,0 +1,157 @@
M = 4;
order = 18;
randkey = 1;
bitpattern = [];
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
N = 2^(order-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Bits = Informationsignal(bitpattern);
Symbols = PAMmapper(M,0).map(Bits);
Symbols.fs = 200e9;
Bits_ = PAMmapper(M, 0, "eth_style", 0).demap(Symbols);
% --- Channel: minimal ISI response + AWGN ---
h = [0.3 0.9 0.3,0.1]; % impulse response (normalized later if desired)
h = h / norm(h); % optional normalization for unit energy
symbols_filt = Symbols.filter(h,1);
%% SHOW FIG 3 in Paper: "ML Base Pre-Eq"
SNR_dB = [20:1:25];
SNR_db = linspace(12,25,12);
ber_ffe = zeros(size(SNR_dB));
ber_mlse_l5 = zeros(size(SNR_dB));
ber_nwf_mlse_l2 = zeros(size(SNR_dB));
ber_ml_mlse_l2 = zeros(size(SNR_dB));
ber_ml_mlse_l3 = zeros(size(SNR_dB));
ber_ml_mlse_l4 = zeros(size(SNR_dB));
epochs_training = 100;
for i = 1:numel(SNR_dB)
symbols_noi = symbols_filt;
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR
% Sequence Est L=5
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',h);
mlse_.DIR = h;
[y_mlse] = mlse_.process(symbols_noi,Symbols);
mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
[~, ~, ber_mlse_l5(i), ~] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('MLSE L5: %.2e \n',ber_mlse_l5(i));
% 2nd Approach
mu_lms = 0.0005;
pf_ncoeffs = 1;
eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",16,"sps",1,"dd_mode",1,"adaption_technique","lms");
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% FFE
[y_ffe, ffe_noise] = eq_.process(symbols_noi, Symbols);
Eq_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ffe);
[~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
fprintf('FFE: %.2e \n',ber_ffe(i));
% Postfilter
[y_white,~] = pf_.process(y_ffe, ffe_noise);
% Sequence Est
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
[y_mlse] = mlse_.process(y_white,Symbols);
mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
[~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
% ML-base MLSE L=2
adaptive_mu = 0;
mu_lms = 0.15;
ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
"traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu);
[y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i));
% ML-base MLSE L=3
mu_lms = 0.15;
ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,...
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
"traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu);
[y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
[~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i));
% % ML-base MLSE L=5
% mu_lms = 0.15;
% ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
% "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
% "traceback_depth",128,"L",5,"delta",4);
% [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
% ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
% [~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
% fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i));
end
%%
figure(); hold on;
% --- define scheme colors (consistent palette)
cols = cbrewer2('SET1',8);
colFFE = cols(1,:); % blue
colMLSE = cols(2,:); % orange
colML_MLSE = cols(3,:); % green
colNWF_MLSE = cols(4,:); % purple
% --- local simulation results
plot(SNR_dB, ber_ffe, '-o', 'Color', colFFE, 'DisplayName','FFE (N=16)');
if M==2, plot(FFE_wpd.X, FFE_wpd.Y, ':', 'LineWidth',1.5, 'Color', colFFE, 'DisplayName','Paper FFE'); end
plot(SNR_dB, ber_mlse_l5, '-s', 'Color', colMLSE, 'DisplayName','MLSE (L=5)');
if M==2, plot(MLSE_wpd.X, MLSE_wpd.Y, ':', 'LineWidth',1.5, 'Color', colMLSE, 'DisplayName','Paper MLSE L=5'); end
plot(SNR_dB, ber_nwf_mlse_l2,'--^','Color', colNWF_MLSE, 'DisplayName','FFE+PF+MLSE (L=2)');
plot(SNR_dB, ber_ml_mlse_l2, '-v', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=2)');
if M==2, plot(ML_MLSE_L_2_wpd.X,ML_MLSE_L_2_wpd.Y,':', 'LineWidth',1.5, 'Color', colML_MLSE, 'DisplayName','Paper ML-based MLSE L=2'); end
plot(SNR_dB, ber_ml_mlse_l3, '-d', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=3)');
if M==2, plot(SNR_dB, ber_ml_mlse_l5, '-p', 'Color', colML_MLSE, 'DisplayName','ML-based MLSE (L=5)'); end
if M==2, plot(ML_MLSE_L_5_wpd.X,ML_MLSE_L_5_wpd.Y,':', 'LineWidth',1.5, 'Color', colML_MLSE, 'DisplayName','Paper ML-based MLSE L=5'); end
% --- imported WebPlotDigitizer data (dotted)
yline(3.8e-3,'HandleVisibility','off');
yline(2.2e-4,'HandleVisibility','off');
% --- formatting
beautifyBERplot;
xlim([SNR_dB(1), SNR_dB(end)]);
ylim([1e-5 0.1]);
xlabel('Input SNR [dB]');
ylabel('Bit Error Rate (BER)');
title('PAM-4; M=4; AWGN Channel');
legend('Location','southwest');
grid on;

View File

@@ -0,0 +1,9 @@
FFE,,MLSE,,ML MLSE L=2,,ML MLSE L=5,
X,Y,X,Y,X,Y,X,Y
7.988476019722099,0.038632829886662855,7.9828552218736,0.02056096426419196,7.988825638727029,0.026145160025945406,7.982882115643211,0.01995262314968881
8.984755714926044,0.02462092401494627,8.991855670103094,0.008868008219069292,8.991519497982967,0.012908315306800594,8.998027790228598,0.009002182769536628
9.993487225459436,0.014339094903163154,9.988632900044824,0.0032424222072799827,9.994320932317347,0.005651632488900345,9.994751232631108,0.0034952501326754757
10.989914836396235,0.007746944324122318,10.991730165844913,0.001020224273461357,10.997256835499776,0.002129417748571358,10.991689825190498,0.0010672369906432938
12.005060510981624,0.0034952501326754757,12.001483639623487,0.00018978455660928717,11.994316450022412,0.0005679993334792185,12.007359928283282,0.0002680778116002006
12.983254146122816,0.0013169376605490738,13.011492604213357,0.000026540740973404924,12.997722994173017,0.00012652429120320801,12.992384580905423,0.000049125201846734525
13.99255042581802,0.0004081967712510506,14.00969520394442,0.000001975386920666194,13.995172568354999,0.00002183385565256239,14.015275661138503,0.0000038826695948713645
1 FFE MLSE ML MLSE L=2 ML MLSE L=5
2 X Y X Y X Y X Y
3 7.988476019722099 0.038632829886662855 7.9828552218736 0.02056096426419196 7.988825638727029 0.026145160025945406 7.982882115643211 0.01995262314968881
4 8.984755714926044 0.02462092401494627 8.991855670103094 0.008868008219069292 8.991519497982967 0.012908315306800594 8.998027790228598 0.009002182769536628
5 9.993487225459436 0.014339094903163154 9.988632900044824 0.0032424222072799827 9.994320932317347 0.005651632488900345 9.994751232631108 0.0034952501326754757
6 10.989914836396235 0.007746944324122318 10.991730165844913 0.001020224273461357 10.997256835499776 0.002129417748571358 10.991689825190498 0.0010672369906432938
7 12.005060510981624 0.0034952501326754757 12.001483639623487 0.00018978455660928717 11.994316450022412 0.0005679993334792185 12.007359928283282 0.0002680778116002006
8 12.983254146122816 0.0013169376605490738 13.011492604213357 0.000026540740973404924 12.997722994173017 0.00012652429120320801 12.992384580905423 0.000049125201846734525
9 13.99255042581802 0.0004081967712510506 14.00969520394442 0.000001975386920666194 13.995172568354999 0.00002183385565256239 14.015275661138503 0.0000038826695948713645

View File

@@ -0,0 +1,229 @@
%%% Run parameters
% TX
M = 4;
m = floor(log2(M)*10)/10;
fsym = 224e9;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 2;
rcalpha = 0.05;
kover = 8;
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1310;
laser_linewidth = 1e6;
% Channel
link_length = 0;
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);
rop = [-8];
bwl = [0.5:0.1:1.5];
fsym = [208:16:256].*1e9;
nonlin_mod = [0.5:0.01:0.75];
fsym = ones(size(nonlin_mod)).*fsym(1);
ffe_results = {};
mlse_results_lin= {};
vnle_results= {};
mlse_results_nonlin= {};
mlse_results_nonlin_states= {};
g_eye = GifWriter('Name','eye','Parallel',true);
g_mod = GifWriter('Name','modulator','Parallel',true);
for r = 1:length(nonlin_mod)
Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha);
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 1;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym(r),"M",M,"order",19,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"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 = 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 = 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 %%%%%
u_pi = 3.2;
vbias = -u_pi*nonlin_mod(r);
[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);
if 1
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]);
g_mod.addFrame(15, r);
Opt_sig.eye(fsym(r), M, "fignum", 103837);
g_eye.addFrame(103837, r);
end
%%%%%% 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);
%%%%%% 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 = 70e9;
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(r));
% Scpe_sig_resampled.signal = Scpe_sig_resampled.signal(1:2*length(Symbols));
[~, 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
%% FFE
% ffe_order = [50, 0, 0];
% eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
%
% ffe_results = ffe(eq_ffe,M,Rx_sig,Symbols,Tx_bits,...
% "precode_mode",duob_mode,...
% 'showAnalysis',0,...
% "postFFE",[],...
% "eth_style_symbol_mapping",0);
%
% ffe_results.metrics.print;
% ffe_results.config.equalizer_structure = "ffe";
%
%% MLSE linear
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2,'debug',1);
[ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
mlse_results_lin{r}.metrics.print;
ffe_results{r}.metrics.print;
%% MLSE nonlinear pre
pf_ncoeffs = 1;
ffe_order = [50, 1, 0];
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',2);
[vnle_results{r}, mlse_results_nonlin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
mlse_results_nonlin{r}.metrics.print;
vnle_results{r}.metrics.print;
%% nonlinear states MLSE linear pre
pf_ncoeffs = 1;
ffe_order = [50, 0, 0];
eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',0,'trellis_state_mode',3);
[~, mlse_results_nonlin_states{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
mlse_results_nonlin_states{r}.metrics.print;
end
end
g_mod.compile(15);
g_eye.compile(103837);
%%
figure();hold on;
plot(nonlin_mod,cellfun(@(x) x.metrics.BER, ffe_results),'DisplayName','FFE')
plot(nonlin_mod,cellfun(@(x) x.metrics.BER, vnle_results),'DisplayName','VNLE')
plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_lin),'DisplayName','FFE+MLSE')
plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_nonlin_states),'DisplayName','FFE+nonlin. states MLSE')
plot(nonlin_mod,cellfun(@(x) x.metrics.BER, mlse_results_nonlin),'DisplayName','VNLE+MLSE')
xlabel('Nonlinear Driving');
ylabel('BER')
set(gca,'YScale','log');
legend;
ylim([1e-4 1e-1]);
beautifyBERplot;

View File

@@ -0,0 +1,252 @@
try
rop = res.settings.rop; % 12 points
wavelengthplan = res.settings.wavelengthplan;
catch
wavelengthplan = [1295,1305,1315,1325];
wavelengthplan = calcWavelengthPlan(16,400e9,1310);
rop = -8.25:0.75:0;
end
N = length(wavelengthplan);
figure(); hold on;
cols = cbrewer2('set2',N); % one color per wavelength (Ch)
fec = 2.2e-4;
fec = 3.8e-3;
Sffe = cell(1,N);
Svnle = cell(1,N);
Smlse = cell(1,N);
Sdbt = cell(1,N);
% Choose your quantile band. For your old style, use 0.04/0.99:
qLow = 0.0; % lower quantile (e.g., 0.04 for old script)
qHigh = 1; % upper quantile (e.g., 0.99 for old script)
cols = linspecer(N); % one color per wavelength (Ch)
cols = cbrewer2('set1',N);
for l = 1:N
% Slice 12x50 cell arrays
ffe_cells = reshape(squeeze(res.ffe(l,:,:)),length(rop),[]);
vnle_cells = reshape(squeeze(res.vnle(l,:,:)),length(rop),[]);
mlse_cells = reshape(squeeze(res.mlse(l,:,:)),length(rop),[]);
dbt_cells = reshape(squeeze(res.dbt(l,:,:)),length(rop),[]);
[Sffe{l}, noX_ffe] = fecCrossings(rop, ffe_cells, fec);
[Svnle{l}, noX_ffe] = fecCrossings(rop, vnle_cells, fec);
[Smlse{l}, noX_ffe] = fecCrossings(rop, mlse_cells, fec);
[Sdbt{l}, noX_ffe] = fecCrossings(rop, dbt_cells, fec);
% Extract BER matrices using only complete realizations (12/12 ROP filled)
ffe_mat = extractCompleteBER(ffe_cells); % 12 x K_ffe
vnle_mat = extractCompleteBER(vnle_cells); % 12 x K_vnle
mlse_mat = extractCompleteBER(mlse_cells); % 12 x K_mlse
mlse_alpha_mat = extractCompleteAlphas(mlse_cells); % 12 x K_mlse
dbt_mat = extractCompleteBER(dbt_cells); % 12 x K_dbt
showLegend = 1; % one legend entry per technique
% Plot shaded band + mean line with boundedline
% plotBandMeanBL(rop, ffe_mat, cols(l,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend);
% scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black');
plotBandMeanBL(rop, vnle_mat, cols(l,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend);
% plotBandMeanBL(rop, mlse_mat, cols(l,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '-o', showLegend);
% plotBandMeanBL(rop, dbt_mat, cols(l,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend);
set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11);
yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5);
end
ylabel('BER');
xlabel('ROP');
title('BER vs. ROP');
xlim([min(rop) max(rop)]);
ylim([1e-5 0.3]);
grid on;
legend show;
S_cell = Sdbt;
S_cell =Smlse;
S_cell = {Svnle,Smlse,Sdbt};
S_cell = {Svnle};
figure(5); hold on;
for i = 1:length(S_cell)
% Pad to rectangular matrix: rows = realizations, cols = wavelengths
Kmax = max(cellfun(@numel, S_cell{i}));
S_mat = NaN(Kmax, N);
for l = 1:N
k = numel(S_cell{i}{l});
if k > 0
S_mat(1:k, l) = S_cell{i}{l};
end
end
% --- Violin plot over wavelengths (columns) ---
cols=linspecer(3);
catLabels = arrayfun(@(nm) sprintf('%d nm', nm), wavelengthplan, 'UniformOutput', false);
vs = violinplot(S_mat, catLabels, ...
'ViolinColor', cols(i,:), ...
'ViolinAlpha', 0.10, ...
'MarkerSize', 20, ...
'ShowMedian', true, ...
'EdgeColor', cols(i,:), ...
'ShowWhiskers', false, ...
'ShowData', true, ...
'ShowBox', false, ...
'Bandwidth', 0.05);
ylim([floor(min(S_mat,[],'all')), ceil(max(S_mat,[],'all'))])
ylim([-8 0]);
ylabel('ROP at FEC crossing');
title(sprintf('RROP to cross BER %.2e', fec));
grid on; box on;
end
%% ================= helper =================
function plotBandMeanBL(x, Y, color, techLabel, qLow, qHigh, lineSpec, showLegend)
% Y: (nPoints x nRealizations)
% Remove realizations that are entirely zero (like removeZeros behavior)
badCols = all(Y == 0, 1);
Y(:, badCols) = [];
Y(Y==0) = 1e-8;
% Stats across realizations
mu = mean(Y, 2, 'omitnan'); % mean line
lo = quantile(Y, qLow, 2); % lower bound
hi = quantile(Y, qHigh, 2); % upper bound
% Convert to asymmetric distances required by boundedline:
% b(:,1) = distance to lower side; b(:,2) = distance to upper side
b = [mu - lo, hi - mu];
% Call boundedline with alpha shading
[hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', ...
'transparency', 0.18);
% Color styling
set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4);
set(hp, 'FaceColor', color, 'HandleVisibility','off'); % patch hidden in legend
% Single legend entry per technique (use first wavelength only)
if showLegend
set(hl, 'DisplayName', techLabel);
else
set(hl, 'HandleVisibility','off');
end
% Optional: outline the bounds if outlinebounds is available
if exist('outlinebounds','file') == 2
ho = outlinebounds(hl, hp);
set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, ...
'HandleVisibility','off');
end
end
function [S, noCrossingMask, Y_keep] = fecCrossings(rop, cells12xR, fec)
% cells12xR: 12xR cell array (one wavelength + scheme slice)
% each cell must be a struct with .metrics.BER
% rop: 12x1 numeric vector of ROP points
% fec: scalar FEC threshold (e.g., 3.8e-3)
%
% Outputs:
% S 1xK vector of crossing ROP per kept realization (NaN if none)
% noCrossingMask 1xK logical mask: true if no crossing for that realization
% Y_keep 12xK numeric BER matrix used for the crossing detection
% 1) keep only complete realization columns
Y = extractCompleteBER(cells12xR); % -> 12 x K
if isempty(Y)
S = [];
noCrossingMask = [];
Y_keep = Y;
return;
end
% 2) optionally drop realizations with mean BER > 0.1
ok = mean(Y,1,'omitnan') <= 0.1;
Y = Y(:, ok);
if isempty(Y)
S = [];
noCrossingMask = [];
Y_keep = Y;
return;
end
% 3) find crossings per realization
nR = size(Y,2);
S = nan(1,nR);
noCrossingMask = true(1,nR);
rop = rop(:); % ensure column
for j = 1:nR
y = Y(:,j);
% sign change from >fec to <=fec (first time it drops below FEC)
above = (y > fec);
idx = find(above(1:end-1) & ~above(2:end), 1, 'first');
if ~isempty(idx)
% linear interpolation between (x1,y1) and (x2,y2)
x1 = rop(idx); y1 = y(idx);
x2 = rop(idx+1); y2 = y(idx+1);
if isfinite(y1) && isfinite(y2) && y2 ~= y1
t = (fec - y1) / (y2 - y1);
S(j) = x1 + t*(x2 - x1);
noCrossingMask(j) = false;
end
end
end
Y_keep = Y;
end
function Y = extractCompleteBER(cellSlice)
% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER
% Keep only those realization columns where ALL 12 ROP entries are valid.
if isempty(cellSlice), Y = []; return; end
nR = size(cellSlice,2);
keep = false(1,nR);
for r = 1:nR
col = cellSlice(:,r);
keep(r) = all(cellfun(@(c) ~isempty(c) , col));
end
if ~any(keep), Y = []; return; end
Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true);
end
function Y = extractCompleteAlphas(cellSlice)
% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER
% Keep only those realization columns where ALL 12 ROP entries are valid.
if isempty(cellSlice), Y = []; return; end
nR = size(cellSlice,2);
keep = false(1,nR);
for r = 1:nR
col = cellSlice(:,r);
keep(r) = all(cellfun(@(c) ~isempty(c) , col));
end
if ~any(keep), Y = []; return; end
Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true);
end

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

@@ -0,0 +1,278 @@
%%% Run parameters
% TX
% --- FIRST LINE: evaluate settings located beside this script ---
run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m'));
s = struct;
s.num_realiz = 1;
s.wavelengthplan = calcWavelengthPlan(16,400e9,1310);
s.wavelengthplan = [1295,1305,1315,1325];
s.link_length = 2;
s.pmd = 0.0;
s.gamma = 0.00;
s.M = 4;
m = floor(log2(s.M)*10)/10;
fsym = 224e9;
fdac = 2*fsym;
fadc = 2*fsym;
s.random_key = 100;
% Laser / s.Modulator
vbias_rel = 0.5;
u_pi = 3.2;
vbias = -vbias_rel*u_pi;
laser_linewidth = 0e6;
% EQ SETTINGS
vnle_order1 = 50;
vnle_order2 = 3;
vnle_order3 = 3;
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dfe_order = [0 0 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;
%DB Stuff
db_precode = 0;
db_encode = 0;
duob_mode = db_mode.no_db;
apply_pulsef = 0;
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
N = numel(s.wavelengthplan);
f_plan = physconst('lightspeed')./(s.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;
kover = 8;
upsample_required = f_nyq./(fdac*kover/2);
upsample_pow = 2^nextpow2(upsample_required);
upsample_ceil = ceil(upsample_required);
s.f_opt = fdac*kover*upsample_pow;
s.f_opt_nyq = s.f_opt/2;
signal_cell = {};
Symbols = {};
Tx_bits = {};
s.rop = -6:0.75:-0.75;
output_ffe = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz);
output_vnle = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz);
output_mlse = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz);
output_dbt = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz);
for realiz = 1:s.num_realiz
parfor l = 1:N
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(...
"fsym",fsym,"M",s.M,"order",18,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",s.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 = s.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);
%%%%% s.MODULATE E/O CONVERSION %%%%%
Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",s.random_key+l+realiz).process(El_sig);
signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",100).process(Eml_out);
end
Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover,...
"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, s.random_key+realiz);
for seg = 1:nSegments
Opt_sig_wdm_fib = DP_Fiber("L",s.link_length/nSegments,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07,...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0,...
"gamma",s.gamma,"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",s.link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"s.gamma",0,"Dslope",0.07).process(Opt_sig)
for ri = 1:length(s.rop)
%%%%%% ROP %%%%%%
Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.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",s.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
ffe_order = [50, 0, 0];
eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l},...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
output_ffe{l,ri,realiz} = ffe_results;
%VNLE
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);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
useviterbi = 0;
if useviterbi
mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels);
else
mlse_ = MLSE("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels);
end
[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ...
"precode_mode", duob_mode,...
'showAnalysis', 0, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
output_vnle{l,ri,realiz} = vnle_results;
output_mlse{l,ri,realiz} = mlse_results;
% DB tgt.
useviterbi = 0;
if useviterbi
mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels);
else
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",s.M,"trellis_states",PAMmapper(s.M,0).levels);
end
ffe_order = [50, 5, 5];
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_, mlse_db_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0,...
"postFFE", []);
output_dbt{l,ri,realiz} = dbt_results;
end
end
res = struct();
res.settings = s;
res.ffe = output_ffe;
res.vnle = output_vnle;
res.mlse = output_mlse;
res.dbt = output_dbt;
% Save results
save(fullfile(output_root, fname), 'res', '-v7.3');
fprintf('Saved results to: %s\n', fullfile(output_root, fname));
disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss'));
end
function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey)
% s.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.07; % 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

View File

@@ -0,0 +1,80 @@
% Add the imdd_simulation framework to the path
if ispc
addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation'));
else
% Linux path on the cluster
addpath(genpath('/work_beegfs/sutef391/imdd_simulation'));
end
% Quiet the ambiguous CET warning (best is to set TZ in sbatch; see below)
warning('off','MATLAB:datetime:AmbiguousTimeZone');
% How many workers?
cpus = str2double(getenv('SLURM_CPUS_PER_TASK'));
if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end
% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME
% Prefer $TMPDIR if your cluster provides it, else tempdir().
tmpbase = getenv('TMPDIR');
if isempty(tmpbase), tmpbase = tempdir; end
jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ...
getenv('USER'), getenv('SLURM_JOB_ID')));
if ~exist(jsl,'dir'); mkdir(jsl); end
% Configure the local cluster explicitly and start the pool
c = parcluster('local');
c.NumWorkers = cpus;
c.JobStorageLocation = jsl;
p = gcp('nocreate');
if isempty(p) || p.NumWorkers ~= cpus
if ~isempty(p), delete(p); end
p = parpool(c, cpus); % avoids the queued state
end
fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation);
% result filename (timestamp + optional job id)
t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss');
jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end
host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end
% Output directory depends on platform
if ispc
output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\');
else
output_root = '/work_beegfs/sutef391/results_WDM';
end
if ~exist(output_root,'dir'), mkdir(output_root); end
% Build filename
t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss');
jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end
host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end
fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid);