453 lines
16 KiB
Matlab
453 lines
16 KiB
Matlab
function output = dsp_400g_recipe(Scpe_sig_raw, Symbols, Tx_bits, options)
|
|
%dsp_400g_recipe Run 400G equalizer schemes from a synchronized scope signal.
|
|
|
|
arguments
|
|
Scpe_sig_raw
|
|
Symbols
|
|
Tx_bits
|
|
options.fsym
|
|
options.M
|
|
options.duob_mode
|
|
options.dataTable table
|
|
options.userParameters struct = struct()
|
|
options.preprocess_mode string = "auto"
|
|
options.tx_pulseformer = []
|
|
options.debug_plots (1,1) logical = false
|
|
end
|
|
|
|
p = defaultRecipeParameters();
|
|
p.preprocess_mode = options.preprocess_mode;
|
|
p = applyUserParameters(p, options.userParameters);
|
|
|
|
output = struct();
|
|
|
|
Scpe_sig = preprocessSignal(Scpe_sig_raw, Symbols, options.fsym, ...
|
|
"mode", p.preprocess_mode, ...
|
|
"tx_pulseformer", options.tx_pulseformer, ...
|
|
"debug_plots", options.debug_plots);
|
|
[Scpe_sig, Symbols, Tx_bits] = alignDspInputs(Scpe_sig, Symbols, Tx_bits, p.eq_sps);
|
|
if isempty(p.ml_mlse_len_tr)
|
|
p.ml_mlse_len_tr = floor(length(Scpe_sig) / 4);
|
|
end
|
|
|
|
if p.plot_input_signal
|
|
showLevelScatter(Scpe_sig.normalize("mode","rms"), Symbols, ...
|
|
"fsym", options.fsym, ...
|
|
"fignum", p.input_plot_fignum, ...
|
|
"normalize", true);
|
|
end
|
|
|
|
if options.duob_mode ~= db_mode.db_encoded
|
|
if p.run_ffe
|
|
eq_ffe = FFE("epochs_tr", p.epochs_tr, ...
|
|
"epochs_dd", p.epochs_dd, ...
|
|
"len_tr", p.len_tr, ...
|
|
"mu_dd", p.ffe_mu_dd, ...
|
|
"mu_tr", p.ffe_mu_tr, ...
|
|
"order", p.ffe_order(1), ...
|
|
"sps", p.eq_sps, ...
|
|
"decide", false, ...
|
|
"optmize_mus", p.optimize_mus, ...
|
|
"dd_mode", p.dd_mode, ...
|
|
"adaption_technique", p.ffe_adaption, ...
|
|
"dc_tracking_mu", p.mu_dc);
|
|
|
|
[ffe_results, equalized_signal] = runFfe(eq_ffe, "FFE", ...
|
|
Scpe_sig, Symbols, Tx_bits, options);
|
|
ffe_results.config.equalizer_structure = equalizer_structure.ffe;
|
|
ffe_results.recipe_config = collectRecipeConfig("ffe", eq_ffe, p, options);
|
|
output.ffe_package = ffe_results;
|
|
|
|
if p.plot_output_signals
|
|
plotEqSignals(equalized_signal, Symbols, options, p.output_plot_fignum, -1);
|
|
end
|
|
end
|
|
|
|
|
|
if p.run_vnle
|
|
% eq_vnle = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2, ...
|
|
% "mu_dd",[0.0004 0.0005 0.0006],"mu_tr",[0.0001 0.0008 0.001], ...
|
|
% "order",[150,5,5],"sps",2,"decide",1, ...
|
|
% "optmize_mus",1,"mu_optimization_len",2^15);
|
|
|
|
eq_vnle = EQ("Ne", p.vnle_ffe_order, ...
|
|
"Nb", p.vnle_dfe_order, ...
|
|
"training_length", p.len_tr, ...
|
|
"training_loops", p.epochs_tr, ...
|
|
"dd_loops", p.epochs_dd, ...
|
|
"K", p.eq_K, ...
|
|
"DCmu", p.mu_dc, ...
|
|
"DDmu", [p.eq_mu_ffe p.mu_dfe], ...
|
|
"DFEmu", p.dfe_mu_feedback, ...
|
|
"FFEmu", 0, ...
|
|
"plotfinal", 0, ...
|
|
"ideal_dfe", false);
|
|
|
|
[vnle_results, equalized_signal] = runFfe(eq_vnle, "VNLE", ...
|
|
Scpe_sig, Symbols, Tx_bits, options);
|
|
vnle_results.config.equalizer_structure = equalizer_structure.ffe;
|
|
ffe_results.recipe_config = collectRecipeConfig("vnle", eq_vnle, p, options);
|
|
output.vnle_package = vnle_results;
|
|
|
|
if p.plot_output_signals
|
|
plotEqSignals(equalized_signal, Symbols, options, p.output_plot_fignum, -1);
|
|
end
|
|
end
|
|
|
|
|
|
if p.run_dfe
|
|
eq_dfe = EQ("Ne", p.dfe_ffe_order, ...
|
|
"Nb", p.dfe_feedback_order, ...
|
|
"training_length", p.len_tr, ...
|
|
"training_loops", p.epochs_tr, ...
|
|
"dd_loops", p.epochs_dd, ...
|
|
"K", p.eq_K, ...
|
|
"DCmu", p.mu_dc, ...
|
|
"DDmu", [p.eq_mu_ffe p.mu_dfe], ...
|
|
"DFEmu", p.dfe_mu_feedback, ...
|
|
"FFEmu", 0, ...
|
|
"plotfinal", 0, ...
|
|
"ideal_dfe", false);
|
|
|
|
dfe_results = ffe(eq_dfe, options.M, Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", [], ...
|
|
"eth_style_symbol_mapping", 0);
|
|
dfe_results.config.equalizer_structure = equalizer_structure.dfe;
|
|
dfe_results.metrics.print("description", resultDescription("DFE", options));
|
|
dfe_results.recipe_config = collectRecipeConfig("dfe", eq_dfe, p, options);
|
|
output.dfe_package = dfe_results;
|
|
end
|
|
|
|
if p.run_vnle_mlse
|
|
eq_vnle = EQ("Ne", p.vnle_ffe_order, ...
|
|
"Nb", p.vnle_dfe_order, ...
|
|
"training_length", p.len_tr, ...
|
|
"training_loops", p.epochs_tr, ...
|
|
"dd_loops", p.epochs_dd, ...
|
|
"K", p.eq_K, ...
|
|
"DCmu", p.mu_dc, ...
|
|
"DDmu", [p.eq_mu_ffe p.mu_dfe], ...
|
|
"DFEmu", p.dfe_mu_feedback, ...
|
|
"FFEmu", 0, ...
|
|
"plotfinal", 0, ...
|
|
"ideal_dfe", false);
|
|
pf = Postfilter("ncoeff", p.pf_ncoeffs, "useBurg", true);
|
|
mlse = buildMlse(options.M, options.duob_mode, p, "pf_mlse");
|
|
|
|
[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_vnle, pf, mlse, ...
|
|
options.M, Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", [], ...
|
|
"eth_style_symbol_mapping", 0);
|
|
|
|
vnle_results.config.equalizer_structure = equalizer_structure.vnle;
|
|
vnle_results.recipe_config = collectRecipeConfig("vnle", eq_vnle, p, options);
|
|
mlse_results.config.equalizer_structure = equalizer_structure.vnle_pf_mlse;
|
|
mlse_results.recipe_config = collectRecipeConfig("vnle_pf_mlse", eq_vnle, p, options);
|
|
vnle_results.metrics.print("description", resultDescription("VNLE", options));
|
|
mlse_results.metrics.print("description", resultDescription("VNLE + PF + MLSE", options));
|
|
|
|
output.vnle_package = vnle_results;
|
|
output.mlse_package = mlse_results;
|
|
end
|
|
|
|
if p.run_dbtgt
|
|
eq_dbtgt = EQ("Ne", p.dbtgt_ffe_order, ...
|
|
"Nb", p.dbtgt_dfe_order, ...
|
|
"training_length", p.len_tr, ...
|
|
"training_loops", p.epochs_tr, ...
|
|
"dd_loops", p.epochs_dd, ...
|
|
"K", p.eq_K, ...
|
|
"DCmu", p.mu_dc, ...
|
|
"DDmu", [p.eq_mu_ffe p.mu_dfe], ...
|
|
"DFEmu", p.dfe_mu_feedback, ...
|
|
"FFEmu", 0, ...
|
|
"plotfinal", 0, ...
|
|
"ideal_dfe", true);
|
|
mlse_db = buildMlse(options.M, options.duob_mode, p, "db_target");
|
|
|
|
dbtgt_results = runDuobinaryTarget(eq_dbtgt, mlse_db, ...
|
|
Scpe_sig, Symbols, Tx_bits, options, p);
|
|
dbtgt_results.config.equalizer_structure = equalizer_structure.vnle_db_mlse;
|
|
dbtgt_results.recipe_config = collectRecipeConfig("vnle_db_mlse", eq_dbtgt, p, options);
|
|
dbtgt_results.metrics.print("description", resultDescription("VNLE DB target + MLSE", options));
|
|
output.dbtgt_package = dbtgt_results;
|
|
end
|
|
|
|
if p.run_ml_mlse
|
|
ml_mlse_equalizer = ML_MLSE("epochs_tr", p.ml_mlse_epochs_tr, ...
|
|
"epochs_dd", p.ml_mlse_epochs_dd, ...
|
|
"len_tr", p.ml_mlse_len_tr, ...
|
|
"mu_dd", p.ml_mlse_mu_dd, ...
|
|
"mu_tr", p.ml_mlse_mu_tr, ...
|
|
"order", p.ml_mlse_order, ...
|
|
"sps", p.eq_sps, ...
|
|
"traceback_depth", p.ml_mlse_traceback_depth, ...
|
|
"L", p.ml_mlse_L, ...
|
|
"delta", p.ml_mlse_delta, ...
|
|
"adaptive_mu", p.ml_mlse_adaptive_mu);
|
|
|
|
ml_mlse_results = ml_mlse(ml_mlse_equalizer, options.M, ...
|
|
Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode);
|
|
ml_mlse_results.config.equalizer_structure = equalizer_structure.ml_mlse;
|
|
ml_mlse_results.recipe_config = collectRecipeConfig("ml_mlse", ml_mlse_equalizer, p, options);
|
|
output.mlmlse_package = ml_mlse_results;
|
|
end
|
|
else
|
|
if p.run_ml_mlse_db
|
|
|
|
ml_mlse_db_equalizer = ML_MLSE_DUOBINARY("epochs_tr", p.ml_mlse_epochs_tr, ...
|
|
"epochs_dd", p.ml_mlse_epochs_dd, ...
|
|
"len_tr", p.ml_mlse_len_tr, ...
|
|
"mu_dd", p.ml_mlse_mu_dd, ...
|
|
"mu_tr", p.ml_mlse_mu_tr, ...
|
|
"order", p.ml_mlse_order, ...
|
|
"sps", p.eq_sps, ...
|
|
"traceback_depth", p.ml_mlse_traceback_depth, ...
|
|
"L", p.ml_mlse_L, ...
|
|
"delta", p.ml_mlse_delta, ...
|
|
"adaptive_mu", p.ml_mlse_adaptive_mu);
|
|
|
|
% Use a precoded reference for the detector; the received waveform remains encoded.
|
|
ref_sym = PAMmapper(options.M,0).map(Tx_bits);
|
|
Symbols_precoded = Duobinary().precode(ref_sym); % precoded
|
|
|
|
|
|
ml_mlse_db_results = ml_mlse(ml_mlse_db_equalizer, options.M, ...
|
|
Scpe_sig, Symbols_precoded, Tx_bits, ...
|
|
"precode_mode", db_mode.db_precoded);
|
|
|
|
ml_mlse_db_results.config.equalizer_structure = equalizer_structure.ml_mlse;
|
|
ml_mlse_db_results.config.comment = 'function: ML-based MLSE; duobinary encoded';
|
|
ml_mlse_db_results.recipe_config = collectRecipeConfig("ml_mlse_db", ml_mlse_db_equalizer, p, options);
|
|
output.mlmlse_db_package = ml_mlse_db_results;
|
|
ml_mlse_db_results.metrics.print;
|
|
end
|
|
|
|
if p.run_mlse_db
|
|
eq_db_enc = EQ("Ne", p.dbtgt_ffe_order, ...
|
|
"Nb", p.dbtgt_dfe_order, ...
|
|
"training_length", p.len_tr, ...
|
|
"training_loops", p.epochs_tr, ...
|
|
"dd_loops", p.epochs_dd, ...
|
|
"K", p.eq_K, ...
|
|
"DCmu", p.mu_dc, ...
|
|
"DDmu", [p.eq_mu_ffe p.mu_dfe], ...
|
|
"DFEmu", p.dfe_mu_feedback, ...
|
|
"FFEmu", 0, ...
|
|
"plotfinal", 0, ...
|
|
"ideal_dfe", true);
|
|
mlse_db_enc = MLSE("DIR", [1,1], ...
|
|
"duobinary_output", 0, ...
|
|
"M", options.M, ...
|
|
"trellis_states", PAMmapper(options.M,0).levels);
|
|
|
|
if isempty(p.decoding_mode)
|
|
mlse_db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, ...
|
|
options.M, Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", []);
|
|
mlse_db_results.metrics.print("description", resultDescription(["DB Encoded; MLSE"], options));
|
|
else
|
|
mlse_db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, ...
|
|
options.M, Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", [],"decoding_mode",p.decoding_mode);
|
|
mlse_db_results.metrics.print("description", resultDescription(["DB Encoded "+string(p.decoding_mode)], options));
|
|
end
|
|
mlse_db_results.config.equalizer_structure = equalizer_structure.db_encoded;
|
|
mlse_db_results.recipe_config = collectRecipeConfig("mlse_db", eq_db_enc, p, options);
|
|
output.mlse_db_package = mlse_db_results;
|
|
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function p = defaultRecipeParameters()
|
|
p = struct();
|
|
|
|
p.run_ffe = 0;
|
|
p.run_vnle = 0;
|
|
p.run_dfe = 0;
|
|
p.run_vnle_mlse = 1;
|
|
p.run_dbtgt = 0;
|
|
p.run_ml_mlse = 0; % non-encoded and precoded branches
|
|
p.run_ml_mlse_db = 0; % db_encoded: ML-based MLSE
|
|
p.run_mlse_db = 1; % db_encoded: conventional MLSE
|
|
|
|
|
|
p.preprocess_mode = "auto";
|
|
p.plot_input_signal = false;
|
|
p.plot_output_signals = false;
|
|
p.input_plot_fignum = 400;
|
|
p.output_plot_fignum = 410;
|
|
|
|
p.eq_sps = 2;
|
|
p.eq_K = 2;
|
|
p.len_tr = 4096*2;
|
|
p.epochs_tr = 5;
|
|
p.epochs_dd = 5;
|
|
p.dd_mode = true;
|
|
p.optimize_mus = true;
|
|
|
|
p.ffe_order = [50, 0, 0];
|
|
p.dfe_ffe_order = [50, 5, 5];
|
|
p.vnle_ffe_order = [50, 5, 5];
|
|
p.dbtgt_ffe_order = [50, 5, 5];
|
|
p.vnle_dfe_order = [0, 0, 0];
|
|
p.dbtgt_dfe_order = [0, 0, 0];
|
|
p.dfe_feedback_order = [2, 0, 0];
|
|
|
|
p.eq_mu_ffe = [0.0001, 0.0008, 0.001];
|
|
p.ffe_mu_tr = 0.4;
|
|
p.ffe_mu_dd = 0.1;
|
|
p.ffe_adaption = "nlms";
|
|
p.mu_dfe = 0.0004;
|
|
p.mu_dc = 1.021e-05;
|
|
p.dfe_mu_feedback = 0.005;
|
|
|
|
p.pf_ncoeffs = 1;
|
|
p.use_viterbi = false;
|
|
p.mlse_scale_mode = 2;
|
|
p.mlse_trellis_state_mode = 2;
|
|
p.dbtgt_trellis_state_mode = 3;
|
|
p.decoding_mode = [];
|
|
|
|
p.ml_mlse_mu_tr = 0.03;
|
|
p.ml_mlse_mu_dd = 0.03;
|
|
p.ml_mlse_epochs_tr = 150;
|
|
p.ml_mlse_epochs_dd = 1;
|
|
p.ml_mlse_len_tr = [];
|
|
p.ml_mlse_order = 11;
|
|
p.ml_mlse_traceback_depth = 256;
|
|
p.ml_mlse_L = 1;
|
|
p.ml_mlse_delta = 4;
|
|
p.ml_mlse_adaptive_mu = false;
|
|
end
|
|
|
|
function p = applyUserParameters(p, userParameters)
|
|
if isempty(userParameters)
|
|
return
|
|
end
|
|
|
|
paramNames = fieldnames(userParameters);
|
|
for paramIdx = 1:numel(paramNames)
|
|
paramName = paramNames{paramIdx};
|
|
if ~isfield(p, paramName)
|
|
warning("dsp_400g_recipe:UnknownUserParameter", ...
|
|
"Ignoring unknown user parameter '%s'.", paramName);
|
|
continue
|
|
end
|
|
p.(paramName) = userParameters.(paramName);
|
|
end
|
|
end
|
|
|
|
function [Scpe_sig, Symbols, Tx_bits] = alignDspInputs(Scpe_sig, Symbols, Tx_bits, sps)
|
|
nSymbols = min(length(Symbols), floor(length(Scpe_sig) / sps));
|
|
if nSymbols <= 0
|
|
error("dsp_400g_recipe:EmptyAlignedSignal", ...
|
|
"No overlapping samples remain after preprocessing and synchronization.");
|
|
end
|
|
|
|
Scpe_sig.signal = real(Scpe_sig.signal(1:sps*nSymbols));
|
|
Symbols.signal = Symbols.signal(1:nSymbols,:);
|
|
|
|
% if isprop(Tx_bits, "signal")
|
|
% Tx_bits.signal = Tx_bits.signal(1:nSymbols,:); <- THIS IS WRONG!!
|
|
% end
|
|
end
|
|
|
|
function [ffe_results, equalized_signal] = runFfe(eq_ffe, description, ...
|
|
Scpe_sig, Symbols, Tx_bits, options)
|
|
[ffe_results, equalized_signal] = ffe(eq_ffe, options.M, Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", [], ...
|
|
"eth_style_symbol_mapping", 0);
|
|
|
|
ffe_results.metrics.print("description", resultDescription(description, options));
|
|
end
|
|
|
|
function dbtgt_results = runDuobinaryTarget(eq_dbtgt, mlse_db, ...
|
|
Scpe_sig, Symbols, Tx_bits, options, p)
|
|
|
|
if isempty(p.decoding_mode)
|
|
dbtgt_results = duobinary_target(eq_dbtgt, mlse_db, options.M, ...
|
|
Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", []);
|
|
else
|
|
dbtgt_results = duobinary_target(eq_dbtgt, mlse_db, options.M, ...
|
|
Scpe_sig, Symbols, Tx_bits, ...
|
|
"precode_mode", options.duob_mode, ...
|
|
"showAnalysis", options.debug_plots, ...
|
|
"postFFE", [], ...
|
|
"decoding_mode", p.decoding_mode);
|
|
end
|
|
|
|
end
|
|
|
|
function mlse = buildMlse(M, duobMode, p, mode)
|
|
if p.use_viterbi
|
|
mlse = MLSE_viterbi("duobinary_output", 0, ...
|
|
"M", M, ...
|
|
"trellis_states", PAMmapper(M,0).levels);
|
|
return
|
|
end
|
|
|
|
if duobMode == db_mode.no_db && M == 6
|
|
trellisExclusion = true;
|
|
else
|
|
trellisExclusion = false;
|
|
end
|
|
|
|
switch string(mode)
|
|
case "db_target"
|
|
mlse = MLSE("DIR", [1,1], ...
|
|
"duobinary_output", 0, ...
|
|
"M", M, ...
|
|
"trellis_states", PAMmapper(M,0).levels, ...
|
|
"scale_mode", p.mlse_scale_mode, ...
|
|
"trellis_exclusion", trellisExclusion, ...
|
|
"trellis_state_mode", p.dbtgt_trellis_state_mode);
|
|
otherwise
|
|
mlse = MLSE("duobinary_output", 0, ...
|
|
"M", M, ...
|
|
"trellis_states", PAMmapper(M,0).levels, ...
|
|
"scale_mode", p.mlse_scale_mode, ...
|
|
"trellis_exclusion", trellisExclusion, ...
|
|
"trellis_state_mode", p.mlse_trellis_state_mode);
|
|
end
|
|
end
|
|
|
|
function config = collectRecipeConfig(storageName, eqObject, p, options)
|
|
config = struct();
|
|
config.storage_name = char(storageName);
|
|
config.recipe = "dsp_400g_recipe";
|
|
config.eq_class = class(eqObject);
|
|
config.run_id = options.dataTable.run_id;
|
|
config.user_parameters = options.userParameters;
|
|
config.parameters = p;
|
|
end
|
|
|
|
function description = resultDescription(prefix, options)
|
|
dt = options.dataTable;
|
|
description = sprintf('%s; run %d; PAM-%d; %.0f GBd; %.0f km', ...
|
|
prefix, dt.run_id, dt.pam_level, dt.symbolrate * 1e-9, dt.fiber_length);
|
|
end
|
|
|
|
function plotEqSignals(equalized_signal, Symbols, options, fignum, output_scale)
|
|
showLevelScatter(equalized_signal .* output_scale, Symbols, ...
|
|
"fsym", options.fsym, ...
|
|
"fignum", fignum + 1, ...
|
|
"normalize", true);
|
|
end
|