halfway merged and pulled?!
This commit is contained in:
316
Functions/EQ_structures/dsp_runid.m
Normal file
316
Functions/EQ_structures/dsp_runid.m
Normal file
@@ -0,0 +1,316 @@
|
||||
function [output] = dsp_runid(run_id, options)
|
||||
|
||||
arguments
|
||||
run_id
|
||||
options.append_to_db = 0;
|
||||
options.max_occurences = 4;
|
||||
options.parameters = struct();
|
||||
options.database_type
|
||||
options.dataBase
|
||||
options.load_file_path = struct();
|
||||
options.storage_path
|
||||
options.mode
|
||||
end
|
||||
|
||||
try
|
||||
% Initialize output structures
|
||||
output.ffe_package = {};
|
||||
output.mlse_package = {};
|
||||
output.vnle_package = {};
|
||||
output.dbtgt_package = {};
|
||||
output.dbenc_package = {};
|
||||
output.mlmlse_package = {};
|
||||
|
||||
if options.mode == "load_run_id" || options.append_to_db
|
||||
% Initialize database connection
|
||||
database = DBHandler("dataBase", [options.dataBase], "type", options.database_type );
|
||||
|
||||
if 0
|
||||
% 2. Check if an equalizer configuration with the same hash exists
|
||||
queryStr = sprintf('SELECT COUNT(DISTINCT eq_id) AS unique_eq_count, COUNT(*) AS entries_for_run FROM `Results` WHERE run_id = %d', run_id);
|
||||
existing_results = database.fetch(queryStr);
|
||||
|
||||
if existing_results.unique_eq_count >= 6
|
||||
if (existing_results.entries_for_run / existing_results.unique_eq_count) > 5
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if options.mode == "load_run_id"
|
||||
|
||||
dataTable = queryRunid(run_id, database);
|
||||
fsym = dataTable.symbolrate;
|
||||
M = double(dataTable.pam_level);
|
||||
duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
|
||||
|
||||
% if database.checkIfRunExists('Results','run_id',run_id)
|
||||
% disp(['Already got at least one reulst for run id: ',num2str(run_id),' '])
|
||||
% return
|
||||
% end
|
||||
|
||||
% Load and Sync signal data from DB
|
||||
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, options);
|
||||
|
||||
elseif options.mode == "load_files"
|
||||
|
||||
Tx_bits = load(options.load_file_path.tx_bits_path);
|
||||
Symbols = load(options.load_file_path.tx_symbols_path);
|
||||
Scpe_sig_raw = load(options.load_file_path.rx_raw_path);
|
||||
|
||||
Tx_bits = Tx_bits.Bits;
|
||||
Symbols = Symbols.Symbols;
|
||||
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
|
||||
|
||||
fsym = Symbols.fs;
|
||||
M = Symbols.logbook.ModifierCopy{1}.M;
|
||||
duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode;
|
||||
|
||||
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym);
|
||||
[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
|
||||
|
||||
else
|
||||
|
||||
% Run quick Simulation
|
||||
tx_simulation;
|
||||
|
||||
end
|
||||
|
||||
% Handle Settings and argument replacement
|
||||
|
||||
len_tr = 4096*2;
|
||||
|
||||
ffe_order = [50, 5, 5];
|
||||
dfe_order = [0, 0, 0];
|
||||
pf_ncoeffs = 1;
|
||||
mu_ffe = [0.0001, 0.0008, 0.001];
|
||||
mu_dfe = 0.0004;
|
||||
mu_dc = 0.005;
|
||||
dc_buffer_len = 1;
|
||||
|
||||
mu_tr = 0;
|
||||
mu_dd = 0.05;
|
||||
adaption= 1;
|
||||
use_dd_mode = 1;
|
||||
|
||||
use_ffe = 0;
|
||||
use_dfe = 0;
|
||||
use_vnle_mlse = 0;
|
||||
use_dbtgt = 0;
|
||||
use_dbenc = 0;
|
||||
use_ml_mlse = 1;
|
||||
|
||||
addProcessingResultToDatabase = 0;
|
||||
|
||||
% Overwrite default parameters if given in options.parameters
|
||||
paramStruct = options.parameters;
|
||||
if ~isempty(paramStruct)
|
||||
paramNames = fieldnames(paramStruct);
|
||||
for i = 1:numel(paramNames)
|
||||
thisName = paramNames{i};
|
||||
thisValue = paramStruct.(thisName);
|
||||
eval([thisName ' = thisValue;']);
|
||||
end
|
||||
end
|
||||
|
||||
% Configure equalizers
|
||||
|
||||
options.max_occurences = min(options.max_occurences,length(Scpe_cell));
|
||||
for r = 1:options.max_occurences
|
||||
|
||||
%FFE
|
||||
% eq_dfe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
|
||||
|
||||
|
||||
%
|
||||
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
|
||||
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
|
||||
eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0,"adaption_technique","lms");
|
||||
eq_post = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
|
||||
% Duobinary signaling (db encoded)
|
||||
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
|
||||
eq_db_enc = EQ("Ne", ffe_order, "Nb", dfe_order, "training_length", len_tr, ...
|
||||
"training_loops", 5, "dd_loops", 5, "K", 2, "DCmu", mu_dc, ...
|
||||
"DDmu", [mu_ffe mu_dfe], "DFEmu", 0.005, "FFEmu", 0, "plotfinal", 0, "ideal_dfe", 1);
|
||||
|
||||
% Preprocess signal
|
||||
Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym);
|
||||
|
||||
Scpe_sig.spectrum("fignum",200,"normalizeTo0dB",1,"displayname",'Rx','addDCoffset',-6);
|
||||
|
||||
Scpe_sig.spectrum("fignum",201,"normalizeTo0dB",0,"displayname",'Rx');
|
||||
|
||||
ylim([-30,3]);
|
||||
xlim([-5,100]);
|
||||
% Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx');
|
||||
% Scpe_sig.eye(fsym,M,"fignum",1024);
|
||||
|
||||
if duob_mode ~= db_mode.db_encoded
|
||||
|
||||
if use_ffe
|
||||
|
||||
ffe_order = [50, 0, 0];
|
||||
eq_dfe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
|
||||
|
||||
ffe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
|
||||
"precode_mode",duob_mode,...
|
||||
'showAnalysis',0,...
|
||||
"postFFE",[],...
|
||||
"eth_style_symbol_mapping",0);
|
||||
|
||||
output.ffe_package{r} = ffe_results;
|
||||
|
||||
ffe_results.metrics.print;
|
||||
ffe_results.config.equalizer_structure = "ffe";
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if use_dfe
|
||||
|
||||
ffe_order = [50, 5, 5];
|
||||
eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
|
||||
|
||||
dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
|
||||
"precode_mode",duob_mode,...
|
||||
'showAnalysis',0,...
|
||||
"postFFE",[],...
|
||||
"eth_style_symbol_mapping",0);
|
||||
|
||||
output.ffe_package{r} = dfe_results;
|
||||
dfe_results.config.equalizer_structure = "dfe";
|
||||
|
||||
dfe_results.metrics.print;
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if use_vnle_mlse
|
||||
|
||||
pf_ncoeffs = 1;
|
||||
ffe_order = [50, 5, 5];
|
||||
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
|
||||
% eq_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0.0004,"order",[50,5,5],"sps",2,"decide",0);
|
||||
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
|
||||
|
||||
useviterbi = 0;
|
||||
if useviterbi
|
||||
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
else
|
||||
|
||||
if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation
|
||||
trellexlusion = 1;
|
||||
else
|
||||
trellexlusion = 0;
|
||||
end
|
||||
|
||||
%state_mode 3 -> stat lvl; state_mode 2 -> use target lvls
|
||||
%scale_mode 2 -> mmse adaption
|
||||
|
||||
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',2);
|
||||
|
||||
end
|
||||
|
||||
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
|
||||
"precode_mode", duob_mode,...
|
||||
'showAnalysis', 0, ...
|
||||
"postFFE", [],...
|
||||
"eth_style_symbol_mapping", 0);
|
||||
|
||||
ffe_results.metrics.print;
|
||||
ffe_results.config.equalizer_structure = "vnle";
|
||||
mlse_results.metrics.print;
|
||||
|
||||
output.mlse_package{r} = mlse_results;
|
||||
output.vnle_package{r} = ffe_results;
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config);
|
||||
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if use_ml_mlse
|
||||
|
||||
%ML-based MLSE (L=2)
|
||||
mu_ml = 0.01; training_epochs = 100;
|
||||
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
|
||||
"len_tr",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
|
||||
"traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0);
|
||||
|
||||
[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode);
|
||||
output.mlmlse_package{r} = ml_mlse_results;
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, ml_mlse_results.metrics, ml_mlse_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if use_dbtgt
|
||||
|
||||
useviterbi = 0;
|
||||
if useviterbi
|
||||
mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
else
|
||||
if duob_mode == db_mode.no_db && M == 6 %only for PAM-6 and no duobinary precoding, otherwise leads to false sequence estimation
|
||||
trellexlusion = 1;
|
||||
else
|
||||
trellexlusion = 0;
|
||||
end
|
||||
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels,'scale_mode',2,'trellis_exclusion',trellexlusion,'trellis_state_mode',3);
|
||||
end
|
||||
ffe_order = [50, 5, 5];
|
||||
eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
|
||||
|
||||
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
|
||||
"precode_mode", duob_mode, ...
|
||||
'showAnalysis', 0,...
|
||||
"postFFE", []);
|
||||
|
||||
dbt_results.metrics.print;
|
||||
|
||||
output.dbtgt_package{r} = dbt_results;
|
||||
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if duob_mode == db_mode.db_encoded
|
||||
|
||||
mlse_db_enc = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
|
||||
|
||||
db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, M, Scpe_sig, Symbols, Tx_bits, "precode_mode",duob_mode, "showAnalysis",0,"postFFE",[]);
|
||||
output.dbenc_package{r} = db_results;
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, db_results.metrics, db_results.config);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
catch ME
|
||||
save('workerError.mat','ME');
|
||||
rethrow(ME);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
192
Functions/EQ_structures/ffe.m
Normal file
192
Functions/EQ_structures/ffe.m
Normal file
@@ -0,0 +1,192 @@
|
||||
function [ffe_results] = ffe(eq_, M, rx_signal, tx_symbols, tx_bits, options)
|
||||
% FFE Processes signals through FFE equalizer
|
||||
%
|
||||
% Inputs:
|
||||
% eq_ - Equalizer object
|
||||
% M - Modulation order
|
||||
% rx_signal - Received signal
|
||||
% tx_symbols - Transmitted symbols
|
||||
% tx_bits - Transmitted bits
|
||||
% options - Optional parameters
|
||||
%
|
||||
% Outputs:
|
||||
% ffe_results - Results from FFE processing
|
||||
|
||||
arguments
|
||||
eq_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
options.precode_mode db_mode
|
||||
options.showAnalysis = 0;
|
||||
options.eth_style_symbol_mapping = 0;
|
||||
options.postFFE = [];
|
||||
options.database = [];
|
||||
end
|
||||
|
||||
%% Process signals through equalizer
|
||||
% FFE or VNLE
|
||||
[eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols);
|
||||
|
||||
% Apply post-FFE if provided
|
||||
if ~isempty(options.postFFE)
|
||||
tic
|
||||
[eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols);
|
||||
toc
|
||||
end
|
||||
|
||||
try
|
||||
ch_coefficients = arburg(eq_noise.signal,1);
|
||||
channel_alpha = ch_coefficients(2);
|
||||
end
|
||||
|
||||
% Hard decision on FFE output
|
||||
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
|
||||
|
||||
%% Calculate BER based on precoding mode
|
||||
[bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
|
||||
|
||||
%% Calculate performance metrics
|
||||
[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_ngmi(eq_signal_sd,tx_symbols);
|
||||
gmi = max(gmi,0);
|
||||
|
||||
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
|
||||
[evm_total, evm_lvl] = calc_evm(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);
|
||||
|
||||
%% Display analysis if requested
|
||||
if options.showAnalysis
|
||||
displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, options.postFFE);
|
||||
end
|
||||
|
||||
|
||||
%% Prepare output structure
|
||||
% Determine postFFE order
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
% Create FFE results structure
|
||||
ffe_results = struct();
|
||||
try
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
eq_.b = [];
|
||||
eq_.b2 = [];
|
||||
eq_.b3 = [];
|
||||
end
|
||||
|
||||
ffe_results.config = Equalizerstruct();
|
||||
ffe_results.config.eq = jsonencode(eq_);
|
||||
ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe);
|
||||
ffe_results.config.comment = 'function: ffe';
|
||||
|
||||
ffe_results.metrics = Metricstruct;
|
||||
ffe_results.metrics.result_id = NaN;
|
||||
ffe_results.metrics.run_id = NaN;
|
||||
ffe_results.metrics.eqParam_id = NaN;
|
||||
ffe_results.metrics.date_of_processing = datetime('now');
|
||||
ffe_results.metrics.BER = ber;
|
||||
ffe_results.metrics.numBits = bits;
|
||||
ffe_results.metrics.numBitErr = errors;
|
||||
ffe_results.metrics.BER_precoded = ber_precoded;
|
||||
ffe_results.metrics.numBitErr_precoded = errors_precoded;
|
||||
ffe_results.metrics.SNR = snr;
|
||||
ffe_results.metrics.SNR_level = snr_lvl;
|
||||
ffe_results.metrics.STD = std_total;
|
||||
ffe_results.metrics.STD_level = std_lvl;
|
||||
ffe_results.metrics.STDrx = std_rxraw_total;
|
||||
ffe_results.metrics.STDrx_level = std_rxraw_lvl;
|
||||
ffe_results.metrics.GMI = gmi;
|
||||
ffe_results.metrics.AIR = air;
|
||||
ffe_results.metrics.EVM = evm_total;
|
||||
ffe_results.metrics.EVM_level = evm_lvl;
|
||||
ffe_results.metrics.Alpha = channel_alpha;
|
||||
|
||||
|
||||
end
|
||||
|
||||
%% Helper Functions
|
||||
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
|
||||
% Calculate BER based on precoding mode
|
||||
mapper = PAMmapper(M, 0, "eth_style", eth_style);
|
||||
|
||||
switch precode_mode
|
||||
case db_mode.no_db
|
||||
% TX Data is not precoded
|
||||
% A) Emulate diff precoding
|
||||
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M);
|
||||
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M);
|
||||
|
||||
tx_symbols_precoded = Duobinary().encode(tx_symbols);
|
||||
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
|
||||
|
||||
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
|
||||
|
||||
rx_bits = mapper.demap(eq_signal_hd_precoded);
|
||||
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
% B) Just determine BER
|
||||
rx_bits = mapper.demap(eq_signal_hd);
|
||||
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
case db_mode.db_precoded
|
||||
% Data is precoded on TX side
|
||||
% A) Decode at Rx if no DB targeting was applied
|
||||
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
|
||||
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
|
||||
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
|
||||
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
% B) Omit the Coding by comparing with demapped TX symbol sequence
|
||||
tx_bits_demapped = mapper.demap(tx_symbols);
|
||||
rx_bits = mapper.demap(eq_signal_hd);
|
||||
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
end
|
||||
end
|
||||
|
||||
function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE)
|
||||
% Display analysis plots and metrics
|
||||
|
||||
% Initialize figure handles
|
||||
% Corrected line - added tx_symbols as second positional argument
|
||||
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100);
|
||||
|
||||
warning off
|
||||
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101);
|
||||
figure(gcf);hold on; plot(((1:length(eq_noise.signal)) / eq_noise.fs) * 1e6,movmean(eq_noise.signal,2000,1), 'LineWidth',3,'Color','black')
|
||||
warning on
|
||||
|
||||
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 102);
|
||||
|
||||
showEQNoisePSD(eq_noise, "fignum", 103, "displayname", 'Residual Noise after FFE');
|
||||
|
||||
% Figure 2: Post-FFE coefficients (if available)
|
||||
if ~isempty(postFFE)
|
||||
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 104);
|
||||
end
|
||||
|
||||
try
|
||||
figure(339);
|
||||
showEQfilter(eq_.e_tr, eq_signal_sd.fs.*2,"displayname",'training','fignum',339);
|
||||
showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339);
|
||||
legend on
|
||||
end
|
||||
|
||||
try
|
||||
figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training');
|
||||
|
||||
figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training');
|
||||
|
||||
figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd');
|
||||
end
|
||||
% eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105);
|
||||
|
||||
end
|
||||
140
Functions/EQ_structures/ml_mlse.m
Normal file
140
Functions/EQ_structures/ml_mlse.m
Normal file
@@ -0,0 +1,140 @@
|
||||
function [ml_mlse_results] = ml_mlse(eq_, M, rx_signal, tx_symbols, tx_bits, options)
|
||||
%
|
||||
%
|
||||
% Inputs:
|
||||
% eq_ - Equalizer object
|
||||
% M - Modulation order
|
||||
% rx_signal - Received signal
|
||||
% tx_symbols - Transmitted symbols
|
||||
% tx_bits - Transmitted bits
|
||||
% options - Optional parameters
|
||||
%
|
||||
% Outputs:
|
||||
% ffe_results - Results from FFE processing
|
||||
|
||||
arguments
|
||||
eq_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
options.precode_mode db_mode
|
||||
options.eth_style_symbol_mapping = 0;
|
||||
options.postFFE = [];
|
||||
|
||||
end
|
||||
|
||||
%% Process signals through equalizer
|
||||
|
||||
[eq_signal_hd,y_ref] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
%% Calculate BER based on precoding mode
|
||||
[bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
|
||||
|
||||
|
||||
% Create FFE results structure
|
||||
ml_mlse_results = struct();
|
||||
try
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
eq_.b = [];
|
||||
eq_.b2 = [];
|
||||
eq_.b3 = [];
|
||||
end
|
||||
|
||||
ml_mlse_results.config = Equalizerstruct();
|
||||
|
||||
eq_small = strip_eq(eq_, 10);
|
||||
json_str = jsonencode(eq_small);
|
||||
|
||||
ml_mlse_results.config.eq = jsonencode(eq_);
|
||||
ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse);
|
||||
ml_mlse_results.config.comment = 'function: ML-based MLSE';
|
||||
|
||||
ml_mlse_results.metrics = Metricstruct;
|
||||
% ml_mlse_results.metrics.result_id = NaN;
|
||||
% ml_mlse_results.metrics.run_id = NaN;
|
||||
% ml_mlse_results.metrics.eqParam_id = NaN;
|
||||
ml_mlse_results.metrics.date_of_processing = datetime('now');
|
||||
ml_mlse_results.metrics.BER = ber;
|
||||
ml_mlse_results.metrics.numBits = bits;
|
||||
ml_mlse_results.metrics.numBitErr = errors;
|
||||
ml_mlse_results.metrics.BER_precoded = ber_precoded;
|
||||
ml_mlse_results.metrics.numBitErr_precoded = errors_precoded;
|
||||
% ml_mlse_results.metrics.SNR = NaN;
|
||||
% ml_mlse_results.metrics.SNR_level = NaN;
|
||||
% ml_mlse_results.metrics.STD = NaN;
|
||||
% ml_mlse_results.metrics.STD_level = NaN;
|
||||
% ml_mlse_results.metrics.STDrx = NaN;
|
||||
% ml_mlse_results.metrics.STDrx_level = NaN;
|
||||
% ml_mlse_results.metrics.GMI = NaN;
|
||||
% ml_mlse_results.metrics.AIR = NaN;
|
||||
% ml_mlse_results.metrics.EVM = NaN;
|
||||
% ml_mlse_results.metrics.EVM_level = NaN;
|
||||
% ml_mlse_results.metrics.Alpha = NaN;
|
||||
|
||||
|
||||
end
|
||||
|
||||
%% Helper Functions
|
||||
function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
|
||||
% Calculate BER based on precoding mode
|
||||
mapper = PAMmapper(M, 0, "eth_style", eth_style);
|
||||
|
||||
switch precode_mode
|
||||
case db_mode.no_db
|
||||
% TX Data is not precoded
|
||||
% A) Emulate diff precoding
|
||||
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M);
|
||||
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M);
|
||||
|
||||
tx_symbols_precoded = Duobinary().encode(tx_symbols);
|
||||
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
|
||||
|
||||
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
|
||||
|
||||
rx_bits = mapper.demap(eq_signal_hd_precoded);
|
||||
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
% B) Just determine BER
|
||||
rx_bits = mapper.demap(eq_signal_hd);
|
||||
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
case db_mode.db_precoded
|
||||
% Data is precoded on TX side
|
||||
% A) Decode at Rx if no DB targeting was applied
|
||||
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
|
||||
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
|
||||
rx_bits_decoded = mapper.demap(eq_signal_hd_decoded);
|
||||
[~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
% B) Omit the Coding by comparing with demapped TX symbol sequence
|
||||
tx_bits_demapped = mapper.demap(tx_symbols);
|
||||
rx_bits = mapper.demap(eq_signal_hd);
|
||||
[bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function eq_out = strip_eq(eq_, max_elems)
|
||||
% strip_eq removes all large fields from the ML_MLSE object
|
||||
% eq_out = strip_eq(eq_, max_elems)
|
||||
% max_elems ... maximum number of elements to keep (default = 10)
|
||||
|
||||
if nargin < 2
|
||||
max_elems = 10; % default threshold
|
||||
end
|
||||
|
||||
props = properties(eq_);
|
||||
for i = 1:numel(props)
|
||||
val = eq_.(props{i});
|
||||
if ~isempty(val)
|
||||
% Count total number of elements
|
||||
if numel(val) > max_elems
|
||||
eq_.(props{i}) = [];
|
||||
end
|
||||
end
|
||||
end
|
||||
eq_out = eq_;
|
||||
end
|
||||
Reference in New Issue
Block a user