während Diss, 400G plots
This commit is contained in:
@@ -403,7 +403,7 @@ classdef Signal
|
|||||||
% Divide by 2*pi for the f/fs axis where Nyquist is 0.5.
|
% Divide by 2*pi for the f/fs axis where Nyquist is 0.5.
|
||||||
end
|
end
|
||||||
|
|
||||||
p_lin = movmean(p_lin,20);
|
p_lin = movmean(p_lin,5);
|
||||||
|
|
||||||
if options.normalizeTo0dB
|
if options.normalizeTo0dB
|
||||||
p_lin = p_lin ./ max(p_lin);
|
p_lin = p_lin ./ max(p_lin);
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ classdef ML_MLSE < handle
|
|||||||
if sym_idx>=obj.L
|
if sym_idx>=obj.L
|
||||||
if obj.adaptive_mu
|
if obj.adaptive_mu
|
||||||
mu_eff=CE_smooth(symbol);
|
mu_eff=CE_smooth(symbol);
|
||||||
mu_eff=max(min(mu_eff,0.2),1e-4);
|
mu_eff=max(min(mu_eff,0.2),1e-5);
|
||||||
else
|
else
|
||||||
mu_eff=mu;
|
mu_eff=mu;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
|
|||||||
%% Calculate performance metrics
|
%% Calculate performance metrics
|
||||||
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
|
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
|
||||||
% [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
|
% [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
|
||||||
[gmi] = calc_ngmi(eq_signal_sd,tx_symbols);
|
[gmi,ngmi] = calc_ngmi(eq_signal_sd,tx_symbols);
|
||||||
gmi = max(gmi,0);
|
gmi = max(gmi,0);
|
||||||
|
|
||||||
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
|
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* ngmi;
|
||||||
[evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols);
|
[evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols);
|
||||||
[std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols);
|
[std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols);
|
||||||
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
|
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ if options.duob_mode ~= db_mode.db_encoded
|
|||||||
[vnle_results, equalized_signal] = runFfe(eq_vnle, "VNLE", ...
|
[vnle_results, equalized_signal] = runFfe(eq_vnle, "VNLE", ...
|
||||||
Scpe_sig, Symbols, Tx_bits, options);
|
Scpe_sig, Symbols, Tx_bits, options);
|
||||||
vnle_results.config.equalizer_structure = equalizer_structure.ffe;
|
vnle_results.config.equalizer_structure = equalizer_structure.ffe;
|
||||||
ffe_results.recipe_config = collectRecipeConfig("vnle", eq_ffe, p, options);
|
ffe_results.recipe_config = collectRecipeConfig("vnle", eq_vnle, p, options);
|
||||||
output.vnle_package = vnle_results;
|
output.vnle_package = vnle_results;
|
||||||
|
|
||||||
if p.plot_output_signals
|
if p.plot_output_signals
|
||||||
@@ -273,14 +273,14 @@ end
|
|||||||
function p = defaultRecipeParameters()
|
function p = defaultRecipeParameters()
|
||||||
p = struct();
|
p = struct();
|
||||||
|
|
||||||
p.run_ffe = false;
|
p.run_ffe = 0;
|
||||||
p.run_vnle = false;
|
p.run_vnle = 0;
|
||||||
p.run_dfe = false;
|
p.run_dfe = 0;
|
||||||
p.run_vnle_mlse = true;
|
p.run_vnle_mlse = 1;
|
||||||
p.run_dbtgt = true;
|
p.run_dbtgt = 0;
|
||||||
p.run_ml_mlse = false; % non-encoded and precoded branches
|
p.run_ml_mlse = 0; % non-encoded and precoded branches
|
||||||
p.run_ml_mlse_db = false; % db_encoded: ML-based MLSE
|
p.run_ml_mlse_db = 0; % db_encoded: ML-based MLSE
|
||||||
p.run_mlse_db = true; % db_encoded: conventional MLSE
|
p.run_mlse_db = 1; % db_encoded: conventional MLSE
|
||||||
|
|
||||||
|
|
||||||
p.preprocess_mode = "auto";
|
p.preprocess_mode = "auto";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
for M = [4]
|
for M = [6]
|
||||||
bits = Signalgenerator("form", signalform.prms,"M", M,"order", 18).process();
|
bits = Signalgenerator("form", signalform.prms,"M", M,"order", 18).process();
|
||||||
mapper = PAMmapper(M,0);
|
mapper = PAMmapper(M,0);
|
||||||
symbols = mapper.map(bits);
|
symbols = mapper.map(bits);
|
||||||
@@ -13,7 +13,7 @@ for M = [4]
|
|||||||
symbols_db_ = awgn_channel(symbols_db,"snr_dB",15);
|
symbols_db_ = awgn_channel(symbols_db,"snr_dB",15);
|
||||||
figure(1003042);
|
figure(1003042);
|
||||||
showLevelHistogram(symbols_db_,symbols_db,"ref_symbol_uncoded",symbols);
|
showLevelHistogram(symbols_db_,symbols_db,"ref_symbol_uncoded",symbols);
|
||||||
showLevelHistogram(symbols_db_,symbols_db);
|
% showLevelHistogram(symbols_db_,symbols_db);
|
||||||
|
|
||||||
symbols_rx = db.decode(symbols_db);
|
symbols_rx = db.decode(symbols_db);
|
||||||
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
||||||
|
|||||||
@@ -16,13 +16,14 @@ for i = 1:length(N_)
|
|||||||
[Mndg,Mdg] = getProducts(N_(i));
|
[Mndg,Mdg] = getProducts(N_(i));
|
||||||
total(i) = sum(Mndg) + sum(Mdg);
|
total(i) = sum(Mndg) + sum(Mdg);
|
||||||
subplot(1,length(N_),i)
|
subplot(1,length(N_),i)
|
||||||
|
fprintf('%d Channel: Non degenerate: %d; Degenerate: %d \n', N_(i),sum(Mndg),sum(Mdg));
|
||||||
xline(calcWavelengthPlan(N_(i), df_hz, center_nm));
|
xline(calcWavelengthPlan(N_(i), df_hz, center_nm));
|
||||||
hold on
|
hold on
|
||||||
stem(channelplan_nm,(Mndg+Mdg),'filled','LineWidth',1,'Marker','o','MarkerSize',2)
|
stem(channelplan_nm,(Mndg+Mdg),'filled','LineWidth',1,'Marker','o','MarkerSize',2)
|
||||||
stem(channelplan_nm,(Mdg),'filled','LineWidth',1,'Marker','none');
|
stem(channelplan_nm,(Mdg),'filled','LineWidth',1,'Marker','none');
|
||||||
ylim([0,100])
|
ylim([0,100])
|
||||||
xlim([1260, 1365]);
|
xlim([1260, 1365]);
|
||||||
grid off
|
% grid off
|
||||||
xlabel('O-band wavelength region in nm');
|
xlabel('O-band wavelength region in nm');
|
||||||
ylabel('Number of FWM products');
|
ylabel('Number of FWM products');
|
||||||
title([num2str(N_(i)),' ch.'])
|
title([num2str(N_(i)),' ch.'])
|
||||||
|
|||||||
@@ -0,0 +1,458 @@
|
|||||||
|
%% Final thesis figure: best EQ curves for NGMI, AIR and FEC rates
|
||||||
|
% Four-panel thesis view based on FIGURE_NGMI.m:
|
||||||
|
% a) NGMI
|
||||||
|
% b) AIR
|
||||||
|
% c) SD+HD FEC NDR
|
||||||
|
% d) O-FEC and KP4+Hamming NDR
|
||||||
|
%
|
||||||
|
% The normal EQ classes use the 2 km data set. DBS + VNLE + MLSE uses the
|
||||||
|
% post-2026 10 km data set because that is the available DBS measurement set.
|
||||||
|
% Within each selected EQ/PAM curve, the best valid result is retained for
|
||||||
|
% every symbol rate. The PAM mapping follows the reference thesis figure:
|
||||||
|
% VNLE -> PAM-8
|
||||||
|
% VNLE + PF + MLSE -> PAM-6
|
||||||
|
% VNLE DBt. + MLSE -> PAM-4
|
||||||
|
% ML pre-EQ + Viterbi -> PAM-4
|
||||||
|
% DBS + VNLE + MLSE -> PAM-4 and PAM-6
|
||||||
|
|
||||||
|
clear;
|
||||||
|
|
||||||
|
%% 1) Configuration
|
||||||
|
|
||||||
|
normalFiberLengthKm = 2;
|
||||||
|
duobinaryFiberLengthKm = 2;
|
||||||
|
selectedWavelengthNm = 1310;
|
||||||
|
selectedRopAttenuation = 0;
|
||||||
|
selectedIsMpi = 0;
|
||||||
|
duobinaryDateCutoff = datetime("2026-01-01 00:00:00");
|
||||||
|
|
||||||
|
% Thesis colors and curvet /PAM mapping.
|
||||||
|
curves = struct;
|
||||||
|
curves(1).name = "VNLE + PF + MLSE";
|
||||||
|
curves(1).algorithm_key = "vnle_pf_mlse";
|
||||||
|
curves(1).pam = 8;
|
||||||
|
curves(1).color = clr.Paired.red;
|
||||||
|
curves(1).marker = "o";
|
||||||
|
curves(1).source = "normal";
|
||||||
|
curves(1).usePrecodedBer = false;
|
||||||
|
|
||||||
|
curves(2).name = "VNLE + PF + MLSE";
|
||||||
|
curves(2).algorithm_key = "vnle_pf_mlse";
|
||||||
|
curves(2).pam = 6;
|
||||||
|
curves(2).color = clr.Paired.green;
|
||||||
|
curves(2).marker = "square";
|
||||||
|
curves(2).source = "normal";
|
||||||
|
curves(2).usePrecodedBer = false;
|
||||||
|
|
||||||
|
curves(3).name = "VNLE DBt. + MLSE";
|
||||||
|
curves(3).algorithm_key = "vnle_db_mlse";
|
||||||
|
curves(3).pam = 4;
|
||||||
|
curves(3).color = clr.Paired.blue;
|
||||||
|
curves(3).marker = "diamond";
|
||||||
|
curves(3).source = "normal";
|
||||||
|
curves(3).usePrecodedBer = true;
|
||||||
|
|
||||||
|
curves(4).name = "ML pre-EQ + Viterbi";
|
||||||
|
curves(4).algorithm_key = "ml_mlse";
|
||||||
|
curves(4).pam = 4;
|
||||||
|
curves(4).color = clr.Paired.purple;
|
||||||
|
curves(4).marker = "^";
|
||||||
|
curves(4).source = "normal";
|
||||||
|
curves(4).usePrecodedBer = true;
|
||||||
|
|
||||||
|
curves(5).name = "DBS + VNLE + MLSE (PAM-4)";
|
||||||
|
curves(5).algorithm_key = "db_encoded";
|
||||||
|
curves(5).pam = 4;
|
||||||
|
curves(5).color = clr.Paired.orange;
|
||||||
|
curves(5).marker = "v";
|
||||||
|
curves(5).source = "duobinary";
|
||||||
|
curves(5).usePrecodedBer = false;
|
||||||
|
|
||||||
|
curves(6).name = "DBS + VNLE + MLSE (PAM-6)";
|
||||||
|
curves(6).algorithm_key = "db_encoded";
|
||||||
|
curves(6).pam = 6;
|
||||||
|
curves(6).color = clr.Paired.orange;
|
||||||
|
curves(6).marker = "v";
|
||||||
|
curves(6).source = "duobinary";
|
||||||
|
curves(6).usePrecodedBer = false;
|
||||||
|
|
||||||
|
%% 2) Query normal 2 km and duobinary 10 km data
|
||||||
|
|
||||||
|
db = DBHandler( ...
|
||||||
|
"dataBase", "labor_highspeed", ...
|
||||||
|
"type", "mysql", ...
|
||||||
|
"server", "192.168.178.192", ...
|
||||||
|
"user", "silas", ...
|
||||||
|
"password", "silas");
|
||||||
|
db.refresh();
|
||||||
|
|
||||||
|
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
|
||||||
|
|
||||||
|
normalRows = queryMeasurementRows(db, selectedFields, normalFiberLengthKm,selectedWavelengthNm);
|
||||||
|
normalRows = cleanMeasurementRows(normalRows);
|
||||||
|
normalRows = normalRows(ismember(normalRows.db_mode, ...
|
||||||
|
[double(db_mode.no_db), double(db_mode.db_precoded)]), :);
|
||||||
|
normalRows.algorithm_key = lower(string(normalRows.equalizer_structure));
|
||||||
|
|
||||||
|
duobinaryRows = queryMeasurementRows(db, selectedFields, duobinaryFiberLengthKm,selectedWavelengthNm);
|
||||||
|
duobinaryRows = cleanMeasurementRows(duobinaryRows);
|
||||||
|
duobinaryRows = duobinaryRows(duobinaryRows.db_mode == double(db_mode.db_encoded), :);
|
||||||
|
duobinaryRows = duobinaryRows( ...
|
||||||
|
equalizerMask(duobinaryRows.equalizer_structure, ...
|
||||||
|
equalizer_structure.db_encoded), :);
|
||||||
|
|
||||||
|
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)>duobinaryDateCutoff,:);
|
||||||
|
duobinaryRows.date_of_processing = string(duobinaryRows.date_of_processing);
|
||||||
|
else
|
||||||
|
warning("figure_ngmi_thesis:NoProcessingDate", ...
|
||||||
|
"date_of_processing was not returned; no DBS date filter was applied.");
|
||||||
|
end
|
||||||
|
% DBS PAM-8 is intentionally excluded from the final thesis figure.
|
||||||
|
duobinaryRows = duobinaryRows(ismember(duobinaryRows.pam_level, [4 6]), :);
|
||||||
|
duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1);
|
||||||
|
|
||||||
|
fprintf("Normal %g km rows: %d\n", normalFiberLengthKm, height(normalRows));
|
||||||
|
fprintf("DBS %g km rows after date filter: %d\n", ...
|
||||||
|
duobinaryFiberLengthKm, height(duobinaryRows));
|
||||||
|
|
||||||
|
%% 3) Extract the best curves and calculate FEC rates
|
||||||
|
|
||||||
|
tp = TransmissionPerformance;
|
||||||
|
results = repmat(emptyResult(), numel(curves), 1);
|
||||||
|
|
||||||
|
for curveIdx = 1:numel(curves)
|
||||||
|
curve = curves(curveIdx);
|
||||||
|
|
||||||
|
if curve.source == "normal"
|
||||||
|
curveRows = normalRows;
|
||||||
|
else
|
||||||
|
curveRows = duobinaryRows;
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows = curveRows(curveRows.pam_level == curve.pam & ...
|
||||||
|
curveRows.algorithm_key == curve.algorithm_key, :);
|
||||||
|
|
||||||
|
if isempty(curveRows)
|
||||||
|
warning("figure_ngmi_thesis:NoCurveRows", ...
|
||||||
|
"No rows found for %s, PAM-%d.", curve.name, curve.pam);
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows.BER_plot = curveRows.BER;
|
||||||
|
curveRows.precode = zeros(height(curveRows), 1);
|
||||||
|
if curve.usePrecodedBer && ismember("BER_precoded", ...
|
||||||
|
string(curveRows.Properties.VariableNames))
|
||||||
|
usePrecoded = isfinite(curveRows.BER_precoded);
|
||||||
|
curveRows.BER_plot(usePrecoded) = curveRows.BER_precoded(usePrecoded);
|
||||||
|
curveRows.precode(usePrecoded) = 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows = addDerivedMetrics(curveRows);
|
||||||
|
|
||||||
|
% Each metric is optimized independently. In particular, the BER
|
||||||
|
% winner does not have to be the NGMI or AIR winner for a given baud
|
||||||
|
% rate because these quantities are stored/calculated separately.
|
||||||
|
berSeries = bestSeries(curveRows, "BER_plot");
|
||||||
|
ngmiSeries = bestSeries(curveRows, "NGMI");
|
||||||
|
airSeries = bestSeries(curveRows, "AIR_Gbps");
|
||||||
|
|
||||||
|
grossRate = double(curveRows.grossrate);
|
||||||
|
measuredNgmi = double(curveRows.NGMI);
|
||||||
|
measuredBer = double(curveRows.BER_plot);
|
||||||
|
measuredNgmi(~isfinite(measuredNgmi) | measuredNgmi < 0 | ...
|
||||||
|
measuredNgmi > 1.05) = NaN;
|
||||||
|
measuredBer(~isfinite(measuredBer) | measuredBer <= 0 | ...
|
||||||
|
measuredBer > 0.5) = NaN;
|
||||||
|
|
||||||
|
ndr = tp.calculateNetRate(grossRate, ...
|
||||||
|
"NGMI", measuredNgmi, ...
|
||||||
|
"BER", measuredBer);
|
||||||
|
|
||||||
|
curveRows.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9;
|
||||||
|
curveRows.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9;
|
||||||
|
curveRows.NDR_KP4_HAMMING = columnVector(ndr.KP4_hamming.NetRate) .* 1e-9;
|
||||||
|
|
||||||
|
results(curveIdx).name = curve.name;
|
||||||
|
results(curveIdx).color = curve.color;
|
||||||
|
results(curveIdx).marker = curve.marker;
|
||||||
|
results(curveIdx).pam = curve.pam;
|
||||||
|
results(curveIdx).ber = berSeries;
|
||||||
|
results(curveIdx).ngmi = ngmiSeries;
|
||||||
|
results(curveIdx).air = airSeries;
|
||||||
|
results(curveIdx).sdhd = bestSeries(curveRows, "NDR_SDHD");
|
||||||
|
results(curveIdx).ofec = bestSeries(curveRows, "NDR_O_FEC");
|
||||||
|
results(curveIdx).kp4Hamming = bestSeries(curveRows, "NDR_KP4_HAMMING");
|
||||||
|
|
||||||
|
fprintf("%s, PAM-%d: BER=%d, NGMI=%d, AIR=%d, SD+HD=%d, O-FEC=%d, KP4+Hamming=%d points\n", ...
|
||||||
|
curve.name, curve.pam, ...
|
||||||
|
numel(results(curveIdx).ber.x), ...
|
||||||
|
numel(results(curveIdx).ngmi.x), ...
|
||||||
|
numel(results(curveIdx).air.x), ...
|
||||||
|
numel(results(curveIdx).sdhd.x), ...
|
||||||
|
numel(results(curveIdx).ofec.x), ...
|
||||||
|
numel(results(curveIdx).kp4Hamming.x));
|
||||||
|
end
|
||||||
|
|
||||||
|
%% 4) Four-panel thesis figure
|
||||||
|
|
||||||
|
fig = figure(72+normalFiberLengthKm); clf;
|
||||||
|
t = tiledlayout(fig, 1, 4, ...
|
||||||
|
"TileSpacing", "compact", ...
|
||||||
|
"Padding", "compact");
|
||||||
|
|
||||||
|
axNgmi = nexttile(t, 1);
|
||||||
|
hold(axNgmi, "on");
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
if isempty(results(curveIdx).ngmi.x)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
plotSeries(axNgmi, results(curveIdx).ngmi, ...
|
||||||
|
results(curveIdx), "-", results(curveIdx).marker, results(curveIdx).name);
|
||||||
|
end
|
||||||
|
formatAxis(axNgmi, "NGMI", [0.90 1.00], [100 220]);
|
||||||
|
title(axNgmi, "a) NGMI");
|
||||||
|
|
||||||
|
axAir = nexttile(t, 2);
|
||||||
|
hold(axAir, "on");
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
if isempty(results(curveIdx).air.x)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
plotSeries(axAir, results(curveIdx).air, results(curveIdx), ...
|
||||||
|
"-", results(curveIdx).marker, results(curveIdx).name);
|
||||||
|
end
|
||||||
|
formatAxis(axAir, "AIR [Gb/s]", [280 440], [100 220]);
|
||||||
|
yline(axAir, 400, "--", "Color", [0.25 0.25 0.25], ...
|
||||||
|
"HandleVisibility", "off");
|
||||||
|
title(axAir, "b) AIR");
|
||||||
|
|
||||||
|
axSdhd = nexttile(t, 3);
|
||||||
|
hold(axSdhd, "on");
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
if isempty(results(curveIdx).sdhd.x)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
plotSeries(axSdhd, results(curveIdx).sdhd, results(curveIdx), ...
|
||||||
|
"-", results(curveIdx).marker, results(curveIdx).name);
|
||||||
|
end
|
||||||
|
formatAxis(axSdhd, "NDR [Gb/s]", [280 430], [100 220]);
|
||||||
|
yline(axSdhd, 400, "--", "Color", [0.25 0.25 0.25], ...
|
||||||
|
"HandleVisibility", "off");
|
||||||
|
title(axSdhd, "c) SD+HD FEC");
|
||||||
|
|
||||||
|
axHd = nexttile(t, 4);
|
||||||
|
hold(axHd, "on");
|
||||||
|
hdHandles = gobjects(numel(curves), 1);
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
curve = results(curveIdx);
|
||||||
|
if ~isempty(curve.ofec.x)
|
||||||
|
hdHandles(curveIdx) = plotSeries(axHd, curve.ofec, curve, ":", curve.marker, ...
|
||||||
|
curve.name + " — O-FEC");
|
||||||
|
end
|
||||||
|
if ~isempty(curve.kp4Hamming.x)
|
||||||
|
plotSeries(axHd, curve.kp4Hamming, curve, "--", "diamond", ...
|
||||||
|
curve.name + " — KP4+Hamming");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
formatAxis(axHd, "NDR [Gb/s]", [280 430], [100 220]);
|
||||||
|
yline(axHd, 400, "--", "Color", [0.25 0.25 0.25], ...
|
||||||
|
"HandleVisibility", "off");
|
||||||
|
title(axHd, "d) O-FEC; KP4+Hamming");
|
||||||
|
|
||||||
|
validLegendHandles = hdHandles(isgraphics(hdHandles));
|
||||||
|
if ~isempty(validLegendHandles)
|
||||||
|
legend(axHd, validLegendHandles, ...
|
||||||
|
{results(isgraphics(hdHandles)).name}, ...
|
||||||
|
"Location", "southoutside", ...
|
||||||
|
"NumColumns", min(3, numel(validLegendHandles)), ...
|
||||||
|
"Interpreter", "none");
|
||||||
|
end
|
||||||
|
|
||||||
|
sgtitle(t, sprintf("Best EQ-class results, lambda=%g nm", selectedWavelengthNm));
|
||||||
|
set(fig, "Position", 1e3 .* [0.18 0.55 1.52 0.31]);
|
||||||
|
|
||||||
|
%% Local helpers
|
||||||
|
|
||||||
|
function T = queryMeasurementRows(db, fields, fiberLengthKm,selectedWavelengthNm)
|
||||||
|
fp = QueryFilter();
|
||||||
|
fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm);
|
||||||
|
% fp.where("Runs", "wavelength", "EQUALS", selectedWavelengthNm);
|
||||||
|
fp.where("Runs", "rop_attenuation", "EQUALS", 0);
|
||||||
|
fp.where("Runs", "is_mpi", "EQUALS", 0);
|
||||||
|
[T, query] = db.queryDB(fp, fields);
|
||||||
|
disp(query);
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = cleanMeasurementRows(T)
|
||||||
|
numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ...
|
||||||
|
"symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ...
|
||||||
|
"rop_attenuation", "numBits", "numBitErr", "BER", ...
|
||||||
|
"numBitErr_precoded", "BER_precoded", "GMI", "AIR", "NGMI"];
|
||||||
|
for fieldIdx = 1:numel(numericFields)
|
||||||
|
fieldName = numericFields(fieldIdx);
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
T.(char(fieldName)) = numericColumn(T.(char(fieldName)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
T.equalizer_structure = lower(string(T.equalizer_structure));
|
||||||
|
end
|
||||||
|
|
||||||
|
function result = emptyResult()
|
||||||
|
result = struct( ...
|
||||||
|
"name", "", ...
|
||||||
|
"color", [0 0 0], ...
|
||||||
|
"marker", "o", ...
|
||||||
|
"pam", NaN, ...
|
||||||
|
"ber", emptySeries(), ...
|
||||||
|
"ngmi", emptySeries(), ...
|
||||||
|
"air", emptySeries(), ...
|
||||||
|
"sdhd", emptySeries(), ...
|
||||||
|
"ofec", emptySeries(), ...
|
||||||
|
"kp4Hamming", emptySeries());
|
||||||
|
end
|
||||||
|
|
||||||
|
function series = emptySeries()
|
||||||
|
series = struct( ...
|
||||||
|
"x", [], ...
|
||||||
|
"y", [], ...
|
||||||
|
"ber", [], ...
|
||||||
|
"ngmi", [], ...
|
||||||
|
"air", [], ...
|
||||||
|
"wavelength", [], ...
|
||||||
|
"precode", [], ...
|
||||||
|
"dbMode", []);
|
||||||
|
end
|
||||||
|
|
||||||
|
function series = bestSeries(T, fieldName)
|
||||||
|
series = emptySeries();
|
||||||
|
if ~ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
values = numericColumn(T.(char(fieldName)));
|
||||||
|
valid = isfinite(values);
|
||||||
|
if strcmp(fieldName, "BER_plot")
|
||||||
|
% BER is minimized; zero is omitted because the axis is logarithmic.
|
||||||
|
valid = valid & values > 0 & values <= 0.5;
|
||||||
|
elseif strcmp(fieldName, "NGMI")
|
||||||
|
valid = valid & values >= 0 & values <= 1.05;
|
||||||
|
else
|
||||||
|
valid = valid & values >= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
candidate = T(valid, :);
|
||||||
|
values = values(valid);
|
||||||
|
if isempty(candidate)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
[groupId, ~] = findgroups(candidate.symbolrate_GBd);
|
||||||
|
keepIndex = zeros(max(groupId), 1);
|
||||||
|
for groupIdx = 1:max(groupId)
|
||||||
|
rowIndex = find(groupId == groupIdx);
|
||||||
|
if strcmp(fieldName, "BER_plot")
|
||||||
|
[~, localIndex] = min(values(rowIndex));
|
||||||
|
else
|
||||||
|
[~, localIndex] = max(values(rowIndex));
|
||||||
|
end
|
||||||
|
keepIndex(groupIdx) = rowIndex(localIndex(1));
|
||||||
|
end
|
||||||
|
|
||||||
|
candidate = candidate(keepIndex, :);
|
||||||
|
series.x = double(candidate.symbolrate_GBd);
|
||||||
|
series.y = values(keepIndex);
|
||||||
|
[series.x, order] = sort(series.x);
|
||||||
|
series.y = series.y(order);
|
||||||
|
|
||||||
|
% Preserve the complete selected database row as data-tip metadata. The
|
||||||
|
% metadata therefore belongs to the plotted point, even when BER, NGMI,
|
||||||
|
% AIR, and NDR select different rows at the same baud rate.
|
||||||
|
series.ber = numericOrNaN(candidate, "BER_plot");
|
||||||
|
series.ngmi = numericOrNaN(candidate, "NGMI");
|
||||||
|
series.air = numericOrNaN(candidate, "AIR_Gbps");
|
||||||
|
series.wavelength = numericOrNaN(candidate, "wavelength");
|
||||||
|
series.precode = numericOrNaN(candidate, "precode");
|
||||||
|
series.dbMode = numericOrNaN(candidate, "db_mode");
|
||||||
|
series.ber = series.ber(order);
|
||||||
|
series.ngmi = series.ngmi(order);
|
||||||
|
series.air = series.air(order);
|
||||||
|
series.wavelength = series.wavelength(order);
|
||||||
|
series.precode = series.precode(order);
|
||||||
|
series.dbMode = series.dbMode(order);
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = addDerivedMetrics(T)
|
||||||
|
T.symbolrate_GBd = double(T.symbolrate) .* 1e-9;
|
||||||
|
|
||||||
|
T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9;
|
||||||
|
gmi = numericOrNaN(T, "GMI");
|
||||||
|
fallbackAir = gmi .* double(T.symbolrate) .* 1e-9;
|
||||||
|
grossRate = double(T.grossrate);
|
||||||
|
useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ...
|
||||||
|
(isfinite(grossRate) & T.AIR_Gbps > grossRate .* 1.05e-9);
|
||||||
|
T.AIR_Gbps(useFallback) = fallbackAir(useFallback);
|
||||||
|
T.NGMI = numericOrNaN(T, "NGMI");
|
||||||
|
end
|
||||||
|
|
||||||
|
function h = plotSeries(ax, series, curve, lineStyle, marker, displayName)
|
||||||
|
h = plot(ax, series.x, series.y, ...
|
||||||
|
"LineStyle", lineStyle, ...
|
||||||
|
"Marker", marker, ...
|
||||||
|
"MarkerSize", 4, ...
|
||||||
|
"LineWidth", 1.35, ...
|
||||||
|
"Color", curve.color, ...
|
||||||
|
"MarkerFaceColor", curve.color, ...
|
||||||
|
"MarkerEdgeColor", curve.color, ...
|
||||||
|
"DisplayName", displayName);
|
||||||
|
h.DataTipTemplate.DataTipRows = [ ...
|
||||||
|
dataTipTextRow("X", series.x); ...
|
||||||
|
dataTipTextRow("Y", series.y); ...
|
||||||
|
dataTipTextRow("symbol rate [GBd]", series.x); ...
|
||||||
|
dataTipTextRow("BER", series.ber); ...
|
||||||
|
dataTipTextRow("NGMI", series.ngmi); ...
|
||||||
|
dataTipTextRow("AIR [Gb/s]", series.air); ...
|
||||||
|
dataTipTextRow("wavelength [nm]", series.wavelength); ...
|
||||||
|
dataTipTextRow("precoding", series.precode); ...
|
||||||
|
dataTipTextRow("db_mode", series.dbMode)];
|
||||||
|
end
|
||||||
|
|
||||||
|
function formatAxis(ax, yLabel, yLimits, xLimits)
|
||||||
|
set(ax, "FontSize", 8, "TickLabelInterpreter", "none");
|
||||||
|
xlabel(ax, "Baud rate [GBd]");
|
||||||
|
ylabel(ax, yLabel);
|
||||||
|
xlim(ax, xLimits);
|
||||||
|
xticks(ax, 100:15:220);
|
||||||
|
ylim(ax, yLimits);
|
||||||
|
grid(ax, "on");
|
||||||
|
grid(ax, "minor");
|
||||||
|
box(ax, "on");
|
||||||
|
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 = numericOrNaN(T, fieldName)
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
values = numericColumn(T.(char(fieldName)));
|
||||||
|
else
|
||||||
|
values = NaN(height(T), 1);
|
||||||
|
end
|
||||||
|
values = values(:);
|
||||||
|
end
|
||||||
|
|
||||||
|
function values = columnVector(values)
|
||||||
|
values = double(values(:));
|
||||||
|
end
|
||||||
|
|
||||||
|
function mask = equalizerMask(equalizerColumn, eqValue)
|
||||||
|
mask = lower(string(equalizerColumn)) == lower(string(eqValue));
|
||||||
|
end
|
||||||
@@ -161,6 +161,7 @@ for ti = 1:3
|
|||||||
grid minor; box on;
|
grid minor; box on;
|
||||||
beautifyBERplot;
|
beautifyBERplot;
|
||||||
yline(400,'HandleVisibility','off');
|
yline(400,'HandleVisibility','off');
|
||||||
|
legend
|
||||||
end
|
end
|
||||||
|
|
||||||
% === FIX FIGURE SIZE FOR TIKZ ==========================================
|
% === FIX FIGURE SIZE FOR TIKZ ==========================================
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
%% Best pre-emphasis and precoding settings at 2 km / 1310 nm
|
||||||
|
% The four requested technique labels map to the stored equalizer classes:
|
||||||
|
% VNLE -> vnle
|
||||||
|
% MLSE -> VNLE + PF + MLSE (vnle_pf_mlse)
|
||||||
|
% DB tgt. -> VNLE DBt. + MLSE (vnle_db_mlse)
|
||||||
|
% ML based -> ML pre-EQ + Viterbi (ml_mlse)
|
||||||
|
%
|
||||||
|
% For every technique and PAM format, the globally lowest valid BER is
|
||||||
|
% selected across the available gross rates, db_mode 0/1 variants, and
|
||||||
|
% BER/BER_precoded variants. The resulting table reports binary settings:
|
||||||
|
% preemph = 0/1 and precode = 0/1.
|
||||||
|
|
||||||
|
clear; clc;
|
||||||
|
|
||||||
|
%% 1) Query the 2 km / 1310 nm normal-link data
|
||||||
|
|
||||||
|
selectedFiberLengthKm = 2;
|
||||||
|
selectedWavelengthNm = 1310;
|
||||||
|
selectedRopAttenuation = 0;
|
||||||
|
selectedIsMpi = 0;
|
||||||
|
selectedPamLevels = [4 6 8];
|
||||||
|
normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)];
|
||||||
|
|
||||||
|
techniques = table( ...
|
||||||
|
["VNLE"; "MLSE"; "DB tgt."; "ML based"], ...
|
||||||
|
["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; "ml_mlse"], ...
|
||||||
|
'VariableNames', ["technique", "algorithm_key"]);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
fields = db.getTableFieldNames('dashboard_ungrouped_alltime');
|
||||||
|
fields = appendMissingFields(fields, {'Runs.precomp_amp'; 'Runs.is_mpi'});
|
||||||
|
[rawData, query] = db.queryDB(fp, fields);
|
||||||
|
disp(query);
|
||||||
|
|
||||||
|
data = cleanRows(rawData);
|
||||||
|
data = data(ismember(data.db_mode, normalDbModes) & ...
|
||||||
|
ismember(data.pam_level, selectedPamLevels), :);
|
||||||
|
|
||||||
|
if ~ismember("precomp_amp", string(data.Properties.VariableNames))
|
||||||
|
warning("check_best_settings:NoPrecompAmp", ...
|
||||||
|
"Runs.precomp_amp was not returned; using db_mode == 0 as preemph = 1.");
|
||||||
|
data.pre_emphasis = data.db_mode == double(db_mode.no_db);
|
||||||
|
else
|
||||||
|
data.pre_emphasis = derivePreEmphasis(data.precomp_amp, data.db_mode);
|
||||||
|
end
|
||||||
|
|
||||||
|
%% 2) Build BER and BER_precoded candidates
|
||||||
|
|
||||||
|
baseRows = data(isfinite(data.BER) & data.BER > 0, :);
|
||||||
|
baseRows.precode = zeros(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) & data.BER_precoded > 0, :);
|
||||||
|
precodedRows.precode = ones(height(precodedRows), 1);
|
||||||
|
precodedRows.BER_plot = precodedRows.BER_precoded;
|
||||||
|
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
|
||||||
|
precodedRows.equalizer_structure);
|
||||||
|
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
|
||||||
|
metricRows = [baseRows; precodedRows];
|
||||||
|
else
|
||||||
|
warning("check_best_settings:NoPrecodedBer", ...
|
||||||
|
"BER_precoded was not returned; only precode = 0 is available.");
|
||||||
|
metricRows = baseRows;
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf("Candidate rows: %d\n", height(metricRows));
|
||||||
|
|
||||||
|
%% 3) Find the lowest BER and its binary settings
|
||||||
|
|
||||||
|
numResults = height(techniques) * numel(selectedPamLevels);
|
||||||
|
best = repmat(struct( ...
|
||||||
|
"technique", "", ...
|
||||||
|
"algorithm_key", "", ...
|
||||||
|
"pam", NaN, ...
|
||||||
|
"BER", NaN, ...
|
||||||
|
"preemph", NaN, ...
|
||||||
|
"precode", NaN, ...
|
||||||
|
"db_mode", NaN, ...
|
||||||
|
"grossrate_Gbps", NaN, ...
|
||||||
|
"symbolrate_GBd", NaN), numResults, 1);
|
||||||
|
|
||||||
|
resultIdx = 0;
|
||||||
|
for techniqueIdx = 1:height(techniques)
|
||||||
|
for pamIdx = 1:numel(selectedPamLevels)
|
||||||
|
resultIdx = resultIdx + 1;
|
||||||
|
algorithmKey = techniques.algorithm_key(techniqueIdx);
|
||||||
|
pamLevel = selectedPamLevels(pamIdx);
|
||||||
|
candidates = metricRows( ...
|
||||||
|
metricRows.algorithm_key == algorithmKey & ...
|
||||||
|
metricRows.pam_level == pamLevel, :);
|
||||||
|
|
||||||
|
best(resultIdx).technique = techniques.technique(techniqueIdx);
|
||||||
|
best(resultIdx).algorithm_key = algorithmKey;
|
||||||
|
best(resultIdx).pam = pamLevel;
|
||||||
|
if isempty(candidates)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
[~, bestRowIdx] = min(candidates.BER_plot);
|
||||||
|
row = candidates(bestRowIdx, :);
|
||||||
|
best(resultIdx).BER = row.BER_plot;
|
||||||
|
best(resultIdx).preemph = double(row.pre_emphasis);
|
||||||
|
best(resultIdx).precode = double(row.precode);
|
||||||
|
best(resultIdx).db_mode = double(row.db_mode);
|
||||||
|
best(resultIdx).grossrate_Gbps = double(row.grossrate) .* 1e-9;
|
||||||
|
best(resultIdx).symbolrate_GBd = double(row.symbolrate) .* 1e-9;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bestRows = struct2table(best);
|
||||||
|
disp(bestRows);
|
||||||
|
|
||||||
|
%% 4) Compact tables for direct use in the thesis configuration
|
||||||
|
|
||||||
|
binaryTable = table(techniques.technique, ...
|
||||||
|
NaN(height(techniques), 1), NaN(height(techniques), 1), ...
|
||||||
|
NaN(height(techniques), 1), NaN(height(techniques), 1), ...
|
||||||
|
NaN(height(techniques), 1), NaN(height(techniques), 1), ...
|
||||||
|
'VariableNames', ["technique", ...
|
||||||
|
"PAM4_preemph", "PAM4_precode", ...
|
||||||
|
"PAM6_preemph", "PAM6_precode", ...
|
||||||
|
"PAM8_preemph", "PAM8_precode"]);
|
||||||
|
|
||||||
|
labelTable = table(techniques.technique, strings(height(techniques), 1), ...
|
||||||
|
strings(height(techniques), 1), strings(height(techniques), 1), ...
|
||||||
|
'VariableNames', ["technique", "PAM4", "PAM6", "PAM8"]);
|
||||||
|
|
||||||
|
for rowIdx = 1:height(bestRows)
|
||||||
|
techniqueIdx = find(techniques.algorithm_key == bestRows.algorithm_key(rowIdx), 1);
|
||||||
|
pamField = sprintf("PAM%d", bestRows.pam(rowIdx));
|
||||||
|
binaryTable.(sprintf("%s_preemph", pamField))(techniqueIdx) = ...
|
||||||
|
bestRows.preemph(rowIdx);
|
||||||
|
binaryTable.(sprintf("%s_precode", pamField))(techniqueIdx) = ...
|
||||||
|
bestRows.precode(rowIdx);
|
||||||
|
|
||||||
|
if isfinite(bestRows.BER(rowIdx))
|
||||||
|
labelTable.(pamField)(techniqueIdx) = sprintf( ...
|
||||||
|
"preemph=%d, precode=%d (BER=%.3g)", ...
|
||||||
|
bestRows.preemph(rowIdx), bestRows.precode(rowIdx), ...
|
||||||
|
bestRows.BER(rowIdx));
|
||||||
|
else
|
||||||
|
labelTable.(pamField)(techniqueIdx) = "no valid BER";
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
disp("Binary setting table:");
|
||||||
|
disp(binaryTable);
|
||||||
|
disp("Settings with winning BER:");
|
||||||
|
disp(labelTable);
|
||||||
|
|
||||||
|
%% 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 T = cleanRows(T)
|
||||||
|
numericFields = ["result_id", "run_id", "eq_id", "bitrate", ...
|
||||||
|
"grossrate", "symbolrate", "pam_level", "wavelength", ...
|
||||||
|
"fiber_length", "db_mode", "rop_attenuation", "precomp_amp", ...
|
||||||
|
"numBits", "numBitErr", "BER", "numBitErr_precoded", ...
|
||||||
|
"BER_precoded", "GMI", "AIR", "NGMI"];
|
||||||
|
for fieldIdx = 1:numel(numericFields)
|
||||||
|
fieldName = numericFields(fieldIdx);
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
T.(char(fieldName)) = numericColumn(T.(char(fieldName)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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 algorithmKey = algorithmKeyFromEqualizer(equalizerColumn)
|
||||||
|
eqNumeric = equalizerNumeric(equalizerColumn);
|
||||||
|
algorithmKey = strings(size(eqNumeric));
|
||||||
|
algorithmKey(eqNumeric == double(equalizer_structure.vnle)) = "vnle";
|
||||||
|
algorithmKey(eqNumeric == double(equalizer_structure.vnle_pf_mlse)) = ...
|
||||||
|
"vnle_pf_mlse";
|
||||||
|
algorithmKey(eqNumeric == double(equalizer_structure.vnle_db_mlse)) = ...
|
||||||
|
"vnle_db_mlse";
|
||||||
|
algorithmKey(eqNumeric == double(equalizer_structure.ml_mlse)) = "ml_mlse";
|
||||||
|
end
|
||||||
|
|
||||||
|
function eqNumeric = equalizerNumeric(equalizerColumn)
|
||||||
|
if isa(equalizerColumn, "equalizer_structure") || isnumeric(equalizerColumn)
|
||||||
|
eqNumeric = double(equalizerColumn);
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
equalizerString = string(equalizerColumn);
|
||||||
|
eqNumeric = str2double(equalizerString);
|
||||||
|
enumNames = ["vnle", "vnle_pf_mlse", "vnle_db_mlse", "ml_mlse"];
|
||||||
|
enumValues = [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(enumNames)
|
||||||
|
missingNumeric = isnan(eqNumeric);
|
||||||
|
eqNumeric(missingNumeric & equalizerString == enumNames(idx)) = ...
|
||||||
|
enumValues(idx);
|
||||||
|
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
|
||||||
@@ -35,10 +35,6 @@ fp.where('Runs', 'wavelength', SqlOperator.EQUALS, wavelength);
|
|||||||
fp.where('Runs', 'is_mpi', SqlOperator.EQUALS, is_mpi);
|
fp.where('Runs', 'is_mpi', SqlOperator.EQUALS, is_mpi);
|
||||||
fp.where('Runs', 'db_mode', 'LESS_THAN', 2);
|
fp.where('Runs', 'db_mode', 'LESS_THAN', 2);
|
||||||
fp.where('Runs', 'rop_attenuation', SqlOperator.EQUALS, rop_attenuation);
|
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 = [db.getTableFieldNames('dashboard_ungrouped_alltime')];
|
||||||
fields = unique(fields, 'stable');
|
fields = unique(fields, 'stable');
|
||||||
@@ -65,6 +61,12 @@ for runIndex = 1:numel(run_ids)
|
|||||||
runData = queryRunid(run_ids(runIndex), db);
|
runData = queryRunid(run_ids(runIndex), db);
|
||||||
fsym = double(runData.symbolrate(1));
|
fsym = double(runData.symbolrate(1));
|
||||||
bitrate = double(runData.bitrate(1));
|
bitrate = double(runData.bitrate(1));
|
||||||
|
symbolrate = double(runData.symbolrate(1));
|
||||||
|
if double(runData.db_mode) == 0 || double(runData.db_mode) == 2
|
||||||
|
ispreemph = 1;
|
||||||
|
elseif double(runData.db_mode) == 1
|
||||||
|
ispreemph = 0;
|
||||||
|
end
|
||||||
|
|
||||||
dsp_options.max_occurences = 20;
|
dsp_options.max_occurences = 20;
|
||||||
dsp_options.start_occurence = 2;
|
dsp_options.start_occurence = 2;
|
||||||
@@ -94,53 +96,54 @@ for runIndex = 1:numel(run_ids)
|
|||||||
"plotfinal", 0, ...
|
"plotfinal", 0, ...
|
||||||
"ideal_dfe", 1);
|
"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, ...
|
dbt = 1;
|
||||||
% "sps",2,"decide",0,"optmize_mus",1,"dd_mode",1, ...
|
if dbt
|
||||||
% "adaption_technique","nlms","dc_tracking_mu",1.021e-05);
|
Symbols_ = Duobinary().encode(Symbols);
|
||||||
|
else
|
||||||
|
Symbols_ = Symbols;
|
||||||
[equalized_signal, eq_noise] = eq_vnle.process(Scpe_sig, Symbols);
|
end
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
[equalized_signal, eq_noise] = eq_vnle.process(Scpe_sig, Symbols_);
|
||||||
eq_noise = eq_noise - mean(eq_noise.signal);
|
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);
|
|
||||||
|
|
||||||
|
if ~dbt
|
||||||
|
postfilter_order = 3;
|
||||||
|
pf = Postfilter("ncoeff", postfilter_order, "useBurg", 1);
|
||||||
|
[mlse_sig_sd,whitened_noise] = pf.process(equalized_signal, eq_noise);
|
||||||
|
end
|
||||||
|
|
||||||
|
fig = figure(220);
|
||||||
|
if ~dbt
|
||||||
|
showEQNoisePSD(eq_noise, ...
|
||||||
|
"fignum", fig.Number, ...
|
||||||
|
"displayname", sprintf('EEN: %.0f GBd; preemph: %d', symbolrate*1e-9,ispreemph), ...
|
||||||
|
"postfilter_taps", pf.coefficients, ...
|
||||||
|
"colormode", "qualitative","color",clr.Paired.lblue);
|
||||||
|
whitened_noise = whitened_noise - mean(whitened_noise.signal);
|
||||||
|
whitened_noise.spectrum("displayname", 'Whitened Noise', "fignum", fig.Number, "normalizeTo0dB", 0,"fft_length",4096,"normalizeToDC",0,"color",clr.Paired.dblue);
|
||||||
|
else
|
||||||
|
showEQNoisePSD(eq_noise, ...
|
||||||
|
"fignum", fig.Number, ...
|
||||||
|
"displayname", sprintf('EEN: %.0f GBd; preemph: %d', symbolrate*1e-9,ispreemph), ...
|
||||||
|
"colormode", "qualitative","color",clr.Paired.lblue);
|
||||||
|
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');
|
||||||
|
|
||||||
|
% ylim([-70 -50]);
|
||||||
max_fs = max(max_fs, eq_noise.fs);
|
max_fs = max(max_fs, eq_noise.fs);
|
||||||
|
xlim([-max_fs/2 max_fs/2]*1e-9);
|
||||||
end
|
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)
|
function signalOut = averageScopeSignals(scopeCell, averageSignals, gain)
|
||||||
signalOut = scopeCell{1};
|
signalOut = scopeCell{1};
|
||||||
|
|||||||
@@ -0,0 +1,407 @@
|
|||||||
|
%% Maximum AIR over wavelength at 10 km
|
||||||
|
% One curve is shown for each selected EQ/PAM contender. For every
|
||||||
|
% wavelength, the row with the maximum AIR is retained across the available
|
||||||
|
% symbol rates and normal db_mode 0/1 variants.
|
||||||
|
% DBS uses db_mode = 2, equalizer_structure = db_encoded, and only results
|
||||||
|
% processed after 2026-01-01.
|
||||||
|
|
||||||
|
clear; clc;
|
||||||
|
|
||||||
|
%% 1) Configuration
|
||||||
|
|
||||||
|
normalFiberLengthKm = 2;
|
||||||
|
duobinaryFiberLengthKm = 10;
|
||||||
|
selectedRopAttenuation = 0;
|
||||||
|
selectedIsMpi = 0;
|
||||||
|
duobinaryDateCutoff = datetime("2026-01-01 00:00:00");
|
||||||
|
|
||||||
|
% Thesis colors and curve/PAM mapping.
|
||||||
|
curves = struct;
|
||||||
|
|
||||||
|
curves(1).name = "DBS + VNLE + MLSE (PAM-4)";
|
||||||
|
curves(1).algorithm_key = "db_encoded";
|
||||||
|
curves(1).pam = 4;
|
||||||
|
curves(1).color = clr.Paired.orange;
|
||||||
|
curves(1).marker = "square";
|
||||||
|
curves(1).source = "duobinary";
|
||||||
|
curves(1).usePrecodedBer = false;
|
||||||
|
|
||||||
|
curves(2).name = "VNLE DBt. + MLSE";
|
||||||
|
curves(2).algorithm_key = "vnle_db_mlse";
|
||||||
|
curves(2).pam = 4;
|
||||||
|
curves(2).color = clr.Paired.blue;
|
||||||
|
curves(2).marker = "square";
|
||||||
|
curves(2).source = "normal";
|
||||||
|
curves(2).usePrecodedBer = true;
|
||||||
|
|
||||||
|
% curves(3).name = "ML pre-EQ + Viterbi";
|
||||||
|
% curves(3).algorithm_key = "ml_mlse";
|
||||||
|
% curves(3).pam = 4;
|
||||||
|
% curves(3).color = clr.Paired.purple;
|
||||||
|
% curves(3).marker = "square";
|
||||||
|
% curves(3).source = "normal";
|
||||||
|
% curves(3).usePrecodedBer = true;
|
||||||
|
|
||||||
|
curves(3).name = "VNLE + PF + MLSE";
|
||||||
|
curves(3).algorithm_key = "vnle_pf_mlse";
|
||||||
|
curves(3).pam = 6;
|
||||||
|
curves(3).color = clr.Paired.green;
|
||||||
|
curves(3).marker = "v";
|
||||||
|
curves(3).source = "normal";
|
||||||
|
curves(3).usePrecodedBer = false;
|
||||||
|
|
||||||
|
% curves(5).name = "ML pre-EQ + Viterbi";
|
||||||
|
% curves(5).algorithm_key = "ml_mlse";
|
||||||
|
% curves(5).pam = 6;
|
||||||
|
% curves(5).color = clr.Paired.purple;
|
||||||
|
% curves(5).marker = "v";
|
||||||
|
% curves(5).source = "normal";
|
||||||
|
% curves(5).usePrecodedBer = true;
|
||||||
|
|
||||||
|
curves(4).name = "DBS + VNLE + MLSE (PAM-6)";
|
||||||
|
curves(4).algorithm_key = "db_encoded";
|
||||||
|
curves(4).pam = 6;
|
||||||
|
curves(4).color = clr.Paired.orange;
|
||||||
|
curves(4).marker = "v";
|
||||||
|
curves(4).source = "duobinary";
|
||||||
|
curves(4).usePrecodedBer = false;
|
||||||
|
|
||||||
|
curves(5).name = "VNLE + PF + MLSE";
|
||||||
|
curves(5).algorithm_key = "vnle_pf_mlse";
|
||||||
|
curves(5).pam = 8;
|
||||||
|
curves(5).color = clr.Paired.red;
|
||||||
|
curves(5).marker = "o";
|
||||||
|
curves(5).source = "normal";
|
||||||
|
curves(5).usePrecodedBer = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%% 2) Query normal and DBS rows over all wavelengths
|
||||||
|
|
||||||
|
db = DBHandler( ...
|
||||||
|
"dataBase", "labor_highspeed", ...
|
||||||
|
"type", "mysql", ...
|
||||||
|
"server", "192.168.178.192", ...
|
||||||
|
"user", "silas", ...
|
||||||
|
"password", "silas");
|
||||||
|
db.refresh();
|
||||||
|
|
||||||
|
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
|
||||||
|
|
||||||
|
normalRows = queryRows(db, selectedFields, normalFiberLengthKm, ...
|
||||||
|
selectedRopAttenuation, selectedIsMpi);
|
||||||
|
normalRows = cleanRows(normalRows);
|
||||||
|
normalRows = normalRows(ismember(normalRows.db_mode, ...
|
||||||
|
[double(db_mode.no_db), double(db_mode.db_precoded)]), :);
|
||||||
|
normalRows.algorithm_key = lower(string(normalRows.equalizer_structure));
|
||||||
|
|
||||||
|
duobinaryRows = queryRows(db, selectedFields, duobinaryFiberLengthKm, ...
|
||||||
|
selectedRopAttenuation, selectedIsMpi);
|
||||||
|
duobinaryRows = cleanRows(duobinaryRows);
|
||||||
|
duobinaryRows = duobinaryRows( ...
|
||||||
|
duobinaryRows.db_mode == double(db_mode.db_encoded), :);
|
||||||
|
duobinaryRows = duobinaryRows( ...
|
||||||
|
equalizerMask(duobinaryRows.equalizer_structure, ...
|
||||||
|
equalizer_structure.db_encoded), :);
|
||||||
|
|
||||||
|
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) > duobinaryDateCutoff, :);
|
||||||
|
else
|
||||||
|
warning("plot_air_wavelength:NoProcessingDate", ...
|
||||||
|
"date_of_processing was not returned; no DBS date filter was applied.");
|
||||||
|
end
|
||||||
|
duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1);
|
||||||
|
|
||||||
|
normalRows = addAirMetric(normalRows);
|
||||||
|
duobinaryRows = addAirMetric(duobinaryRows);
|
||||||
|
|
||||||
|
fprintf("Normal %g km rows: %d\n", normalFiberLengthKm, height(normalRows));
|
||||||
|
fprintf("DBS %g km rows after date filter: %d\n", ...
|
||||||
|
duobinaryFiberLengthKm, height(duobinaryRows));
|
||||||
|
|
||||||
|
%% 3) Select maximum AIR per wavelength for every configured curve
|
||||||
|
|
||||||
|
tp = TransmissionPerformance;
|
||||||
|
results = repmat(emptyResult(), numel(curves), 1);
|
||||||
|
for curveIdx = 1:numel(curves)
|
||||||
|
curve = curves(curveIdx);
|
||||||
|
if curve.source == "normal"
|
||||||
|
sourceRows = normalRows;
|
||||||
|
else
|
||||||
|
sourceRows = duobinaryRows;
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows = sourceRows(sourceRows.pam_level == curve.pam & ...
|
||||||
|
sourceRows.algorithm_key == curve.algorithm_key, :);
|
||||||
|
curveRows.BER_plot = curveRows.BER;
|
||||||
|
if curve.usePrecodedBer && ismember("BER_precoded", ...
|
||||||
|
string(curveRows.Properties.VariableNames))
|
||||||
|
usePrecoded = isfinite(curveRows.BER_precoded);
|
||||||
|
curveRows.BER_plot(usePrecoded) = curveRows.BER_precoded(usePrecoded);
|
||||||
|
end
|
||||||
|
|
||||||
|
measuredNgmi = numericOrNaN(curveRows, "NGMI");
|
||||||
|
measuredBer = numericOrNaN(curveRows, "BER_plot");
|
||||||
|
grossRate = numericOrNaN(curveRows, "grossrate");
|
||||||
|
measuredNgmi(~isfinite(measuredNgmi) | measuredNgmi < 0 | ...
|
||||||
|
measuredNgmi > 1.05) = NaN;
|
||||||
|
measuredBer(~isfinite(measuredBer) | measuredBer <= 0 | ...
|
||||||
|
measuredBer > 0.5) = NaN;
|
||||||
|
|
||||||
|
ndr = tp.calculateNetRate(grossRate, ...
|
||||||
|
"NGMI", measuredNgmi, ...
|
||||||
|
"BER", measuredBer);
|
||||||
|
curveRows.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9;
|
||||||
|
|
||||||
|
bestRows = bestMetricByWavelength(curveRows, "AIR_Gbps");
|
||||||
|
bestOfecRows = bestMetricByWavelength(curveRows, "NDR_O_FEC");
|
||||||
|
|
||||||
|
results(curveIdx).name = curve.name;
|
||||||
|
results(curveIdx).color = curve.color;
|
||||||
|
results(curveIdx).marker = curve.marker;
|
||||||
|
results(curveIdx).pam = curve.pam;
|
||||||
|
results(curveIdx).air = makeMetricSeries( ...
|
||||||
|
bestRows, "AIR_Gbps", curve.usePrecodedBer);
|
||||||
|
results(curveIdx).ofec = makeMetricSeries( ...
|
||||||
|
bestOfecRows, "NDR_O_FEC", curve.usePrecodedBer);
|
||||||
|
|
||||||
|
fprintf("%s, PAM-%d: AIR=%d, O-FEC=%d wavelength points\n", ...
|
||||||
|
curve.name, curve.pam, ...
|
||||||
|
numel(results(curveIdx).air.x), ...
|
||||||
|
numel(results(curveIdx).ofec.x));
|
||||||
|
end
|
||||||
|
|
||||||
|
%% 4) Plot maximum AIR and O-FEC NDR over wavelength
|
||||||
|
|
||||||
|
fig = figure(435); clf;
|
||||||
|
t = tiledlayout(fig, 1, 2, ...
|
||||||
|
"TileSpacing", "compact", ...
|
||||||
|
"Padding", "compact");
|
||||||
|
|
||||||
|
axAir = nexttile(t, 1);
|
||||||
|
airHandles = plotMetricPanel(axAir, results, "air", ...
|
||||||
|
"AIR [Gb/s]", "a) Maximum AIR");
|
||||||
|
|
||||||
|
axOfec = nexttile(t, 2);
|
||||||
|
ofecHandles = plotMetricPanel(axOfec, results, "ofec", ...
|
||||||
|
"O-FEC NDR [Gb/s]", "b) O-FEC");
|
||||||
|
|
||||||
|
allWavelengthCells = cell(numel(results), 1);
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
allWavelengthCells{curveIdx} = [ ...
|
||||||
|
results(curveIdx).air.x(:); ...
|
||||||
|
results(curveIdx).ofec.x(:)];
|
||||||
|
end
|
||||||
|
allWavelengths = vertcat(allWavelengthCells{:});
|
||||||
|
allWavelengths = allWavelengths(isfinite(allWavelengths));
|
||||||
|
if ~isempty(allWavelengths)
|
||||||
|
xLimits = [min(allWavelengths) - 1, max(allWavelengths) + 1];
|
||||||
|
for ax = [axAir, axOfec]
|
||||||
|
xlim(ax, xLimits);
|
||||||
|
xticks(ax, unique(allWavelengths));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
validOfecMask = isgraphics(ofecHandles);
|
||||||
|
if any(validOfecMask)
|
||||||
|
legend(axOfec, ofecHandles(validOfecMask), ...
|
||||||
|
makeLegendNames(results(validOfecMask)), ...
|
||||||
|
"Location", "southoutside", ...
|
||||||
|
"NumColumns", min(3, nnz(validOfecMask)), ...
|
||||||
|
"Interpreter", "none");
|
||||||
|
elseif any(isgraphics(airHandles))
|
||||||
|
validAirMask = isgraphics(airHandles);
|
||||||
|
legend(axAir, airHandles(validAirMask), ...
|
||||||
|
makeLegendNames(results(validAirMask)), ...
|
||||||
|
"Location", "southoutside", ...
|
||||||
|
"NumColumns", min(3, nnz(validAirMask)), ...
|
||||||
|
"Interpreter", "none");
|
||||||
|
end
|
||||||
|
|
||||||
|
sgtitle(t, sprintf("Best AIR and O-FEC results over wavelength, %.0f km", ...
|
||||||
|
normalFiberLengthKm));
|
||||||
|
set(fig, "Position", 1e3 .* [0.08 0.42 1.75 0.48]);
|
||||||
|
|
||||||
|
%% Local helpers
|
||||||
|
|
||||||
|
function T = queryRows(db, fields, fiberLengthKm, ropAttenuation, isMpi)
|
||||||
|
fp = QueryFilter();
|
||||||
|
fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm);
|
||||||
|
fp.where("Runs", "rop_attenuation", "EQUALS", ropAttenuation);
|
||||||
|
fp.where("Runs", "is_mpi", "EQUALS", isMpi);
|
||||||
|
[T, query] = db.queryDB(fp, fields);
|
||||||
|
disp(query);
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = cleanRows(T)
|
||||||
|
numericFields = ["result_id", "run_id", "eq_id", "bitrate", ...
|
||||||
|
"grossrate", "symbolrate", "pam_level", "wavelength", ...
|
||||||
|
"fiber_length", "db_mode", "rop_attenuation", "numBits", ...
|
||||||
|
"numBitErr", "BER", "numBitErr_precoded", "BER_precoded", ...
|
||||||
|
"GMI", "AIR", "NGMI"];
|
||||||
|
for fieldIdx = 1:numel(numericFields)
|
||||||
|
fieldName = numericFields(fieldIdx);
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
T.(char(fieldName)) = numericColumn(T.(char(fieldName)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
T.equalizer_structure = lower(string(T.equalizer_structure));
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = addAirMetric(T)
|
||||||
|
T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9;
|
||||||
|
gmi = numericOrNaN(T, "GMI");
|
||||||
|
symbolrate = numericOrNaN(T, "symbolrate");
|
||||||
|
grossrate = numericOrNaN(T, "grossrate");
|
||||||
|
fallbackAir = gmi .* symbolrate .* 1e-9;
|
||||||
|
useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ...
|
||||||
|
(isfinite(grossrate) & T.AIR_Gbps > grossrate .* 1.05e-9);
|
||||||
|
T.AIR_Gbps(useFallback) = fallbackAir(useFallback);
|
||||||
|
end
|
||||||
|
|
||||||
|
function bestRows = bestMetricByWavelength(T, metricField)
|
||||||
|
if isempty(T)
|
||||||
|
bestRows = T;
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
values = numericOrNaN(T, metricField);
|
||||||
|
valid = isfinite(T.wavelength) & isfinite(values) & values >= 0;
|
||||||
|
candidate = T(valid, :);
|
||||||
|
values = values(valid);
|
||||||
|
if isempty(candidate)
|
||||||
|
bestRows = candidate;
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
[groupId, ~] = findgroups(candidate.wavelength);
|
||||||
|
keepIdx = zeros(max(groupId), 1);
|
||||||
|
for groupIdx = 1:max(groupId)
|
||||||
|
rowIdx = find(groupId == groupIdx);
|
||||||
|
[~, localIdx] = max(values(rowIdx));
|
||||||
|
keepIdx(groupIdx) = rowIdx(localIdx(1));
|
||||||
|
end
|
||||||
|
bestRows = sortrows(candidate(keepIdx, :), "wavelength");
|
||||||
|
end
|
||||||
|
|
||||||
|
function series = makeMetricSeries(T, metricField, usePrecodedBer)
|
||||||
|
series = emptyMetricSeries();
|
||||||
|
if isempty(T)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
series.x = numericOrNaN(T, "wavelength");
|
||||||
|
series.y = numericOrNaN(T, metricField);
|
||||||
|
series.air = numericOrNaN(T, "AIR_Gbps");
|
||||||
|
series.ngmi = numericOrNaN(T, "NGMI");
|
||||||
|
series.ber = numericOrNaN(T, "BER_plot");
|
||||||
|
series.symbolrate = numericOrNaN(T, "symbolrate") .* 1e-9;
|
||||||
|
series.grossrate = numericOrNaN(T, "grossrate") .* 1e-9;
|
||||||
|
series.precoding = repmat(double(usePrecodedBer), height(T), 1);
|
||||||
|
series.dbMode = numericOrNaN(T, "db_mode");
|
||||||
|
end
|
||||||
|
|
||||||
|
function series = emptyMetricSeries()
|
||||||
|
series = struct( ...
|
||||||
|
"x", [], ...
|
||||||
|
"y", [], ...
|
||||||
|
"air", [], ...
|
||||||
|
"ngmi", [], ...
|
||||||
|
"ber", [], ...
|
||||||
|
"symbolrate", [], ...
|
||||||
|
"grossrate", [], ...
|
||||||
|
"precoding", [], ...
|
||||||
|
"dbMode", []);
|
||||||
|
end
|
||||||
|
|
||||||
|
function handles = plotMetricPanel(ax, results, seriesField, yLabel, panelTitle)
|
||||||
|
hold(ax, "on");
|
||||||
|
handles = gobjects(numel(results), 1);
|
||||||
|
for curveIdx = 1:numel(results)
|
||||||
|
result = results(curveIdx);
|
||||||
|
series = result.(seriesField);
|
||||||
|
if isempty(series.x)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
handles(curveIdx) = plot(ax, series.x, series.y, ...
|
||||||
|
"LineStyle", "-", ...
|
||||||
|
"Marker", result.marker, ...
|
||||||
|
"MarkerSize", 5, ...
|
||||||
|
"LineWidth", 1.35, ...
|
||||||
|
"Color", result.color, ...
|
||||||
|
"MarkerFaceColor", result.color, ...
|
||||||
|
"MarkerEdgeColor", result.color, ...
|
||||||
|
"DisplayName", result.name);
|
||||||
|
|
||||||
|
handles(curveIdx).DataTipTemplate.DataTipRows = [ ...
|
||||||
|
dataTipTextRow(yLabel, series.y); ...
|
||||||
|
dataTipTextRow("AIR [Gb/s]", series.air); ...
|
||||||
|
dataTipTextRow("NGMI", series.ngmi); ...
|
||||||
|
dataTipTextRow("BER", series.ber); ...
|
||||||
|
dataTipTextRow("Symbol rate [GBd]", series.symbolrate); ...
|
||||||
|
dataTipTextRow("Gross rate [Gb/s]", series.grossrate); ...
|
||||||
|
dataTipTextRow("precoding", series.precoding); ...
|
||||||
|
dataTipTextRow("db_mode", series.dbMode)];
|
||||||
|
end
|
||||||
|
|
||||||
|
set(ax, "FontSize", 9, "TickLabelInterpreter", "none");
|
||||||
|
xlabel(ax, "Wavelength [nm]");
|
||||||
|
ylabel(ax, yLabel);
|
||||||
|
title(ax, panelTitle);
|
||||||
|
grid(ax, "on");
|
||||||
|
grid(ax, "minor");
|
||||||
|
box(ax, "on");
|
||||||
|
end
|
||||||
|
|
||||||
|
function names = makeLegendNames(results)
|
||||||
|
names = strings(numel(results), 1);
|
||||||
|
for idx = 1:numel(results)
|
||||||
|
if contains(results(idx).name, "PAM-")
|
||||||
|
names(idx) = results(idx).name;
|
||||||
|
else
|
||||||
|
names(idx) = results(idx).name + ...
|
||||||
|
" (PAM-" + string(results(idx).pam) + ")";
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function result = emptyResult()
|
||||||
|
result = struct( ...
|
||||||
|
"name", "", ...
|
||||||
|
"color", [0 0 0], ...
|
||||||
|
"marker", "o", ...
|
||||||
|
"pam", NaN, ...
|
||||||
|
"air", emptyMetricSeries(), ...
|
||||||
|
"ofec", emptyMetricSeries());
|
||||||
|
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 = numericOrNaN(T, fieldName)
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
values = numericColumn(T.(char(fieldName)));
|
||||||
|
else
|
||||||
|
values = NaN(height(T), 1);
|
||||||
|
end
|
||||||
|
values = values(:);
|
||||||
|
end
|
||||||
|
|
||||||
|
function values = columnVector(values)
|
||||||
|
values = double(values(:));
|
||||||
|
end
|
||||||
|
|
||||||
|
function mask = equalizerMask(equalizerColumn, eqValue)
|
||||||
|
mask = lower(string(equalizerColumn)) == lower(string(eqValue));
|
||||||
|
end
|
||||||
@@ -33,7 +33,7 @@ db.refresh();
|
|||||||
|
|
||||||
fp = QueryFilter();
|
fp = QueryFilter();
|
||||||
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
|
fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm);
|
||||||
fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
|
% fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm);
|
||||||
if ~isempty(selectedRopAttenuation)
|
if ~isempty(selectedRopAttenuation)
|
||||||
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
|
fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation);
|
||||||
end
|
end
|
||||||
@@ -141,7 +141,7 @@ for pamIdx = 1:numel(selectedPamLevels)
|
|||||||
algoData = sortrows(bestPlotData(rowMask, :), "grossrate_Gbps");
|
algoData = sortrows(bestPlotData(rowMask, :), "grossrate_Gbps");
|
||||||
|
|
||||||
if showRawEntries
|
if showRawEntries
|
||||||
scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
|
rawHandle = scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
|
||||||
9, ...
|
9, ...
|
||||||
"Marker", ".", ...
|
"Marker", ".", ...
|
||||||
"MarkerEdgeColor", style.color, ...
|
"MarkerEdgeColor", style.color, ...
|
||||||
@@ -149,10 +149,14 @@ for pamIdx = 1:numel(selectedPamLevels)
|
|||||||
"MarkerEdgeAlpha", 0.25, ...
|
"MarkerEdgeAlpha", 0.25, ...
|
||||||
"MarkerFaceAlpha", 0.25, ...
|
"MarkerFaceAlpha", 0.25, ...
|
||||||
"HandleVisibility", "off");
|
"HandleVisibility", "off");
|
||||||
|
rawHandle.DataTipTemplate.DataTipRows = [ ...
|
||||||
|
dataTipTextRow("Gross rate [Gb/s]", algoData.grossrate_Gbps); ...
|
||||||
|
dataTipTextRow("BER", algoData.BER_plot); ...
|
||||||
|
dataTipTextRow("db_mode", algoData.db_mode)];
|
||||||
end
|
end
|
||||||
|
|
||||||
if showBestLine
|
if showBestLine
|
||||||
plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
|
bestHandle = plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ...
|
||||||
"LineStyle", style.lineStyle, ...
|
"LineStyle", style.lineStyle, ...
|
||||||
"Marker", style.marker, ...
|
"Marker", style.marker, ...
|
||||||
"MarkerSize", 5, ...
|
"MarkerSize", 5, ...
|
||||||
@@ -161,6 +165,10 @@ for pamIdx = 1:numel(selectedPamLevels)
|
|||||||
"MarkerFaceColor", style.markerFaceColor, ...
|
"MarkerFaceColor", style.markerFaceColor, ...
|
||||||
"MarkerEdgeColor", style.color, ...
|
"MarkerEdgeColor", style.color, ...
|
||||||
"DisplayName", style.name);
|
"DisplayName", style.name);
|
||||||
|
bestHandle.DataTipTemplate.DataTipRows = [ ...
|
||||||
|
dataTipTextRow("Gross rate [Gb/s]", algoData.grossrate_Gbps); ...
|
||||||
|
dataTipTextRow("BER", algoData.BER_plot); ...
|
||||||
|
dataTipTextRow("db_mode", algoData.db_mode)];
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ if isempty(plotData)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
plotData.bitrate_Gbps = plotData.bitrate .* 1e-9;
|
plotData.bitrate_Gbps = plotData.grossrate .* 1e-9;
|
||||||
plotData = sortrows(plotData, ...
|
plotData = sortrows(plotData, ...
|
||||||
["pam_level", "wavelength", "detection_type", "bitrate_Gbps", "run_id"]);
|
["pam_level", "wavelength", "detection_type", "bitrate_Gbps", "run_id"]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
% algorithm and is restricted to the post-2026 processing results.
|
% algorithm and is restricted to the post-2026 processing results.
|
||||||
%
|
%
|
||||||
% The figure has one column per PAM format and one row per metric:
|
% 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.
|
% BER, NGMI, GMI, AIR, and all requested FEC NDR curves.
|
||||||
% NDR values are calculated from the measured BER/NGMI using
|
% NDR values are calculated from the measured BER/NGMI using
|
||||||
% TransmissionPerformance.
|
% TransmissionPerformance.
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ clear; clc;
|
|||||||
%% 1) Query data
|
%% 1) Query data
|
||||||
|
|
||||||
selectedPamLevels = [4, 6, 8];
|
selectedPamLevels = [4, 6, 8];
|
||||||
selectedFiberLengthKm = 10;
|
selectedFiberLengthKm = 2;
|
||||||
selectedWavelengthNm = 1310;
|
selectedWavelengthNm = 1310;
|
||||||
selectedRopAttenuation = 0;
|
selectedRopAttenuation = 0;
|
||||||
selectedIsMpi = 0;
|
selectedIsMpi = 0;
|
||||||
@@ -21,7 +21,7 @@ selectedIsMpi = 0;
|
|||||||
normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)];
|
normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)];
|
||||||
duobinaryDbMode = double(db_mode.db_encoded);
|
duobinaryDbMode = double(db_mode.db_encoded);
|
||||||
|
|
||||||
showRawEntries = false;
|
showRawEntries = true; % set true to overlay all candidate rows
|
||||||
maxBerForPlot = 0.5;
|
maxBerForPlot = 0.5;
|
||||||
|
|
||||||
algoStyles = defaultAlgorithmStyles();
|
algoStyles = defaultAlgorithmStyles();
|
||||||
@@ -76,6 +76,9 @@ end
|
|||||||
if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames))
|
if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames))
|
||||||
duobinaryRows.date_of_processing = datetime(string(duobinaryRows.date_of_processing));
|
duobinaryRows.date_of_processing = datetime(string(duobinaryRows.date_of_processing));
|
||||||
duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>datetime("2026-01-01 00:00:00"),:);
|
duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>datetime("2026-01-01 00:00:00"),:);
|
||||||
|
% Keep the table variable type compatible with the normal rows before
|
||||||
|
% concatenating both result sets.
|
||||||
|
duobinaryRows.date_of_processing = string(duobinaryRows.date_of_processing);
|
||||||
else
|
else
|
||||||
warning("plot_best_metrics:NoProcessingDate", ...
|
warning("plot_best_metrics:NoProcessingDate", ...
|
||||||
"date_of_processing was not returned; no duobinary date filtering was applied.");
|
"date_of_processing was not returned; no duobinary date filtering was applied.");
|
||||||
@@ -94,7 +97,8 @@ plotData = plotData(plotData.symbolrate_GBd > 0 & ...
|
|||||||
isfinite(plotData.symbolrate_GBd), :);
|
isfinite(plotData.symbolrate_GBd), :);
|
||||||
|
|
||||||
fprintf("Remaining metric rows: %d\n", height(plotData));
|
fprintf("Remaining metric rows: %d\n", height(plotData));
|
||||||
disp(groupcounts(plotData, ["algorithm_key", "pam_level", "precode"]));
|
disp(groupcounts(plotData, ...
|
||||||
|
["algorithm_key", "pam_level", "pre_emphasis", "precode"]));
|
||||||
|
|
||||||
%% 3) Calculate BER-/NGMI-dependent net rates
|
%% 3) Calculate BER-/NGMI-dependent net rates
|
||||||
|
|
||||||
@@ -108,30 +112,48 @@ ber(~isfinite(ber) | ber <= 0 | ber > maxBerForPlot) = NaN;
|
|||||||
|
|
||||||
ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber);
|
ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber);
|
||||||
plotData.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9;
|
plotData.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9;
|
||||||
|
plotData.NDR_STAIR = columnVector(ndr.STAIR.NetRate) .* 1e-9;
|
||||||
berBasedRates = [ ...
|
plotData.NDR_HD = columnVector(ndr.HD.NetRate) .* 1e-9;
|
||||||
columnVector(ndr.STAIR.NetRate); ...
|
plotData.NDR_KP4 = columnVector(ndr.KP4.NetRate) .* 1e-9;
|
||||||
columnVector(ndr.HD.NetRate); ...
|
plotData.NDR_KP4_HAMMING = columnVector(ndr.KP4_hamming.NetRate) .* 1e-9;
|
||||||
columnVector(ndr.KP4.NetRate); ...
|
plotData.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9;
|
||||||
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
|
%% 4) Keep the best row for every plotted metric and group
|
||||||
|
|
||||||
metricDefinitions = struct( ...
|
metricDefinitions = struct;
|
||||||
"field", {"BER_plot", "NGMI", "GMI", "AIR_Gbps", "NDR_SDHD", "NDR_BER_BEST"}, ...
|
metricDefinitions(1).fields = "BER_plot";
|
||||||
"label", {"BER", "NGMI", "GMI [bit/sym]", "AIR [Gb/s]", ...
|
metricDefinitions(1).label = "BER";
|
||||||
"SD+HD NDR [Gb/s]", "Best BER-FEC NDR [Gb/s]"}, ...
|
metricDefinitions(1).seriesLabels = "BER";
|
||||||
"scale", {"log", "linear", "linear", "linear", "linear", "linear"});
|
metricDefinitions(1).seriesLineStyles = "-";
|
||||||
|
metricDefinitions(2).fields = "NGMI";
|
||||||
|
metricDefinitions(2).label = "NGMI";
|
||||||
|
metricDefinitions(2).seriesLabels = "NGMI";
|
||||||
|
metricDefinitions(2).seriesLineStyles = "-";
|
||||||
|
metricDefinitions(3).fields = "GMI";
|
||||||
|
metricDefinitions(3).label = "GMI [bit/sym]";
|
||||||
|
metricDefinitions(3).seriesLabels = "GMI";
|
||||||
|
metricDefinitions(3).seriesLineStyles = "-";
|
||||||
|
metricDefinitions(4).fields = "AIR_Gbps";
|
||||||
|
metricDefinitions(4).label = "AIR [Gb/s]";
|
||||||
|
metricDefinitions(4).seriesLabels = "AIR";
|
||||||
|
metricDefinitions(4).seriesLineStyles = "-";
|
||||||
|
metricDefinitions(5).fields = ["NDR_SDHD", "NDR_STAIR", "NDR_HD", ...
|
||||||
|
"NDR_KP4", "NDR_KP4_HAMMING", "NDR_O_FEC"];
|
||||||
|
metricDefinitions(5).label = "NDR [Gb/s]";
|
||||||
|
metricDefinitions(5).seriesLabels = ["SD+HD", "Staircase", "HD-FEC", ...
|
||||||
|
"KP4", "KP4 + Hamming", "O-FEC"];
|
||||||
|
metricDefinitions(5).seriesLineStyles = ["-"; "--"; ":"; "-."; "-"; "--"];
|
||||||
|
|
||||||
bestMetricData = cell(numel(metricDefinitions), 1);
|
bestMetricData = cell(numel(metricDefinitions), 1);
|
||||||
for metricIdx = 1:numel(metricDefinitions)
|
for metricIdx = 1:numel(metricDefinitions)
|
||||||
bestMetricData{metricIdx} = bestMetricRows(plotData, ...
|
bestMetricData{metricIdx} = cell(numel(metricDefinitions(metricIdx).fields), 1);
|
||||||
metricDefinitions(metricIdx).field);
|
for seriesIdx = 1:numel(metricDefinitions(metricIdx).fields)
|
||||||
|
bestMetricData{metricIdx}{seriesIdx} = bestMetricRows(plotData, ...
|
||||||
|
metricDefinitions(metricIdx).fields(seriesIdx));
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
availableStyles = algoStyles(hasAlgorithmRows(plotData, algoStyles), :);
|
availableStyles = algoStyles(hasPlotStyleRows(plotData, algoStyles), :);
|
||||||
if isempty(availableStyles)
|
if isempty(availableStyles)
|
||||||
warning("plot_best_metrics:NoSelectedAlgorithms", ...
|
warning("plot_best_metrics:NoSelectedAlgorithms", ...
|
||||||
"None of the configured algorithm styles match the queried rows.");
|
"None of the configured algorithm styles match the queried rows.");
|
||||||
@@ -147,53 +169,60 @@ t = tiledlayout(fig, numel(metricDefinitions), numel(selectedPamLevels), ...
|
|||||||
|
|
||||||
for metricIdx = 1:numel(metricDefinitions)
|
for metricIdx = 1:numel(metricDefinitions)
|
||||||
metric = metricDefinitions(metricIdx);
|
metric = metricDefinitions(metricIdx);
|
||||||
metricData = bestMetricData{metricIdx};
|
|
||||||
|
|
||||||
for pamIdx = 1:numel(selectedPamLevels)
|
for pamIdx = 1:numel(selectedPamLevels)
|
||||||
selectedPamLevel = selectedPamLevels(pamIdx);
|
selectedPamLevel = selectedPamLevels(pamIdx);
|
||||||
ax = nexttile(t); hold(ax, "on");
|
ax = nexttile(t); hold(ax, "on");
|
||||||
pamMask = metricData.pam_level == selectedPamLevel;
|
for seriesIdx = 1:numel(metric.fields)
|
||||||
|
metricData = bestMetricData{metricIdx}{seriesIdx};
|
||||||
|
metricField = metric.fields(seriesIdx);
|
||||||
|
pamMask = metricData.pam_level == selectedPamLevel;
|
||||||
|
|
||||||
for styleIdx = 1:height(availableStyles)
|
for styleIdx = 1:height(availableStyles)
|
||||||
style = availableStyles(styleIdx, :);
|
style = availableStyles(styleIdx, :);
|
||||||
rowMask = pamMask & metricData.algorithm_key == style.algorithm_key;
|
rowMask = pamMask & ...
|
||||||
if ~any(rowMask)
|
metricData.algorithm_key == style.algorithm_key & ...
|
||||||
continue
|
metricData.pre_emphasis == style.pre_emphasis;
|
||||||
end
|
if ~any(rowMask)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
algoData = sortrows(metricData(rowMask, :), "symbolrate_GBd");
|
algoData = sortrows(metricData(rowMask, :), "symbolrate_GBd");
|
||||||
y = algoData.(metric.field);
|
y = algoData.(char(metricField));
|
||||||
valid = isfinite(y);
|
valid = isfinite(y);
|
||||||
if ~any(valid)
|
if ~any(valid)
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
if showRawEntries
|
if isequal(showRawEntries, true)
|
||||||
scatter(ax, algoData.symbolrate_GBd(valid), y(valid), ...
|
scatter(ax, algoData.symbolrate_GBd(valid), y(valid), ...
|
||||||
9, ...
|
9, ...
|
||||||
"Marker", ".", ...
|
"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", metric.seriesLineStyles(seriesIdx), ...
|
||||||
|
"Marker", style.marker, ...
|
||||||
|
"MarkerSize", 4, ...
|
||||||
|
"LineWidth", 1.35, ...
|
||||||
|
"Color", style.color, ...
|
||||||
|
"MarkerFaceColor", style.markerFaceColor, ...
|
||||||
"MarkerEdgeColor", style.color, ...
|
"MarkerEdgeColor", style.color, ...
|
||||||
"MarkerFaceColor", style.color, ...
|
"DisplayName", style.displayName);
|
||||||
"MarkerEdgeAlpha", 0.25, ...
|
|
||||||
"MarkerFaceAlpha", 0.25, ...
|
|
||||||
"HandleVisibility", "off");
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
formatMetricAxis(ax, metric, selectedPamLevel);
|
formatMetricAxis(ax, metric, selectedPamLevel);
|
||||||
|
|
||||||
if metricIdx == 1 && pamIdx == 1
|
if metricIdx == 1 && pamIdx == 1
|
||||||
legend(ax, "Location", "southwest", "Interpreter", "none");
|
legend(ax, "Location", "southwest", "Interpreter", "none");
|
||||||
|
elseif metricIdx == numel(metricDefinitions) && pamIdx == 1
|
||||||
|
addNdrLegend(ax, metric.seriesLabels, metric.seriesLineStyles);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -229,17 +258,27 @@ values = double(values(:));
|
|||||||
end
|
end
|
||||||
|
|
||||||
function styles = defaultAlgorithmStyles()
|
function styles = defaultAlgorithmStyles()
|
||||||
styles = table( ...
|
algorithmKey = repelem(["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; ...
|
||||||
["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; "ml_mlse"; "db_encoded"], ...
|
"ml_mlse"; "db_encoded"], 2, 1);
|
||||||
["VNLE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"; ...
|
name = repelem(["VNLE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"; ...
|
||||||
"ML pre-EQ + Viterbi"; "DBS + VNLE + MLSE"], ...
|
"ML pre-EQ + Viterbi"; "DBS + VNLE + MLSE"], 2, 1);
|
||||||
["o"; "square"; "diamond"; "^"; "v"], ...
|
preEmphasis = repmat([false; true], 5, 1);
|
||||||
["-"; "-"; "-"; "-"; "-"], ...
|
preEmphasisLabel = repmat(["w/o pre-emph."; "w/ pre-emph."], 5, 1);
|
||||||
["w"; "w"; "w"; "w"; "w"], ...
|
preEmphasisLabel(algorithmKey == "db_encoded") = "duobinary";
|
||||||
[clr.Paired.red; clr.Paired.green; clr.Paired.blue; ...
|
|
||||||
clr.Paired.purple; clr.Paired.orange], ...
|
styles = table(algorithmKey, name, preEmphasis, preEmphasisLabel, ...
|
||||||
'VariableNames', ["algorithm_key", "name", "marker", ...
|
repelem(["o"; "square"; "diamond"; "^"; "v"], 2, 1), ...
|
||||||
"lineStyle", "markerFaceColor", "color"]);
|
repmat("-", 10, 1), ...
|
||||||
|
repmat("w", 10, 1), ...
|
||||||
|
[clr.Paired.lred; clr.Paired.dred; ...
|
||||||
|
clr.Paired.lgreen; clr.Paired.dgreen; ...
|
||||||
|
clr.Paired.lblue; clr.Paired.dblue; ...
|
||||||
|
clr.Paired.llila; clr.Paired.dlila; ...
|
||||||
|
clr.Paired.lorange; clr.Paired.dorange], ...
|
||||||
|
'VariableNames', ["algorithm_key", "name", "pre_emphasis", ...
|
||||||
|
"preEmphasisLabel", "marker", "lineStyle", ...
|
||||||
|
"markerFaceColor", "color"]);
|
||||||
|
styles.displayName = styles.name + "; " + styles.preEmphasisLabel;
|
||||||
end
|
end
|
||||||
|
|
||||||
function plotData = buildNormalMetricRows(data)
|
function plotData = buildNormalMetricRows(data)
|
||||||
@@ -247,6 +286,7 @@ baseRows = data(isfinite(data.BER), :);
|
|||||||
baseRows.precode = false(height(baseRows), 1);
|
baseRows.precode = false(height(baseRows), 1);
|
||||||
baseRows.BER_plot = baseRows.BER;
|
baseRows.BER_plot = baseRows.BER;
|
||||||
baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure);
|
baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure);
|
||||||
|
baseRows.pre_emphasis = baseRows.db_mode == double(db_mode.db_precoded);
|
||||||
baseRows = baseRows(baseRows.algorithm_key ~= "", :);
|
baseRows = baseRows(baseRows.algorithm_key ~= "", :);
|
||||||
|
|
||||||
if ismember("BER_precoded", string(data.Properties.VariableNames))
|
if ismember("BER_precoded", string(data.Properties.VariableNames))
|
||||||
@@ -255,6 +295,7 @@ if ismember("BER_precoded", string(data.Properties.VariableNames))
|
|||||||
precodedRows.BER_plot = precodedRows.BER_precoded;
|
precodedRows.BER_plot = precodedRows.BER_precoded;
|
||||||
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
|
precodedRows.algorithm_key = algorithmKeyFromEqualizer( ...
|
||||||
precodedRows.equalizer_structure);
|
precodedRows.equalizer_structure);
|
||||||
|
precodedRows.pre_emphasis = precodedRows.db_mode == double(db_mode.db_precoded);
|
||||||
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
|
precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :);
|
||||||
plotData = [baseRows; precodedRows];
|
plotData = [baseRows; precodedRows];
|
||||||
else
|
else
|
||||||
@@ -269,6 +310,7 @@ plotData = data(isfinite(data.BER), :);
|
|||||||
plotData.precode = false(height(plotData), 1);
|
plotData.precode = false(height(plotData), 1);
|
||||||
plotData.BER_plot = plotData.BER;
|
plotData.BER_plot = plotData.BER;
|
||||||
plotData.algorithm_key = repmat("db_encoded", height(plotData), 1);
|
plotData.algorithm_key = repmat("db_encoded", height(plotData), 1);
|
||||||
|
plotData.pre_emphasis = false(height(plotData), 1);
|
||||||
end
|
end
|
||||||
|
|
||||||
function plotData = addDerivedMetrics(plotData)
|
function plotData = addDerivedMetrics(plotData)
|
||||||
@@ -343,7 +385,7 @@ if isempty(candidateData)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
groupVars = ["pam_level", "algorithm_key", "symbolrate_GBd"];
|
groupVars = ["pam_level", "algorithm_key", "pre_emphasis", "symbolrate_GBd"];
|
||||||
groupId = findgroups(candidateData(:, groupVars));
|
groupId = findgroups(candidateData(:, groupVars));
|
||||||
keepIdx = zeros(max(groupId), 1);
|
keepIdx = zeros(max(groupId), 1);
|
||||||
for curGroup = 1:max(groupId)
|
for curGroup = 1:max(groupId)
|
||||||
@@ -359,10 +401,11 @@ end
|
|||||||
bestData = sortrows(candidateData(keepIdx, :), groupVars);
|
bestData = sortrows(candidateData(keepIdx, :), groupVars);
|
||||||
end
|
end
|
||||||
|
|
||||||
function keep = hasAlgorithmRows(data, algoStyles)
|
function keep = hasPlotStyleRows(data, algoStyles)
|
||||||
keep = false(height(algoStyles), 1);
|
keep = false(height(algoStyles), 1);
|
||||||
for idx = 1:height(algoStyles)
|
for idx = 1:height(algoStyles)
|
||||||
keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx));
|
keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx) & ...
|
||||||
|
data.pre_emphasis == algoStyles.pre_emphasis(idx));
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -376,7 +419,7 @@ grid(ax, "on");
|
|||||||
grid(ax, "minor");
|
grid(ax, "minor");
|
||||||
box(ax, "on");
|
box(ax, "on");
|
||||||
|
|
||||||
switch metric.field
|
switch metric.fields(1)
|
||||||
case "BER_plot"
|
case "BER_plot"
|
||||||
set(ax, "YScale", "log");
|
set(ax, "YScale", "log");
|
||||||
ylim(ax, [1e-5 0.2]);
|
ylim(ax, [1e-5 0.2]);
|
||||||
@@ -405,3 +448,15 @@ else
|
|||||||
title(ax, sprintf("PAM-%d", pamLevel));
|
title(ax, sprintf("PAM-%d", pamLevel));
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function addNdrLegend(ax, seriesLabels, seriesLineStyles)
|
||||||
|
legendHandles = gobjects(numel(seriesLabels), 1);
|
||||||
|
for idx = 1:numel(seriesLabels)
|
||||||
|
legendHandles(idx) = plot(ax, NaN, NaN, ...
|
||||||
|
"Color", [0.2 0.2 0.2], ...
|
||||||
|
"LineStyle", seriesLineStyles(idx), ...
|
||||||
|
"LineWidth", 1.35, ...
|
||||||
|
"DisplayName", seriesLabels(idx));
|
||||||
|
end
|
||||||
|
legend(ax, legendHandles, "Location", "southwest", "Interpreter", "none");
|
||||||
|
end
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ clear; clc;
|
|||||||
%% 1) Query data
|
%% 1) Query data
|
||||||
|
|
||||||
selectedPamLevels = [4, 6, 8];
|
selectedPamLevels = [4, 6, 8];
|
||||||
selectedFiberLengthKm = 1;
|
selectedFiberLengthKm = 10;
|
||||||
selectedWavelengthNm = 1310;
|
selectedWavelengthNm = 1310;
|
||||||
selectedBitrateGbps = 360;
|
selectedBitrateGbps = 420;
|
||||||
selectedIsMpi = 0; % set [] to use all entries
|
selectedIsMpi = 0; % set [] to use all entries
|
||||||
maxPowerPdIn = []; % set a numeric limit to enable
|
maxPowerPdIn = []; % set a numeric limit to enable
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ clear; clc;
|
|||||||
%% 1) Query data
|
%% 1) Query data
|
||||||
|
|
||||||
selectedPamLevels = [4, 6, 8];
|
selectedPamLevels = [4, 6, 8];
|
||||||
selectedFiberLengthKm = 5;
|
selectedFiberLengthKm = 10;
|
||||||
selectedBitratesGbps = 300:30:480; % set [] to use all available bitrates
|
selectedBitratesGbps = 300:30:480; % set [] to use all available bitrates
|
||||||
selectedRopAttenuation = 0; % set [] to use all ROP attenuation values
|
selectedRopAttenuation = 0; % set [] to use all ROP attenuation values
|
||||||
selectedIsMpi = 0; % set [] to use all entries
|
selectedIsMpi = 0; % set [] to use all entries
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ dsp_options.recipe = @dsp_400g_recipe;
|
|||||||
dsp_options.append_to_db = false;
|
dsp_options.append_to_db = false;
|
||||||
% dsp_options.append_mpi_reduction_db = false;
|
% dsp_options.append_mpi_reduction_db = false;
|
||||||
dsp_options.start_occurence = 1;
|
dsp_options.start_occurence = 1;
|
||||||
dsp_options.max_occurences = 3;
|
dsp_options.max_occurences = 16;
|
||||||
dsp_options.debug_plots = false;
|
dsp_options.debug_plots = false;
|
||||||
|
|
||||||
|
|
||||||
@@ -33,65 +33,148 @@ db = DBHandler("dataBase", [dsp_options.dataBase], ...
|
|||||||
"password", dsp_options.password);
|
"password", dsp_options.password);
|
||||||
|
|
||||||
%% Load normal Signal w/o preemphasis
|
%% 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');
|
cols = cbrewer2('Spectral',6);
|
||||||
[dataTable, query] = db.queryDB(fp, fields);
|
cols = linspecer(2);
|
||||||
|
len = [1 2 3 5 6 8 10];
|
||||||
|
idx = 1;
|
||||||
|
for lambda = 1302%[1297.5]
|
||||||
|
|
||||||
|
fp = QueryFilter();
|
||||||
|
len = 10;
|
||||||
|
fp.where('Runs','fiber_length','EQUALS', 10); % 1 2 3 5 6 8 10
|
||||||
|
fp.where('Runs','wavelength','EQUALS', lambda); %
|
||||||
|
fp.where('Runs','bitrate','EQUALS', 450e9);
|
||||||
|
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, S, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
||||||
|
|
||||||
|
average_signals = 1;
|
||||||
|
if average_signals
|
||||||
|
Scpe_sig_avg = S{1};
|
||||||
|
scope_mean = zeros(size(S{1}.signal));
|
||||||
|
for n=1:numel(S)
|
||||||
|
scope_mean = scope_mean + S{n}.signal;
|
||||||
|
end
|
||||||
|
scope_mean = scope_mean ./ n;
|
||||||
|
Scpe_sig_avg.signal = scope_mean;
|
||||||
|
ScopeSignal_preemph = preprocessSignal(Scpe_sig_avg, Symbols_preemph, Symbols_preemph.fs,"gaussian_cutoff_factor",0.8);
|
||||||
|
else
|
||||||
|
ScopeSignal = S{1};
|
||||||
|
ScopeSignal_preemph = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs);
|
||||||
|
end
|
||||||
|
|
||||||
|
fignum = 2;
|
||||||
|
dn = sprintf("Rx Spectrum; %d nm",floor(lambda));
|
||||||
|
ScopeSignal_preemph.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx,:));
|
||||||
|
|
||||||
[~, Symbols_preemph, Scpe_cell_preemph, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
if len == 10
|
||||||
ScopeSignal = Scpe_cell_preemph{1};
|
|
||||||
ScopeSignal_preemph = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs);
|
fp.where('Runs', 'db_mode','EQUALS', 2);
|
||||||
|
fields = db.getTableFieldNames('Runs');
|
||||||
|
[dataTable, query] = db.queryDB(fp, fields);
|
||||||
|
|
||||||
|
[~, Symbols_db, S, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
||||||
|
ScopeSignal = S{1};
|
||||||
|
average_signals = 1;
|
||||||
|
if average_signals
|
||||||
|
Scpe_sig_avg = S{1};
|
||||||
|
scope_mean = zeros(size(S{1}.signal));
|
||||||
|
for n=1:numel(S)
|
||||||
|
scope_mean = scope_mean + S{n}.signal;
|
||||||
|
end
|
||||||
|
scope_mean = scope_mean ./ n;
|
||||||
|
Scpe_sig_avg.signal = scope_mean;
|
||||||
|
ScopeSignal = preprocessSignal(Scpe_sig_avg, Symbols_preemph, Symbols_preemph.fs,"gaussian_cutoff_factor",0.8);
|
||||||
|
else
|
||||||
|
ScopeSignal = S{1};
|
||||||
|
ScopeSignal = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs);
|
||||||
|
end
|
||||||
|
|
||||||
|
ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs);
|
||||||
|
end
|
||||||
|
|
||||||
|
if len == 10
|
||||||
|
fignum = 2;
|
||||||
|
dn = sprintf("DBS Rx Spectrum; %d nm",floor(lambda));
|
||||||
|
ScopeSignal_DB.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx+1,:));
|
||||||
|
% Symbols_db.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',1024,'show_onesided',1,'color',cols(idx+1,:));
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% Fiber and system parameters
|
||||||
|
lambda0 = 1314e-9; % zero-dispersion wavelength [m]
|
||||||
|
lambda = lambda*1e-9; % operating wavelength [m]
|
||||||
|
S0 = 0.092; % dispersion slope [ps/(nm²·km)]
|
||||||
|
L = 10e3; % fiber length [m]
|
||||||
|
c = physconst('lightspeed');
|
||||||
|
|
||||||
|
% Derived quantities
|
||||||
|
S0_si = S0 * 1e3; % → s/m³
|
||||||
|
D_lambda = (S0/4) * (lambda*1e9 - (lambda0*1e9)^4/(lambda*1e9)^3); % ps/(nm·km)
|
||||||
|
D_si = D_lambda * 1e-6; % → s/m²
|
||||||
|
b2 = -D_si * lambda^2 / (2*pi*c); % s²/m
|
||||||
|
|
||||||
|
Dacc = D_lambda * L;
|
||||||
|
fprintf('Accumulated Dispersion: %.2f ps/nm \n', Dacc / 1e3);
|
||||||
|
|
||||||
|
% Frequency grid
|
||||||
|
f_max = 200e9;
|
||||||
|
f = linspace(0, f_max, 5000); % [Hz]
|
||||||
|
|
||||||
|
% IM/DD transfer function (power fading)
|
||||||
|
phi = 2*pi^2 * b2 * f.^2 * L;
|
||||||
|
H = abs(cos(phi));
|
||||||
|
|
||||||
|
% Plot
|
||||||
|
plot(f/1e9, 10*log10(H), 'LineWidth', 1,'Color','black','DisplayName','CD Transfer function','HandleVisibility','on');
|
||||||
|
grid on; box on;
|
||||||
|
xlabel('Frequency [GHz]');
|
||||||
|
ylabel('Magnitude [dB]');
|
||||||
|
% title(sprintf('IM/DD Power Fading: 10 km; 1275nm', lambda*1e9, L/1000),"Interpreter","latex");
|
||||||
|
% ylim([-20 0]);
|
||||||
|
|
||||||
|
idx = idx +1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
%% Load normal Signal w/o preemphasis
|
%% Load normal Signal w/o preemphasis
|
||||||
fp = QueryFilter();
|
fp.where('Runs', 'db_mode','EQUALS', 0);
|
||||||
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');
|
fields = db.getTableFieldNames('Runs');
|
||||||
[dataTable, query] = db.queryDB(fp, fields);
|
[dataTable, query] = db.queryDB(fp, fields);
|
||||||
|
|
||||||
[~, Symbols, Scpe_cell, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
[~, Symbols, Scpe_cell, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
||||||
ScopeSignal = Scpe_cell{1};
|
ScopeSignal = Scpe_cell{1};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ScopeSignal_no_preemph = preprocessSignal(ScopeSignal, Symbols, Symbols.fs);
|
ScopeSignal_no_preemph = preprocessSignal(ScopeSignal, Symbols, Symbols.fs);
|
||||||
|
|
||||||
|
fignum = len;
|
||||||
|
ScopeSignal_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',fignum,'normalizeTo0dB',0,'color',clr.Paired.dgreen,'fft_length',4096*6);
|
||||||
|
|
||||||
|
|
||||||
%% Duobinary
|
%% Duobinary
|
||||||
fp = QueryFilter();
|
if len == 10
|
||||||
fp.where('Runs','fiber_length','EQUALS', 10);
|
fp.where('Runs', 'db_mode','EQUALS', 2);
|
||||||
fp.where('Runs','wavelength','EQUALS', 1310);
|
|
||||||
fp.where('Runs','bitrate','EQUALS', 420e9);
|
fields = db.getTableFieldNames('Runs');
|
||||||
fp.where('Runs','pam_level','EQUALS', 4);
|
[dataTable, query] = db.queryDB(fp, fields);
|
||||||
fp.where('Runs','rop_attenuation','EQUALS', 0);
|
|
||||||
fp.where('Runs','is_mpi','EQUALS', 0);
|
[~, Symbols_db, Scpe_cell_db, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options);
|
||||||
fp.where('Runs', 'db_mode','EQUALS', 2);
|
ScopeSignal = Scpe_cell_db{1};
|
||||||
|
ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs);
|
||||||
|
end
|
||||||
|
|
||||||
fields = db.getTableFieldNames('Runs');
|
if len == 10
|
||||||
[dataTable, query] = db.queryDB(fp, fields);
|
fignum = 2;
|
||||||
|
dn = sprintf("DBS Rx Spectrum; %d nm",floor(lambda));
|
||||||
|
ScopeSignal_DB.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx,:));
|
||||||
|
end
|
||||||
|
|
||||||
[~, 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);
|
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
%% Best AIR and FEC data rates over all wavelengths for 2 km and 10 km
|
||||||
|
% The table follows the contender mapping used in the thesis figures:
|
||||||
|
% PAM-4: DB target + MLSE, with DBS + VNLE + MLSE at 10 km
|
||||||
|
% PAM-6: VNLE + PF + MLSE, with DBS + VNLE + MLSE at 10 km
|
||||||
|
% PAM-8: VNLE + PF + MLSE
|
||||||
|
%
|
||||||
|
% For every configured contender, the maximum valid rate is selected across
|
||||||
|
% the available gross rates and symbol rates. BER_precoded is used for the
|
||||||
|
% DB-target curve, as in FIGURE_NGMI_THESIS_FINAL.m.
|
||||||
|
|
||||||
|
clear;
|
||||||
|
|
||||||
|
%% Configuration
|
||||||
|
|
||||||
|
normalFiberLengthsKm = [2 10];
|
||||||
|
duobinaryFiberLengthKm = 10;
|
||||||
|
selectedRopAttenuation = 0;
|
||||||
|
selectedIsMpi = 0;
|
||||||
|
duobinaryDateCutoff = datetime("2026-01-01 00:00:00");
|
||||||
|
|
||||||
|
curves = struct( ...
|
||||||
|
"name", {"DBt. + MLSE"; "DBS + VNLE + MLSE"; ...
|
||||||
|
"VNLE + PF + MLSE"; "DBS + VNLE + MLSE"; ...
|
||||||
|
"VNLE + PF + MLSE"}, ...
|
||||||
|
"algorithm_key", {"vnle_db_mlse"; "db_encoded"; ...
|
||||||
|
"vnle_pf_mlse"; "db_encoded"; ...
|
||||||
|
"vnle_pf_mlse"}, ...
|
||||||
|
"pam", {4; 4; 6; 6; 8}, ...
|
||||||
|
"source", {"normal"; "duobinary"; "normal"; "duobinary"; "normal"}, ...
|
||||||
|
"usePrecodedBer", {true; false; false; false; false});
|
||||||
|
|
||||||
|
%% Query the normal and DBS data
|
||||||
|
|
||||||
|
db = DBHandler( ...
|
||||||
|
"dataBase", "labor_highspeed", ...
|
||||||
|
"type", "mysql", ...
|
||||||
|
"server", "192.168.178.192", ...
|
||||||
|
"user", "silas", ...
|
||||||
|
"password", "silas");
|
||||||
|
db.refresh();
|
||||||
|
selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime');
|
||||||
|
|
||||||
|
normalRows = cell(size(normalFiberLengthsKm));
|
||||||
|
for lengthIdx = 1:numel(normalFiberLengthsKm)
|
||||||
|
normalRows{lengthIdx} = queryRows(db, selectedFields, ...
|
||||||
|
normalFiberLengthsKm(lengthIdx), ...
|
||||||
|
selectedRopAttenuation, selectedIsMpi);
|
||||||
|
normalRows{lengthIdx} = cleanRows(normalRows{lengthIdx});
|
||||||
|
normalRows{lengthIdx} = normalRows{lengthIdx}(ismember( ...
|
||||||
|
normalRows{lengthIdx}.db_mode, ...
|
||||||
|
[double(db_mode.no_db), double(db_mode.db_precoded)]), :);
|
||||||
|
normalRows{lengthIdx}.algorithm_key = ...
|
||||||
|
lower(string(normalRows{lengthIdx}.equalizer_structure));
|
||||||
|
fprintf("Normal %g km rows: %d\n", normalFiberLengthsKm(lengthIdx), ...
|
||||||
|
height(normalRows{lengthIdx}));
|
||||||
|
end
|
||||||
|
|
||||||
|
duobinaryRows = queryRows(db, selectedFields, duobinaryFiberLengthKm, ...
|
||||||
|
selectedRopAttenuation, selectedIsMpi);
|
||||||
|
duobinaryRows = cleanRows(duobinaryRows);
|
||||||
|
duobinaryRows = duobinaryRows(duobinaryRows.db_mode == ...
|
||||||
|
double(db_mode.db_encoded), :);
|
||||||
|
duobinaryRows = duobinaryRows( ...
|
||||||
|
duobinaryRows.equalizer_structure == "db_encoded", :);
|
||||||
|
if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames))
|
||||||
|
processedDate = datetime(string(duobinaryRows.date_of_processing));
|
||||||
|
duobinaryRows = duobinaryRows(processedDate > duobinaryDateCutoff, :);
|
||||||
|
else
|
||||||
|
warning("table_best_rates:NoProcessingDate", ...
|
||||||
|
"date_of_processing was not returned; no DBS date filter was applied.");
|
||||||
|
end
|
||||||
|
duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1);
|
||||||
|
fprintf("DBS %g km rows after date filter: %d\n", ...
|
||||||
|
duobinaryFiberLengthKm, height(duobinaryRows));
|
||||||
|
|
||||||
|
%% Calculate maximum rates
|
||||||
|
|
||||||
|
tp = TransmissionPerformance;
|
||||||
|
resultRows = repmat(emptyResultRow(), 0, 1);
|
||||||
|
|
||||||
|
for lengthIdx = 1:numel(normalFiberLengthsKm)
|
||||||
|
for curveIdx = 1:numel(curves)
|
||||||
|
curve = curves(curveIdx);
|
||||||
|
if curve.source ~= "normal"
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
resultRows(end+1) = evaluateCurve(normalRows{lengthIdx}, curve, ...
|
||||||
|
normalFiberLengthsKm(lengthIdx), tp); %#ok<SAGROW>
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for curveIdx = 1:numel(curves)
|
||||||
|
curve = curves(curveIdx);
|
||||||
|
if curve.source ~= "duobinary"
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
resultRows(end+1) = evaluateCurve(duobinaryRows, curve, ...
|
||||||
|
duobinaryFiberLengthKm, tp); %#ok<SAGROW>
|
||||||
|
end
|
||||||
|
|
||||||
|
ratesTable = struct2table(resultRows);
|
||||||
|
ratesTable = ratesTable(:, ...
|
||||||
|
["pam", "fiber_length_km", "technique", "AIR_Gbps", ...
|
||||||
|
"SDHD_Gbps", "HD_Gbps", "AIR_grossrate_Gbps", ...
|
||||||
|
"SDHD_grossrate_Gbps", "HD_grossrate_Gbps", ...
|
||||||
|
"AIR_symbolrate_GBd", "SDHD_symbolrate_GBd", ...
|
||||||
|
"HD_symbolrate_GBd"]);
|
||||||
|
|
||||||
|
disp("Best rates (Gb/s):");
|
||||||
|
disp(ratesTable(:, ["pam", "fiber_length_km", "technique", ...
|
||||||
|
"AIR_Gbps", "SDHD_Gbps", "HD_Gbps"]));
|
||||||
|
|
||||||
|
displayTable = table( ...
|
||||||
|
ratesTable.pam(:), ...
|
||||||
|
ratesTable.fiber_length_km(:), ...
|
||||||
|
string(ratesTable.technique(:)), ...
|
||||||
|
formatRateWithSymbol(ratesTable.AIR_Gbps(:), ratesTable.AIR_symbolrate_GBd(:)), ...
|
||||||
|
formatRateWithSymbol(ratesTable.SDHD_Gbps(:), ratesTable.SDHD_symbolrate_GBd(:)), ...
|
||||||
|
formatRateWithSymbol(ratesTable.HD_Gbps(:), ratesTable.HD_symbolrate_GBd(:)), ...
|
||||||
|
'VariableNames', {'PAM', 'Distance_km', 'EQ_contender', ...
|
||||||
|
'AIR', 'SDHD_FEC', 'HD_FEC'});
|
||||||
|
disp("Best rates with the symbol rate used for each metric:");
|
||||||
|
disp(displayTable);
|
||||||
|
|
||||||
|
%% Local functions
|
||||||
|
|
||||||
|
function T = queryRows(db, fields, fiberLengthKm, ropAttenuation, isMpi)
|
||||||
|
fp = QueryFilter();
|
||||||
|
fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm);
|
||||||
|
fp.where("Runs", "rop_attenuation", "EQUALS", ropAttenuation);
|
||||||
|
fp.where("Runs", "is_mpi", "EQUALS", isMpi);
|
||||||
|
[T, query] = db.queryDB(fp, fields);
|
||||||
|
disp(query);
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = cleanRows(T)
|
||||||
|
numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ...
|
||||||
|
"symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ...
|
||||||
|
"rop_attenuation", "numBits", "numBitErr", "BER", ...
|
||||||
|
"numBitErr_precoded", "BER_precoded", "GMI", "AIR", "NGMI"];
|
||||||
|
for fieldIdx = 1:numel(numericFields)
|
||||||
|
fieldName = numericFields(fieldIdx);
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
T.(char(fieldName)) = numericColumn(T.(char(fieldName)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
T.equalizer_structure = lower(string(T.equalizer_structure));
|
||||||
|
end
|
||||||
|
|
||||||
|
function result = evaluateCurve(sourceRows, curve, fiberLengthKm, tp)
|
||||||
|
result = emptyResultRow();
|
||||||
|
result.pam = curve.pam;
|
||||||
|
result.fiber_length_km = fiberLengthKm;
|
||||||
|
result.technique = curve.name;
|
||||||
|
|
||||||
|
curveRows = sourceRows(sourceRows.pam_level == curve.pam & ...
|
||||||
|
sourceRows.algorithm_key == curve.algorithm_key, :);
|
||||||
|
if isempty(curveRows)
|
||||||
|
warning("table_best_rates:NoRows", ...
|
||||||
|
"No rows found for %s, PAM-%d, %g km.", ...
|
||||||
|
curve.name, curve.pam, fiberLengthKm);
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows.BER_plot = numericOrNaN(curveRows, "BER");
|
||||||
|
if curve.usePrecodedBer && ismember("BER_precoded", ...
|
||||||
|
string(curveRows.Properties.VariableNames))
|
||||||
|
precodedBer = numericOrNaN(curveRows, "BER_precoded");
|
||||||
|
usePrecoded = isfinite(precodedBer);
|
||||||
|
curveRows.BER_plot(usePrecoded) = precodedBer(usePrecoded);
|
||||||
|
end
|
||||||
|
|
||||||
|
curveRows = addAirMetric(curveRows);
|
||||||
|
grossRate = numericOrNaN(curveRows, "grossrate");
|
||||||
|
ngmi = numericOrNaN(curveRows, "NGMI");
|
||||||
|
ber = numericOrNaN(curveRows, "BER_plot");
|
||||||
|
ngmi(~isfinite(ngmi) | ngmi < 0 | ngmi > 1.05) = NaN;
|
||||||
|
ber(~isfinite(ber) | ber <= 0 | ber > 0.5) = NaN;
|
||||||
|
|
||||||
|
ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber);
|
||||||
|
curveRows.SDHD_Gbps = columnVector(ndr.SDHD.NetRate) .* 1e-9;
|
||||||
|
curveRows.HD_Gbps = columnVector(ndr.O_FEC.NetRate) .* 1e-9;
|
||||||
|
|
||||||
|
result = assignMaximum(result, curveRows, "AIR_Gbps");
|
||||||
|
result = assignMaximum(result, curveRows, "SDHD_Gbps");
|
||||||
|
result = assignMaximum(result, curveRows, "HD_Gbps");
|
||||||
|
end
|
||||||
|
|
||||||
|
function result = assignMaximum(result, T, fieldName)
|
||||||
|
values = numericOrNaN(T, fieldName);
|
||||||
|
valid = isfinite(values) & values >= 0;
|
||||||
|
if ~any(valid)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
[maximum, localIndex] = max(values(valid));
|
||||||
|
validRows = find(valid);
|
||||||
|
rowIndex = validRows(localIndex);
|
||||||
|
prefix = erase(fieldName, "_Gbps");
|
||||||
|
result.(fieldName) = maximum;
|
||||||
|
result.(char(prefix + "_grossrate_Gbps")) = ...
|
||||||
|
numericOrNaN(T(rowIndex, :), "grossrate") .* 1e-9;
|
||||||
|
result.(char(prefix + "_symbolrate_GBd")) = ...
|
||||||
|
numericOrNaN(T(rowIndex, :), "symbolrate") .* 1e-9;
|
||||||
|
end
|
||||||
|
|
||||||
|
function T = addAirMetric(T)
|
||||||
|
T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9;
|
||||||
|
gmi = numericOrNaN(T, "GMI");
|
||||||
|
fallbackAir = gmi .* numericOrNaN(T, "symbolrate") .* 1e-9;
|
||||||
|
grossRate = numericOrNaN(T, "grossrate");
|
||||||
|
useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ...
|
||||||
|
(isfinite(grossRate) & T.AIR_Gbps > grossRate .* 1.05e-9);
|
||||||
|
T.AIR_Gbps(useFallback) = fallbackAir(useFallback);
|
||||||
|
end
|
||||||
|
|
||||||
|
function result = emptyResultRow()
|
||||||
|
result = struct( ...
|
||||||
|
"pam", NaN, ...
|
||||||
|
"fiber_length_km", NaN, ...
|
||||||
|
"technique", "", ...
|
||||||
|
"AIR_Gbps", NaN, ...
|
||||||
|
"SDHD_Gbps", NaN, ...
|
||||||
|
"HD_Gbps", NaN, ...
|
||||||
|
"AIR_grossrate_Gbps", NaN, ...
|
||||||
|
"SDHD_grossrate_Gbps", NaN, ...
|
||||||
|
"HD_grossrate_Gbps", NaN, ...
|
||||||
|
"AIR_symbolrate_GBd", NaN, ...
|
||||||
|
"SDHD_symbolrate_GBd", NaN, ...
|
||||||
|
"HD_symbolrate_GBd", NaN);
|
||||||
|
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 = numericOrNaN(T, fieldName)
|
||||||
|
if ismember(fieldName, string(T.Properties.VariableNames))
|
||||||
|
values = numericColumn(T.(char(fieldName)));
|
||||||
|
else
|
||||||
|
values = NaN(height(T), 1);
|
||||||
|
end
|
||||||
|
values = values(:);
|
||||||
|
end
|
||||||
|
|
||||||
|
function values = columnVector(values)
|
||||||
|
values = double(values(:));
|
||||||
|
end
|
||||||
|
|
||||||
|
function text = formatRateWithSymbol(rateGbps, symbolrateGBd)
|
||||||
|
text = strings(size(rateGbps));
|
||||||
|
valid = isfinite(rateGbps) & isfinite(symbolrateGBd);
|
||||||
|
text(valid) = compose("%.0f (%.0f GBd)", ...
|
||||||
|
rateGbps(valid), symbolrateGBd(valid));
|
||||||
|
text(~valid) = "--";
|
||||||
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
rates = [420e9];
|
rates = [400e9];
|
||||||
rcalpha = 0.05;
|
rcalpha = 0.05;
|
||||||
fsym = rates/2;
|
fsym = rates/2;
|
||||||
apply_pulsef = 1;
|
apply_pulsef = 1;
|
||||||
@@ -56,8 +56,6 @@ Digi_sig_rx.spectrum(...
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Digi_sig_pre_rx.spectrum(...
|
Digi_sig_pre_rx.spectrum(...
|
||||||
"displayname",'Rx w/ pre-emphasis',...
|
"displayname",'Rx w/ pre-emphasis',...
|
||||||
"fignum",2,"normalizeTo0dB",0,"color",clr.Paired.dblue);
|
"fignum",2,"normalizeTo0dB",0,"color",clr.Paired.dblue);
|
||||||
@@ -80,6 +78,9 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"al
|
|||||||
"mrds_code",0,"mrds_blocklength",512).process();
|
"mrds_code",0,"mrds_blocklength",512).process();
|
||||||
Digi_sig_DB= Digi_sig_DB.normalize("mode","oneone");
|
Digi_sig_DB= Digi_sig_DB.normalize("mode","oneone");
|
||||||
|
|
||||||
|
Digi_sig_DB_rx = precomp_est.apply(...
|
||||||
|
Digi_sig_DB,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn);
|
||||||
|
|
||||||
maxamp = -38; %optimized for DB!
|
maxamp = -38; %optimized for DB!
|
||||||
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig_DB.fs);
|
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig_DB.fs);
|
||||||
precomp_path = "W:\labdata\sioe_labor\precomp";
|
precomp_path = "W:\labdata\sioe_labor\precomp";
|
||||||
@@ -89,27 +90,33 @@ 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.resample("fs_out",256e9);
|
||||||
Digi_sig_DB_pre = Digi_sig_DB_pre.normalize("mode","oneone");
|
Digi_sig_DB_pre = Digi_sig_DB_pre.normalize("mode","oneone");
|
||||||
|
|
||||||
|
Digi_sig_DB_pre_rx = precomp_est.apply(...
|
||||||
|
Digi_sig_DB_pre,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn);
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
AWG_ = M8199B("kover",4);
|
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);
|
% 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 = AWG_.process(Digi_sig);
|
||||||
El_sig_preemph = AWG_.process(Digi_sig_pre);
|
El_sig_preemph = AWG_.process(Digi_sig_pre);
|
||||||
El_sig_channel = AWG_.process(Digi_sig_channel);
|
% 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.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_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_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_preemph = AWG_.process(Digi_sig_DB_pre);
|
||||||
El_sig_DB = AWG_.process(Digi_sig_DB);
|
El_sig_DB = AWG_.process(Digi_sig_DB);
|
||||||
|
El_sig_DB_rx = AWG_.process(Digi_sig_DB_pre_rx);
|
||||||
|
|
||||||
|
Digi_sig_DB.spectrum("displayname",'DB Response w/o preemphasis','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.lorange);
|
||||||
|
Digi_sig_DB_pre.spectrum("displayname",'DB Response w/ preemphasis','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.dorange);
|
||||||
|
|
||||||
El_sig_DB.spectrum("displayname",'DB Response w/o preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.lorange);
|
Digi_sig_DB_rx.spectrum("displayname",'DB Response w/o preemphasis at Rx','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.lred);
|
||||||
El_sig_DB_preemph.spectrum("displayname",'DB Response w/ preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.dorange);
|
Digi_sig_DB_pre_rx.spectrum("displayname",'DB Response w/ preemphasis at Rx','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.dred);
|
||||||
@@ -5,7 +5,7 @@ dsp_options.recipe = @dsp_400g_recipe;
|
|||||||
dsp_options.append_to_db = false;
|
dsp_options.append_to_db = false;
|
||||||
% dsp_options.append_mpi_reduction_db = false;
|
% dsp_options.append_mpi_reduction_db = false;
|
||||||
dsp_options.start_occurence = 1;
|
dsp_options.start_occurence = 1;
|
||||||
dsp_options.max_occurences = 3;
|
dsp_options.max_occurences = 1;
|
||||||
dsp_options.debug_plots = false;
|
dsp_options.debug_plots = false;
|
||||||
|
|
||||||
|
|
||||||
@@ -36,11 +36,12 @@ maxRunIds = 1; % keep small until the recipe settings are sett
|
|||||||
fp = QueryFilter();
|
fp = QueryFilter();
|
||||||
fp.where('Runs','fiber_length','EQUALS', 10);
|
fp.where('Runs','fiber_length','EQUALS', 10);
|
||||||
fp.where('Runs','wavelength','EQUALS', 1310);
|
fp.where('Runs','wavelength','EQUALS', 1310);
|
||||||
% fp.where('Runs','bitrate','LESS_THAN', 480e9);
|
% fp.where('Runs','bitrate','LESS_THAN', 330e9);
|
||||||
% fp.where('Runs','pam_level','EQUALS', 4);
|
fp.where('Runs','symbolrate','EQUALS', 174e9);
|
||||||
|
fp.where('Runs','pam_level','EQUALS', 6);
|
||||||
fp.where('Runs','rop_attenuation','EQUALS', 0);
|
fp.where('Runs','rop_attenuation','EQUALS', 0);
|
||||||
fp.where('Runs','is_mpi','EQUALS', 0);
|
fp.where('Runs','is_mpi','EQUALS', 0);
|
||||||
% fp.where('Runs', 'db_mode','EQUALS', 2);
|
fp.where('Runs', 'db_mode','EQUALS', 1);
|
||||||
|
|
||||||
fields = db.getTableFieldNames('Runs');
|
fields = db.getTableFieldNames('Runs');
|
||||||
[dataTable, query] = db.queryDB(fp, fields);
|
[dataTable, query] = db.queryDB(fp, fields);
|
||||||
@@ -67,7 +68,7 @@ dsp_options.userParameters = struct();
|
|||||||
% dsp_options.userParameters.len_tr = 4096*2;
|
% dsp_options.userParameters.len_tr = 4096*2;
|
||||||
% dsp_options.userParameters.pf_ncoeffs = [1,2,3];
|
% dsp_options.userParameters.pf_ncoeffs = [1,2,3];
|
||||||
|
|
||||||
dsp_options.userParameters.decoding_mode = [db_decoder.sequencedetection];
|
% dsp_options.userParameters.decoding_mode = [db_decoder.sequencedetection];
|
||||||
% dsp_options.userParameters.pf_ncoeffs = [1, 2, 3];
|
% dsp_options.userParameters.pf_ncoeffs = [1, 2, 3];
|
||||||
% dsp_options.userParameters.mu_dc = [0, 1e-5, 1e-4];
|
% dsp_options.userParameters.mu_dc = [0, 1e-5, 1e-4];
|
||||||
% dsp_options.userParameters.run_ml_mlse_db = [false, true];
|
% dsp_options.userParameters.run_ml_mlse_db = [false, true];
|
||||||
@@ -86,7 +87,7 @@ fprintf("-> [ %d run_id(s) x %d userParam combination(s) = %d job(s) ] x %d real
|
|||||||
|
|
||||||
%% Run
|
%% Run
|
||||||
|
|
||||||
[results, wh] = submitJobs(run_ids, dsp_options, processingMode.parallel, ...
|
[results, wh] = submitJobs(run_ids, dsp_options, processingMode.serial, ...
|
||||||
"wh", wh, ...
|
"wh", wh, ...
|
||||||
"waitbar", true);
|
"waitbar", true);
|
||||||
|
|
||||||
|
|||||||
@@ -210,8 +210,8 @@ classdef ml_mlse_pam < handle
|
|||||||
% ==============================================================
|
% ==============================================================
|
||||||
% ML-Based Branch Metric Estimation + Viterbi
|
% ML-Based Branch Metric Estimation + Viterbi
|
||||||
% ==============================================================
|
% ==============================================================
|
||||||
% debug = 1;
|
debug = 0;
|
||||||
% showPlots = 1;
|
showPlots = 0;
|
||||||
|
|
||||||
nSymbols = ceil(N/obj.sps);
|
nSymbols = ceil(N/obj.sps);
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ ber_ml_mlse_l4 = zeros(size(SNR_dB));
|
|||||||
|
|
||||||
epochs_training = 100;
|
epochs_training = 100;
|
||||||
|
|
||||||
parfor i = 1:numel(SNR_dB)
|
for i = 1:numel(SNR_dB)
|
||||||
|
|
||||||
symbols_noi = symbols_filt;
|
symbols_noi = symbols_filt;
|
||||||
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR
|
symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR
|
||||||
@@ -73,46 +73,46 @@ parfor i = 1:numel(SNR_dB)
|
|||||||
[~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
|
[~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
|
||||||
fprintf('FFE: %.2e \n',ber_ffe(i));
|
fprintf('FFE: %.2e \n',ber_ffe(i));
|
||||||
|
|
||||||
% Postfilter
|
% % Postfilter
|
||||||
[y_white,~] = pf_.process(y_ffe, ffe_noise);
|
% [y_white,~] = pf_.process(y_ffe, ffe_noise);
|
||||||
|
%
|
||||||
% Sequence Est
|
% % Sequence Est
|
||||||
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
|
% mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients);
|
||||||
[y_mlse] = mlse_.process(y_white,Symbols);
|
% [y_mlse] = mlse_.process(y_white,Symbols);
|
||||||
mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
|
% mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse);
|
||||||
[~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
% [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
||||||
fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
|
% fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i));
|
||||||
|
%
|
||||||
% ML-base MLSE L=2
|
% % ML-base MLSE L=2
|
||||||
adaptive_mu = 0;
|
% adaptive_mu = 0;
|
||||||
mu_lms = 0.15;
|
|
||||||
ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
|
|
||||||
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
|
||||||
"traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu);
|
|
||||||
[y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
|
||||||
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
|
||||||
[~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
|
||||||
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i));
|
|
||||||
|
|
||||||
% ML-base MLSE L=3
|
|
||||||
mu_lms = 0.15;
|
|
||||||
ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,...
|
|
||||||
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
|
||||||
"traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu);
|
|
||||||
[y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
|
||||||
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
|
||||||
[~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
|
||||||
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i));
|
|
||||||
|
|
||||||
% % ML-base MLSE L=5
|
|
||||||
% mu_lms = 0.15;
|
% mu_lms = 0.15;
|
||||||
% ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
|
% ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
|
||||||
% "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
% "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
||||||
% "traceback_depth",128,"L",5,"delta",4);
|
% "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu);
|
||||||
% [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
% [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
||||||
% ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
% ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
||||||
% [~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
% [~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
||||||
% fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i));
|
% fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i));
|
||||||
|
%
|
||||||
|
% % ML-base MLSE L=3
|
||||||
|
% mu_lms = 0.15;
|
||||||
|
% ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,...
|
||||||
|
% "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
||||||
|
% "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu);
|
||||||
|
% [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
||||||
|
% ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
||||||
|
% [~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
||||||
|
% fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i));
|
||||||
|
|
||||||
|
% ML-base MLSE L=5
|
||||||
|
mu_lms = 0.15;
|
||||||
|
ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,...
|
||||||
|
"mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,...
|
||||||
|
"traceback_depth",128,"L",5,"delta",4);
|
||||||
|
[y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols);
|
||||||
|
ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse);
|
||||||
|
[~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1);
|
||||||
|
fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i));
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
base = "C:\Users\Silas\Nextcloud4\Cluster";
|
base = "C:\Users\Silas\Nextcloud2\CAU\Cluster";
|
||||||
all_files = dir(fullfile(base, "**/*.mat"));
|
all_files = dir(fullfile(base, "**/*.mat"));
|
||||||
|
|
||||||
schemes = ["co","pair","alt","seg"];
|
schemes = ["co","pair","alt","seg"];
|
||||||
@@ -77,7 +77,7 @@ res_all = drop_empty_realizations(res_all);
|
|||||||
S = plot_BER_vs_ROP(res_all, 'fec', 3.8e-3);
|
S = plot_BER_vs_ROP(res_all, 'fec', 3.8e-3);
|
||||||
|
|
||||||
%% Routine B: violin plot (independent)
|
%% Routine B: violin plot (independent)
|
||||||
plot_FEC_violin(res_all, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0],'eval_ptr',5);
|
plot_FEC_violin(res_all, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0],'eval_ptr',10);
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ function WDM_model(options)
|
|||||||
arguments
|
arguments
|
||||||
options.num_channels = 16;
|
options.num_channels = 16;
|
||||||
options.channel_spacing = 400e9;
|
options.channel_spacing = 400e9;
|
||||||
options.fiber_length_km = 0;
|
options.fiber_length_km = 1;
|
||||||
options.rand_key = 1;
|
options.rand_key = 1;
|
||||||
options.num_realiz = 1;
|
options.num_realiz = 1;
|
||||||
options.fwm_mitigation_technique = "co";
|
options.fwm_mitigation_technique = "pair";
|
||||||
end
|
end
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@@ -72,7 +72,6 @@ host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end
|
|||||||
fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique);
|
fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique);
|
||||||
|
|
||||||
|
|
||||||
%%
|
|
||||||
s.num_realiz = options.num_realiz;
|
s.num_realiz = options.num_realiz;
|
||||||
% s.wavelengthplan = calcWavelengthPlan(16,400e9,1310);
|
% s.wavelengthplan = calcWavelengthPlan(16,400e9,1310);
|
||||||
s.wavelengthplan = calcWavelengthPlan(options.num_channels,options.channel_spacing,1310);
|
s.wavelengthplan = calcWavelengthPlan(options.num_channels,options.channel_spacing,1310);
|
||||||
@@ -162,7 +161,7 @@ end
|
|||||||
for realiz = 1:s.num_realiz
|
for realiz = 1:s.num_realiz
|
||||||
|
|
||||||
|
|
||||||
parfor l = 1:N
|
for l = 1:N
|
||||||
|
|
||||||
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(...
|
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(...
|
||||||
"fsym",fsym,"M",s.M,"order",18,"useprbs",0,...
|
"fsym",fsym,"M",s.M,"order",18,"useprbs",0,...
|
||||||
@@ -199,9 +198,9 @@ for realiz = 1:s.num_realiz
|
|||||||
|
|
||||||
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
|
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
|
||||||
|
|
||||||
% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0);
|
Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',1);
|
||||||
|
|
||||||
% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2);
|
%% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2);
|
||||||
|
|
||||||
%%%%%% Fiber %%%%%%
|
%%%%%% Fiber %%%%%%
|
||||||
Opt_sig_wdm_fib=Opt_sig_wdm;
|
Opt_sig_wdm_fib=Opt_sig_wdm;
|
||||||
@@ -219,20 +218,17 @@ for realiz = 1:s.num_realiz
|
|||||||
"beat_len",10,"corr_len",100,"dz",1,"manakov",0,...
|
"beat_len",10,"corr_len",100,"dz",1,"manakov",0,...
|
||||||
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,...
|
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,...
|
||||||
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib);
|
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib);
|
||||||
|
|
||||||
|
|
||||||
propdist = segment_length;
|
propdist = segment_length;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
%%%%%% Demux after 2 km %%%%%%
|
%% %%%% Demux after 2 km %%%%%%
|
||||||
Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib);
|
Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib);
|
||||||
|
|
||||||
for ri = 1:length(s.rop)
|
for ri = 1:length(s.rop)
|
||||||
|
|
||||||
parfor l = 1:N
|
for l = 1:N
|
||||||
|
|
||||||
%%%%%% ROP %%%%%%
|
%%%%%% ROP %%%%%%
|
||||||
Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); % rop+10*log10(N)
|
Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); % rop+10*log10(N)
|
||||||
@@ -276,14 +272,13 @@ for realiz = 1:s.num_realiz
|
|||||||
|
|
||||||
output_ffe{l,ri,realiz} = ffe_results;
|
output_ffe{l,ri,realiz} = ffe_results;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%VNLE
|
%VNLE
|
||||||
pf_ncoeffs = 1;
|
pf_ncoeffs = 1;
|
||||||
ffe_order = [50, 5, 5];
|
ffe_order = [50, 5, 5];
|
||||||
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
|
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
|
||||||
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
|
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
|
||||||
|
|
||||||
|
|
||||||
useviterbi = 0;
|
useviterbi = 0;
|
||||||
if useviterbi
|
if useviterbi
|
||||||
mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels);
|
mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels);
|
||||||
|
|||||||
BIN
tmp/pdfs/paper-2.png
Normal file
BIN
tmp/pdfs/paper-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 685 KiB |
BIN
tmp/pdfs/paper-3.png
Normal file
BIN
tmp/pdfs/paper-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 480 KiB |
BIN
workerError.mat
BIN
workerError.mat
Binary file not shown.
Reference in New Issue
Block a user