new plots for Diss. Mostly AI gen. Few changes in actual codebase
This commit is contained in:
158
projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m
Normal file
158
projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m
Normal 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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
384
projects/Diss/400G_revisit/PLOT_EYES_400G_REVISIT.m
Normal file
384
projects/Diss/400G_revisit/PLOT_EYES_400G_REVISIT.m
Normal 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
|
||||
@@ -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
|
||||
307
projects/Diss/400G_revisit/PLOT_MLSE_N_TAP_BER_BY_DB_MODE.m
Normal file
307
projects/Diss/400G_revisit/PLOT_MLSE_N_TAP_BER_BY_DB_MODE.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
85
projects/Diss/400G_revisit/REPLOT_OPT_FILT_ANALYSIS_BY_PAM.m
Normal file
85
projects/Diss/400G_revisit/REPLOT_OPT_FILT_ANALYSIS_BY_PAM.m
Normal 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);
|
||||
@@ -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
|
||||
97
projects/Diss/400G_revisit/RX_sprectra.m
Normal file
97
projects/Diss/400G_revisit/RX_sprectra.m
Normal 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);
|
||||
115
projects/Diss/400G_revisit/TX_spectra.m
Normal file
115
projects/Diss/400G_revisit/TX_spectra.m
Normal 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);
|
||||
BIN
projects/Diss/400G_revisit/auswertung_baudrate.mlx
Normal file
BIN
projects/Diss/400G_revisit/auswertung_baudrate.mlx
Normal file
Binary file not shown.
@@ -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);
|
||||
|
||||
BIN
projects/Diss/400G_revisit/mlse_n_tap_pam4.mat
Normal file
BIN
projects/Diss/400G_revisit/mlse_n_tap_pam4.mat
Normal file
Binary file not shown.
BIN
projects/Diss/400G_revisit/mlse_n_tap_pam4_2km.mat
Normal file
BIN
projects/Diss/400G_revisit/mlse_n_tap_pam4_2km.mat
Normal file
Binary file not shown.
BIN
projects/Diss/400G_revisit/opt_filt_analysis.fig
Normal file
BIN
projects/Diss/400G_revisit/opt_filt_analysis.fig
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
projects/Diss/400G_revisit/rop_vs_baudrate_sweep.mat
Normal file
BIN
projects/Diss/400G_revisit/rop_vs_baudrate_sweep.mat
Normal file
Binary file not shown.
Reference in New Issue
Block a user