new plots for Diss. Mostly AI gen. Few changes in actual codebase

This commit is contained in:
Silas Oettinghaus
2026-07-30 08:35:45 +02:00
parent 125d8508ca
commit 7a9deaeb0c
62 changed files with 6171 additions and 630 deletions

View File

@@ -0,0 +1,158 @@
% Minimal all-time database plot: PAM-4 baudrate sweep versus VNLE noise.
% The dashed curves are the Burg postfilter responses estimated by
% VNLE + postfilter + MLSE.
%% SettingsC:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Advanced_DSP_for_400G_IMDD_experiments\Auswertung_JLT\final\FIGURE_EQ_NOISE_VS_BAUDRATE.m
M = 4;
bitrate = [390e9]; % [] -> all matching baudrates in the DB
dsp_options.storage_path = "W:\labdata\sioe_labor\";
dsp_options.max_occurences = 1; % standard routine: first synchronized signal
fiber_length = 10;
wavelength = 1310;
is_mpi = 0;
db_mode_filter = int32(db_mode.no_db);
rop_attenuation = 0;
vnle_order = [50 5 5];
dfe_order = [0 0 0];
training_length = 4096*2;
postfilter_order = 1;
%% Query the all-time database
db = DBHandler( ...
"dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
fp = QueryFilter();
fp.where('Runs', 'pam_level', SqlOperator.EQUALS, M);
fp.where('Runs', 'fiber_length', SqlOperator.EQUALS, fiber_length);
fp.where('Runs', 'wavelength', SqlOperator.EQUALS, wavelength);
fp.where('Runs', 'is_mpi', SqlOperator.EQUALS, is_mpi);
fp.where('Runs', 'db_mode', 'LESS_THAN', 2);
fp.where('Runs', 'rop_attenuation', SqlOperator.EQUALS, rop_attenuation);
% fp.where('Runs', 'symbolrate','EQUALS', 180e9);
% if ~isempty(baudrate_GBd)
% fp.where('Runs', 'symbolrate', SqlOperator.IN, baudrate_GBd*1e9);
% end
fields = [db.getTableFieldNames('dashboard_ungrouped_alltime')];
fields = unique(fields, 'stable');
[runs, ~] = db.queryDB(fp, fields);
if ~isempty(bitrate)
runs = runs(ismember(runs.bitrate, bitrate), :);
end
if isempty(runs)
error('FIGURE_EQ_NOISE_VS_BAUDRATE:NoData', ...
'No PAM-%d runs matched the selected baudrate range.', M);
end
run_ids = unique(runs.run_id, 'stable');
%% One plot: VNLE residual noise and Burg tap response
fig = figure(220);
clf(fig);
hold on;
max_fs = 0;
for runIndex = 1:numel(run_ids)
runData = queryRunid(run_ids(runIndex), db);
fsym = double(runData.symbolrate(1));
bitrate = double(runData.bitrate(1));
dsp_options.max_occurences = 20;
dsp_options.start_occurence = 2;
[Tx_bits, Symbols, Scpe_cell, found_sync] = loadAndSyncRunSignals(runData, dsp_options);
if ~found_sync || isempty(Scpe_cell)
warning('Skipping run %g: no synchronized signal found.', runData.run_id(1));
continue
end
useavg = 0;
Scpe_sig = averageScopeSignals(Scpe_cell, useavg, 1);
Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym);
Scpe_sig = Scpe_sig.normalize("mode", "rms");
% VNLE: retain the difference between equalizer output and reference.
eq_vnle = EQ( ...
"Ne", vnle_order, ...
"Nb", dfe_order, ...
"training_length", training_length, ...
"training_loops", 5, ...
"dd_loops", 5, ...
"K", 2, ...
"DCmu", 0.001, ...
"DDmu", [0.0004 0.0004 0.0004 0.0004], ...
"DFEmu", 0.05, ...
"FFEmu", 0, ...
"plotfinal", 0, ...
"ideal_dfe", 1);
% eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ...
% "mu_dd",1e-1,"mu_tr",0.4,"order",50, ...
% "sps",2,"decide",0,"optmize_mus",1,"dd_mode",1, ...
% "adaption_technique","nlms","dc_tracking_mu",1.021e-05);
[equalized_signal, eq_noise] = eq_vnle.process(Scpe_sig, Symbols);
postfilter_order = 3;
pf = Postfilter("ncoeff", postfilter_order, "useBurg", 1);
[mlse_sig_sd,whitened_noise] = pf.process(equalized_signal, eq_noise);
% mlse = MLSE( ...
% "DIR", [0 0], ...
% "duobinary_output", 0, ...
% "M", M, ...
% "trellis_states", PAMmapper(M, 0).levels);
%
% [results, ~] = vnle_postfilter_mlse( ...
% eq_vnle, pf, mlse, M, Scpe_sig, Symbols, Tx_bits, ...
% "precode_mode", db_mode.no_db, ...
% "showAnalysis", 0, ...
% "postFFE", [], ...
% "eth_style_symbol_mapping", 0);
eq_noise = eq_noise - mean(eq_noise.signal);
fig = figure(220+runIndex);
showEQNoisePSD(eq_noise, ...
"fignum", fig.Number, ...
"displayname", sprintf('%.0f Gbps: VNLE Noise', bitrate*1e-9), ...
"postfilter_taps", pf.coefficients, ...
"colormode", "qualitative");
whitened_noise = whitened_noise - mean(whitened_noise.signal);
whitened_noise.spectrum("displayname", 'Whitened Noise', "fignum", fig.Number, "normalizeTo0dB", 0,"fft_length",4096,"normalizeToDC",0);
max_fs = max(max_fs, eq_noise.fs);
end
xlabel('Frequency in GHz');
ylabel('normalized to 0 dB');
title('Noise of soft decision signal (not MLSE)');
grid on;
grid minor;
legend('show', 'Interpreter', 'none', 'Location', 'best');
xlim([-max_fs/2 max_fs/2]*1e-9);
ylim([-20 0]);
function signalOut = averageScopeSignals(scopeCell, averageSignals, gain)
signalOut = scopeCell{1};
if ~averageSignals
return
end
commonLength = min(cellfun(@(s) numel(s.signal), scopeCell));
scopeMean = zeros(commonLength, 1);
for idx = 1:numel(scopeCell)
scopeMean = scopeMean + scopeCell{idx}.signal(1:commonLength);
end
scopeMean = scopeMean ./ numel(scopeCell);
signalOut.signal = gain .* scopeMean;
end

View File

@@ -8,7 +8,7 @@ clear; clc;
%% 1) Query data
selectedPamLevel = 8;
selectedPamLevels = [4, 6, 8];
selectedFiberLengthKm = 10;
selectedWavelengthNm = 1310;
selectedRopAttenuation = 0; % set [] to use all ROP attenuation values
@@ -33,7 +33,6 @@ db.refresh();
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
fp.where('Runs', 'pam_level', 'EQUALS', selectedPamLevel);
fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
if ~isempty(selectedRopAttenuation)
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
@@ -66,6 +65,8 @@ for fieldIdx = 1:numel(numericFields)
end
end
data = data(ismember(data.pam_level, selectedPamLevels), :);
if ~ismember("precomp_amp", string(data.Properties.VariableNames))
warning("plot_best_algos:NoPrecompAmp", ...
"Runs.precomp_amp was not returned. Falling back to pre_emphasis = (db_mode == 0).");
@@ -85,6 +86,9 @@ if ismember("equalizer_structure", string(duobinaryRows.Properties.VariableNames
equalizerMask(duobinaryRows.equalizer_structure, ...
equalizer_structure.db_encoded), :);
end
duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>datetime("2026-01-01 00:00:00"),:);
duobinaryPlotData = buildDuobinarySignalingRows(duobinaryRows);
duobinaryPlotData = duobinaryPlotData(isfinite(duobinaryPlotData.BER_plot) & ...
duobinaryPlotData.BER_plot > 0 & ...
@@ -117,70 +121,75 @@ if isempty(availableStyles)
return
end
fig = figure(); clf;
ax = axes(fig); hold(ax, "on");
fig = figure(432); clf;
t = tiledlayout(fig, 1, numel(selectedPamLevels), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for styleIdx = 1:height(availableStyles)
style = availableStyles(styleIdx, :);
rowMask = bestPlotData.algorithm_key == style.algorithm_key;
if ~any(rowMask)
continue
for pamIdx = 1:numel(selectedPamLevels)
selectedPamLevel = selectedPamLevels(pamIdx);
ax = nexttile(t); hold(ax, "on");
pamMask = bestPlotData.pam_level == selectedPamLevel;
for styleIdx = 1:height(availableStyles)
style = availableStyles(styleIdx, :);
rowMask = pamMask & bestPlotData.algorithm_key == style.algorithm_key;
if ~any(rowMask)
continue
end
algoData = sortrows(bestPlotData(rowMask, :), "grossrate_Gbps");
if showRawEntries
scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
9, ...
"Marker", ".", ...
"MarkerEdgeColor", style.color, ...
"MarkerFaceColor", style.color, ...
"MarkerEdgeAlpha", 0.25, ...
"MarkerFaceAlpha", 0.25, ...
"HandleVisibility", "off");
end
if showBestLine
plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
"LineStyle", style.lineStyle, ...
"Marker", style.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.5, ...
"Color", style.color, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", style.color, ...
"DisplayName", style.name);
end
end
algoData = sortrows(bestPlotData(rowMask, :), "grossrate_Gbps");
yline(ax, [2.2e-4, 4.85e-3, 2e-2], ...
"LineWidth", 1, ...
"LineStyle", "--", ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
if showRawEntries
scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
9, ...
"Marker", ".", ...
"MarkerEdgeColor", style.color, ...
"MarkerFaceColor", style.color, ...
"MarkerEdgeAlpha", 0.25, ...
"MarkerFaceAlpha", 0.25, ...
"HandleVisibility", "off");
end
% title(ax, sprintf("PAM-%d, %.0f km, %.0f nm", ...
% selectedPamLevel, selectedFiberLengthKm, selectedWavelengthNm));
xlabel(ax, "Gross rate [Gb/s]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [8e-5, 0.1]);
grid(ax, "on");
box(ax, "on");
if showBestLine
plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
"LineStyle", style.lineStyle, ...
"Marker", style.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.5, ...
"Color", style.color, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", style.color, ...
"DisplayName", style.name);
xticks(ax, 300:30:480);
xlim(ax, [300, 480]);
legend(ax, "Location", "northeast", "Interpreter", "none");
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
end
yline(ax, [2.2e-4, 4.85e-3, 2e-2], ...
"LineWidth", 1, ...
"LineStyle", "--", ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
title(ax, sprintf("PAM-%d, %.0f km, %.0f nm", ...
selectedPamLevel, selectedFiberLengthKm, selectedWavelengthNm));
xlabel(ax, "Gross rate [Gb/s]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-5, maxBerForPlot]);
grid(ax, "on");
box(ax, "on");
xTicks = unique(bestPlotData.grossrate_Gbps(isfinite(bestPlotData.grossrate_Gbps)));
if ~isempty(xTicks)
xticks(ax, xTicks);
xlim(ax, [min(xTicks), max(xTicks)]);
end
legend(ax, "Location", "best", "Interpreter", "none");
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 0.7200 0.4200]);
set(fig, "Position", 1e3 .* [0.1070 0.5497 1.0585 0.2282]);
%% Local helpers
@@ -220,7 +229,7 @@ styles = table( ...
"VNLE + PF + MLSE"; ...
"VNLE DBt. + MLSE"; ...
"ML pre-EQ + Viterbi"; ...
"Duobinary signaling"], ...
"DBS + VNLE + MLSE"], ...
["o"; "square"; "diamond"; "^"; "v"], ...
["-"; "-"; "-"; "-"; "-"], ...
["w"; "w"; "w"; "w"; "w"], ...
@@ -322,7 +331,7 @@ value = double(enumEntry);
end
function bestData = bestBerByAlgorithmAndGrossRate(data)
groupVars = ["algorithm_key", "grossrate_Gbps"];
groupVars = ["pam_level", "algorithm_key", "grossrate_Gbps"];
groupId = findgroups(data(:, groupVars));
keepIdx = NaN(max(groupId), 1);

View File

@@ -8,7 +8,7 @@ clear; clc;
%% 1) Gather data
selectedPamLevel = 4;
selectedFiberLengthKm = 10;
selectedFiberLengthKm = 2;
selectedWavelength =1310; % set [] to use all wavelengths
selectedRopAttenuation = []; % set [] to use all ROP attenuation values
selectedIsMpi = []; % set [] to use all entries
@@ -98,7 +98,7 @@ if isempty(availableEqStyles)
return
end
fig = figure(401); clf;
fig = figure(); clf;
tiledlayout(1, height(availableEqStyles), ...
"TileSpacing", "compact", ...
"Padding", "compact");

View File

@@ -14,7 +14,9 @@ selectedWavelengthNm = 1310;
selectedRopAttenuation = 0;
selectedIsMpi = 0;
selectedDbMode = db_mode.db_encoded;
selectedEqualizerStructure = equalizer_structure.db_encoded;
selectedEqualizerStructures = [ ...
equalizer_structure.db_encoded, ...
equalizer_structure.ml_mlse];
maxBerForPlot = 0.5;
showRawEntries = false;
@@ -60,8 +62,13 @@ end
data = data(ismember(data.pam_level, selectedPamLevels), :);
if ismember("equalizer_structure", string(data.Properties.VariableNames)) && ...
~isempty(selectedEqualizerStructure)
data = data(equalizerMask(data.equalizer_structure, selectedEqualizerStructure), :);
~isempty(selectedEqualizerStructures)
eqMask = false(height(data), 1);
for eqIdx = 1:numel(selectedEqualizerStructures)
eqMask = eqMask | equalizerMask(data.equalizer_structure, ...
selectedEqualizerStructures(eqIdx));
end
data = data(eqMask, :);
end
plotData = buildDetectionMetricRows(data);
@@ -150,6 +157,9 @@ for pamIdx = 1:numel(availablePamLevels)
xlim(ax, [min(xTicks), max(xTicks)]);
end
xticks(ax, 300:30:480);
xlim(ax, [300, 480]);
legend(ax, "Location", "best", "Interpreter", "none");
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
@@ -182,31 +192,52 @@ values = double(values);
end
function plotData = buildDetectionMetricRows(data)
baseRows = data(isfinite(data.BER), :);
dbEncodedRows = data(equalizerMask(data.equalizer_structure, ...
equalizer_structure.db_encoded), :);
baseRows = dbEncodedRows(isfinite(dbEncodedRows.BER), :);
baseRows.detection_type = repmat("VNLE + MLSE", height(baseRows), 1);
baseRows.BER_plot = baseRows.BER;
if ismember("BER_precoded", string(data.Properties.VariableNames))
memorylessRows = data(isfinite(data.BER_precoded), :);
memorylessRows = dbEncodedRows(isfinite(dbEncodedRows.BER_precoded), :);
memorylessRows.detection_type = repmat("VNLE + memoryless", ...
height(memorylessRows), 1);
memorylessRows.BER_plot = memorylessRows.BER_precoded;
plotData = [baseRows; memorylessRows];
else
warning("plot_duobinary_detection:NoPrecodedBer", ...
"BER_precoded was not returned. Plotting only VNLE + MLSE rows.");
plotData = baseRows;
"BER_precoded was not returned. Plotting only BER rows.");
memorylessRows = dbEncodedRows([], :);
end
mlMlseRows = data(equalizerMask(data.equalizer_structure, ...
equalizer_structure.ml_mlse), :);
if ismember("BER_precoded", string(data.Properties.VariableNames))
mlMlsePrecodedRows = mlMlseRows(isfinite(mlMlseRows.BER_precoded), :);
mlMlsePrecodedRows.detection_type = repmat( ...
"ML pre-EQ + Viterbi", ...
height(mlMlsePrecodedRows), 1);
mlMlsePrecodedRows.BER_plot = mlMlsePrecodedRows.BER_precoded;
else
mlMlsePrecodedRows = mlMlseRows([], :);
end
plotData = [baseRows; memorylessRows; mlMlsePrecodedRows];
end
function styles = defaultDetectionStyles()
styles = table( ...
["VNLE + MLSE"; "VNLE + memoryless"], ...
["VNLE + MLSE"; "VNLE + memoryless"], ...
["o"; "square"], ...
["-"; "--"], ...
["w"; "none"], ...
[clr.Paired.blue; clr.Paired.orange], ...
["VNLE + MLSE"; ...
"VNLE + memoryless"; ...
"ML pre-EQ + Viterbi"], ...
["VNLE + MLSE"; ...
"VNLE + memoryless"; ...
"ML pre-EQ + Viterbi"], ...
["o"; "square"; "^"], ...
["-"; "--"; "-"], ...
["w"; "none"; "w"], ...
[clr.Paired.blue; ...
clr.Paired.orange; ...
clr.Paired.purple], ...
'VariableNames', ["detection_type", "name", "marker", ...
"lineStyle", "markerFaceColor", "color"]);
end

View File

@@ -0,0 +1,384 @@
%% 400G revisit: eye comparison for PAM-4/6/8
% The native Signal.eye() method is used for the eye display. Optional TikZ
% export is configured below and uses mat2tikz_improved().
%
% db_mode_setting:
% 0 - no duobinary processing; use the copied FFE configuration
% 1 - DB-targeted VNLE; the VNLE target is Duobinary().encode(Symbols)
% 2 - DB-encoded data; equalize the stored DB-encoded Symbols with a VNLE
clc;
%% Configuration
M_values = [4];
selectedBitrate = 360e9;
selectedFiberLengthKm = 10;
selectedWavelengthNm = 1310;
selectedRopAttenuation = 0;
selectedIsMpi = 0;
db_mode_setting = 2; % Set to 0, 1, or 2.
average_signals = 1;
show_histograms = true;
export_tikz = 0;
max_occurences = 10;
histogram_figure_base = 600;
eye_figure_base = 500;
psd_figure_base = 700;
% PSD colors follow the established DSP algorithm styles. The target is
% black and the received waveform is grey.
dsp_styles = defaultAlgorithmStyles();
psd_color_target = [0, 0, 0];
psd_color_received = [0.3500, 0.3500, 0.3500];
psd_color_equalized = dsp_styles.color(dsp_styles.algorithm_key == "vnle", :);
tikz_root = 'C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\04_Experimental_Evaluation\tikz\400g';
tikz_eye_folder = fullfile(tikz_root, 'eyes');
tikz_histogram_folder = fullfile(tikz_root, 'histograms');
tikz_psd_folder = fullfile(tikz_root, 'psd');
% The averaging/gain/normalization follows FIGURE_EYES.m.
average_gain = 1.25;
% Common equalizer parameters copied from FIGURE_EYES.m.
len_tr = 4096*2;
training_loops = 5;
dd_loops = 5;
eq_K = 2;
mu_dc = 0.005;
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
if ~ismember(db_mode_setting, [0, 1, 2])
error('PLOT_EYES_400G_REVISIT:InvalidDbMode', ...
'db_mode_setting must be 0, 1, or 2.');
end
if export_tikz
if ~exist(tikz_eye_folder, 'dir')
mkdir(tikz_eye_folder);
end
if ~exist(tikz_histogram_folder, 'dir')
mkdir(tikz_histogram_folder);
end
if ~exist(tikz_psd_folder, 'dir')
mkdir(tikz_psd_folder);
end
end
%% Database setup
dsp_options = struct();
dsp_options.storage_path = 'W:\labdata\sioe_labor\';
dsp_options.start_occurence = 1;
dsp_options.max_occurences = max_occurences;
database = DBHandler( ...
"dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
%% DSP phase: query, synchronize, equalize, and collect all signals
selectedRuns = [];
eyeSignals = cell(size(M_values));
rawSignals = cell(size(M_values));
eyeReferences = cell(size(M_values));
symbolRates = NaN(size(M_values));
for mIdx = 1:numel(M_values)
M = M_values(mIdx);
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
fp.where('Runs', 'is_mpi', 'EQUALS', selectedIsMpi);
fp.where('Runs', 'pam_level', 'EQUALS', M);
fp.where('Runs', 'db_mode', 'EQUALS', db_mode_setting);
if ~isempty(selectedBitrate)
fp.where('Runs', 'bitrate', 'EQUALS', selectedBitrate);
end
[dataTable, ~] = database.queryDB(fp, database.getTableFieldNames('Runs'));
if isempty(dataTable)
error('PLOT_EYES_400G_REVISIT:MissingRun', ...
'No run found for PAM-%d with the selected filters.', M);
end
dataTable = sortrows(dataTable, {'bitrate', 'run_id'});
dataTable = dataTable(1, :);
if isempty(selectedRuns)
selectedRuns = dataTable;
else
selectedRuns = [selectedRuns; dataTable]; %#ok<AGROW>
end
fsym = double(dataTable.symbolrate(1));
symbolRates(mIdx) = fsym;
[~, Symbols, Scpe_cell, found_sync] = ...
loadAndSyncRunSignals(dataTable, dsp_options);
if ~found_sync || isempty(Scpe_cell)
error('PLOT_EYES_400G_REVISIT:SynchronizationFailed', ...
'Could not synchronize the selected PAM-%d run.', M);
end
Scpe_sig = averageScopeSignals(Scpe_cell, average_signals, average_gain);
Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym);
Scpe_sig = Scpe_sig.normalize("mode", "rms");
switch db_mode_setting
case 0
% Simply FFE, copied from FIGURE_EYES.m.
eq_ = EQ("Ne", [50, 0, 0], ...
"Nb", [0, 0, 0], ...
"training_length", len_tr, ...
"training_loops", training_loops, ...
"dd_loops", dd_loops, ...
"K", eq_K, ...
"DCmu", mu_dc, ...
"DDmu", [mu_ffe, mu_dfe], ...
"DFEmu", 0.005, ...
"FFEmu", 0, ...
"plotfinal", 0, ...
"ideal_dfe", false);
referenceForEye = Symbols;
case 1
% DB-targeted VNLE: train against the DB waveform generated
% from the stored (precoded but not DB-encoded) symbols.
eq_ = makeVnle(len_tr, training_loops, dd_loops, eq_K, ...
mu_dc, mu_ffe, mu_dfe);
referenceForEye = Duobinary().encode(Symbols, "M", M);
case 2
% DB-encoded data: the stored Symbols already contain the DB
% encoded reference waveform used by the VNLE.
eq_ = makeVnle(len_tr, training_loops, dd_loops, eq_K, ...
mu_dc, mu_ffe, mu_dfe);
referenceForEye = Symbols;
end
[equalized_signal, ~] = eq_.process(Scpe_sig, referenceForEye);
eyeSignals{mIdx} = equalized_signal;
rawSignals{mIdx} = Scpe_sig;
eyeReferences{mIdx} = referenceForEye;
fprintf('PAM-%d: run %d, %.3f GBd, db_mode %d\n', ...
M, dataTable.run_id(1), fsym*1e-9, db_mode_setting);
end
%% Plotting and export phase
show_histograms = 1;
showEye = 1;
showPSD = 1;
for mIdx = 1:numel(M_values)
M = M_values(mIdx);
fsym = symbolRates(mIdx);
rawSignal = rawSignals{mIdx};
equalized_signal = eyeSignals{mIdx};
referenceForEye = eyeReferences{mIdx};
if showPSD
psdFigure = psd_figure_base + mIdx + db_mode_setting;
figure(psdFigure); clf;
switch db_mode_setting
case 0
referencePsdName = sprintf('PAM-%d reference', M);
equalizedPsdName = 'FFE output';
psd_color_equalized = dsp_styles.color( ...
dsp_styles.algorithm_key == "vnle", :);
case 1
referencePsdName = 'DB-target reference';
equalizedPsdName = 'DB-targeted VNLE output';
psd_color_equalized = dsp_styles.color( ...
dsp_styles.algorithm_key == "vnle_db_mlse", :);
case 2
referencePsdName = 'DB-encoded reference';
equalizedPsdName = 'DB-encoded VNLE output';
psd_color_equalized = dsp_styles.color( ...
dsp_styles.algorithm_key == "db_encoded", :);
end
referenceForEye.spectrum( ...
"fignum", psdFigure, ...
"show_onesided", 1, ...
"fft_length", 4096, ...
"displayname", referencePsdName, ...
"color", psd_color_target);
rawSignal.spectrum( ...
"fignum", psdFigure, ...
"show_onesided", 1, ...
"fft_length", 4096, ...
"displayname", 'Received signal', ...
"color", psd_color_received);
equalized_signal.spectrum( ...
"fignum", psdFigure, ...
"show_onesided", 1, ...
"fft_length", 4096, ...
"displayname", equalizedPsdName, ...
"color", psd_color_equalized);
eq_noise = equalized_signal - referenceForEye;
showEQNoisePSD(eq_noise, ...
"fignum", 800, ...
"displayname", sprintf('%.0f Gbps: VNLE Noise', selectedBitrate), ...
"colormode", "diverging");
% PSDs are line-only, irrespective of the marker styles used by
% algorithm comparison plots.
set(findall(gca, 'Type', 'Line'), 'Marker', 'none');
figure(psdFigure);
title(sprintf('PSD comparison: %.0f GBd PAM-%d', fsym*1e-9, M), ...
'Interpreter', 'none');
legend('Location', 'best', 'Interpreter', 'none');
grid on;
box on;
if export_tikz
baudrateLabel = sprintf('%.0fGBd', fsym*1e-9);
psdFilename = fullfile(tikz_psd_folder, ...
sprintf('psd_pam_%d_%s_db_mode_%d.tikz', ...
M, baudrateLabel, db_mode_setting));
mat2tikz_improved(psdFilename);
end
end
if showEye
% Use the native Signal eye implementation for the displayed eye.
eyeFigure = eye_figure_base + mIdx+ db_mode_setting;
equalized_signal.eye(fsym, M, ...
"fignum", eyeFigure, ...
"displayname", sprintf('PAM-%d, db_mode = %d', M, db_mode_setting));
figure(eyeFigure);
set(gcf, "Position", [100 + 430*(mIdx - 1), 100, 400, 360]);
stripEyeAxes(gca);
if export_tikz
baudrateLabel = sprintf('%.0fGBd', fsym*1e-9);
eyeFilename = fullfile(tikz_eye_folder, ...
sprintf('eye_pam_%d_%s_db_mode_%d.tikz', ...
M, baudrateLabel, db_mode_setting));
mat2tikz_improved(eyeFilename);
end
end
if show_histograms || export_tikz
histogramFigure = histogram_figure_base + M + db_mode_setting;
plotHistogram(equalized_signal, referenceForEye, ...
M, histogramFigure, db_mode_setting, histogramLegendLabels(M));
if export_tikz
figure(histogramFigure);
histogramFilename = fullfile(tikz_histogram_folder, ...
sprintf('histogram_pam_%d_%s_db_mode_%d.tikz', ...
M, baudrateLabel, db_mode_setting));
mat2tikz_improved(histogramFilename,"cleanfigure",1);
end
end
end
%% Local functions
function signalOut = averageScopeSignals(scopeCell, averageSignals, gain)
signalOut = scopeCell{1};
if ~averageSignals
return
end
commonLength = min(cellfun(@(s) numel(s.signal), scopeCell));
scopeMean = zeros(commonLength, 1);
for idx = 1:numel(scopeCell)
scopeMean = scopeMean + scopeCell{idx}.signal(1:commonLength);
end
scopeMean = scopeMean ./ numel(scopeCell);
signalOut.signal = gain .* scopeMean;
end
function eq_ = makeVnle(lenTr, trainingLoops, ddLoops, eqK, muDc, muFfe, muDfe)
eq_ = EQ("Ne", [50, 5, 5], ...
"Nb", [0, 0, 0], ...
"training_length", lenTr, ...
"training_loops", trainingLoops, ...
"dd_loops", ddLoops, ...
"K", eqK, ...
"DCmu", muDc, ...
"DDmu", [muFfe, muDfe], ...
"DFEmu", 0.005, ...
"FFEmu", 0, ...
"plotfinal", 0, ...
"ideal_dfe", true);
end
function plotHistogram(equalizedSignal, referenceForEye, M, figureNumber, ...
dbModeSetting, legendLabels)
figure(figureNumber); clf;
if dbModeSetting ~= 0
uncodedReference = Duobinary().decode(referenceForEye, "M", M);
showLevelHistogram(equalizedSignal, referenceForEye, ...
"fignum", figureNumber, ...
"ref_symbol_uncoded", uncodedReference, ...
"legendLabels", legendLabels);
else
showLevelHistogram(equalizedSignal, referenceForEye, ...
"fignum", figureNumber, ...
"legendLabels", legendLabels);
end
title(sprintf('PAM-%d histogram, db\_mode = %d', M, dbModeSetting));
end
function labels = histogramLegendLabels(M)
labels = arrayfun(@(idx) sprintf('$p(z|d=d_{%d})$', idx), ...
1:M, 'UniformOutput', false);
end
function stripEyeAxes(ax)
title(ax, '');
xlabel(ax, '');
ylabel(ax, '');
set(ax, ...
'XTick', [], ...
'YTick', [], ...
'XTickLabel', [], ...
'YTickLabel', [], ...
'XMinorTick', 'off', ...
'YMinorTick', 'off');
grid(ax, 'off');
end
function styles = defaultAlgorithmStyles()
styles = table( ...
["vnle"; ...
"vnle_pf_mlse"; ...
"vnle_db_mlse"; ...
"ml_mlse"; ...
"db_encoded"], ...
[equalizer_structure.vnle; ...
equalizer_structure.vnle_pf_mlse; ...
equalizer_structure.vnle_db_mlse; ...
equalizer_structure.ml_mlse; ...
equalizer_structure.db_encoded], ...
["VNLE"; ...
"VNLE + PF + MLSE"; ...
"VNLE DBt. + MLSE"; ...
"ML pre-EQ + Viterbi"; ...
"DBS + VNLE + MLSE"], ...
["o"; "square"; "diamond"; "^"; "v"], ...
["-"; "-"; "-"; "-"; "-"], ...
["w"; "w"; "w"; "w"; "w"], ...
[clr.Paired.red; ...
clr.Paired.green; ...
clr.Paired.blue; ...
clr.Paired.purple; ...
clr.Paired.orange], ...
'VariableNames', ["algorithm_key", "eq", "name", "marker", ...
"lineStyle", "markerFaceColor", "color"]);
end

View File

@@ -0,0 +1,168 @@
%% Memoryless DB-target BER versus baudrate for PAM4/6/8
clear; clc;
warehouseFile = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Diss\400G_revisit\results_duobinary_eq_memoryless_2km_pam468.mat";
pamLevels = [4, 6, 8];
%% Load warehouse and matching run metadata
S = load(warehouseFile, "wh");
wh = S.wh;
db = DBHandler("dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
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);
fp.where('Runs', 'db_mode', 'EQUALS', 0);
[runTable, ~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
warehouseRunIds = warehouseIds(wh);
runTable = runTable(ismember(double(runTable.run_id), warehouseRunIds), :);
%% Extract BER precoded from dbtgt_package
warehouseRows = zeros(0, 3); % PAM level, baudrate [GBd], BER precoded
for k = 1:numel(wh.sto.dbtgt_package)
[phys, realizationResults] = wh.getPhysAndValueByLinIndex( ...
"dbtgt_package", k);
if ~isfield(phys, "run_id")
continue
end
runRow = find(double(runTable.run_id) == double(phys.run_id), 1);
if isempty(runRow)
continue
end
for realization = 1:numel(realizationResults)
package = realizationResults{realization};
berPrecoded = readMetric(package, "BER_precoded");
if isfinite(berPrecoded) && berPrecoded > 0
warehouseRows(end+1, :) = [ ...
double(runTable.pam_level(runRow)), ...
double(runTable.grossrate(runRow)) * 1e-9, ...
berPrecoded]; %#ok<AGROW>
end
end
end
warehouseCurve = minByBaudrate(warehouseRows, "BER_precoded");
%% Load database BER and BER precoded for VNLE + DB target/MLSE
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
[databaseRows, ~] = db.queryDB(fp, selectedFields);
databaseRows = databaseRows(ismember(double(databaseRows.pam_level), pamLevels), :);
databaseRows = databaseRows(equalizerMask( ...
databaseRows.equalizer_structure, equalizer_structure.vnle_db_mlse), :);
berRows = databaseRows(isfinite(double(databaseRows.BER)) & ...
double(databaseRows.BER) > 0, :);
berPrecodedRows = databaseRows(isfinite(double(databaseRows.BER_precoded)) & ...
double(databaseRows.BER_precoded) > 0, :);
databaseBerCurve = minByBaudrate( ...
[double(berRows.pam_level), double(berRows.grossrate) * 1e-9, ...
double(berRows.BER)], "BER");
databaseBerPrecodedCurve = minByBaudrate( ...
[double(berPrecodedRows.pam_level), ...
double(berPrecodedRows.grossrate) * 1e-9, ...
double(berPrecodedRows.BER_precoded)], "BER_precoded");
%% Plot: three curves per PAM format
figure(470); clf;
tiledlayout(1, 3, "TileSpacing", "compact", "Padding", "compact");
for pamLevel = pamLevels
ax = nexttile; hold(ax, "on");
plotCurve(ax, warehouseCurve, pamLevel, "BER_precoded", ...
"o-", "precode + memoryless");
plotCurve(ax, databaseBerCurve, pamLevel, "BER", ...
"s-", "DBt. + MLSE");
plotCurve(ax, databaseBerPrecodedCurve, pamLevel, "BER_precoded", ...
"s--", "precode + DBt. + MLSE");
ylim([1e-4, 1e-1]);
title(ax, sprintf("PAM-%d", pamLevel));
xlabel(ax, "Baudrate [GBd]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
%% Local helpers
function runIds = warehouseIds(wh)
runIds = zeros(0, 1);
for k = 1:numel(wh.sto.dbtgt_package)
[phys, ~] = wh.getPhysAndValueByLinIndex("dbtgt_package", k);
if isfield(phys, "run_id")
runIds(end+1, 1) = double(phys.run_id); %#ok<AGROW>
end
end
runIds = unique(runIds);
end
function value = readMetric(package, metricName)
value = NaN;
if isempty(package) || ~isstruct(package) || ~isfield(package, "metrics")
return
end
metrics = package.metrics;
if isstruct(metrics) && isfield(metrics, metricName)
value = double(metrics.(metricName));
elseif isobject(metrics) && isprop(metrics, metricName)
value = double(metrics.(metricName));
end
end
function curve = minByBaudrate(rows, metricName)
if isempty(rows)
curve = table(zeros(0, 1), zeros(0, 1), ...
'VariableNames', ["pam_level", "baudrate_GBd"]);
curve.(metricName) = zeros(0, 1);
return
end
curve = table(rows(:, 1), rows(:, 2), rows(:, 3), ...
'VariableNames', ["pam_level", "baudrate_GBd", metricName]);
curve = groupsummary(curve, ["pam_level", "baudrate_GBd"], ...
"min", metricName);
curve.Properties.VariableNames(end) = metricName;
end
function plotCurve(ax, curve, pamLevel, metricName, style, label)
if isempty(curve) || ~ismember(metricName, string(curve.Properties.VariableNames))
return
end
rows = curve(curve.pam_level == pamLevel, :);
if isempty(rows)
return
end
rows = sortrows(rows, "baudrate_GBd");
plot(ax, rows.baudrate_GBd, rows.(metricName), style, ...
"LineWidth", 1.3, "MarkerSize", 5, "DisplayName", label);
end
function mask = equalizerMask(values, target)
if isa(values, "equalizer_structure")
mask = values == target;
elseif isnumeric(values)
mask = double(values) == double(target);
else
valuesString = string(values);
mask = valuesString == string(target) | ...
str2double(valuesString) == double(target);
end
mask = mask(:);
end

View File

@@ -0,0 +1,307 @@
%% Minimal BERp plot for MLSE postfilter orders and duobinary modes
clear; clc;
%% Configuration
warehouseFile = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Diss\400G_revisit\mlse_n_tap_pam4.mat";
pfValues = [1, 2, 3];
%% Load warehouse and query run metadata
loadedData = load(warehouseFile, "wh");
wh = loadedData.wh;
wh.showInfo;
db = DBHandler("dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
warehouseRunIds = getWarehouseRunIds(wh, "mlse_package");
runTable = queryRunsById(db, warehouseRunIds);
if isempty(runTable)
error("plot_mlse_n_tap:NoRunOverlap", ...
"The DB returned no metadata for the warehouse run_ids %s.", ...
mat2str(warehouseRunIds));
end
%% Extract minimum BERp for every run_id and pf_ncoeffs
plotData = warehouseToTable(wh, runTable, pfValues);
hasBerp = isfinite(plotData.BERp) & plotData.BERp > 0;
hasBer = isfinite(plotData.BER) & plotData.BER > 0;
if ~any(hasBerp | hasBer)
error("plot_mlse_n_tap:NoBerp", ...
"No positive BER or BER_precoded values were found in mlse_package.");
end
berpData = plotData(hasBerp, :);
berpData = groupsummary(berpData, ...
["db_mode", "pf_ncoeffs", "bitrate_Gbps"], "min", "BERp");
berData = plotData(hasBer, :);
berData = groupsummary(berData, ...
["db_mode", "pf_ncoeffs", "bitrate_Gbps"], "min", "BER");
vnleData = vnleBerTable(wh, runTable);
vnleBerData = vnleData(isfinite(vnleData.BER) & vnleData.BER > 0, :);
if ~isempty(vnleBerData)
vnleBerData = groupsummary(vnleBerData, ...
["db_mode", "bitrate_Gbps"], "min", "BER");
end
vnleBerpData = vnleData(isfinite(vnleData.BERp) & vnleData.BERp > 0, :);
if ~isempty(vnleBerpData)
vnleBerpData = groupsummary(vnleBerpData, ...
["db_mode", "bitrate_Gbps"], "min", "BERp");
end
%% Plot db_mode = 0 and db_mode = 1 in separate panels
fig = figure(461); clf;
tiledlayout(1, 2, "TileSpacing", "compact", "Padding", "compact");
markers = ["o", "square", "diamond"];
for dbMode = [1, 0]
ax = nexttile; hold(ax, "on");
colors = modeColors(dbMode);
for pfIdx = 1:numel(pfValues)
pf = pfValues(pfIdx);
berpMask = berpData.db_mode == dbMode & ...
berpData.pf_ncoeffs == pf;
if any(berpMask)
curveData = sortrows(berpData(berpMask, :), "bitrate_Gbps");
plot(ax, curveData.bitrate_Gbps, curveData.min_BERp, ...
"LineStyle", "--", ...
"LineWidth", 1.3, ...
"Marker", markers(pfIdx), ...
"MarkerSize", 5, ...
"Color", colors(pfIdx, :), ...
"DisplayName", sprintf("L = %d, precoded",pf));
end
berMask = berData.db_mode == dbMode & ...
berData.pf_ncoeffs == pf;
if any(berMask)
curveData = sortrows(berData(berMask, :), "bitrate_Gbps");
plot(ax, curveData.bitrate_Gbps, curveData.min_BER, ...
"LineStyle", "-", ...
"LineWidth", 1.3, ...
"Marker", markers(pfIdx), ...
"MarkerSize", 5, ...
"Color", colors(pfIdx, :), ...
"DisplayName", sprintf("L = %d",pf));
end
end
vnleBerMask = vnleBerData.db_mode == dbMode;
if any(vnleBerMask)
curveData = sortrows(vnleBerData(vnleBerMask, :), "bitrate_Gbps");
plot(ax, curveData.bitrate_Gbps, curveData.min_BER, ...
"LineStyle", "-", ...
"LineWidth", 1.5, ...
"Color", clr.Set1.gray, ...
"DisplayName", sprintf("VNLE"));
end
vnleBerpMask = vnleBerpData.db_mode == dbMode;
if any(vnleBerpMask)
curveData = sortrows(vnleBerpData(vnleBerpMask, :), "bitrate_Gbps");
plot(ax, curveData.bitrate_Gbps, curveData.min_BERp, ...
"LineStyle", "--", ...
"LineWidth", 1.5, ...
"Color", clr.Set1.gray, ...
"DisplayName", sprintf("VNLE precoded"));
end
yline(ax, [2.2e-4, 4.85e-3, 2e-2], ...
"LineStyle", "--", ...
"LineWidth", 0.8, ...
"Color", [0.25, 0.25, 0.25], ...
"HandleVisibility", "off");
title(ax, sprintf("db\\_mode = %d", dbMode));
xlabel(ax, "Gross rate [Gb/s]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [3e-4, 0.1]);
xlim([360, 410])
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
beautifyBERplot("setcolors",false,"setmarkers",false);
end
set(fig, "Position", [100, 500, 1050, 380]);
%% Local helpers
function colors = modeColors(dbMode)
switch dbMode
case 0
colors = [clr.Paired.dred; clr.Paired.dblue; clr.Paired.dgreen];
case 1
colors = [clr.Paired.dred; clr.Paired.dblue; clr.Paired.dgreen];
otherwise
error("plot_mlse_n_tap:UnknownDbMode", ...
"Unsupported db_mode: %d.", dbMode);
end
end
function runIds = getWarehouseRunIds(wh, storageName)
runIds = zeros(0, 1);
if ~isfield(wh.sto, storageName)
return
end
storageValues = wh.sto.(storageName);
for linIdx = 1:numel(storageValues)
[phys, ~] = wh.getPhysAndValueByLinIndex(storageName, linIdx);
if isfield(phys, "run_id")
runIds(end+1, 1) = double(phys.run_id); %#ok<AGROW>
end
end
runIds = unique(runIds);
end
function runTable = queryRunsById(db, runIds)
runTable = table();
fields = db.getTableFieldNames('Runs');
for runIdx = 1:numel(runIds)
fp = QueryFilter();
fp.where('Runs', 'run_id', 'EQUALS', runIds(runIdx));
[oneRun, ~] = db.queryDB(fp, fields);
if isempty(oneRun)
warning("plot_mlse_n_tap:MissingRunMetadata", ...
"No DB metadata found for warehouse run_id %d.", runIds(runIdx));
continue
end
if isempty(runTable)
runTable = oneRun;
else
runTable = [runTable; oneRun]; %#ok<AGROW>
end
end
if ~isempty(runTable) && ismember("db_mode", string(runTable.Properties.VariableNames))
runTable = runTable(double(runTable.db_mode) < 2, :);
end
end
function plotData = warehouseToTable(wh, runTable, pfValues)
runIdCol = zeros(0, 1);
dbModeCol = zeros(0, 1);
pfCol = zeros(0, 1);
bitrateCol = zeros(0, 1);
berCol = zeros(0, 1);
berpCol = zeros(0, 1);
storageValues = wh.sto.mlse_package;
for linIdx = 1:numel(storageValues)
[phys, storedValue] = wh.getPhysAndValueByLinIndex("mlse_package", linIdx);
if ~isfield(phys, "run_id") || ~isfield(phys, "pf_ncoeffs")
continue
end
pf = double(phys.pf_ncoeffs);
if ~ismember(pf, pfValues)
continue
end
runId = double(phys.run_id);
rowIdx = find(double(runTable.run_id) == runId, 1, "first");
if isempty(rowIdx)
continue
end
ber = minMetricValue(storedValue, "BER");
berp = minMetricValue(storedValue, "BER_precoded");
if (~isfinite(ber) || ber <= 0) && ...
(~isfinite(berp) || berp <= 0)
continue
end
runIdCol(end+1, 1) = runId; %#ok<AGROW>
dbModeCol(end+1, 1) = double(runTable.db_mode(rowIdx)); %#ok<AGROW>
pfCol(end+1, 1) = pf; %#ok<AGROW>
bitrateCol(end+1, 1) = double(runTable.bitrate(rowIdx)) .* 1e-9; %#ok<AGROW>
berCol(end+1, 1) = ber; %#ok<AGROW>
berpCol(end+1, 1) = berp; %#ok<AGROW>
end
plotData = table(runIdCol, dbModeCol, pfCol, bitrateCol, berCol, berpCol, ...
'VariableNames', ["run_id", "db_mode", "pf_ncoeffs", ...
"bitrate_Gbps", "BER", "BERp"]);
end
function plotData = vnleBerTable(wh, runTable)
plotData = table(zeros(0, 1), zeros(0, 1), zeros(0, 1), zeros(0, 1), zeros(0, 1), ...
'VariableNames', ["run_id", "db_mode", "bitrate_Gbps", "BER", "BERp"]);
if ~isfield(wh.sto, "vnle_package")
return
end
runIdCol = zeros(0, 1);
dbModeCol = zeros(0, 1);
bitrateCol = zeros(0, 1);
berCol = zeros(0, 1);
berpCol = zeros(0, 1);
storageValues = wh.sto.vnle_package;
for linIdx = 1:numel(storageValues)
[phys, storedValue] = wh.getPhysAndValueByLinIndex("vnle_package", linIdx);
if ~isfield(phys, "run_id")
continue
end
runId = double(phys.run_id);
rowIdx = find(double(runTable.run_id) == runId, 1, "first");
if isempty(rowIdx)
continue
end
ber = minMetricValue(storedValue, "BER");
berp = minMetricValue(storedValue, "BER_precoded");
if (~isfinite(ber) || ber <= 0) && ...
(~isfinite(berp) || berp <= 0)
continue
end
runIdCol(end+1, 1) = runId; %#ok<AGROW>
dbModeCol(end+1, 1) = double(runTable.db_mode(rowIdx)); %#ok<AGROW>
bitrateCol(end+1, 1) = double(runTable.bitrate(rowIdx)) .* 1e-9; %#ok<AGROW>
berCol(end+1, 1) = ber; %#ok<AGROW>
berpCol(end+1, 1) = berp; %#ok<AGROW>
end
plotData = table(runIdCol, dbModeCol, bitrateCol, berCol, berpCol, ...
'VariableNames', ["run_id", "db_mode", "bitrate_Gbps", "BER", "BERp"]);
end
function minValue = minMetricValue(value, metricName)
minValue = NaN;
if isempty(value)
return
end
if ~iscell(value)
value = {value};
end
metricValues = NaN(1, numel(value));
for idx = 1:numel(value)
package = value{idx};
if ~isstruct(package) || ~isfield(package, "metrics")
continue
end
metrics = package.metrics;
fieldName = char(metricName);
if isstruct(metrics) && isfield(metrics, fieldName)
metricValues(idx) = metrics.(fieldName);
elseif isobject(metrics) && isprop(metrics, fieldName)
metricValues(idx) = metrics.(fieldName);
end
end
metricValues = metricValues(isfinite(metricValues) & metricValues > 0);
if ~isempty(metricValues)
minValue = min(metricValues);
end
end

View File

@@ -0,0 +1,407 @@
%% Best NGMI, GMI, AIR and FEC rates over symbol rate
% The normal algorithms are reduced to the best result per algorithm,
% PAM format and symbol rate. Duobinary signaling is added as a separate
% algorithm and is restricted to the post-2026 processing results.
%
% The figure has one column per PAM format and one row per metric:
% BER, NGMI, GMI, AIR, NGMI-based SD+HD NDR, and the best BER-based NDR.
% NDR values are calculated from the measured BER/NGMI using
% TransmissionPerformance.
clear; clc;
%% 1) Query data
selectedPamLevels = [4, 6, 8];
selectedFiberLengthKm = 10;
selectedWavelengthNm = 1310;
selectedRopAttenuation = 0;
selectedIsMpi = 0;
normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)];
duobinaryDbMode = double(db_mode.db_encoded);
showRawEntries = false;
maxBerForPlot = 0.5;
algoStyles = defaultAlgorithmStyles();
db = DBHandler( ...
"dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
db.refresh();
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
fp.where('Runs', 'is_mpi', 'EQUALS', selectedIsMpi);
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
selectedFields = appendMissingFields(selectedFields, {'Runs.precomp_amp'});
[rawData, query] = db.queryDB(fp, selectedFields);
disp(query);
fprintf("Fetched %d rows.\n", height(rawData));
%% 2) Clean data and keep the requested algorithms
data = rawData;
numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ...
"symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ...
"rop_attenuation", "precomp_amp", "is_mpi", "numBits", "numBitErr", ...
"BER", "numBitErr_precoded", "BER_precoded", "GMI", "AIR", "NGMI"];
for fieldIdx = 1:numel(numericFields)
fieldName = numericFields(fieldIdx);
if ismember(fieldName, string(data.Properties.VariableNames))
data.(char(fieldName)) = numericColumn(data.(char(fieldName)));
end
end
data = data(ismember(data.pam_level, selectedPamLevels), :);
normalRows = data(ismember(data.db_mode, normalDbModes), :);
normalPlotData = buildNormalMetricRows(normalRows);
duobinaryRows = data(data.db_mode == duobinaryDbMode, :);
if ismember("equalizer_structure", string(duobinaryRows.Properties.VariableNames))
duobinaryRows = duobinaryRows( ...
equalizerMask(duobinaryRows.equalizer_structure, ...
equalizer_structure.db_encoded), :);
end
% Duobinary results before this date are not comparable to the current set.
if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames))
duobinaryRows.date_of_processing = datetime(string(duobinaryRows.date_of_processing));
duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>datetime("2026-01-01 00:00:00"),:);
else
warning("plot_best_metrics:NoProcessingDate", ...
"date_of_processing was not returned; no duobinary date filtering was applied.");
end
duobinaryPlotData = buildDuobinaryMetricRows(duobinaryRows);
plotData = [normalPlotData; duobinaryPlotData];
if isempty(plotData)
warning("plot_best_metrics:NoRows", ...
"No rows remain after the database and PAM-format filters.");
return
end
plotData = addDerivedMetrics(plotData);
plotData = plotData(plotData.symbolrate_GBd > 0 & ...
isfinite(plotData.symbolrate_GBd), :);
fprintf("Remaining metric rows: %d\n", height(plotData));
disp(groupcounts(plotData, ["algorithm_key", "pam_level", "precode"]));
%% 3) Calculate BER-/NGMI-dependent net rates
tp = TransmissionPerformance;
grossRate = double(plotData.grossrate);
ngmi = double(plotData.NGMI);
ber = double(plotData.BER_plot);
ngmi(~isfinite(ngmi) | ngmi < 0 | ngmi > 1.05) = NaN;
ber(~isfinite(ber) | ber <= 0 | ber > maxBerForPlot) = NaN;
ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber);
plotData.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9;
berBasedRates = [ ...
columnVector(ndr.STAIR.NetRate); ...
columnVector(ndr.HD.NetRate); ...
columnVector(ndr.KP4.NetRate); ...
columnVector(ndr.KP4_hamming.NetRate); ...
columnVector(ndr.O_FEC.NetRate)];
plotData.NDR_BER_BEST = max(reshape(berBasedRates, height(plotData), []), [], 2, "omitnan") .* 1e-9;
%% 4) Keep the best row for every plotted metric and group
metricDefinitions = struct( ...
"field", {"BER_plot", "NGMI", "GMI", "AIR_Gbps", "NDR_SDHD", "NDR_BER_BEST"}, ...
"label", {"BER", "NGMI", "GMI [bit/sym]", "AIR [Gb/s]", ...
"SD+HD NDR [Gb/s]", "Best BER-FEC NDR [Gb/s]"}, ...
"scale", {"log", "linear", "linear", "linear", "linear", "linear"});
bestMetricData = cell(numel(metricDefinitions), 1);
for metricIdx = 1:numel(metricDefinitions)
bestMetricData{metricIdx} = bestMetricRows(plotData, ...
metricDefinitions(metricIdx).field);
end
availableStyles = algoStyles(hasAlgorithmRows(plotData, algoStyles), :);
if isempty(availableStyles)
warning("plot_best_metrics:NoSelectedAlgorithms", ...
"None of the configured algorithm styles match the queried rows.");
return
end
%% 5) Plot one figure: metric rows x PAM columns
fig = figure(433); clf;
t = tiledlayout(fig, numel(metricDefinitions), numel(selectedPamLevels), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for metricIdx = 1:numel(metricDefinitions)
metric = metricDefinitions(metricIdx);
metricData = bestMetricData{metricIdx};
for pamIdx = 1:numel(selectedPamLevels)
selectedPamLevel = selectedPamLevels(pamIdx);
ax = nexttile(t); hold(ax, "on");
pamMask = metricData.pam_level == selectedPamLevel;
for styleIdx = 1:height(availableStyles)
style = availableStyles(styleIdx, :);
rowMask = pamMask & metricData.algorithm_key == style.algorithm_key;
if ~any(rowMask)
continue
end
algoData = sortrows(metricData(rowMask, :), "symbolrate_GBd");
y = algoData.(metric.field);
valid = isfinite(y);
if ~any(valid)
continue
end
if showRawEntries
scatter(ax, algoData.symbolrate_GBd(valid), y(valid), ...
9, ...
"Marker", ".", ...
"MarkerEdgeColor", style.color, ...
"MarkerFaceColor", style.color, ...
"MarkerEdgeAlpha", 0.25, ...
"MarkerFaceAlpha", 0.25, ...
"HandleVisibility", "off");
end
plot(ax, algoData.symbolrate_GBd(valid), y(valid), ...
"LineStyle", style.lineStyle, ...
"Marker", style.marker, ...
"MarkerSize", 4, ...
"LineWidth", 1.35, ...
"Color", style.color, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", style.color, ...
"DisplayName", style.name);
end
formatMetricAxis(ax, metric, selectedPamLevel);
if metricIdx == 1 && pamIdx == 1
legend(ax, "Location", "southwest", "Interpreter", "none");
end
end
end
title(t, sprintf("Best information metrics, %.0f km, %.0f nm", ...
selectedFiberLengthKm, selectedWavelengthNm));
set(fig, "Position", 1e3 .* [0.08 0.04 1.18 0.86]);
%% Local helpers
function fields = appendMissingFields(fields, extraFields)
fields = cellstr(fields);
extraFields = cellstr(extraFields);
for idx = 1:numel(extraFields)
if ~any(strcmp(fields, extraFields{idx}))
fields{end+1, 1} = extraFields{idx}; %#ok<AGROW>
end
end
end
function values = numericColumn(values)
if iscell(values)
values = string(values);
end
if isstring(values) || ischar(values)
values = str2double(values);
end
values = double(values);
end
function values = columnVector(values)
values = double(values(:));
end
function styles = defaultAlgorithmStyles()
styles = table( ...
["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; "ml_mlse"; "db_encoded"], ...
["VNLE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"; ...
"ML pre-EQ + Viterbi"; "DBS + VNLE + MLSE"], ...
["o"; "square"; "diamond"; "^"; "v"], ...
["-"; "-"; "-"; "-"; "-"], ...
["w"; "w"; "w"; "w"; "w"], ...
[clr.Paired.red; clr.Paired.green; clr.Paired.blue; ...
clr.Paired.purple; clr.Paired.orange], ...
'VariableNames', ["algorithm_key", "name", "marker", ...
"lineStyle", "markerFaceColor", "color"]);
end
function plotData = buildNormalMetricRows(data)
baseRows = data(isfinite(data.BER), :);
baseRows.precode = false(height(baseRows), 1);
baseRows.BER_plot = baseRows.BER;
baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure);
baseRows = baseRows(baseRows.algorithm_key ~= "", :);
if ismember("BER_precoded", string(data.Properties.VariableNames))
precodedRows = data(isfinite(data.BER_precoded), :);
precodedRows.precode = true(height(precodedRows), 1);
precodedRows.BER_plot = precodedRows.BER_precoded;
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
precodedRows.equalizer_structure);
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
plotData = [baseRows; precodedRows];
else
warning("plot_best_metrics:NoPrecodedBer", ...
"BER_precoded was not returned; plotting only BER rows.");
plotData = baseRows;
end
end
function plotData = buildDuobinaryMetricRows(data)
plotData = data(isfinite(data.BER), :);
plotData.precode = false(height(plotData), 1);
plotData.BER_plot = plotData.BER;
plotData.algorithm_key = repmat("db_encoded", height(plotData), 1);
end
function plotData = addDerivedMetrics(plotData)
plotData.symbolrate_GBd = double(plotData.symbolrate) .* 1e-9;
plotData.grossrate_Gbps = double(plotData.grossrate) .* 1e-9;
% AIR is stored in bit/s. Reconstruct it from GMI when the stored value is
% missing or outside the physically meaningful range.
plotData.AIR_Gbps = numericOrNaN(plotData, "AIR") .* 1e-9;
gmi = numericOrNaN(plotData, "GMI");
symbolrate = double(plotData.symbolrate);
grossrate = double(plotData.grossrate);
fallbackAir = gmi .* symbolrate .* 1e-9;
useFallback = ~isfinite(plotData.AIR_Gbps) | plotData.AIR_Gbps < 0 | ...
(isfinite(grossrate) & plotData.AIR_Gbps > grossrate .* 1.05e-9);
plotData.AIR_Gbps(useFallback) = fallbackAir(useFallback);
plotData.GMI = gmi;
plotData.NGMI = numericOrNaN(plotData, "NGMI");
end
function values = numericOrNaN(data, fieldName)
if ismember(fieldName, string(data.Properties.VariableNames))
values = numericColumn(data.(char(fieldName)));
else
values = NaN(height(data), 1);
end
values = values(:);
end
function algorithmKey = algorithmKeyFromEqualizer(equalizerColumn)
if isnumeric(equalizerColumn) || islogical(equalizerColumn)
eqNumeric = double(equalizerColumn);
algorithmKey = strings(size(eqNumeric));
knownKeys = ["vnle", "vnle_pf_mlse", "vnle_db_mlse", "ml_mlse"];
knownValues = [double(equalizer_structure.vnle), ...
double(equalizer_structure.vnle_pf_mlse), ...
double(equalizer_structure.vnle_db_mlse), ...
double(equalizer_structure.ml_mlse)];
for idx = 1:numel(knownKeys)
algorithmKey(eqNumeric == knownValues(idx)) = knownKeys(idx);
end
return
end
algorithmKey = lower(string(equalizerColumn));
algorithmKey(~ismember(algorithmKey, ...
["vnle", "vnle_pf_mlse", "vnle_db_mlse", "ml_mlse"])) = "";
end
function mask = equalizerMask(equalizerColumn, eqValue)
if isnumeric(equalizerColumn) || islogical(equalizerColumn)
mask = double(equalizerColumn) == double(eqValue);
else
mask = lower(string(equalizerColumn)) == lower(string(eqValue));
end
end
function bestData = bestMetricRows(data, metricField)
valid = isfinite(data.(metricField));
if strcmp(metricField, "BER_plot")
valid = valid & data.(metricField) > 0;
elseif strcmp(metricField, "NGMI")
valid = valid & data.(metricField) >= 0 & data.(metricField) <= 1.05;
else
valid = valid & data.(metricField) >= 0;
end
candidateData = data(valid, :);
if isempty(candidateData)
bestData = candidateData;
return
end
groupVars = ["pam_level", "algorithm_key", "symbolrate_GBd"];
groupId = findgroups(candidateData(:, groupVars));
keepIdx = zeros(max(groupId), 1);
for curGroup = 1:max(groupId)
rowIdx = find(groupId == curGroup);
values = candidateData.(metricField)(rowIdx);
if strcmp(metricField, "BER_plot")
[~, localIdx] = min(values);
else
[~, localIdx] = max(values);
end
keepIdx(curGroup) = rowIdx(localIdx(1));
end
bestData = sortrows(candidateData(keepIdx, :), groupVars);
end
function keep = hasAlgorithmRows(data, algoStyles)
keep = false(height(algoStyles), 1);
for idx = 1:height(algoStyles)
keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx));
end
end
function formatMetricAxis(ax, metric, pamLevel)
set(ax, "FontSize", 8, "TickLabelInterpreter", "none");
xlabel(ax, "Symbol rate [GBd]");
ylabel(ax, metric.label);
xlim(ax, [95 245]);
xticks(ax, 100:20:240);
grid(ax, "on");
grid(ax, "minor");
box(ax, "on");
switch metric.field
case "BER_plot"
set(ax, "YScale", "log");
ylim(ax, [1e-5 0.2]);
yline(ax, [2.2e-4 4.85e-3 2e-2], ...
"LineWidth", 0.8, "LineStyle", ":", ...
"Color", [0.25 0.25 0.25], "HandleVisibility", "off");
case "NGMI"
ylim(ax, [0.45 1.02]);
case "GMI"
set(ax, "YScale", "linear");
ylim(ax, [0 max(3.2, log2(pamLevel) + 0.15)]);
otherwise
set(ax, "YScale", "linear");
ylim(ax, [0 500]);
yline(ax, 400, "LineWidth", 0.8, "LineStyle", "--", ...
"Color", [0.25 0.25 0.25], "HandleVisibility", "off");
end
if pamLevel == 4
title(ax, "PAM-4");
elseif pamLevel == 6
title(ax, "PAM-6");
elseif pamLevel == 8
title(ax, "PAM-8");
else
title(ax, sprintf("PAM-%d", pamLevel));
end
end

View File

@@ -0,0 +1,471 @@
%% Analysis plots for the reprocessed dsp_400g_recipe warehouse
% Source warehouse is produced by RUN_REPROCESS_BAUDRATE_FILES_DSP400G.m.
clear; clc;
%% 1) Configuration and warehouse loading
warehouseFile = "D:\baudrate_sweep_b2b\PAMX_b2b_baudrate20241024_210648_dsp400g_reprocessed_wh.mat";
selectedRopAttenForBaudrate = 0;
selectedEqForRopCurves = "mlse"; % "ffe", "mlse", or "db"
acquisitionAggregation = "min"; % "min", "median", or "mean"
fecThreshold = 1e-2;
polyfitOrderMax = 4;
showRateAsDatarate = false;
showRawRopMarkers = true;
showPolynomialFits = true;
loadedData = load(warehouseFile);
if isfield(loadedData, "wh")
wh = loadedData.wh;
elseif isfield(loadedData, "obj")
wh = loadedData.obj;
else
error("plot_reprocessed_wh:NoWarehouse", ...
"Warehouse file must contain a variable named wh or obj.");
end
wh.showInfo;
eqStyles = defaultEqStyles();
availableEqStyles = eqStyles(hasWarehouseStorage(wh, eqStyles.storage), :);
if isempty(availableEqStyles)
error("plot_reprocessed_wh:NoEqStorage", ...
"None of the configured BER storages are present in wh.sto.");
end
rawData = warehouseToTable(wh, availableEqStyles);
rawData = rawData(isfinite(rawData.ber) & rawData.ber > 0, :);
allData = aggregateAcquisitions(rawData, acquisitionAggregation);
[allData.rop_axis, ropAxisLabel] = deriveRopAxis(allData);
fprintf("Loaded %d finite acquisition BER rows from warehouse.\n", height(rawData));
fprintf("Aggregated to %d grouped BER rows using acquisitionAggregation = %s.\n", ...
height(allData), acquisitionAggregation);
disp(groupcounts(allData, ["M", "eq"]));
pamVals = sort(unique(allData.M).');
fsymVals = sort(unique(allData.fsym).');
ropAttenVals = sort(unique(allData.rop_atten).');
ropAxisVals = sort(unique(allData.rop_axis(isfinite(allData.rop_axis))).');
%% 2) BER versus baud rate at fixed ROP attenuation
fig = figure(450); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for eqIdx = 1:height(availableEqStyles)
eqStyle = availableEqStyles(eqIdx, :);
rowMask = allData.M == pamLevel & ...
allData.eq == eqStyle.eq & ...
allData.rop_atten == selectedRopAttenForBaudrate;
if ~any(rowMask)
continue
end
curData = sortrows(allData(rowMask, :), "fsym_GBd");
plot(ax, curData.fsym_GBd, curData.ber, ...
"LineStyle", eqStyle.lineStyle, ...
"Marker", eqStyle.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.4, ...
"Color", eqStyle.color, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", eqStyle.color, ...
"DisplayName", eqStyle.name);
end
plotFecLines(ax, fecThreshold);
title(ax, sprintf("PAM-%d, ROP atten. %.1f dB", ...
pamLevel, selectedRopAttenForBaudrate));
xlabel(ax, "Symbol rate [GBd]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-5, 0.5]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
applyBerStyle();
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
%% 3) ROP attenuation curves for one EQ scheme
selectedRopEqStyle = availableEqStyles(availableEqStyles.eq == selectedEqForRopCurves, :);
if isempty(selectedRopEqStyle)
error("plot_reprocessed_wh:MissingSelectedEq", ...
"selectedEqForRopCurves = %s is not available in this warehouse.", ...
selectedEqForRopCurves);
end
fig = figure(451); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
rateColors = rateColorMap(numel(fsymVals));
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for fsymIdx = 1:numel(fsymVals)
fsym = fsymVals(fsymIdx);
rowMask = allData.M == pamLevel & ...
allData.eq == selectedRopEqStyle.eq & ...
allData.fsym == fsym;
if ~any(rowMask)
continue
end
curData = sortrows(allData(rowMask, :), "rop_axis");
curveColor = rateColors(fsymIdx, :);
displayName = sprintf("%.0f GBd", fsym .* 1e-9);
if showRawRopMarkers
plot(ax, curData.rop_axis, curData.ber, ...
"LineStyle", "none", ...
"Marker", "o", ...
"MarkerSize", 3, ...
"LineWidth", 0.8, ...
"Color", curveColor, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", curveColor, ...
"DisplayName", displayName);
end
if showPolynomialFits
[xFit, yFit] = fitLogBerCurve(curData.rop_axis, ...
curData.ber, polyfitOrderMax);
if ~isempty(xFit)
plot(ax, xFit, yFit, ...
"LineStyle", "-", ...
"LineWidth", 1.1, ...
"Color", curveColor, ...
"HandleVisibility", "off");
end
end
end
title(ax, sprintf("PAM-%d, %s", pamLevel, selectedRopEqStyle.name));
xlabel(ax, ropAxisLabel);
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-5, 0.5]);
xlim(ax, [min(ropAxisVals), max(ropAxisVals)]);
plotFecLines(ax, fecThreshold);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
applyBerStyle();
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
%% 4) Required ROP attenuation at FEC threshold versus baud rate
fecData = computeFecCrossings(allData, availableEqStyles, fecThreshold, ...
polyfitOrderMax);
if isempty(fecData)
warning("plot_reprocessed_wh:NoFecCrossings", ...
"No FEC crossings were found for threshold %.3g.", fecThreshold);
else
fig = figure(452); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for eqIdx = 1:height(availableEqStyles)
eqStyle = availableEqStyles(eqIdx, :);
rowMask = fecData.M == pamLevel & fecData.eq == eqStyle.eq;
if ~any(rowMask)
continue
end
curData = sortrows(fecData(rowMask, :), "fsym_GBd");
if showRateAsDatarate
xData = curData.datarate_Gbps;
xLabelText = "Datarate [Gb/s]";
else
xData = curData.fsym_GBd;
xLabelText = "Symbol rate [GBd]";
end
plot(ax, xData, curData.required_rop_axis, ...
"LineStyle", eqStyle.lineStyle, ...
"Marker", eqStyle.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.4, ...
"Color", eqStyle.color, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", eqStyle.color, ...
"DisplayName", eqStyle.name);
end
title(ax, sprintf("PAM-%d, BER = %.2g", pamLevel, fecThreshold));
xlabel(ax, xLabelText);
ylabel(ax, "Required " + ropAxisLabel);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
end
%% Local helpers
function styles = defaultEqStyles()
styles = table( ...
["ffe"; "mlse"; "db"], ...
["ber_ffe"; "ber_mlse"; "ber_db"], ...
["FFE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"], ...
["o"; "square"; "diamond"], ...
["-"; "-"; "-"], ...
[clr.Paired.red; clr.Paired.green; clr.Paired.blue], ...
'VariableNames', ["eq", "storage", "name", "marker", ...
"lineStyle", "color"]);
end
function keep = hasWarehouseStorage(wh, storageNames)
stoFields = string(fieldnames(wh.sto));
keep = ismember(storageNames, stoFields);
end
function data = warehouseToTable(wh, eqStyles)
lastIdx = wh.getLastLinIndice();
rows = cell(lastIdx * height(eqStyles), 11);
rowIdx = 0;
for eqIdx = 1:height(eqStyles)
eqStyle = eqStyles(eqIdx, :);
for linIdx = 1:lastIdx
[phys, value] = wh.getPhysAndValueByLinIndex(eqStyle.storage, linIdx);
if isempty(value) || ~isnumeric(value) || ~isscalar(value)
continue
end
fsym = double(phys.fsym);
pamLevel = double(phys.M);
ropAtten = double(phys.rop_atten);
acquisitionIdx = double(phys.acquisition_idx);
ropValue = getOptionalStoredScalarByLinIndex(wh, "rop", linIdx);
pdInValue = getOptionalStoredScalarByLinIndex(wh, "pd_in", linIdx);
rowIdx = rowIdx + 1;
rows(rowIdx, :) = { ...
fsym, ...
fsym .* 1e-9, ...
ropAtten, ...
pamLevel, ...
acquisitionIdx, ...
eqStyle.eq, ...
eqStyle.storage, ...
double(value), ...
ropValue, ...
pdInValue, ...
fsym .* floor(log2(pamLevel) * 10) / 10 .* 1e-9};
end
end
rows = rows(1:rowIdx, :);
data = cell2table(rows, 'VariableNames', ...
["fsym", "fsym_GBd", "rop_atten", "M", "acquisition_idx", ...
"eq", "storage", "ber", "rop_dBm", "pd_in_dBm", "datarate_Gbps"]);
data.eq = string(data.eq);
data.storage = string(data.storage);
end
function data = aggregateAcquisitions(rawData, aggregationMode)
groupVars = ["fsym", "fsym_GBd", "rop_atten", "M", "eq", ...
"storage", "rop_dBm", "pd_in_dBm", "datarate_Gbps"];
switch aggregationMode
case "min"
data = groupsummary(rawData, groupVars, "min", "ber");
data.ber = data.min_ber;
data = removevars(data, "min_ber");
case "median"
data = groupsummary(rawData, groupVars, "median", "ber");
data.ber = data.median_ber;
data = removevars(data, "median_ber");
case "mean"
data = groupsummary(rawData, groupVars, "mean", "ber");
data.ber = data.mean_ber;
data = removevars(data, "mean_ber");
otherwise
error("plot_reprocessed_wh:UnknownAggregation", ...
"Unknown acquisitionAggregation: %s", aggregationMode);
end
end
function value = getOptionalStoredScalarByLinIndex(wh, storageName, linIdx)
value = NaN;
if ~isfield(wh.sto, storageName)
return
end
storedValue = wh.sto.(storageName){linIdx};
if isnumeric(storedValue) && isscalar(storedValue)
value = double(storedValue);
end
end
function [ropAxis, ropAxisLabel] = deriveRopAxis(data)
if ismember("rop_dBm", string(data.Properties.VariableNames)) && ...
any(isfinite(data.rop_dBm))
ropAxis = data.rop_dBm;
ropAxisLabel = "ROP [dBm]";
elseif ismember("pd_in_dBm", string(data.Properties.VariableNames)) && ...
any(isfinite(data.pd_in_dBm))
ropAxis = data.pd_in_dBm;
ropAxisLabel = "PD input power [dBm]";
else
ropAxis = data.rop_atten;
ropAxisLabel = "ROP attenuation [dB]";
end
end
function cmap = rateColorMap(numColors)
anchors = [ ...
clr.Paired.lightblue; ...
clr.Paired.blue; ...
clr.Paired.green; ...
clr.Paired.orange; ...
clr.Paired.red; ...
clr.Paired.purple];
if numColors <= size(anchors, 1)
cmap = anchors(1:numColors, :);
return
end
xAnchor = linspace(0, 1, size(anchors, 1));
xQuery = linspace(0, 1, numColors);
cmap = interp1(xAnchor, anchors, xQuery, "linear");
end
function [xFit, yFit] = fitLogBerCurve(x, y, maxOrder)
valid = isfinite(x) & isfinite(y) & y > 0;
x = x(valid);
y = y(valid);
if numel(unique(x)) < 2
xFit = [];
yFit = [];
return
end
[x, orderIdx] = sort(x(:));
y = y(orderIdx);
fitOrder = min(maxOrder, numel(unique(x)) - 1);
coeff = polyfit(x, log10(y), fitOrder);
xFit = linspace(min(x), max(x), 300).';
yFit = 10 .^ polyval(coeff, xFit);
end
function fecData = computeFecCrossings(data, eqStyles, fecThreshold, maxOrder)
pamVals = unique(data.M).';
fsymVals = unique(data.fsym).';
rows = cell(height(eqStyles) * numel(pamVals) * numel(fsymVals), 6);
rowIdx = 0;
for eqIdx = 1:height(eqStyles)
eqStyle = eqStyles(eqIdx, :);
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
for fsymIdx = 1:numel(fsymVals)
fsym = fsymVals(fsymIdx);
rowMask = data.eq == eqStyle.eq & ...
data.M == pamLevel & ...
data.fsym == fsym;
if ~any(rowMask)
continue
end
curData = sortrows(data(rowMask, :), "rop_axis");
requiredRopAxis = fecCrossingFromCurve( ...
curData.rop_axis, curData.ber, fecThreshold, maxOrder);
if ~isfinite(requiredRopAxis)
continue
end
rowIdx = rowIdx + 1;
rows(rowIdx, :) = { ...
fsym, ...
fsym .* 1e-9, ...
pamLevel, ...
eqStyle.eq, ...
requiredRopAxis, ...
fsym .* floor(log2(pamLevel) * 10) / 10 .* 1e-9};
end
end
end
if rowIdx == 0
fecData = table();
else
rows = rows(1:rowIdx, :);
fecData = cell2table(rows, 'VariableNames', ...
["fsym", "fsym_GBd", "M", "eq", "required_rop_axis", ...
"datarate_Gbps"]);
fecData.eq = string(fecData.eq);
end
end
function requiredRopAtten = fecCrossingFromCurve( ...
ropAtten, ber, fecThreshold, maxOrder)
[xFit, yFit] = fitLogBerCurve(ropAtten, ber, maxOrder);
requiredRopAtten = NaN;
if isempty(xFit)
return
end
crossingMask = isfinite(yFit) & yFit > 0;
xFit = xFit(crossingMask);
yFit = yFit(crossingMask);
if numel(xFit) < 2
return
end
delta = log10(yFit) - log10(fecThreshold);
crossingIdx = find(delta(1:end-1) .* delta(2:end) <= 0, 1, "first");
if isempty(crossingIdx)
return
end
x1 = xFit(crossingIdx);
x2 = xFit(crossingIdx + 1);
y1 = delta(crossingIdx);
y2 = delta(crossingIdx + 1);
requiredRopAtten = x1 - y1 .* (x2 - x1) ./ (y2 - y1);
end
function plotFecLines(ax, fecThreshold)
xl = xlim(ax);
h = plot(ax, xl, [fecThreshold, fecThreshold], ...
"LineStyle", "--", ...
"LineWidth", 1, ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
h.Annotation.LegendInformation.IconDisplayStyle = "off";
end
function applyBerStyle()
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
end

View File

@@ -0,0 +1,377 @@
%% 400G BER over ROP: best normal algorithms plus duobinary signaling
% Normal algorithms are reduced to the best BER per ROP point across
% db_mode 0/1, pre-emphasis on/off, and BER/BER_precoded result variants.
% Duobinary signaling uses db_mode = 2 and only the sequence-detection BER
% stored in the BER field.
clear; clc;
%% 1) Query data
selectedPamLevels = [4, 6, 8];
selectedFiberLengthKm = 1;
selectedWavelengthNm = 1310;
selectedBitrateGbps = 360;
selectedIsMpi = 0; % set [] to use all entries
maxPowerPdIn = []; % set a numeric limit to enable
normalDbModes = [double(db_mode.no_db)];
duobinaryDbMode = double(db_mode.db_encoded);
maxBerForPlot = 0.5;
showRawEntries = false;
showBestLine = true;
algoStyles = defaultAlgorithmStyles();
db = DBHandler( ...
"dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
db.refresh();
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
fp.where('Runs', 'bitrate', 'EQUALS', selectedBitrateGbps .* 1e9);
if ~isempty(selectedIsMpi)
fp.where('Runs', 'is_mpi', 'EQUALS', selectedIsMpi);
end
if ~isempty(maxPowerPdIn)
fp.where('Runs', 'power_pd_in', 'LESS_THAN', maxPowerPdIn);
end
selectedFields = [ ...
db.getTableFieldNames('power_state_info'); ...
db.getTableFieldNames('dashboard_ungrouped_alltime')];
selectedFields = appendMissingFields(selectedFields, ...
{'Runs.precomp_amp'; 'Runs.is_mpi'; 'Runs.power_pd_in'});
selectedFields = selectedFields(:);
[rawData, query] = db.queryDB(fp, selectedFields);
disp(query);
fprintf("Fetched %d 400G ROP result rows.\n", height(rawData));
%% 2) Clean data and build the five plotted curves
data = rawData;
numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ...
"symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ...
"rop_attenuation", "precomp_amp", "is_mpi", "power_rop", ...
"power_mzm", "power_pd_in", "voa_atten", "numBits", "numBitErr", ...
"BER", "numBitErr_precoded", "BER_precoded", "STD", "STDrx", ...
"GMI", "AIR", "NGMI", "EVM", "Alpha"];
for fieldIdx = 1:numel(numericFields)
fieldName = numericFields(fieldIdx);
if ismember(fieldName, string(data.Properties.VariableNames))
data.(char(fieldName)) = numericColumn(data.(char(fieldName)));
end
end
data = data(ismember(data.pam_level, selectedPamLevels), :);
if ~ismember("precomp_amp", string(data.Properties.VariableNames))
warning("plot_rop_best_algos:NoPrecompAmp", ...
"Runs.precomp_amp was not returned. Falling back to pre_emphasis = (db_mode == 0).");
data.pre_emphasis = data.db_mode == double(db_mode.no_db);
else
data.pre_emphasis = derivePreEmphasis(data.precomp_amp, data.db_mode);
end
normalRows = data(ismember(data.db_mode, normalDbModes), :);
normalPlotData = buildNormalMetricRows(normalRows);
normalPlotData = normalPlotData(isfinite(normalPlotData.BER_plot) & ...
normalPlotData.BER_plot > 0 & normalPlotData.BER_plot < maxBerForPlot, :);
duobinaryRows = data(data.db_mode == duobinaryDbMode, :);
if ismember("equalizer_structure", string(duobinaryRows.Properties.VariableNames))
duobinaryRows = duobinaryRows( ...
equalizerMask(duobinaryRows.equalizer_structure, ...
equalizer_structure.db_encoded), :);
end
duobinaryPlotData = buildDuobinarySignalingRows(duobinaryRows);
duobinaryPlotData = duobinaryPlotData(isfinite(duobinaryPlotData.BER_plot) & ...
duobinaryPlotData.BER_plot > 0 & ...
duobinaryPlotData.BER_plot < maxBerForPlot, :);
plotData = [normalPlotData; duobinaryPlotData];
if isempty(plotData)
warning("plot_rop_best_algos:NoRows", ...
"No rows remain after length/PAM/rate/BER filtering.");
return
end
[plotData.rop_axis, ropAxisLabel] = deriveRopAxis(plotData);
plotData.rop_axis = round(plotData.rop_axis, 4);
plotData.bitrate_Gbps = plotData.bitrate .* 1e-9;
plotData.grossrate_Gbps = plotData.grossrate .* 1e-9;
plotData = plotData(isfinite(plotData.rop_axis), :);
fprintf("Remaining candidate BER rows: %d\n", height(plotData));
disp(groupcounts(plotData, ["algorithm_key", "db_mode", "pre_emphasis", "precode"]));
bestPlotData = bestBerByAlgorithmAndRop(plotData);
fprintf("Keeping %d best-BER rows across PAM/algorithm/ROP groups.\n", ...
height(bestPlotData));
disp(groupcounts(bestPlotData, "algorithm_key"));
%% 3) Plot one 1x3 figure with five lines per PAM
availableStyles = algoStyles(hasAlgorithmRows(bestPlotData, algoStyles), :);
if isempty(availableStyles)
warning("plot_rop_best_algos:NoSelectedAlgorithms", ...
"None of the configured algorithm styles match the queried rows.");
return
end
fig = figure(432); clf;
t = tiledlayout(fig, 1, numel(selectedPamLevels), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(selectedPamLevels)
selectedPamLevel = selectedPamLevels(pamIdx);
ax = nexttile(t); hold(ax, "on");
pamMask = bestPlotData.pam_level == selectedPamLevel;
for styleIdx = 1:height(availableStyles)
style = availableStyles(styleIdx, :);
rowMask = pamMask & bestPlotData.algorithm_key == style.algorithm_key;
if ~any(rowMask)
continue
end
algoData = sortrows(bestPlotData(rowMask, :), "rop_axis");
if showRawEntries
scatter(ax, algoData.rop_axis, algoData.BER_plot, ...
9, ...
"Marker", ".", ...
"MarkerEdgeColor", style.color, ...
"MarkerFaceColor", style.color, ...
"MarkerEdgeAlpha", 0.25, ...
"MarkerFaceAlpha", 0.25, ...
"HandleVisibility", "off");
end
if showBestLine
plot(ax, algoData.rop_axis, algoData.BER_plot, ...
"LineStyle", style.lineStyle, ...
"Marker", style.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.5, ...
"Color", style.color, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", style.color, ...
"DisplayName", style.name);
end
end
yline(ax, [2.2e-4, 4.85e-3, 2e-2], ...
"LineWidth", 1, ...
"LineStyle", "--", ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
xlabel(ax, ropAxisLabel);
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [8e-5, 0.1]);
grid(ax, "on");
box(ax, "on");
xTicks = unique(bestPlotData.rop_axis(pamMask & ...
isfinite(bestPlotData.rop_axis)));
if ~isempty(xTicks)
if isscalar(xTicks)
xlim(ax, xTicks + [-0.5, 0.5]);
else
xlim(ax, [min(xTicks), max(xTicks)]);
end
end
legend(ax, "Location", "northeast", "Interpreter", "none");
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
end
set(fig, "Position", 1e3 .* [0.1070 0.5497 1.0585 0.2282]);
%% Local helpers
function fields = appendMissingFields(fields, extraFields)
fields = cellstr(fields);
extraFields = cellstr(extraFields);
for idx = 1:numel(extraFields)
if ~any(strcmp(fields, extraFields{idx}))
fields{end+1, 1} = extraFields{idx}; %#ok<AGROW>
end
end
end
function values = numericColumn(values)
if iscell(values)
values = string(values);
end
if isstring(values) || ischar(values)
values = str2double(values);
end
values = double(values);
end
function styles = defaultAlgorithmStyles()
styles = table( ...
["vnle"; ...
"vnle_pf_mlse"; ...
"vnle_db_mlse"; ...
"ml_mlse"; ...
"db_encoded"], ...
[equalizer_structure.vnle; ...
equalizer_structure.vnle_pf_mlse; ...
equalizer_structure.vnle_db_mlse; ...
equalizer_structure.ml_mlse; ...
equalizer_structure.db_encoded], ...
["VNLE"; ...
"VNLE + PF + MLSE"; ...
"VNLE DBt. + MLSE"; ...
"ML pre-EQ + Viterbi"; ...
"DBS + VNLE + MLSE"], ...
["o"; "square"; "diamond"; "^"; "v"], ...
["-"; "-"; "-"; "-"; "-"], ...
["w"; "w"; "w"; "w"; "w"], ...
[clr.Paired.red; ...
clr.Paired.green; ...
clr.Paired.blue; ...
clr.Paired.purple; ...
clr.Paired.orange], ...
'VariableNames', ["algorithm_key", "eq", "name", "marker", ...
"lineStyle", "markerFaceColor", "color"]);
end
function preEmphasis = derivePreEmphasis(precompAmp, dbMode)
preEmphasis = false(size(dbMode));
validPrecomp = isfinite(precompAmp);
preEmphasis(validPrecomp) = precompAmp(validPrecomp) > -45;
missingPrecomp = ~validPrecomp;
preEmphasis(missingPrecomp) = dbMode(missingPrecomp) == double(db_mode.no_db);
end
function plotData = buildNormalMetricRows(data)
baseRows = data(isfinite(data.BER), :);
baseRows.precode = false(height(baseRows), 1);
baseRows.BER_plot = baseRows.BER;
baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure);
baseRows = baseRows(baseRows.algorithm_key ~= "", :);
if ismember("BER_precoded", string(data.Properties.VariableNames))
precodedRows = data(isfinite(data.BER_precoded), :);
precodedRows.precode = true(height(precodedRows), 1);
precodedRows.BER_plot = precodedRows.BER_precoded;
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
precodedRows.equalizer_structure);
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
plotData = [baseRows; precodedRows];
else
warning("plot_rop_best_algos:NoPrecodedBer", ...
"BER_precoded was not returned. Plotting only BER rows for normal algorithms.");
plotData = baseRows;
end
end
function plotData = buildDuobinarySignalingRows(data)
plotData = data(isfinite(data.BER), :);
plotData.precode = false(height(plotData), 1);
plotData.BER_plot = plotData.BER;
plotData.algorithm_key = repmat("db_encoded", height(plotData), 1);
end
function [ropAxis, label] = deriveRopAxis(data)
if ismember("power_mzm", string(data.Properties.VariableNames)) && ...
any(isfinite(data.power_mzm))
ropAxis = data.power_mzm;
label = "ROP [dBm]";
elseif ismember("power_pd_in", string(data.Properties.VariableNames)) && ...
any(isfinite(data.power_pd_in))
ropAxis = data.power_pd_in;
label = "PD input power [dBm]";
else
ropAxis = data.rop_attenuation;
label = "ROP attenuation [dB]";
end
end
function algorithmKey = algorithmKeyFromEqualizer(equalizerColumn)
eqNumeric = equalizerNumeric(equalizerColumn);
algorithmKey = strings(size(eqNumeric));
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle)) = "vnle";
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle_pf_mlse)) = ...
"vnle_pf_mlse";
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle_db_mlse)) = ...
"vnle_db_mlse";
algorithmKey(eqNumeric == enumValue(equalizer_structure.ml_mlse)) = "ml_mlse";
end
function mask = equalizerMask(equalizerColumn, eqValue)
eqNumeric = equalizerNumeric(equalizerColumn);
mask = eqNumeric == enumValue(eqValue);
end
function eqNumeric = equalizerNumeric(equalizerColumn)
if isa(equalizerColumn, "equalizer_structure")
eqNumeric = double(equalizerColumn);
elseif isnumeric(equalizerColumn)
eqNumeric = double(equalizerColumn);
else
equalizerString = string(equalizerColumn);
eqNumeric = str2double(equalizerString);
enumNames = ["vnle", "ffe", "dfe", "vnle_pf_mlse", ...
"vnle_db_mlse", "db_encoded", "ml_mlse"];
enumValues = [ ...
enumValue(equalizer_structure.vnle), ...
enumValue(equalizer_structure.ffe), ...
enumValue(equalizer_structure.dfe), ...
enumValue(equalizer_structure.vnle_pf_mlse), ...
enumValue(equalizer_structure.vnle_db_mlse), ...
enumValue(equalizer_structure.db_encoded), ...
enumValue(equalizer_structure.ml_mlse)];
for idx = 1:numel(enumNames)
missingNumeric = isnan(eqNumeric);
eqNumeric(missingNumeric & equalizerString == enumNames(idx)) = ...
enumValues(idx);
end
end
end
function value = enumValue(enumEntry)
value = double(enumEntry);
end
function bestData = bestBerByAlgorithmAndRop(data)
groupVars = ["pam_level", "algorithm_key", "rop_axis"];
groupId = findgroups(data(:, groupVars));
keepIdx = NaN(max(groupId), 1);
for curGroup = 1:max(groupId)
rowIdx = find(groupId == curGroup);
[~, localBestIdx] = min(data.BER_plot(rowIdx));
keepIdx(curGroup) = rowIdx(localBestIdx);
end
bestData = sortrows(data(keepIdx, :), groupVars);
end
function keep = hasAlgorithmRows(data, algoStyles)
keep = false(height(algoStyles), 1);
for idx = 1:height(algoStyles)
keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx));
end
end

View File

@@ -0,0 +1,317 @@
%% PAM4/6/8 SNR and uncoded BER versus symbolrate
clear; clc;
warehouseFile = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Diss\400G_revisit\results_snr_duobinary_and_partialresponse_pam468_10km.mat";
pamLevels = [4, 6, 8];
storageNames = ["mlse_package", "vnle_package", ...
"dbtgt_package", "mlse_db_package"];
techniqueNames = ["MLSE", "VNLE", "DB target", ...
"duobinary signaling"];
%% Load warehouse and matching run metadata
S = load(warehouseFile, "wh");
wh = S.wh;
db = DBHandler("dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', 10);
fp.where('Runs', 'wavelength', 'EQUALS', 1310);
fp.where('Runs', 'rop_attenuation', 'EQUALS', 0);
fp.where('Runs', 'is_mpi', 'EQUALS', 0);
[runTable, ~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
runTable = runTable(ismember(double(runTable.pam_level), pamLevels), :);
%% Extract package metrics
techniqueCol = strings(0, 1);
pamCol = zeros(0, 1);
dbModeCol = zeros(0, 1);
symbolrateCol = zeros(0, 1);
snrCol = zeros(0, 1);
berCol = zeros(0, 1);
for techniqueIdx = 1:numel(storageNames)
storageName = storageNames(techniqueIdx);
if ~isfield(wh.sto, storageName)
continue
end
for k = 1:numel(wh.sto.(storageName))
[phys, realizationResults] = wh.getPhysAndValueByLinIndex( ...
storageName, k);
if ~isfield(phys, "run_id") || isempty(realizationResults)
continue
end
runRow = find(double(runTable.run_id) == double(phys.run_id), 1);
if isempty(runRow)
continue
end
for realization = 1:numel(realizationResults)
package = realizationResults{realization};
snr = readMetric(package, "SNR");
ber = readMetric(package, "BER");
if ~isfinite(snr) && ~isfinite(ber)
continue
end
techniqueCol(end+1, 1) = techniqueNames(techniqueIdx); %#ok<AGROW>
pamCol(end+1, 1) = double(runTable.pam_level(runRow)); %#ok<AGROW>
dbModeCol(end+1, 1) = double(runTable.db_mode(runRow)); %#ok<AGROW>
symbolrateCol(end+1, 1) = double(runTable.symbolrate(runRow)) * 1e-9; %#ok<AGROW>
snrCol(end+1, 1) = snr; %#ok<AGROW>
berCol(end+1, 1) = ber; %#ok<AGROW>
end
end
end
data = table(techniqueCol, pamCol, dbModeCol, symbolrateCol, snrCol, berCol, ...
'VariableNames', ["technique", "pam_level", "db_mode", ...
"symbolrate_GBd", "SNR", "BER"]);
% SNR: maximum realization value at each PAM/mode/symbolrate point.
snrData = data(isfinite(data.SNR), :);
snrData = groupsummary(snrData, ...
["technique", "pam_level", "db_mode", "symbolrate_GBd"], ...
"max", "SNR");
snrData.Properties.VariableNames(end) = "SNR";
% BER: minimum positive, uncoded BER at each PAM/mode/symbolrate point.
berData = data(isfinite(data.BER) & data.BER > 0, :);
berData = groupsummary(berData, ...
["technique", "pam_level", "db_mode", "symbolrate_GBd"], ...
"min", "BER");
berData.Properties.VariableNames(end) = "BER";
%% SNR figure: one tile per technique, PAM4/6/8 together
figure(472); clf;
tiledlayout(1, numel(pamLevels), "TileSpacing", "compact", "Padding", "compact");
for pamLevel = pamLevels
ax = nexttile; hold(ax, "on");
plotMetric(ax, snrData, pamLevel, techniqueNames, "SNR");
title(ax, sprintf("PAM-%d", pamLevel));
xlabel(ax, "Symbolrate [GBd]");
ylabel(ax, "SNR [dB]");
ylim(ax, [15, 25]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
if exist("beautifyBERplot", "file")
beautifyBERplot("changemarkers", 0, "setcolors", false);
end
end
%% BER figure: one tile per PAM format
figure(473); clf;
tiledlayout(1, numel(pamLevels), "TileSpacing", "compact", "Padding", "compact");
for pamLevel = pamLevels
ax = nexttile; hold(ax, "on");
plotMetric(ax, berData, pamLevel, techniqueNames, "BER");
title(ax, sprintf("PAM-%d", pamLevel));
xlabel(ax, "Symbolrate [GBd]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
%% Shared SNR figure: one tile per technique, PAM4/6/8 together
figure(474); clf;
tiledlayout(1, 4, "TileSpacing", "compact", "Padding", "compact");
ax = nexttile; hold(ax, "on");
plotTechniqueSNR(ax, snrData, "VNLE", pamLevels, ...
[0, 1], [clr.Paired.lred; clr.Paired.dred]);
title(ax, "Full Response");
ax = nexttile; hold(ax, "on");
plotTechniqueSNR(ax, snrData, "MLSE", pamLevels, ...
[0, 1], [clr.Paired.lblue; clr.Paired.dblue]);
title(ax, "Partial Response");
ax = nexttile; hold(ax, "on");
plotTechniqueSNR(ax, snrData, "DB target", pamLevels, ...
[0, 1], [clr.Paired.lgreen; clr.Paired.dgreen]);
title(ax, "Duobinary Target");
ax = nexttile; hold(ax, "on");
plotTechniqueSNR(ax, snrData, "duobinary signaling", pamLevels, ...
2, clr.Paired.dlila);
title(ax, "Duobinary Signaling");
%% Shared BER figure: one tile per technique, PAM4/6/8 together
figure(475); clf;
tiledlayout(1, 4, "TileSpacing", "compact", "Padding", "compact");
ax = nexttile; hold(ax, "on");
plotTechniqueBER(ax, berData, "VNLE", pamLevels, ...
[0, 1], [clr.Paired.lred; clr.Paired.dred]);
title(ax, "VNLE");
ax = nexttile; hold(ax, "on");
plotTechniqueBER(ax, berData, "MLSE", pamLevels, ...
[0, 1], [clr.Paired.lblue; clr.Paired.dblue]);
title(ax, "MLSE");
ax = nexttile; hold(ax, "on");
plotTechniqueBER(ax, berData, "DB target", pamLevels, ...
[0, 1], [clr.Paired.lgreen; clr.Paired.dgreen]);
title(ax, "DBt.");
ax = nexttile; hold(ax, "on");
plotTechniqueBER(ax, berData, "duobinary signaling", pamLevels, ...
2, clr.Paired.dlila);
title(ax, "DB signaling");
%% Local helpers
function value = readMetric(package, metricName)
value = NaN;
if isempty(package) || ~isstruct(package) || ~isfield(package, "metrics")
return
end
metrics = package.metrics;
if isstruct(metrics) && isfield(metrics, metricName)
value = double(metrics.(metricName));
elseif isobject(metrics) && isprop(metrics, metricName)
value = double(metrics.(metricName));
end
end
function plotMetric(ax, data, pamLevel, techniqueNames, metricName)
markers = ["o", "s", "diamond", "^"];
lineStyles = ["-", "--", ":"];
for techniqueIdx = 1:numel(techniqueNames)
for dbMode = 0:2
rows = data(data.pam_level == pamLevel & ...
data.technique == techniqueNames(techniqueIdx) & ...
data.db_mode == dbMode, :);
if isempty(rows)
continue
end
rows = sortrows(rows, "symbolrate_GBd");
plot(ax, rows.symbolrate_GBd, rows.(metricName), ...
"LineStyle", lineStyles(dbMode + 1), ...
"Marker", markers(techniqueIdx), ...
"MarkerSize", 5, ...
"LineWidth", 1.3, ...
"Color", pamModeColor(pamLevel, dbMode), ...
"DisplayName", sprintf("%s, %s", ...
techniqueNames(techniqueIdx), modeLabel(dbMode)));
end
end
end
function plotTechniqueSNR(ax, data, techniqueName, pamLevels, dbModes, colors)
markers = ["square", "hexagram", "*"];
lineStyles = ["-", "--", ":"];
for modeIdx = 1:numel(dbModes)
dbMode = dbModes(modeIdx);
for pamIdx = 1:numel(pamLevels)
pamLevel = pamLevels(pamIdx);
rows = data(data.technique == techniqueName & ...
data.pam_level == pamLevel & data.db_mode == dbMode, :);
if isempty(rows)
continue
end
rows = sortrows(rows, "symbolrate_GBd");
plot(ax, rows.symbolrate_GBd, rows.SNR, ...
"LineStyle", lineStyles(dbMode + 1), ...
"Marker", markers(pamIdx), ...
"MarkerSize", 6, ...
"LineWidth", 1.3, ...
"Color", pamModeColor(pamLevel, dbMode), ...
"DisplayName", sprintf("%s, PAM-%d", ...
modeLabel(dbMode), pamLevel));
end
end
xlabel(ax, "Symbolrate [GBd]");
ylabel(ax, "SNR [dB]");
ylim(ax, [13, 24]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
function plotTechniqueBER(ax, data, techniqueName, pamLevels, dbModes, colors)
markers = ["square", "hexagram", "*"];
lineStyles = ["-", "--", ":"];
for modeIdx = 1:numel(dbModes)
dbMode = dbModes(modeIdx);
for pamIdx = 1:numel(pamLevels)
pamLevel = pamLevels(pamIdx);
rows = data(data.technique == techniqueName & ...
data.pam_level == pamLevel & data.db_mode == dbMode, :);
if isempty(rows)
continue
end
rows = sortrows(rows, "symbolrate_GBd");
plot(ax, rows.symbolrate_GBd, rows.BER, ...
"LineStyle", lineStyles(dbMode + 1), ...
"Marker", markers(pamIdx), ...
"MarkerSize", 6, ...
"LineWidth", 1.3, ...
"Color", pamModeColor(pamLevel, dbMode), ...
"DisplayName", sprintf("%s, PAM-%d", ...
modeLabel(dbMode), pamLevel));
end
end
xlabel(ax, "Symbolrate [GBd]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-4, 1e-1]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
function color = pamModeColor(pamLevel, dbMode)
switch pamLevel
case 4
lightColor = clr.Paired.lgreen;
darkColor = clr.Paired.dgreen;
case 6
lightColor = clr.Paired.lblue;
darkColor = clr.Paired.dblue;
case 8
lightColor = clr.Paired.lred;
darkColor = clr.Paired.dred;
otherwise
error("plot_pam_comparison:UnknownPam", ...
"Unsupported PAM level %d.", pamLevel);
end
if dbMode == 0
color = lightColor;
else
color = darkColor;
end
end
function label = modeLabel(dbMode)
switch dbMode
case 0
label = "with preemphasis";
case 1
label = "no preemphasis";
case 2
label = "db encoded";
otherwise
label = sprintf("db\_mode = %d", dbMode);
end
end

View File

@@ -0,0 +1,440 @@
%% Warehouse baud-rate and ROP analysis for FFE, MLSE, and duobinary target
% Template source:
% projects/Diss/400G_revisit/auswertung_baudrate.mlx
clear; clc;
%% 1) Configuration and warehouse loading
warehouseFile = "W:\labdata\sioe_labor\baudrate_sweep_b2b\PAMX_b2b_baudrate20241024_210648_wh_final.mat";
selectedRopAttenForBaudrate = 0;
selectedEqForRopCurves = "mlse"; % "ffe", "mlse", or "db"
fecThreshold = 2.2e-2;
polyfitOrderMax = 4;
showRateAsDatarate = false;
showRawRopMarkers = true;
showPolynomialFits = true;
loadedData = load(warehouseFile);
if isfield(loadedData, "obj")
wh = loadedData.obj;
elseif isfield(loadedData, "wh")
wh = loadedData.wh;
else
error("plot_warehouse:NoWarehouse", ...
"Warehouse file must contain a variable named obj or wh.");
end
wh.showInfo;
fsymVals = double(wh.parameter.fsym.values(:).');
ropAttenVals = double(wh.parameter.rop_atten.values(:).');
pamVals = sort(double(wh.parameter.M.values(:).'));
eqStyles = defaultEqStyles();
availableEqStyles = eqStyles(hasWarehouseStorage(wh, eqStyles.storage), :);
if isempty(availableEqStyles)
error("plot_warehouse:NoEqStorage", ...
"None of the configured BER storages are present in wh.sto.");
end
allData = warehouseToTable(wh, fsymVals, ropAttenVals, pamVals, availableEqStyles);
allData = allData(isfinite(allData.ber) & allData.ber > 0, :);
fprintf("Loaded %d finite BER rows from warehouse.\n", height(allData));
disp(groupcounts(allData, ["M", "eq"]));
for eqIdx = 1:height(availableEqStyles)
if ~any(allData.eq == availableEqStyles.eq(eqIdx))
warning("plot_warehouse:NoPositiveBer", ...
"Storage %s exists, but contains no positive BER values to plot.", ...
availableEqStyles.storage(eqIdx));
end
end
%% 2) BER versus baud rate at fixed ROP attenuation
fig = figure(440); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for eqIdx = 1:height(availableEqStyles)
eqStyle = availableEqStyles(eqIdx, :);
rowMask = allData.M == pamLevel & ...
allData.eq == eqStyle.eq & ...
allData.rop_atten == selectedRopAttenForBaudrate;
if ~any(rowMask)
continue
end
curData = sortrows(allData(rowMask, :), "fsym_GBd");
plot(ax, curData.fsym_GBd, curData.ber, ...
"LineStyle", eqStyle.lineStyle, ...
"Marker", eqStyle.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.4, ...
"Color", eqStyle.color, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", eqStyle.color, ...
"DisplayName", eqStyle.name);
end
yline(ax, fecThreshold, ...
"LineStyle", "--", ...
"LineWidth", 1, ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
title(ax, sprintf("PAM-%d, ROP atten. %.1f dB", ...
pamLevel, selectedRopAttenForBaudrate));
xlabel(ax, "Symbol rate [GBd]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-5, 0.5]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
applyBerStyle();
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
%% 3) ROP attenuation curves for one EQ scheme
selectedRopEqStyle = availableEqStyles(availableEqStyles.eq == selectedEqForRopCurves, :);
if isempty(selectedRopEqStyle)
error("plot_warehouse:MissingSelectedEq", ...
"selectedEqForRopCurves = %s is not available in this warehouse.", ...
selectedEqForRopCurves);
end
fig = figure(441); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
rateColors = rateColorMap(numel(fsymVals));
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for fsymIdx = 1:numel(fsymVals)
fsym = fsymVals(fsymIdx);
rowMask = allData.M == pamLevel & ...
allData.eq == selectedRopEqStyle.eq & ...
allData.fsym == fsym;
if ~any(rowMask)
continue
end
curData = sortrows(allData(rowMask, :), "rop_atten");
curveColor = rateColors(fsymIdx, :);
displayName = sprintf("%.0f GBd", fsym .* 1e-9);
if showRawRopMarkers
plot(ax, curData.rop_atten, curData.ber, ...
"LineStyle", "none", ...
"Marker", "o", ...
"MarkerSize", 3, ...
"LineWidth", 0.8, ...
"Color", curveColor, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", curveColor, ...
"DisplayName", displayName);
end
if showPolynomialFits
[xFit, yFit] = fitLogBerCurve(curData.rop_atten, ...
curData.ber, polyfitOrderMax);
if ~isempty(xFit)
plot(ax, xFit, yFit, ...
"LineStyle", "-", ...
"LineWidth", 1.1, ...
"Color", curveColor, ...
"HandleVisibility", "off");
end
end
end
yline(ax, fecThreshold, ...
"LineStyle", "--", ...
"LineWidth", 1, ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
title(ax, sprintf("PAM-%d, %s", pamLevel, selectedRopEqStyle.name));
xlabel(ax, "ROP attenuation [dB]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
ylim(ax, [1e-5, 0.5]);
xlim(ax, [min(ropAttenVals), max(ropAttenVals)]);
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
applyBerStyle();
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
%% 4) Required power at FEC threshold versus baud rate
fecData = computeFecCrossings(allData, availableEqStyles, fecThreshold, ...
polyfitOrderMax);
if isempty(fecData)
warning("plot_warehouse:NoFecCrossings", ...
"No FEC crossings were found for threshold %.3g.", fecThreshold);
else
fig = figure(442); clf;
tiledlayout(1, numel(pamVals), ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
ax = nexttile; hold(ax, "on");
for eqIdx = 1:height(availableEqStyles)
eqStyle = availableEqStyles(eqIdx, :);
rowMask = fecData.M == pamLevel & fecData.eq == eqStyle.eq;
if ~any(rowMask)
continue
end
curData = sortrows(fecData(rowMask, :), "fsym_GBd");
if showRateAsDatarate
xData = curData.datarate_Gbps;
xLabelText = "Datarate [Gb/s]";
else
xData = curData.fsym_GBd;
xLabelText = "Symbol rate [GBd]";
end
plot(ax, xData, curData.required_rop_dBm, ...
"LineStyle", eqStyle.lineStyle, ...
"Marker", eqStyle.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.4, ...
"Color", eqStyle.color, ...
"MarkerFaceColor", "w", ...
"MarkerEdgeColor", eqStyle.color, ...
"DisplayName", eqStyle.name);
end
title(ax, sprintf("PAM-%d, BER = %.2g", pamLevel, fecThreshold));
xlabel(ax, xLabelText);
ylabel(ax, "Required ROP [dBm]");
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
end
set(fig, "Position", 1e3 .* [0.1000 0.5500 1.4113 0.3200]);
end
%% Local helpers
function styles = defaultEqStyles()
styles = table( ...
["ffe"; "mlse"; "db"], ...
["ber_ffe"; "ber_mlse"; "ber_db"], ...
["FFE"; "MLSE"; "Duobinary target"], ...
["o"; "square"; "diamond"], ...
["-"; "-"; "-"], ...
[clr.Paired.red; clr.Paired.green; clr.Paired.blue], ...
'VariableNames', ["eq", "storage", "name", "marker", ...
"lineStyle", "color"]);
end
function keep = hasWarehouseStorage(wh, storageNames)
stoFields = string(fieldnames(wh.sto));
keep = ismember(storageNames, stoFields);
end
function data = warehouseToTable(wh, fsymVals, ropAttenVals, pamVals, eqStyles)
rows = {};
for eqIdx = 1:height(eqStyles)
eqStyle = eqStyles(eqIdx, :);
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
for fsymIdx = 1:numel(fsymVals)
fsym = fsymVals(fsymIdx);
berValues = wh.getStoValue(eqStyle.storage, fsym, ...
ropAttenVals, pamLevel);
ropValues = getOptionalStoValues(wh, "rop", fsym, ...
ropAttenVals, pamLevel);
pdInValues = getOptionalStoValues(wh, "pd_in", fsym, ...
ropAttenVals, pamLevel);
berValues = berValues(:);
ropValues = ropValues(:);
pdInValues = pdInValues(:);
for ropIdx = 1:numel(ropAttenVals)
rows(end+1, :) = { ... %#ok<AGROW>
fsym, ...
fsym .* 1e-9, ...
ropAttenVals(ropIdx), ...
pamLevel, ...
eqStyle.eq, ...
eqStyle.storage, ...
berValues(ropIdx), ...
ropValues(ropIdx), ...
pdInValues(ropIdx), ...
fsym .* floor(log2(pamLevel) * 10) / 10 .* 1e-9};
end
end
end
end
data = cell2table(rows, 'VariableNames', ...
["fsym", "fsym_GBd", "rop_atten", "M", "eq", "storage", ...
"ber", "rop_dBm", "pd_in_dBm", "datarate_Gbps"]);
data.eq = string(data.eq);
data.storage = string(data.storage);
end
function values = getOptionalStoValues(wh, storageName, fsym, ropAttenVals, pamLevel)
if ismember(storageName, string(fieldnames(wh.sto)))
values = wh.getStoValue(storageName, fsym, ropAttenVals, pamLevel);
else
values = nan(size(ropAttenVals));
end
end
function cmap = rateColorMap(numColors)
anchors = [ ...
clr.Paired.lightblue; ...
clr.Paired.blue; ...
clr.Paired.green; ...
clr.Paired.orange; ...
clr.Paired.red; ...
clr.Paired.purple];
if numColors <= size(anchors, 1)
cmap = anchors(1:numColors, :);
return
end
xAnchor = linspace(0, 1, size(anchors, 1));
xQuery = linspace(0, 1, numColors);
cmap = interp1(xAnchor, anchors, xQuery, "linear");
end
function [xFit, yFit] = fitLogBerCurve(x, y, maxOrder)
valid = isfinite(x) & isfinite(y) & y > 0;
x = x(valid);
y = y(valid);
if numel(unique(x)) < 2
xFit = [];
yFit = [];
return
end
[x, orderIdx] = sort(x(:));
y = y(orderIdx);
fitOrder = min(maxOrder, numel(unique(x)) - 1);
coeff = polyfit(x, log10(y), fitOrder);
xFit = linspace(min(x), max(x), 300).';
yFit = 10 .^ polyval(coeff, xFit);
end
function fecData = computeFecCrossings(data, eqStyles, fecThreshold, maxOrder)
rows = {};
pamVals = unique(data.M).';
fsymVals = unique(data.fsym).';
for eqIdx = 1:height(eqStyles)
eqStyle = eqStyles(eqIdx, :);
for pamIdx = 1:numel(pamVals)
pamLevel = pamVals(pamIdx);
for fsymIdx = 1:numel(fsymVals)
fsym = fsymVals(fsymIdx);
rowMask = data.eq == eqStyle.eq & ...
data.M == pamLevel & ...
data.fsym == fsym;
if ~any(rowMask)
continue
end
curData = sortrows(data(rowMask, :), "rop_atten");
[requiredRopAtten, requiredRop] = fecCrossingFromCurve( ...
curData.rop_atten, curData.ber, curData.rop_dBm, ...
fecThreshold, maxOrder);
if ~isfinite(requiredRop)
continue
end
rows(end+1, :) = { ... %#ok<AGROW>
fsym, ...
fsym .* 1e-9, ...
pamLevel, ...
eqStyle.eq, ...
requiredRopAtten, ...
requiredRop, ...
fsym .* floor(log2(pamLevel) * 10) / 10 .* 1e-9};
end
end
end
if isempty(rows)
fecData = table();
else
fecData = cell2table(rows, 'VariableNames', ...
["fsym", "fsym_GBd", "M", "eq", "required_rop_atten", ...
"required_rop_dBm", "datarate_Gbps"]);
fecData.eq = string(fecData.eq);
end
end
function [requiredRopAtten, requiredRop] = fecCrossingFromCurve( ...
ropAtten, ber, rop, fecThreshold, maxOrder)
[xFit, yFit] = fitLogBerCurve(ropAtten, ber, maxOrder);
requiredRopAtten = NaN;
requiredRop = NaN;
if isempty(xFit)
return
end
crossingMask = isfinite(yFit) & yFit > 0;
xFit = xFit(crossingMask);
yFit = yFit(crossingMask);
if numel(xFit) < 2
return
end
delta = log10(yFit) - log10(fecThreshold);
crossingIdx = find(delta(1:end-1) .* delta(2:end) <= 0, 1, "first");
if isempty(crossingIdx)
return
end
x1 = xFit(crossingIdx);
x2 = xFit(crossingIdx + 1);
y1 = delta(crossingIdx);
y2 = delta(crossingIdx + 1);
requiredRopAtten = x1 - y1 .* (x2 - x1) ./ (y2 - y1);
validRop = isfinite(ropAtten) & isfinite(rop);
if nnz(validRop) >= 2
requiredRop = interp1(ropAtten(validRop), rop(validRop), ...
requiredRopAtten, "linear", "extrap");
else
requiredRop = requiredRopAtten;
end
end
function applyBerStyle()
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
end

View File

@@ -0,0 +1,691 @@
%% 400G BER over wavelength: best normal algorithms plus duobinary signaling
% Normal algorithms are reduced to the best BER per wavelength across
% db_mode 0/1, pre-emphasis on/off, and BER/BER_precoded result variants.
% Duobinary signaling uses db_mode = 2 and only the sequence-detection BER
% stored in the BER field.
clear; clc;
%% 1) Query data
selectedPamLevels = [4, 6, 8];
selectedFiberLengthKm = 5;
selectedBitratesGbps = 300:30:480; % set [] to use all available bitrates
selectedRopAttenuation = 0; % set [] to use all ROP attenuation values
selectedIsMpi = 0; % set [] to use all entries
normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)];
duobinaryDbMode = double(db_mode.db_encoded);
maxBerForPlot = 0.5;
showRawEntries = false;
showBestLine = true;
showPolynomialFits = true;
polyfitOrder = 4;
fecThreshold = 2e-2;
algoStyles = defaultAlgorithmStyles();
areaResults = cell(numel(selectedPamLevels), 1);
for pamIdx = 1:numel(selectedPamLevels)
selectedPamLevel = selectedPamLevels(pamIdx);
db = DBHandler( ...
"dataBase", "labor_highspeed", ...
"type", "mysql", ...
"server", "192.168.178.192", ...
"user", "silas", ...
"password", "silas");
db.refresh();
fp = QueryFilter();
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
fp.where('Runs', 'pam_level', 'EQUALS', selectedPamLevel);
if ~isempty(selectedRopAttenuation)
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
end
if ~isempty(selectedIsMpi)
fp.where('Runs', 'is_mpi', 'EQUALS', selectedIsMpi);
end
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
selectedFields = appendMissingFields(selectedFields, ...
{'Runs.precomp_amp'; 'Runs.is_mpi'});
selectedFields = selectedFields(:);
[rawData, query] = db.queryDB(fp, selectedFields);
disp(query);
fprintf("Fetched %d wavelength-sweep result rows.\n", height(rawData));
%% 2) Clean data and build the five plotted curves
data = rawData;
numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ...
"symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ...
"rop_attenuation", "precomp_amp", "is_mpi", "numBits", "numBitErr", ...
"BER", "numBitErr_precoded", "BER_precoded", "STD", "STDrx", ...
"GMI", "AIR", "NGMI", "EVM", "Alpha"];
for fieldIdx = 1:numel(numericFields)
fieldName = numericFields(fieldIdx);
if ismember(fieldName, string(data.Properties.VariableNames))
data.(char(fieldName)) = numericColumn(data.(char(fieldName)));
end
end
data.bitrate_Gbps = data.bitrate .* 1e-9;
if ~isempty(selectedBitratesGbps)
data = data(ismember(round(data.bitrate_Gbps, 6), selectedBitratesGbps), :);
end
if ~ismember("precomp_amp", string(data.Properties.VariableNames))
warning("plot_wavelength_best_algos:NoPrecompAmp", ...
"Runs.precomp_amp was not returned. Falling back to pre_emphasis = (db_mode == 0).");
data.pre_emphasis = data.db_mode == double(db_mode.no_db);
else
data.pre_emphasis = derivePreEmphasis(data.precomp_amp, data.db_mode);
end
normalRows = data(ismember(data.db_mode, normalDbModes), :);
normalPlotData = buildNormalMetricRows(normalRows);
normalPlotData = normalPlotData(isfinite(normalPlotData.BER_plot) & ...
normalPlotData.BER_plot > 0 & normalPlotData.BER_plot < maxBerForPlot, :);
duobinaryRows = data(data.db_mode == duobinaryDbMode, :);
if ismember("equalizer_structure", string(duobinaryRows.Properties.VariableNames))
duobinaryRows = duobinaryRows( ...
equalizerMask(duobinaryRows.equalizer_structure, ...
equalizer_structure.db_encoded), :);
end
duobinaryPlotData = buildDuobinarySignalingRows(duobinaryRows);
duobinaryPlotData = duobinaryPlotData(isfinite(duobinaryPlotData.BER_plot) & ...
duobinaryPlotData.BER_plot > 0 & ...
duobinaryPlotData.BER_plot < maxBerForPlot, :);
plotData = [normalPlotData; duobinaryPlotData];
if isempty(plotData)
warning("plot_wavelength_best_algos:NoRows", ...
"No rows remain after length/PAM/rate/BER filtering.");
continue
end
plotData.bitrate_Gbps = plotData.bitrate .* 1e-9;
plotData.grossrate_Gbps = plotData.grossrate .* 1e-9;
fprintf("Remaining candidate BER rows: %d\n", height(plotData));
disp(groupcounts(plotData, ["algorithm_key", "db_mode", "pre_emphasis", "precode"]));
bestPlotData = bestBerByAlgorithmAndWavelength(plotData);
fprintf("Keeping %d best-BER rows across algorithm/wavelength groups.\n", ...
height(bestPlotData));
disp(groupcounts(bestPlotData, "algorithm_key"));
%% 3) Plot one tile per algorithm
availableStyles = algoStyles(hasAlgorithmRows(bestPlotData, algoStyles), :);
if isempty(availableStyles)
warning("plot_wavelength_best_algos:NoSelectedAlgorithms", ...
"None of the configured algorithm styles match the queried rows.");
continue
end
fig = figure(432 + pamIdx); clf;
t = tiledlayout(fig, 1, height(availableStyles), ...
"TileSpacing","compact", ...
"Padding", "compact");
availableBitratesGbps = unique(bestPlotData.bitrate_Gbps(isfinite( ...
bestPlotData.bitrate_Gbps))).';
bitrateMarkers = bitrateMarkerSet(numel(availableBitratesGbps));
for styleIdx = 1:height(availableStyles)
style = availableStyles(styleIdx, :);
ax = nexttile(t); hold(ax, "on");
bitrateColors = sequentialColors(style.color, numel(availableBitratesGbps), ...
style.algorithm_key);
for bitrateIdx = 1:numel(availableBitratesGbps)
bitrateGbps = availableBitratesGbps(bitrateIdx);
rowMask = bestPlotData.algorithm_key == style.algorithm_key & ...
bestPlotData.bitrate_Gbps == bitrateGbps;
if ~any(rowMask)
continue
end
algoData = sortrows(bestPlotData(rowMask, :), "wavelength");
curveColor = bitrateColors(bitrateIdx, :);
if showRawEntries
scatter(ax, algoData.wavelength, algoData.BER_plot, ...
9, ...
"Marker", ".", ...
"MarkerEdgeColor", curveColor, ...
"MarkerFaceColor", curveColor, ...
"MarkerEdgeAlpha", 0.25, ...
"MarkerFaceAlpha", 0.25, ...
"HandleVisibility", "off");
end
if showBestLine
plot(ax, algoData.wavelength, algoData.BER_plot, ...
"LineStyle", style.lineStyle, ...
"Marker", bitrateMarkers(bitrateIdx), ...
"MarkerSize", 2.5, ...
"LineWidth", 1.0, ...
"Color", curveColor, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", curveColor, ...
"HandleVisibility", "off");
end
if showPolynomialFits
[xFit, yFit] = fitLogBerCurve(algoData.wavelength, ...
algoData.BER_plot, polyfitOrder);
if ~isempty(xFit)
plot(ax, xFit, yFit, ...
"LineStyle", ":", ...
"LineWidth", 1.1, ...
"Color", curveColor, ...
"HandleVisibility", "off");
end
end
end
% title(ax, style.name, "Interpreter", "none");
xlabel(ax, "Wavelength [nm]");
ylabel(ax, "BER");
set(ax, "YScale", "log");
if selectedPamLevel == 4
ylim(ax, [1e-6, 0.3]);
elseif selectedPamLevel == 6
ylim(ax, [1e-4, 0.3]);
elseif selectedPamLevel == 8
ylim(ax, [1e-4, 0.3]);
end
grid(ax, "on");
box(ax, "on");
if styleIdx > 1
ylabel(ax, "");
yticklabels(ax, {});
end
xTicks = unique(bestPlotData.wavelength(isfinite(bestPlotData.wavelength)));
xTicks = xTicks(1:2:end);
xTicks_own = [1300,1310,1320];
set(ax, "XTick", xTicks_own);
if ~isempty(xTicks)
xlim(ax, [min(xTicks)-1, max(xTicks)+1]);
end
plotFecLines(ax, fecThreshold);
if exist("beautifyBERplot", "file")
beautifyBERplot("logscale", true, "setcolors", false, ...
"setmarkers", false, "changemarkers", false);
end
text(ax, 0.02, 0.02, style.name, ...
"Units", "normalized", ...
"HorizontalAlignment", "left", ...
"VerticalAlignment", "bottom", ...
"BackgroundColor", "white", ...
"EdgeColor", [0.60 0.60 0.60], ...
"LineWidth", 0.5, ...
"Margin", 2, ...
"FontSize", 8, ...
"Interpreter", "none", ...
"Clipping", "on");
end
set(fig, "Position", 1e3 .* [0.1070 0.5497 1.4113 0.3253]);
wavelengthTikzPath = sprintf( ...
"C:/Users/Silas/Documents/6971e0b65b380ca6d71c837f/04_Experimental_Evaluation/tikz/400g/10km_wavelength_compare_pam%d.tikz", ...
selectedPamLevel);
% mat2tikz_improved(wavelengthTikzPath, "cleanfigure", 0);
areaData = computePermissibleWavelengthAreas(bestPlotData, availableStyles, ...
fecThreshold, polyfitOrder);
if isempty(areaData)
warning("plot_wavelength_best_algos:NoPermissibleAreas", ...
"No measured wavelength samples are at or below the BER threshold %.3g.", ...
fecThreshold);
else
areaResults{pamIdx} = areaData;
end
end
%% 4) Permissible wavelength area versus gross rate for all PAM levels
areaFig = figure(436); clf;
areaLayout = tiledlayout(areaFig, 1, 3, ...
"TileSpacing", "compact", ...
"Padding", "compact");
for pamIdx = 1:numel(selectedPamLevels)
areaAx = nexttile(areaLayout); hold(areaAx, "on");
areaData = areaResults{pamIdx};
if isempty(areaData)
grid(areaAx, "on");
box(areaAx, "on");
else
for styleIdx = 1:height(algoStyles)
style = algoStyles(styleIdx, :);
rowMask = areaData.algorithm_key == style.algorithm_key;
if ~any(rowMask)
continue
end
algoAreaData = sortrows(areaData(rowMask, :), "grossrate_Gbps");
plot(areaAx, algoAreaData.grossrate_Gbps, ...
algoAreaData.permissible_wavelength_area_nm, ...
"LineStyle", style.lineStyle, ...
"Marker", style.marker, ...
"MarkerSize", 5, ...
"LineWidth", 1.4, ...
"Color", style.color, ...
"MarkerFaceColor", style.markerFaceColor, ...
"MarkerEdgeColor", style.color, ...
"DisplayName", style.name, ...
"HandleVisibility", "on");
end
end
ylim([5 35]);
xlabel(areaAx, "Gross rate [Gb/s]");
if pamIdx == 1
ylabel(areaAx, "Permissible wavelength area [nm]");
else
ylabel(areaAx, "");
% Ensure yticklabels refers to the function, not a variable
if exist("yticklabels", "var")
clear yticklabels
end
yticks(areaAx,[5:5:35]);
yticklabels(areaAx, "");
end
grid(areaAx, "on");
box(areaAx, "on");
text(areaAx, 0.2, 0.06, sprintf("PAM%d", selectedPamLevels(pamIdx)), ...
"Units", "normalized", ...
"HorizontalAlignment", "left", ...
"VerticalAlignment", "bottom", ...
"BackgroundColor", "white", ...
"EdgeColor", [0.60 0.60 0.60], ...
"LineWidth", 0.5, ...
"Margin", 2, ...
"FontSize", 8, ...
"Interpreter", "none", ...
"Clipping", "on");
end
legend
set(areaFig, "Position", 1e3 .* [0.3500 0.3500 0.7000 0.8000]);
% mat2tikz_improved( ...
% "C:/Users/Silas/Documents/6971e0b65b380ca6d71c837f/04_Experimental_Evaluation/tikz/400g/10km_permissible_wavelength.tikz", ...
% "cleanfigure", 0);
%% Local helpers
function fields = appendMissingFields(fields, extraFields)
fields = cellstr(fields);
extraFields = cellstr(extraFields);
for idx = 1:numel(extraFields)
if ~any(strcmp(fields, extraFields{idx}))
fields{end+1, 1} = extraFields{idx}; %#ok<AGROW>
end
end
end
function values = numericColumn(values)
if iscell(values)
values = string(values);
end
if isstring(values) || ischar(values)
values = str2double(values);
end
values = double(values);
end
function styles = defaultAlgorithmStyles()
styles = table( ...
["vnle"; ...
"vnle_pf_mlse"; ...
"vnle_db_mlse"; ...
"ml_mlse"; ...
"db_encoded"], ...
[equalizer_structure.vnle; ...
equalizer_structure.vnle_pf_mlse; ...
equalizer_structure.vnle_db_mlse; ...
equalizer_structure.ml_mlse; ...
equalizer_structure.db_encoded], ...
["VNLE"; ...
"VNLE + PF + MLSE"; ...
"VNLE DBt. + MLSE"; ...
"ML pre-EQ + Viterbi"; ...
"DBS + VNLE + MLSE"], ...
["o"; "square"; "diamond"; "^"; "v"], ...
["-"; "-"; "-"; "-"; "-"], ...
["w"; "w"; "w"; "w"; "w"], ...
[clr.Paired.red; ...
clr.Paired.green; ...
clr.Paired.blue; ...
clr.Paired.purple; ...
clr.Paired.orange], ...
'VariableNames', ["algorithm_key", "eq", "name", "marker", ...
"lineStyle", "markerFaceColor", "color"]);
end
function preEmphasis = derivePreEmphasis(precompAmp, dbMode)
preEmphasis = false(size(dbMode));
validPrecomp = isfinite(precompAmp);
preEmphasis(validPrecomp) = precompAmp(validPrecomp) > -45;
missingPrecomp = ~validPrecomp;
preEmphasis(missingPrecomp) = dbMode(missingPrecomp) == double(db_mode.no_db);
end
function plotData = buildNormalMetricRows(data)
baseRows = data(isfinite(data.BER), :);
baseRows.precode = false(height(baseRows), 1);
baseRows.BER_plot = baseRows.BER;
baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure);
baseRows = baseRows(baseRows.algorithm_key ~= "", :);
if ismember("BER_precoded", string(data.Properties.VariableNames))
precodedRows = data(isfinite(data.BER_precoded), :);
precodedRows.precode = true(height(precodedRows), 1);
precodedRows.BER_plot = precodedRows.BER_precoded;
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
precodedRows.equalizer_structure);
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
plotData = [baseRows; precodedRows];
else
warning("plot_wavelength_best_algos:NoPrecodedBer", ...
"BER_precoded was not returned. Plotting only BER rows for normal algorithms.");
plotData = baseRows;
end
end
function plotData = buildDuobinarySignalingRows(data)
plotData = data(isfinite(data.BER), :);
plotData.precode = false(height(plotData), 1);
plotData.BER_plot = plotData.BER;
plotData.algorithm_key = repmat("db_encoded", height(plotData), 1);
end
function algorithmKey = algorithmKeyFromEqualizer(equalizerColumn)
eqNumeric = equalizerNumeric(equalizerColumn);
algorithmKey = strings(size(eqNumeric));
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle)) = "vnle";
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle_pf_mlse)) = ...
"vnle_pf_mlse";
algorithmKey(eqNumeric == enumValue(equalizer_structure.vnle_db_mlse)) = ...
"vnle_db_mlse";
algorithmKey(eqNumeric == enumValue(equalizer_structure.ml_mlse)) = "ml_mlse";
end
function mask = equalizerMask(equalizerColumn, eqValue)
eqNumeric = equalizerNumeric(equalizerColumn);
mask = eqNumeric == enumValue(eqValue);
end
function eqNumeric = equalizerNumeric(equalizerColumn)
if isa(equalizerColumn, "equalizer_structure")
eqNumeric = double(equalizerColumn);
elseif isnumeric(equalizerColumn)
eqNumeric = double(equalizerColumn);
else
equalizerString = string(equalizerColumn);
eqNumeric = str2double(equalizerString);
enumNames = ["vnle", "ffe", "dfe", "vnle_pf_mlse", ...
"vnle_db_mlse", "db_encoded", "ml_mlse"];
enumValues = [ ...
enumValue(equalizer_structure.vnle), ...
enumValue(equalizer_structure.ffe), ...
enumValue(equalizer_structure.dfe), ...
enumValue(equalizer_structure.vnle_pf_mlse), ...
enumValue(equalizer_structure.vnle_db_mlse), ...
enumValue(equalizer_structure.db_encoded), ...
enumValue(equalizer_structure.ml_mlse)];
for idx = 1:numel(enumNames)
missingNumeric = isnan(eqNumeric);
eqNumeric(missingNumeric & equalizerString == enumNames(idx)) = ...
enumValues(idx);
end
end
end
function value = enumValue(enumEntry)
value = double(enumEntry);
end
function bestData = bestBerByAlgorithmAndWavelength(data)
groupVars = ["algorithm_key", "bitrate_Gbps", "wavelength"];
groupId = findgroups(data(:, groupVars));
keepIdx = NaN(max(groupId), 1);
for curGroup = 1:max(groupId)
rowIdx = find(groupId == curGroup);
[~, localBestIdx] = min(data.BER_plot(rowIdx));
keepIdx(curGroup) = rowIdx(localBestIdx);
end
bestData = sortrows(data(keepIdx, :), groupVars);
end
function [xFit, yFit, fitModel] = fitLogBerCurve(x, y, maxOrder)
valid = isfinite(x) & isfinite(y) & y > 0;
x = x(valid);
y = y(valid);
[x, orderIdx] = sort(x(:));
y = y(orderIdx);
[x, uniqueIdx] = unique(x);
y = y(uniqueIdx);
if numel(x) < 2
xFit = [];
yFit = [];
fitModel = [];
return
end
% Keep the requested fourth-order fit when enough wavelength points exist.
% With fewer than five unique points, use the highest identifiable order.
fitOrder = min(maxOrder, numel(x) - 1);
[coefficients, ~, mu] = polyfit(x, log10(y), fitOrder);
fitModel.coefficients = coefficients;
fitModel.mu = mu;
xFit = linspace(min(x), max(x), 300).';
yFit = 10 .^ polyval(coefficients, (xFit - mu(1)) ./ mu(2));
end
function areaData = computePermissibleWavelengthAreas(data, algoStyles, ...
fecThreshold, maxOrder)
grossrateValues = unique(data.grossrate_Gbps(isfinite( ...
data.grossrate_Gbps))).';
wavelengthGrid = unique(data.wavelength(isfinite(data.wavelength))).';
rows = cell(height(algoStyles) * numel(grossrateValues), 5);
rowIdx = 0;
for styleIdx = 1:height(algoStyles)
style = algoStyles(styleIdx, :);
algorithmMask = data.algorithm_key == style.algorithm_key;
for grossrateIdx = 1:numel(grossrateValues)
grossrateGb = grossrateValues(grossrateIdx);
rowMask = algorithmMask & data.grossrate_Gbps == grossrateGb;
if ~any(rowMask)
continue
end
curveData = sortrows(data(rowMask, :), "wavelength");
[wavelengthMin, wavelengthMax] = permissibleWavelengthRange( ...
curveData.wavelength, curveData.BER_plot, fecThreshold, ...
maxOrder, wavelengthGrid);
if any(~isfinite([wavelengthMin, wavelengthMax]))
continue
end
rowIdx = rowIdx + 1;
rows(rowIdx, :) = { ...
style.algorithm_key, ...
grossrateGb, ...
wavelengthMin, ...
wavelengthMax, ...
wavelengthMax - wavelengthMin};
end
end
if rowIdx == 0
areaData = table();
else
rows = rows(1:rowIdx, :);
areaData = cell2table(rows, "VariableNames", ...
["algorithm_key", "grossrate_Gbps", ...
"wavelength_min_allowed_nm", "wavelength_max_allowed_nm", ...
"permissible_wavelength_area_nm"]);
areaData.algorithm_key = string(areaData.algorithm_key);
end
end
function [wavelengthMin, wavelengthMax] = permissibleWavelengthRange(x, y, ...
fecThreshold, maxOrder, wavelengthGrid)
wavelengthMin = NaN;
wavelengthMax = NaN;
valid = isfinite(x) & isfinite(y) & y > 0;
x = x(valid);
y = y(valid);
if isempty(x)
return
end
[x, orderIdx] = sort(x(:));
y = y(orderIdx);
[x, uniqueIdx] = unique(x);
y = y(uniqueIdx);
wavelengthGrid = sort(wavelengthGrid(:));
if isempty(wavelengthGrid)
return
end
globalRange = [wavelengthGrid(1), wavelengthGrid(end)];
edgeTolerance = max(1e-9, 1e-9 .* max(abs(globalRange)));
missingLeftEdge = x(1) > globalRange(1) + edgeTolerance;
missingRightEdge = x(end) < globalRange(2) - edgeTolerance;
% If all observed points are below the FEC, use the complete sweep span
% instead of requiring polynomial roots outside the observed range.
if all(y <= fecThreshold)
wavelengthMin = globalRange(1);
wavelengthMax = globalRange(2);
return
end
[~, ~, fitModel] = fitLogBerCurve(x, y, maxOrder);
if isempty(fitModel)
return
end
xRange = [x(1), x(end)];
crossingCoefficients = fitModel.coefficients;
crossingCoefficients(end) = crossingCoefficients(end) - log10(fecThreshold);
rootsAtThreshold = roots(crossingCoefficients);
realRoots = real(rootsAtThreshold(abs(imag(rootsAtThreshold)) < ...
1e-7 .* max(1, abs(real(rootsAtThreshold)))));
realRoots = realRoots .* fitModel.mu(2) + fitModel.mu(1);
realRoots = sort(realRoots(realRoots >= xRange(1) & realRoots <= xRange(2)));
if isempty(realRoots)
return
end
% Include the observed boundaries in the interval search. This lets each
% side be handled independently: a side that remains below FEC uses the
% corresponding observed edge, while a side that crosses FEC uses its root.
intervalEdges = [xRange(1); realRoots(:); xRange(2)];
intervalWidths = diff(intervalEdges);
intervalIsAllowed = false(size(intervalWidths));
thresholdLogBer = log10(fecThreshold);
for intervalIdx = 1:numel(intervalEdges)-1
midpoint = mean(intervalEdges(intervalIdx:intervalIdx + 1));
normalizedMidpoint = (midpoint - fitModel.mu(1)) ./ fitModel.mu(2);
intervalIsAllowed(intervalIdx) = polyval(fitModel.coefficients, ...
normalizedMidpoint) <= thresholdLogBer;
end
allowedIntervals = find(intervalIsAllowed);
if isempty(allowedIntervals)
return
end
[~, widestAllowedIdx] = max(intervalWidths(allowedIntervals));
selectedInterval = allowedIntervals(widestAllowedIdx);
wavelengthMin = intervalEdges(selectedInterval);
wavelengthMax = intervalEdges(selectedInterval + 1);
% A missing terminal wavelength sample is treated as below FEC when the
% permissible interval reaches that side of the observed curve.
if selectedInterval == 1 && missingLeftEdge
wavelengthMin = globalRange(1);
end
if selectedInterval == numel(intervalWidths) && missingRightEdge
wavelengthMax = globalRange(2);
end
end
function keep = hasAlgorithmRows(data, algoStyles)
keep = false(height(algoStyles), 1);
for idx = 1:height(algoStyles)
keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx));
end
end
function plotFecLines(ax, fecLevels)
xl = xlim(ax);
for idx = 1:numel(fecLevels)
h = plot(ax, xl, [fecLevels(idx), fecLevels(idx)], ...
"LineWidth", 1, ...
"LineStyle", "--", ...
"Color", [0.25 0.25 0.25], ...
"HandleVisibility", "off");
h.Annotation.LegendInformation.IconDisplayStyle = "off";
end
end
function markers = bitrateMarkerSet(n)
markerOptions = ["o", "square", "diamond", "^", "v", ">", "<"];
if n <= numel(markerOptions)
markers = markerOptions(1:n);
else
markers = markerOptions(mod(0:n-1, numel(markerOptions)) + 1);
end
end
function colors = sequentialColors(baseColor, n, algorithmKey)
if n <= 1
colors = baseColor;
return
end
lightBlend = linspace(0.72, 0.00, n).';
darkBlend = linspace(0.00, 0.35, n).';
if algorithmKey == "db_encoded"
lightBlend = linspace(0.62, 0.00, n).';
darkBlend = linspace(0.00, 0.12, n).';
end
colors = zeros(n, 3);
for idx = 1:n
color = (1 - lightBlend(idx)) .* baseColor + lightBlend(idx) .* [1 1 1];
color = (1 - darkBlend(idx)) .* color;
colors(idx, :) = color;
end
colors = flip(colors);
end

View File

@@ -0,0 +1,85 @@
%% Replot opt_filt_analysis.fig grouped by PAM level
clear; close all; clc;
figPath = fullfile(fileparts(mfilename("fullpath")), "opt_filt_analysis.fig");
figIn = openfig(figPath, "invisible");
srcAx = findall(figIn, "Type", "axes");
srcAx = srcAx(1);
srcLines = flipud(findall(srcAx, "Type", "line"));
% Get every plotted datapoint and its display name.
data = arrayfun(@(h) struct( ...
"XData", h.XData, ...
"YData", h.YData, ...
"DisplayName", string(h.DisplayName)), srcLines);
displayNames = string({data.DisplayName})';
disp(displayNames);
selectedEqType = "ffe"; % choose "ffe" or "mlse"
eqtype = ["ffe", "mlse"];
eqGroup = repmat("", size(displayNames));
for k = 1:numel(eqtype)
eqGroup(contains(lower(displayNames), eqtype(k))) = eqtype(k);
end
if ~ismember(selectedEqType, eqtype)
error("selectedEqType must be either 'ffe' or 'mlse'.");
end
keep = eqGroup == selectedEqType;
srcLines = srcLines(keep);
data = data(keep);
displayNames = displayNames(keep);
pamLevels = ["pam4", "pam6", "pam8"];
pamGroup = repmat("", size(displayNames));
for k = 1:numel(pamLevels)
pamGroup(contains(lower(displayNames), pamLevels(k))) = pamLevels(k);
end
% Group by the first part of the display name.
groupLabels = ["A", "B", "C", "D", "E"];
groupGroup = repmat("", size(displayNames));
nameLower = lower(displayNames);
groupGroup(startsWith(nameLower, "120 gb") | ...
startsWith(nameLower, "144 gb") | startsWith(nameLower, "170 gb")) = "A";
groupGroup(startsWith(nameLower, "optfil")) = "B";
groupGroup(startsWith(nameLower, "thormax")) = "C";
groupGroup(startsWith(nameLower, "thormax optfil")) = "E";
groupGroup(startsWith(nameLower, "thormax optfil ohne fl")) = "D";
groupColors = [clr.Paired.red; clr.Paired.blue; clr.Paired.green; ...
clr.Paired.orange; clr.Paired.purple];
groupStyles = {"-", "--", ":", ":", ":"};
groupMarkers = {"o", "s", "^", "d", "v"};
figOut = figure("Name", "opt_filt_analysis by " + upper(selectedEqType) + " and PAM");
layout = tiledlayout(figOut, 1, 3, "TileSpacing", "compact", "Padding", "compact");
for k = 1:numel(pamLevels)
ax = nexttile(layout);
hold(ax, "on");
for lineIdx = find(pamGroup == pamLevels(k))'
newLine = copyobj(srcLines(lineIdx), ax);
groupIdx = find(groupLabels == groupGroup(lineIdx), 1);
newLine.Color = groupColors(groupIdx, :);
newLine.Marker = groupMarkers{groupIdx};
newLine.LineWidth = 1;
newLine.LineStyle = groupStyles{groupIdx};
newLine.MarkerSize = 3;
newLine.DisplayName = groupGroup(lineIdx);
end
ax.XScale = srcAx.XScale;
ax.YScale = srcAx.YScale;
title(ax, upper(pamLevels(k)));
xlabel(ax, srcAx.XLabel.String);
ylabel(ax, srcAx.YLabel.String);
grid(ax, "on");
legend(ax, "show", "Interpreter", "none", "Location", "best");
beautifyBERplot("setcolors", false, "setmarkers", false);
end
close(figIn);

View File

@@ -0,0 +1,430 @@
%% Reprocess baudrate-sweep MAT files with dsp_400g_recipe
% File naming convention:
% PAMX_b2b_baudrate20241024_210648PAM_4_fsym_100_bits.mat
% PAMX_b2b_baudrate20241024_210648PAM_4_fsym_100_symbols.mat
% PAMX_b2b_baudrate20241024_210648PAM_4_fsym_100_rop_0_5_rx_signal.mat
clear; clc;
%% Configuration
dataDir = "D:\baudrate_sweep_b2b";
filePrefix = "PAMX_b2b_baudrate20241024_210648";
outputFile = fullfile(dataDir, filePrefix + "_dsp400g_reprocessed_wh.mat");
originalWarehouseFile = fullfile(dataDir, filePrefix + "_wh_final.mat");
pamLevels = [4, 6, 8];
fsymGBd = 100:6:196;
ropAtten = 0:0.5:7;
% ropAtten = 0:0.5:7;
useParallel = true;
batchSize = 24; % warehouse writes happen after each batch
maxJobs = inf; % use a small number for smoke tests
saveEveryBatches = 1;
debugPlots = false;
storeDspOutput = false; % full output objects can make the warehouse very large
copyOriginalPowerMetadata = true;
recipeParams = struct( ...
"run_ffe", true, ...
"run_vnle_mlse", true, ...
"run_dbtgt", true, ...
"run_ml_mlse", true, ...
"run_ml_mlse_db", false, ...
"run_mlse_db", false, ...
"plot_input_signal", false, ...
"plot_output_signals", false);
%% Discover all file/acquisition jobs
jobs = buildJobList(dataDir, filePrefix, pamLevels, fsymGBd, ropAtten);
if isempty(jobs)
error("reprocess:NoJobs", "No processable RX acquisition jobs were found.");
end
if isfinite(maxJobs)
jobs = jobs(1:min(numel(jobs), maxJobs));
end
maxAcquisitionIdx = max([jobs.acquisition_idx]);
fprintf("Discovered %d acquisition jobs, max acquisition index S{%d}.\n", ...
numel(jobs), maxAcquisitionIdx);
powerLookup = loadOriginalPowerLookup(originalWarehouseFile, ...
copyOriginalPowerMetadata);
%% Build output warehouse
params = struct();
params.fsym = fsymGBd .* 1e9;
params.rop_atten = ropAtten;
params.M = pamLevels;
params.acquisition_idx = 1:maxAcquisitionIdx;
wh = DataStorage(params);
wh.addStorage("ber_ffe");
wh.addStorage("ber_mlse");
wh.addStorage("ber_db");
wh.addStorage("ber_ffe_precoded");
wh.addStorage("ber_mlse_precoded");
wh.addStorage("ber_db_precoded");
wh.addStorage("rop");
wh.addStorage("pd_in");
wh.addStorage("rx_file");
wh.addStorage("status");
wh.addStorage("error_message");
if storeDspOutput
wh.addStorage("dsp_output");
end
parallelEnabled = useParallel && ensureParallelPool();
if parallelEnabled
pool = gcp("nocreate");
fprintf("Processing with parfor on %d workers.\n", pool.NumWorkers);
else
fprintf("Processing serially.\n");
end
%% Process jobs in parallel batches, write warehouse serially
numBatches = ceil(numel(jobs) / batchSize);
for batchIdx = 1:numBatches
firstJob = (batchIdx - 1) * batchSize + 1;
lastJob = min(batchIdx * batchSize, numel(jobs));
batchJobs = jobs(firstJob:lastJob);
batchResults = cell(numel(batchJobs), 1);
fprintf("Batch %d/%d: jobs %d-%d of %d\n", ...
batchIdx, numBatches, firstJob, lastJob, numel(jobs));
if parallelEnabled
parfor localIdx = 1:numel(batchJobs)
batchResults{localIdx} = processOneJob( ...
batchJobs(localIdx), recipeParams, debugPlots, storeDspOutput);
end
else
for localIdx = 1:numel(batchJobs)
batchResults{localIdx} = processOneJob( ...
batchJobs(localIdx), recipeParams, debugPlots, storeDspOutput);
end
end
for localIdx = 1:numel(batchResults)
wh = writeResultToWarehouse(wh, batchResults{localIdx}, ...
storeDspOutput, powerLookup);
end
if mod(batchIdx, saveEveryBatches) == 0 || batchIdx == numBatches
save(outputFile, "wh", "recipeParams", "dataDir", "filePrefix", ...
"jobs", "storeDspOutput", "copyOriginalPowerMetadata", ...
"originalWarehouseFile", "-v7.3");
fprintf("Saved checkpoint to %s\n", outputFile);
end
end
fprintf("Saved reprocessed warehouse to %s\n", outputFile);
wh.showInfo;
%% Local helpers
function jobs = buildJobList(dataDir, filePrefix, pamLevels, fsymGBd, ropAtten)
jobs = struct( ...
"M", {}, ...
"fsym_GBd", {}, ...
"fsym", {}, ...
"rop_atten", {}, ...
"acquisition_idx", {}, ...
"bits_file", {}, ...
"symbols_file", {}, ...
"rx_file", {});
for M = pamLevels
for fsymGb = fsymGBd
fsym = fsymGb .* 1e9;
bitsFile = fullfile(dataDir, sprintf("%sPAM_%d_fsym_%d_bits.mat", ...
filePrefix, M, fsymGb));
symbolsFile = fullfile(dataDir, sprintf("%sPAM_%d_fsym_%d_symbols.mat", ...
filePrefix, M, fsymGb));
if ~isfile(bitsFile) || ~isfile(symbolsFile)
warning("reprocess:MissingReference", ...
"Skipping PAM-%d %.0f GBd: missing bits or symbols file.", ...
M, fsymGb);
continue
end
for rop = ropAtten
rxFile = fullfile(dataDir, sprintf("%sPAM_%d_fsym_%d_rop_%s_rx_signal.mat", ...
filePrefix, M, fsymGb, ropToken(rop)));
if ~isfile(rxFile)
warning("reprocess:MissingRx", "Missing RX file: %s", rxFile);
continue
end
numAcquisitions = acquisitionCount(rxFile);
for acqIdx = 1:numAcquisitions
jobs(end+1) = struct( ... %#ok<AGROW>
"M", M, ...
"fsym_GBd", fsymGb, ...
"fsym", fsym, ...
"rop_atten", rop, ...
"acquisition_idx", acqIdx, ...
"bits_file", bitsFile, ...
"symbols_file", symbolsFile, ...
"rx_file", rxFile);
end
end
end
end
end
function numAcq = acquisitionCount(rxFile)
info = whos("-file", rxFile, "S");
if isempty(info)
warning("reprocess:MissingS", "RX file has no variable S: %s", rxFile);
numAcq = 0;
else
numAcq = prod(info.size);
end
end
function result = processOneJob(job, recipeParams, debugPlots, storeDspOutput)
result = emptyJobResult(job);
try
bitsData = load(job.bits_file, "Bits");
symbolsData = load(job.symbols_file, "Symbols");
ScpeSigRaw = loadRxSignal(job.rx_file, job.acquisition_idx);
dataTable = makeRecipeDataTable(job);
fprintf("PAM-%d, %.0f GBd, ROP atten %.1f dB, S{%d}\n", ...
job.M, job.fsym_GBd, job.rop_atten, job.acquisition_idx);
dspOut = dsp_400g_recipe(ScpeSigRaw, symbolsData.Symbols, bitsData.Bits, ...
"fsym", job.fsym, ...
"M", job.M, ...
"duob_mode", db_mode.no_db, ...
"dataTable", dataTable, ...
"userParameters", recipeParams, ...
"debug_plots", debugPlots);
result.ber_ffe = extractBer(dspOut, "ffe_package", "BER");
result.ber_mlse = extractBer(dspOut, "mlse_package", "BER");
result.ber_db = extractBer(dspOut, "dbtgt_package", "BER");
result.ber_ffe_precoded = extractBer(dspOut, "ffe_package", "BER_precoded");
result.ber_mlse_precoded = extractBer(dspOut, "mlse_package", "BER_precoded");
result.ber_db_precoded = extractBer(dspOut, "dbtgt_package", "BER_precoded");
result.status = "ok";
if storeDspOutput
result.dsp_output = dspOut;
end
catch err
result.status = "failed";
result.error_message = string(err.message);
warning("reprocess:DspFailed", ...
"DSP failed for PAM-%d %.0f GBd ROP %.1f dB S{%d}: %s", ...
job.M, job.fsym_GBd, job.rop_atten, job.acquisition_idx, err.message);
end
end
function result = emptyJobResult(job)
result = struct( ...
"M", job.M, ...
"fsym", job.fsym, ...
"fsym_GBd", job.fsym_GBd, ...
"rop_atten", job.rop_atten, ...
"acquisition_idx", job.acquisition_idx, ...
"rx_file", job.rx_file, ...
"ber_ffe", NaN, ...
"ber_mlse", NaN, ...
"ber_db", NaN, ...
"ber_ffe_precoded", NaN, ...
"ber_mlse_precoded", NaN, ...
"ber_db_precoded", NaN, ...
"status", "not_run", ...
"error_message", "", ...
"dsp_output", []);
end
function wh = writeResultToWarehouse(wh, result, storeDspOutput, powerLookup)
idx = {result.fsym, result.rop_atten, result.M, result.acquisition_idx};
[ropValue, pdInValue] = lookupOriginalPower(powerLookup, ...
result.fsym, result.rop_atten, result.M);
wh.addValueToStorage(result.ber_ffe, "ber_ffe", idx{:});
wh.addValueToStorage(result.ber_mlse, "ber_mlse", idx{:});
wh.addValueToStorage(result.ber_db, "ber_db", idx{:});
wh.addValueToStorage(result.ber_ffe_precoded, "ber_ffe_precoded", idx{:});
wh.addValueToStorage(result.ber_mlse_precoded, "ber_mlse_precoded", idx{:});
wh.addValueToStorage(result.ber_db_precoded, "ber_db_precoded", idx{:});
wh.addValueToStorage(ropValue, "rop", idx{:});
wh.addValueToStorage(pdInValue, "pd_in", idx{:});
wh.addValueToStorage(char(result.rx_file), "rx_file", idx{:});
wh.addValueToStorage(char(result.status), "status", idx{:});
wh.addValueToStorage(char(result.error_message), "error_message", idx{:});
if storeDspOutput
wh.addValueToStorage(result.dsp_output, "dsp_output", idx{:});
end
end
function powerLookup = loadOriginalPowerLookup(originalWarehouseFile, enabled)
powerLookup = struct("enabled", false, "data", table());
if ~enabled
return
end
if ~isfile(originalWarehouseFile)
warning("reprocess:MissingOriginalWarehouse", ...
"Original warehouse not found, storing NaN for rop/pd_in: %s", ...
originalWarehouseFile);
return
end
loadedData = load(originalWarehouseFile);
if isfield(loadedData, "wh")
originalWh = loadedData.wh;
elseif isfield(loadedData, "obj")
originalWh = loadedData.obj;
else
warning("reprocess:NoOriginalWarehouseObject", ...
"Original warehouse file contains neither wh nor obj: %s", ...
originalWarehouseFile);
return
end
requiredStorages = ["rop", "pd_in"];
availableStorages = string(fieldnames(originalWh.sto));
if ~all(ismember(requiredStorages, availableStorages))
warning("reprocess:MissingPowerStorage", ...
"Original warehouse has no complete rop/pd_in storage. Storing NaN.");
return
end
fsymVals = double(originalWh.parameter.fsym.values(:).');
ropAttenVals = double(originalWh.parameter.rop_atten.values(:).');
pamVals = double(originalWh.parameter.M.values(:).');
rows = cell(numel(fsymVals) * numel(ropAttenVals) * numel(pamVals), 5);
rowIdx = 0;
for pamLevel = pamVals
for fsym = fsymVals
for ropAtten = ropAttenVals
rowIdx = rowIdx + 1;
rows(rowIdx, :) = { ...
fsym, ...
ropAtten, ...
pamLevel, ...
getOriginalScalar(originalWh, "rop", fsym, ropAtten, pamLevel), ...
getOriginalScalar(originalWh, "pd_in", fsym, ropAtten, pamLevel)};
end
end
end
powerLookup.enabled = true;
powerLookup.data = cell2table(rows, 'VariableNames', ...
["fsym", "rop_atten", "M", "rop", "pd_in"]);
fprintf("Loaded %d original rop/pd_in metadata rows from %s\n", ...
height(powerLookup.data), originalWarehouseFile);
end
function value = getOriginalScalar(originalWh, storageName, fsym, ropAtten, pamLevel)
value = NaN;
try
rawValue = originalWh.getStoValue(storageName, fsym, ropAtten, pamLevel);
catch
return
end
if isnumeric(rawValue) && isscalar(rawValue)
value = double(rawValue);
end
end
function [ropValue, pdInValue] = lookupOriginalPower(powerLookup, fsym, ropAtten, pamLevel)
ropValue = NaN;
pdInValue = NaN;
if ~powerLookup.enabled || isempty(powerLookup.data)
return
end
match = powerLookup.data.fsym == fsym & ...
powerLookup.data.rop_atten == ropAtten & ...
powerLookup.data.M == pamLevel;
if ~any(match)
return
end
firstMatch = find(match, 1, "first");
ropValue = powerLookup.data.rop(firstMatch);
pdInValue = powerLookup.data.pd_in(firstMatch);
end
function sig = loadRxSignal(rxFile, acquisitionIdx)
rxData = load(rxFile, "S");
if ~iscell(rxData.S) || isempty(rxData.S)
error("RX file %s does not contain a nonempty cell array S.", rxFile);
end
if acquisitionIdx > numel(rxData.S)
error("Requested acquisition S{%d}, but %s only contains %d acquisitions.", ...
acquisitionIdx, rxFile, numel(rxData.S));
end
sig = rxData.S{acquisitionIdx};
end
function token = ropToken(rop)
token = strrep(sprintf("%.1f", rop), ".", "_");
token = regexprep(token, "_0$", "");
end
function dataTable = makeRecipeDataTable(job)
dataTable = table();
dataTable.run_id = makeRunId(job.M, job.fsym, job.rop_atten, job.acquisition_idx);
dataTable.pam_level = job.M;
dataTable.symbolrate = job.fsym;
dataTable.bitrate = job.fsym .* log2(job.M);
dataTable.grossrate = dataTable.bitrate;
dataTable.fiber_length = 0;
dataTable.wavelength = 1310;
dataTable.rop_attenuation = job.rop_atten;
dataTable.acquisition_idx = job.acquisition_idx;
dataTable.rx_file = string(job.rx_file);
end
function runId = makeRunId(M, fsym, rop, acquisitionIdx)
runId = M .* 1e10 + round(fsym .* 1e-6) .* 1e2 + ...
round(rop .* 10) .* 10 + acquisitionIdx;
end
function ber = extractBer(dspOut, packageName, metricName)
ber = NaN;
if ~isfield(dspOut, packageName)
return
end
pkg = dspOut.(packageName);
if ~isfield(pkg, "metrics")
return
end
if isobject(pkg.metrics) && isprop(pkg.metrics, metricName)
ber = pkg.metrics.(metricName);
elseif isstruct(pkg.metrics) && isfield(pkg.metrics, metricName)
ber = pkg.metrics.(metricName);
end
end
function ok = ensureParallelPool()
ok = false;
try
pool = gcp("nocreate");
if ~isempty(pool) && contains(string(class(pool)), "ThreadPool")
delete(pool);
pool = [];
end
if isempty(pool)
pool = parpool("local");
end
ok = ~isempty(pool);
catch err
warning("reprocess:NoParallelPool", ...
"Parallel pool unavailable, falling back to serial processing: %s", ...
err.message);
end
end

View File

@@ -0,0 +1,97 @@
% === 400G DSP settings ===
dsp_options = struct();
dsp_options.mode = "run_id";
dsp_options.recipe = @dsp_400g_recipe;
dsp_options.append_to_db = false;
% dsp_options.append_mpi_reduction_db = false;
dsp_options.start_occurence = 1;
dsp_options.max_occurences = 3;
dsp_options.debug_plots = false;
dsp_options.database_type = "mysql";
dsp_options.dataBase = "labor_highspeed";
if ismac
dsp_options.storage_path = "/Volumes/media/labdata/sioe_labor";
else
dsp_options.storage_path = "W:\labdata\sioe_labor";
end
dsp_options.server = "192.168.178.192";
dsp_options.port = 3306;
dsp_options.user = "silas";
dsp_options.password = "silas";
db = DBHandler("dataBase", [dsp_options.dataBase], ...
"type", dsp_options.database_type, ...
"server", dsp_options.server, ...
"user", dsp_options.user, ...
"password", dsp_options.password);
%% Load normal Signal w/o preemphasis
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 10);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','bitrate','EQUALS', 420e9);
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, query] = db.queryDB(fp, fields);
[~, Symbols_preemph, Scpe_cell_preemph, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
ScopeSignal = Scpe_cell_preemph{1};
ScopeSignal_preemph = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs);
%% Load normal Signal w/o preemphasis
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 10);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','bitrate','EQUALS', 420e9);
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', 1);
fields = db.getTableFieldNames('Runs');
[dataTable, query] = db.queryDB(fp, fields);
[~, Symbols, Scpe_cell, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
ScopeSignal = Scpe_cell{1};
ScopeSignal_no_preemph = preprocessSignal(ScopeSignal, Symbols, Symbols.fs);
%% Duobinary
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 10);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','bitrate','EQUALS', 420e9);
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', 2);
fields = db.getTableFieldNames('Runs');
[dataTable, query] = db.queryDB(fp, fields);
[~, Symbols_db, Scpe_cell_db, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
ScopeSignal = Scpe_cell_db{1};
ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs);
%%
ScopeSignal_no_preemph.spectrum("displayname",'Full Response w/o preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dblue);
ScopeSignal_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dgreen);
ScopeSignal_DB.spectrum("displayname",'DB Response w/ preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dorange);

View File

@@ -0,0 +1,115 @@
rates = [420e9];
rcalpha = 0.05;
fsym = rates/2;
apply_pulsef = 1;
M = 4;
%% Normal Tx Signal
duob_mode = db_mode.no_db;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
[Digi_sig,~,~] = PAMsource(...
"fsym",fsym,"M",M,"order",21,"useprbs",0,...
"fs_out",256e9,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",1,...
'duobinary_mode',duob_mode,...
"mrds_code",0,"mrds_blocklength",512).process();
Digi_sig= Digi_sig.normalize("mode","oneone");
Digi_sig = Digi_sig-mean(Digi_sig.signal);
maxamp = -28; %optimized for DB!
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
precomp_path = "W:\labdata\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.signal = reshape(Digi_sig_pre.signal,[],1);
Digi_sig_pre = Digi_sig_pre.resample("fs_out",256e9);
Digi_sig_pre= Digi_sig_pre.normalize("mode","oneone");
Digi_sig_pre = Digi_sig_pre-mean(Digi_sig_pre.signal);
% precomp_est.plot
Digi_sig_rx = precomp_est.apply(...
Digi_sig,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig_pre_rx = precomp_est.apply(...
Digi_sig_pre,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig.spectrum(...
"displayname",'Tx w/o pre-emphasis',...
"fignum",2,"normalizeTo0dB",0,"color",[0,0,0]);
Digi_sig_pre.spectrum(...
"displayname",'Tx w/ pre-emphasis',...
"fignum",2,"normalizeTo0dB",0,"color",clr.Paired.lblue);
Digi_sig_rx.spectrum(...
"displayname",'Rx w/o pre-emphasis',...
"fignum",2,"normalizeTo0dB",0,"color",clr.Paired.dred);
Digi_sig_pre_rx.spectrum(...
"displayname",'Rx w/ pre-emphasis',...
"fignum",2,"normalizeTo0dB",0,"color",clr.Paired.dblue);
%% Duobinary Encoded Tx Signal
duob_mode = db_mode.db_encoded;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
[Digi_sig_DB,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",21,"useprbs",0,...
"fs_out",256e9,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",apply_pulsef,"pulseformer",Pform,...
"randkey",1,...
'duobinary_mode',duob_mode,...
"mrds_code",0,"mrds_blocklength",512).process();
Digi_sig_DB= Digi_sig_DB.normalize("mode","oneone");
maxamp = -38; %optimized for DB!
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig_DB.fs);
precomp_path = "W:\labdata\sioe_labor\precomp";
precomp_fn = "lab_high_speed";
Digi_sig_DB_pre = precomp_est.precomp(Digi_sig_DB,'maxampdb',maxamp,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig_DB_pre.signal = reshape(Digi_sig_DB_pre.signal,[],1);
Digi_sig_DB_pre = Digi_sig_DB_pre.resample("fs_out",256e9);
Digi_sig_DB_pre = Digi_sig_DB_pre.normalize("mode","oneone");
%%
AWG_ = M8199B("kover",4);
% AWG_ = AWG("fdac",256e9,"f_cutoff",fsym,"lpf_active",0,"kover",4,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1);
El_sig = AWG_.process(Digi_sig);
El_sig_preemph = AWG_.process(Digi_sig_pre);
El_sig_channel = AWG_.process(Digi_sig_channel);
El_sig.spectrum("displayname",'Full Response w/o preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.lblue);
El_sig_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.dblue);
El_sig_channel.spectrum("displayname",'Full Response w/ measured channel','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.blue);
El_sig_DB_preemph = AWG_.process(Digi_sig_DB_pre);
El_sig_DB = AWG_.process(Digi_sig_DB);
El_sig_DB.spectrum("displayname",'DB Response w/o preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.lorange);
El_sig_DB_preemph.spectrum("displayname",'DB Response w/ preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.dorange);

Binary file not shown.

View File

@@ -5,9 +5,11 @@ dsp_options.recipe = @dsp_400g_recipe;
dsp_options.append_to_db = false;
% dsp_options.append_mpi_reduction_db = false;
dsp_options.start_occurence = 1;
dsp_options.max_occurences = 1;
dsp_options.max_occurences = 3;
dsp_options.debug_plots = false;
dsp_options.database_type = "mysql";
dsp_options.dataBase = "labor_highspeed";
@@ -34,15 +36,15 @@ maxRunIds = 1; % keep small until the recipe settings are sett
fp = QueryFilter();
fp.where('Runs','fiber_length','EQUALS', 10);
fp.where('Runs','wavelength','EQUALS', 1310);
fp.where('Runs','bitrate','EQUALS', 330e9);
fp.where('Runs','pam_level','EQUALS', 4);
% fp.where('Runs','bitrate','LESS_THAN', 480e9);
% 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', 2);
% fp.where('Runs', 'db_mode','EQUALS', 2);
fields = db.getTableFieldNames('Runs');
[dataTable, query] = db.queryDB(fp, fields);
disp(query);
% disp(query);
dataTable = sortrows(dataTable, {'bitrate', 'run_id'});
@@ -60,21 +62,12 @@ fprintf("Selected %d run_id(s): %s\n", numel(run_ids), mat2str(run_ids));
%% Parameter sweep
dsp_options.userParameters = struct();
dsp_options.userParameters.run_ml_mlse_db = true;
dsp_options.userParameters.run_mlse_db = true;
% Enable/disable equalizer branches.
% dsp_options.userParameters.run_ffe = false;
% dsp_options.userParameters.run_vnle = false;
% dsp_options.userParameters.run_dfe = false;
% dsp_options.userParameters.run_vnle_mlse = true;
% dsp_options.userParameters.run_dbtgt = true;
% Examples for parameter loops. DataStorage expands every vector-valued field.
% dsp_options.userParameters.len_tr = 4096*2;
% dsp_options.userParameters.pf_ncoeffs = 1;
% dsp_options.userParameters.pf_ncoeffs = [1,2,3];
% dsp_options.userParameters.decoding_mode = [db_decoder.memoryless,db_decoder.sequencedetection];
dsp_options.userParameters.decoding_mode = [db_decoder.sequencedetection];
% dsp_options.userParameters.pf_ncoeffs = [1, 2, 3];
% dsp_options.userParameters.mu_dc = [0, 1e-5, 1e-4];
% dsp_options.userParameters.run_ml_mlse_db = [false, true];
@@ -93,7 +86,7 @@ fprintf("-> [ %d run_id(s) x %d userParam combination(s) = %d job(s) ] x %d real
%% Run
[results, wh] = submitJobs(run_ids, dsp_options, processingMode.serial, ...
[results, wh] = submitJobs(run_ids, dsp_options, processingMode.parallel, ...
"wh", wh, ...
"waitbar", true);
@@ -102,6 +95,7 @@ fprintf("-> [ %d run_id(s) x %d userParam combination(s) = %d job(s) ] x %d real
printBerSummary(wh);
plotBerVsBitrateQuick(wh, dataTable);
plotMlseBerPrecodedVsBitrateByPf(wh, dataTable);
function printBerSummary(wh)
storageNames = fieldnames(wh.sto);
@@ -182,7 +176,7 @@ for storageIdx = 1:numel(storageNames)
end
modeData = sortrows(plotData(rowMask, :), "bitrate_Gbps");
summaryData = groupsummary(modeData, "bitrate_Gbps", "median", "BER");
summaryData = groupsummary(modeData, "bitrate_Gbps", "min", "BER");
summaryData = sortrows(summaryData, "bitrate_Gbps");
color = quickPlotColor(modeIdx);
marker = markers(1 + mod(storageIdx - 1, numel(markers)));
@@ -196,7 +190,7 @@ for storageIdx = 1:numel(storageNames)
"MarkerEdgeAlpha", 0.25, ...
"HandleVisibility", "off");
plot(ax, summaryData.bitrate_Gbps, summaryData.median_BER, ...
plot(ax, summaryData.bitrate_Gbps, summaryData.min_BER, ...
"LineStyle", lineStyles(metricIdx), ...
"Marker", marker, ...
"MarkerSize", 5, ...
@@ -279,6 +273,108 @@ if ~isempty(plotData)
end
end
function plotMlseBerPrecodedVsBitrateByPf(wh, dataTable)
storageName = "mlse_package";
if ~isfield(wh.sto, storageName)
fprintf("No %s storage available for BERp plot.\n", storageName);
return
end
runIds = dataTable.run_id(:);
bitrates = dataTable.bitrate(:);
pfCol = zeros(0, 1);
bitrateCol = zeros(0, 1);
berpCol = zeros(0, 1);
storageValues = wh.sto.(storageName);
for linIdx = 1:numel(storageValues)
[phys, storedValue] = wh.getPhysAndValueByLinIndex(storageName, linIdx);
if ~isfield(phys, "pf_ncoeffs")
continue
end
berp = extractMinMetricValue(storedValue, "BER_precoded");
if ~isfinite(berp) || berp <= 0
continue
end
runId = resolveRunId(phys, runIds);
bitrate = resolveBitrate(runId, runIds, bitrates);
if ~isfinite(bitrate)
continue
end
pfCol(end+1, 1) = double(phys.pf_ncoeffs); %#ok<AGROW>
bitrateCol(end+1, 1) = double(bitrate) * 1e-9; %#ok<AGROW>
berpCol(end+1, 1) = double(berp); %#ok<AGROW>
end
if isempty(berpCol)
fprintf("No BERp values available for %s.\n", storageName);
return
end
plotData = table(pfCol, bitrateCol, berpCol, ...
'VariableNames', ["pf_ncoeffs", "bitrate_Gbps", "BERp"]);
summaryData = groupsummary(plotData, ["pf_ncoeffs", "bitrate_Gbps"], ...
"min", "BERp");
fig = figure(403); clf;
ax = axes(fig); hold(ax, "on");
pfValues = [1, 2, 3];
markers = ["o", "square", "diamond"];
colors = lines(numel(pfValues));
for pfIdx = 1:numel(pfValues)
pfValue = pfValues(pfIdx);
rowMask = summaryData.pf_ncoeffs == pfValue;
if ~any(rowMask)
continue
end
curveData = sortrows(summaryData(rowMask, :), "bitrate_Gbps");
plot(ax, curveData.bitrate_Gbps, curveData.min_BERp, ...
"LineWidth", 1.3, ...
"Marker", markers(pfIdx), ...
"MarkerSize", 5, ...
"Color", colors(pfIdx, :), ...
"DisplayName", sprintf("pf\\_ncoeffs = %d", pfValue));
end
xlabel(ax, "Bitrate [Gb/s]");
ylabel(ax, "min BERp");
title(ax, "MLSE BERp vs bitrate");
set(ax, "YScale", "log");
grid(ax, "on");
box(ax, "on");
legend(ax, "Location", "best", "Interpreter", "none");
set(fig, "Position", [1200, 450, 560, 360]);
end
function minValue = extractMinMetricValue(value, metricName)
minValue = NaN;
if isempty(value)
return
end
if ~iscell(value)
value = {value};
end
metricValues = NaN(1, numel(value));
for packageIdx = 1:numel(value)
package = value{packageIdx};
if ~isstruct(package) || ~isfield(package, "metrics")
continue
end
metricValues(packageIdx) = readMetricValue(package.metrics, metricName);
end
metricValues = metricValues(isfinite(metricValues) & metricValues > 0);
if ~isempty(metricValues)
minValue = min(metricValues);
end
end
function metricRows = extractBerMetricRows(values)
metricNames = strings(0, 1);
berValues = zeros(0, 1);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.