Many changes in DBHandler
new general processing structure just before splitting off the projects folder from this repo
This commit is contained in:
137
Functions/EQ_structures/dsp_runid.m
Normal file
137
Functions/EQ_structures/dsp_runid.m
Normal file
@@ -0,0 +1,137 @@
|
||||
function [output] = dsp_runid(run_id, options)
|
||||
|
||||
arguments
|
||||
run_id
|
||||
options.append_to_db = 0;
|
||||
options.max_occurences = 4;
|
||||
options.parameters = struct();
|
||||
options.database_path
|
||||
options.database_name
|
||||
options.storage_path
|
||||
end
|
||||
|
||||
% Initialize output structures
|
||||
output.ffe_package = {};
|
||||
output.mlse_package = {};
|
||||
output.vnle_package = {};
|
||||
output.dbtgt_package = {};
|
||||
output.dbenc_package = {};
|
||||
|
||||
% Initialize database connection
|
||||
database = DBHandler("pathToDB", [options.database_path, options.database_name], "type", "sqlite");
|
||||
dataTable = queryRunid(run_id, database);
|
||||
fsym = dataTable.symbolrate;
|
||||
M = double(dataTable.pam_level);
|
||||
duob_mode = db_mode(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
|
||||
|
||||
|
||||
% 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.00;
|
||||
|
||||
use_ffe = 1;
|
||||
use_vnle_mlse = 1;
|
||||
use_dbtgt = 1;
|
||||
use_dbenc = 0;
|
||||
|
||||
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
|
||||
eq_lin = EQ("Ne",[50,0,0],"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);
|
||||
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
|
||||
mlse_db_ = MLSE_viterbi("DIR",[1,1],"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);
|
||||
% Duobinary signaling (db encoded)
|
||||
mlse_db_enc = MLSE_viterbi("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);
|
||||
|
||||
|
||||
% Load signal data
|
||||
[Tx_bits, Symbols, Scpe_cell, ~] = loadSignalData(dataTable, options);
|
||||
|
||||
|
||||
for r = 1:numel(Scpe_cell)
|
||||
|
||||
% Preprocess signal
|
||||
Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym);
|
||||
|
||||
if duob_mode ~= db_mode.db_encoded
|
||||
if use_ffe
|
||||
ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,...
|
||||
"precode_mode",duob_mode,...
|
||||
'showAnalysis',1,...
|
||||
"postFFE",[],...
|
||||
"eth_style_symbol_mapping",0);
|
||||
output.ffe_package{r} = ffe_results;
|
||||
if options.append_to_db
|
||||
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
|
||||
end
|
||||
end
|
||||
|
||||
if use_vnle_mlse
|
||||
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
|
||||
"precode_mode", duob_mode,...
|
||||
'showAnalysis', 1, ...
|
||||
"postFFE", [],...
|
||||
"eth_style_symbol_mapping", 0);
|
||||
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_dbtgt
|
||||
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
|
||||
"precode_mode", duob_mode, ...
|
||||
'showAnalysis', 0,...
|
||||
"postFFE", []);
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
end
|
||||
@@ -1,96 +1,129 @@
|
||||
function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options)
|
||||
%Duobinary Signaling
|
||||
function [db_results] = duobinary_signaling(eq_, mlse_, M, rx_signal, tx_symbols, tx_bits, options)
|
||||
% DUOBINARY_SIGNALING Processes signals through duobinary signaling
|
||||
%
|
||||
% Inputs:
|
||||
% eq_ - Equalizer object
|
||||
% mlse_ - MLSE object
|
||||
% M - Modulation order
|
||||
% rx_signal - Received signal
|
||||
% tx_symbols - Transmitted symbols
|
||||
% tx_bits - Transmitted bits
|
||||
% options - Optional parameters
|
||||
%
|
||||
% Outputs:
|
||||
% db_results - Results from duobinary signaling processing
|
||||
|
||||
arguments
|
||||
eq_
|
||||
mlse_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
options.postFFE = [];
|
||||
eq_
|
||||
mlse_
|
||||
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
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal, tx_symbols);
|
||||
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
% Apply post-FFE if provided
|
||||
if ~isempty(options.postFFE)
|
||||
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,tx_symbols);
|
||||
[eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols);
|
||||
end
|
||||
|
||||
|
||||
% Process through MLSE
|
||||
eq_signal = mlse_.process(eq_signal);
|
||||
|
||||
% Apply duobinary encoding and decoding
|
||||
eq_signal = Duobinary().encode(eq_signal);
|
||||
eq_signal = Duobinary().decode(eq_signal);
|
||||
|
||||
% M = numel(unique(eq_signal.signal));
|
||||
rx_bits = PAMmapper(M,0).demap(eq_signal);
|
||||
% Demap symbols to bits
|
||||
rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(eq_signal);
|
||||
|
||||
[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
%% Calculate BER and metrics
|
||||
[bits_db, errors_db, ber_db, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
eq_package.ber = ber_db;
|
||||
|
||||
resultsDBsignaling = struct( ...
|
||||
'result_id', NaN, ... %
|
||||
'run_id', NaN, ... % Beispielhafte Run-ID
|
||||
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
|
||||
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
|
||||
'numBits', bits_db, ... % Beispiel: 1.000.000 Bits
|
||||
'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler
|
||||
'BER_precoded', [], ... % BER = 120 / 1.000.000
|
||||
'numBitErr_precoded', [], ... % Beispiel: 120 Bitfehler
|
||||
'BER', ber_db, ... % BER = 120 / 1.000.000
|
||||
'SNR', [], ... % Beispielhafte SNR
|
||||
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
|
||||
'GMI', [], ... % Beispielhafter GMI-Wert
|
||||
'AIR', [], ... % Beispielhafter AIR-Wert
|
||||
'EVM', [], ... % Beispielhafte EVM
|
||||
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
|
||||
'Alpha', [] ... % Beispielhafter Alpha-Wert
|
||||
);
|
||||
% Calculate performance metrics
|
||||
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
|
||||
[gmi] = calc_air(eq_signal, tx_symbols, "skip_front", 10000, "skip_end", 10000);
|
||||
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
|
||||
[evm_total, evm_lvl] = calc_evm(eq_signal, tx_symbols);
|
||||
[std_total, std_lvl] = calc_std(eq_signal, tx_symbols);
|
||||
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
|
||||
|
||||
%% Prepare output structure
|
||||
% Determine postFFE order
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
equalizerConfigDBsignaling = struct( ...
|
||||
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
|
||||
'equalizer_structure', int32(equalizer_structure.db_encoded), ... % Beispiel: 1 (z.B. für vnle)
|
||||
'M', M, ... % Ordnung der PAM-Konstellation
|
||||
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
|
||||
'db_target', 1, ... % 0 oder 1
|
||||
'diff_precode', 1, ... % 0 oder 1
|
||||
'postFFE', ~isempty(options.postFFE), ... % Beispielwert
|
||||
'NpostFFE', npostFFE, ... % Beispielwert
|
||||
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
|
||||
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
|
||||
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
|
||||
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
|
||||
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
|
||||
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
|
||||
'K', eq_.K, ... % Samples pro Symbol
|
||||
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
|
||||
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
|
||||
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
|
||||
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
|
||||
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
|
||||
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
|
||||
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
|
||||
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
|
||||
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
|
||||
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
|
||||
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
|
||||
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
|
||||
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
|
||||
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
|
||||
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
|
||||
'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare
|
||||
'config_hash', NaN ...
|
||||
);
|
||||
% Create results structure
|
||||
db_results = struct();
|
||||
db_results.metrics = Metricstruct;
|
||||
db_results.metrics.result_id = NaN;
|
||||
db_results.metrics.run_id = NaN;
|
||||
db_results.metrics.eqParam_id = NaN;
|
||||
db_results.metrics.date_of_processing = datetime('now');
|
||||
db_results.metrics.BER = ber_db;
|
||||
db_results.metrics.numBits = bits_db;
|
||||
db_results.metrics.numBitErr = errors_db;
|
||||
db_results.metrics.SNR = snr;
|
||||
db_results.metrics.SNR_level = snr_lvl;
|
||||
db_results.metrics.STD = std_total;
|
||||
db_results.metrics.STD_level = std_lvl;
|
||||
db_results.metrics.STDrx = std_rxraw_total;
|
||||
db_results.metrics.STDrx_level = std_rxraw_lvl;
|
||||
db_results.metrics.GMI = gmi;
|
||||
db_results.metrics.AIR = air;
|
||||
db_results.metrics.EVM = evm_total;
|
||||
db_results.metrics.EVM_level = evm_lvl;
|
||||
db_results.metrics.MLSE_dir = mlse_.DIR;
|
||||
|
||||
eq_package.resultsDBsignaling = resultsDBsignaling;
|
||||
eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling;
|
||||
% Create configuration structure
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
db_results.config = Equalizerstruct();
|
||||
db_results.config.eq = jsonencode(eq_);
|
||||
mlse_.DIR = [];
|
||||
db_results.config.mlse = jsonencode(mlse_);
|
||||
db_results.config.equalizer_structure = int32(equalizer_structure.db_encoded);
|
||||
db_results.config.comment = 'function: duobinary_signaling';
|
||||
|
||||
%% Display analysis if requested
|
||||
if options.showAnalysis
|
||||
displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, options.postFFE);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
%% Helper Function
|
||||
function displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, postFFE)
|
||||
% Display analysis plots and metrics
|
||||
figure(336);
|
||||
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after Duobinary');
|
||||
|
||||
if ~isempty(postFFE)
|
||||
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
|
||||
end
|
||||
|
||||
showEQfilter(eq_.e, eq_signal.fs.*2);
|
||||
|
||||
figure(341); clf;
|
||||
showLevelHistogram(eq_signal, tx_symbols, "fignum", 341);
|
||||
|
||||
warning off
|
||||
figure(400); clf;
|
||||
showLevelScatter(eq_signal, tx_symbols, "fignum", 400);
|
||||
|
||||
figure(401); clf;
|
||||
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
|
||||
warning on
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
function [eq_package] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options)
|
||||
function [db_results] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options)
|
||||
|
||||
arguments
|
||||
eq_
|
||||
@@ -22,7 +22,7 @@ if ~isempty(options.postFFE)
|
||||
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence);
|
||||
end
|
||||
|
||||
% dir = [1,1];
|
||||
mlse_.DIR = [1,1];
|
||||
mlse_sig_sd = mlse_.process(eq_signal);
|
||||
|
||||
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
|
||||
@@ -73,70 +73,36 @@ end
|
||||
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
|
||||
[bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
eq_package.ber = ber_db;
|
||||
|
||||
resultsDBtgt = struct( ...
|
||||
'result_id', NaN, ... %
|
||||
'run_id', NaN, ... % Beispielhafte Run-ID
|
||||
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
|
||||
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
|
||||
'numBits', bits_db, ... % Beispiel: 1.000.000 Bits
|
||||
'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler
|
||||
'BER_precoded', ber_db_diff_precoded, ... % BER = 120 / 1.000.000
|
||||
'numBitErr_precoded', errors_db_diff_precoded, ... % Beispiel: 120 Bitfehler
|
||||
'BER', ber_db, ... % BER = 120 / 1.000.000
|
||||
'SNR', [], ... % Beispielhafte SNR
|
||||
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
|
||||
'GMI', [], ... % Beispielhafter GMI-Wert
|
||||
'AIR', [], ... % Beispielhafter AIR-Wert
|
||||
'EVM', [], ... % Beispielhafte EVM
|
||||
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
|
||||
'Alpha', [] ... % Beispielhafter Alpha-Wert
|
||||
);
|
||||
db_results = struct();
|
||||
db_results.metrics = Metricstruct;
|
||||
db_results.metrics.result_id = NaN;
|
||||
db_results.metrics.run_id = NaN;
|
||||
db_results.metrics.eqParam_id = NaN;
|
||||
db_results.metrics.date_of_processing = datetime('now');
|
||||
db_results.metrics.BER = ber_db;
|
||||
db_results.metrics.numBits = bits_db;
|
||||
db_results.metrics.numBitErr = errors_db;
|
||||
db_results.metrics.BER_precoded = ber_db_diff_precoded;
|
||||
db_results.metrics.numBitErr_precoded = errors_db_diff_precoded;
|
||||
db_results.metrics.SNR = NaN;
|
||||
db_results.metrics.SNR_level = NaN;
|
||||
db_results.metrics.GMI = NaN;
|
||||
db_results.metrics.AIR = NaN;
|
||||
db_results.metrics.EVM = NaN;
|
||||
db_results.metrics.EVM_level = NaN;
|
||||
db_results.metrics.MLSE_dir = mlse_.DIR;
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
equalizerConfigDBtgt = struct( ...
|
||||
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
|
||||
'equalizer_structure', int32(equalizer_structure.vnle_db_mlse), ... % Beispiel: 1 (z.B. für vnle)
|
||||
'M', M, ... % Ordnung der PAM-Konstellation
|
||||
'target_constellation', jsonencode(round(db_ref_constellation,5)), ... % Beispielhafter Target-String
|
||||
'db_target', 1, ... % 0 oder 1
|
||||
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
|
||||
'postFFE', ~isempty(options.postFFE), ... % Beispielwert
|
||||
'NpostFFE', npostFFE, ... % Beispielwert
|
||||
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
|
||||
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
|
||||
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
|
||||
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
|
||||
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
|
||||
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
|
||||
'K', eq_.K, ... % Samples pro Symbol
|
||||
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
|
||||
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
|
||||
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
|
||||
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
|
||||
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
|
||||
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
|
||||
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
|
||||
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
|
||||
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
|
||||
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
|
||||
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
|
||||
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
|
||||
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
|
||||
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
|
||||
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
|
||||
'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare
|
||||
'config_hash', NaN ...
|
||||
);
|
||||
|
||||
eq_package.resultsDBtgt = resultsDBtgt;
|
||||
eq_package.equalizerConfigDBtgt = equalizerConfigDBtgt;
|
||||
% Create DB results structure
|
||||
db_results.config = Equalizerstruct();
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
db_results.config.eq = jsonencode(eq_);
|
||||
mlse_.DIR = [];
|
||||
db_results.config.mlse = jsonencode(mlse_);
|
||||
db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse);
|
||||
db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)';
|
||||
|
||||
if options.showAnalysis
|
||||
eq_noise = eq_noise - mean(eq_noise.signal);
|
||||
|
||||
163
Functions/EQ_structures/ffe.m
Normal file
163
Functions/EQ_structures/ffe.m
Normal file
@@ -0,0 +1,163 @@
|
||||
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
|
||||
|
||||
% 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);
|
||||
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();
|
||||
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
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;
|
||||
|
||||
|
||||
|
||||
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
|
||||
figure(336);
|
||||
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after FFE');
|
||||
|
||||
if ~isempty(postFFE)
|
||||
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
|
||||
end
|
||||
|
||||
showEQfilter(eq_.e, eq_signal_sd.fs.*2);
|
||||
|
||||
figure(341); clf;
|
||||
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341);
|
||||
|
||||
warning off
|
||||
figure(400); clf;
|
||||
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400);
|
||||
|
||||
figure(401); clf;
|
||||
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
|
||||
warning on
|
||||
end
|
||||
@@ -1,192 +0,0 @@
|
||||
function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
|
||||
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
|
||||
|
||||
mudc_given = eq_.mu_dc;
|
||||
|
||||
%FFE or VNLE
|
||||
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
tic
|
||||
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
|
||||
toc
|
||||
end
|
||||
|
||||
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
|
||||
|
||||
% precoding to mitigate error propagation, most prominently used in
|
||||
% combination with duobinary signaling to avoid catastrophic error
|
||||
% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling)
|
||||
|
||||
switch options.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 = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
|
||||
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded);
|
||||
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
%B) Just determine BER
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
|
||||
[bits_vnle,errors_vnle,ber_vnle,error_pos_vnle] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
max_burst_length = 10;
|
||||
burst_count = count_error_bursts(error_pos_vnle, max_burst_length);
|
||||
|
||||
case db_mode.db_precoded
|
||||
|
||||
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
|
||||
|
||||
% A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here!
|
||||
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_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded);
|
||||
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_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 = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols);
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
|
||||
[bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",30000,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
|
||||
end
|
||||
|
||||
% METRICS OF VNLE SD Signal:
|
||||
[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal);
|
||||
[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
|
||||
air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M));
|
||||
[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols);
|
||||
[std_vnle_total,std_vnle_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);
|
||||
|
||||
eq_package.ber_vnle = ber_vnle;
|
||||
eq_package.evm_vnle_total = evm_vnle_total;
|
||||
eq_package.evm_vnle_lvl = evm_vnle_lvl;
|
||||
eq_package.gmi = gmi_vnle;
|
||||
|
||||
eq_package.eq = eq_;
|
||||
|
||||
resultsVNLE = struct( ...
|
||||
'result_id', NaN, ... %
|
||||
'run_id', NaN, ... % Beispielhafte Run-ID
|
||||
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
|
||||
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
|
||||
'BER', ber_vnle, ... % BER = 120 / 1.000.000
|
||||
'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits
|
||||
'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler
|
||||
'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000
|
||||
'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler
|
||||
'SNR', snr_vnle, ... % Beispielhafte SNR
|
||||
'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array
|
||||
'STD', std_vnle_total, ...
|
||||
'STD_level', jsonencode(std_vnle_lvl),...
|
||||
'STDrx' , std_rxraw_total, ...
|
||||
'STDrx_level', jsonencode(std_rxraw_lvl),...
|
||||
'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert
|
||||
'AIR', air_vnle, ... % Beispielhafter AIR-Wert
|
||||
'EVM', evm_vnle_total, ... % Beispielhafte EVM
|
||||
'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array
|
||||
'Alpha', [] ... % Beispielhafter Alpha-Wert
|
||||
);
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
equalizerConfigVNLE = struct( ...
|
||||
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
|
||||
'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle)
|
||||
'M', M, ... % Ordnung der PAM-Konstellation
|
||||
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
|
||||
'db_target', 0, ... % 0 oder 1
|
||||
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
|
||||
'postFFE', int32(~isempty(options.postFFE)), ... % Beispielwert
|
||||
'NpostFFE', npostFFE, ... % Beispielwert
|
||||
'Ne1', eq_.order, ... % Feedforward Koeffizienten 1. Ordnung
|
||||
'K', eq_.sps, ... % Samples pro Symbol
|
||||
'DCmu', mudc_given, ... % Anpassungsrate für DC-Tap
|
||||
'training_length', eq_.len_tr, ... % Anzahl Trainingssymbole
|
||||
'training_loops', eq_.epochs_tr, ... % Anzahl Trainingsdurchläufe
|
||||
'TRmu1', eq_.mu_tr, ... % mu für DD-Modus (1. Ordnung)
|
||||
'dd_loops', eq_.epochs_dd, ... % Anzahl Durchläufe im DD-Modus
|
||||
'DDmu1', eq_.mu_dd, ... % mu für DD-Modus (1. Ordnung)
|
||||
'comment', 'function: ffe dc removal', ... % Zusätzliche Kommentare
|
||||
'ffe_buffer_len', eq_.ffe_buffer_len, ...
|
||||
'smoothing_buffer_len', eq_.smoothing_buffer_length, ...
|
||||
'smoothing_buffer_update', eq_.smoothing_buffer_update, ...
|
||||
'dc_buffer_len', eq_.dc_buffer_len, ...
|
||||
'config_hash', NaN ...
|
||||
);
|
||||
|
||||
eq_package.resultsVNLE = resultsVNLE;
|
||||
eq_package.equalizerConfigVNLE = equalizerConfigVNLE;
|
||||
|
||||
|
||||
% eq_package.vnle_out = eq_signal_sd;
|
||||
if options.showAnalysis
|
||||
|
||||
|
||||
% fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
|
||||
|
||||
% fprintf('VNLE BER: %.2e \n',ber_vnle);
|
||||
%
|
||||
% fprintf('MLSE BER: %.2e \n',ber_mlse);
|
||||
|
||||
figure(336);
|
||||
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE');
|
||||
|
||||
% figure(337);clf;
|
||||
% rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal');
|
||||
|
||||
% figure(338);clf;
|
||||
% showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338);
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338);
|
||||
end
|
||||
|
||||
showEQfilter(eq_.e,eq_signal_sd.fs.*2);
|
||||
|
||||
% figure(340);clf;
|
||||
% eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340);
|
||||
|
||||
figure(341);clf;
|
||||
showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341);
|
||||
|
||||
figure(400);clf;
|
||||
warning off
|
||||
showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400);
|
||||
|
||||
showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",400);
|
||||
warning on
|
||||
% autoArrangeFigures(3,3,2)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
function [eq_package] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
function [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, rx_signal, tx_symbols, tx_bits, options)
|
||||
% VNLE_POSTFILTER_MLSE Processes signals through VNLE, postfilter, and MLSE
|
||||
%
|
||||
% Inputs:
|
||||
% eq_ - Equalizer object
|
||||
% pf_ - Postfilter object
|
||||
% mlse_ - MLSE 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/VNLE processing
|
||||
% mlse_results - Results from MLSE processing
|
||||
|
||||
arguments
|
||||
eq_
|
||||
@@ -15,274 +30,202 @@ arguments
|
||||
options.database = [];
|
||||
end
|
||||
|
||||
%FFE or VNLE
|
||||
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
%% Process signals through equalizers
|
||||
% 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);
|
||||
[eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols);
|
||||
toc
|
||||
end
|
||||
|
||||
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
|
||||
|
||||
mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise);
|
||||
% Hard decision on VNLE output
|
||||
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
|
||||
|
||||
% Process through postfilter and MLSE
|
||||
mlse_sig_sd = pf_.process(eq_signal_sd, eq_noise);
|
||||
mlse_.DIR = pf_.coefficients;
|
||||
% [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols);
|
||||
mlse_sig_sd = mlse_.process(mlse_sig_sd);
|
||||
mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
|
||||
|
||||
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
|
||||
%% Calculate BER based on precoding mode
|
||||
[numbits, errors, bers, ~] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
|
||||
|
||||
% precoding to mitigate error propagation, most prominently used in
|
||||
% combination with duobinary signaling to avoid catastrophic error
|
||||
% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling)
|
||||
%% Calculate performance metrics
|
||||
% VNLE metrics
|
||||
[snr_vnle, snr_vnle_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
|
||||
[gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
|
||||
air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M));
|
||||
[evm_vnle_total, evm_vnle_lvl] = calc_evm(eq_signal_sd, tx_symbols);
|
||||
[std_vnle_total, std_vnle_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);
|
||||
|
||||
switch options.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);
|
||||
|
||||
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M);
|
||||
|
||||
tx_symbols_precoded = Duobinary().encode(tx_symbols);
|
||||
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
|
||||
|
||||
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
|
||||
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded);
|
||||
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded);
|
||||
[~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
%B) Just determine BER
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
|
||||
[bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
[bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
case db_mode.db_precoded
|
||||
|
||||
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
|
||||
|
||||
% A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here!
|
||||
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_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded);
|
||||
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M);
|
||||
rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded);
|
||||
[~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
% B) Omit the Coding by comparing with demapped TX symbol sequence
|
||||
|
||||
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols);
|
||||
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
|
||||
[bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
[bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
end
|
||||
|
||||
% METRICS OF VNLE SD Signal:
|
||||
[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal);
|
||||
[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
|
||||
air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M));
|
||||
[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols);
|
||||
[std_vnle_total,std_vnle_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);
|
||||
|
||||
% METRICS OF MLSE (HD-VITERBI)
|
||||
pf_.ncoeff = 1;
|
||||
pf_.process(eq_signal_sd,eq_noise);
|
||||
% MLSE metrics
|
||||
alpha = pf_.coefficients(2);
|
||||
|
||||
eq_package.ber_mlse = ber_mlse;
|
||||
eq_package.ber_vnle = ber_vnle;
|
||||
eq_package.evm_vnle_total = evm_vnle_total;
|
||||
eq_package.evm_vnle_lvl = evm_vnle_lvl;
|
||||
eq_package.gmi = gmi_vnle;
|
||||
|
||||
eq_package.eq = eq_;
|
||||
eq_package.pf = pf_;
|
||||
eq_package.mlse = mlse_;
|
||||
|
||||
resultsVNLE = struct( ...
|
||||
'result_id', NaN, ... %
|
||||
'run_id', NaN, ... % Beispielhafte Run-ID
|
||||
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
|
||||
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
|
||||
'BER', ber_vnle, ... % BER = 120 / 1.000.000
|
||||
'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits
|
||||
'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler
|
||||
'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000
|
||||
'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler
|
||||
'SNR', snr_vnle, ... % Beispielhafte SNR
|
||||
'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array
|
||||
'STD', std_vnle_total, ...
|
||||
'STD_level', jsonencode(std_vnle_lvl),...
|
||||
'STDrx' , std_rxraw_total, ...
|
||||
'STDrx_level', jsonencode(std_rxraw_lvl),...
|
||||
'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert
|
||||
'AIR', air_vnle, ... % Beispielhafter AIR-Wert
|
||||
'EVM', evm_vnle_total, ... % Beispielhafte EVM
|
||||
'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array
|
||||
'Alpha', [] ... % Beispielhafter Alpha-Wert
|
||||
);
|
||||
|
||||
%% Prepare output structures
|
||||
% Determine postFFE order
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
equalizerConfigVNLE = struct( ...
|
||||
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
|
||||
'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle)
|
||||
'M', M, ... % Ordnung der PAM-Konstellation
|
||||
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
|
||||
'db_target', 0, ... % 0 oder 1
|
||||
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
|
||||
'postFFE', ~isempty(options.postFFE), ... % Beispielwert
|
||||
'NpostFFE', npostFFE, ... % Beispielwert
|
||||
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
|
||||
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
|
||||
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
|
||||
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
|
||||
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
|
||||
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
|
||||
'K', eq_.K, ... % Samples pro Symbol
|
||||
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
|
||||
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
|
||||
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
|
||||
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
|
||||
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
|
||||
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
|
||||
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
|
||||
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
|
||||
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
|
||||
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
|
||||
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
|
||||
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
|
||||
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
|
||||
'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare
|
||||
'config_hash', NaN ...
|
||||
);
|
||||
ffe_results = struct();
|
||||
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 = bers.vnle;
|
||||
ffe_results.metrics.numBits = numbits.vnle;
|
||||
ffe_results.metrics.numBitErr = errors.vnle;
|
||||
ffe_results.metrics.BER_precoded = bers.vnle_precoded;
|
||||
ffe_results.metrics.numBitErr_precoded = errors.vnle_precoded;
|
||||
ffe_results.metrics.SNR = snr_vnle;
|
||||
ffe_results.metrics.SNR_level = snr_vnle_lvl;
|
||||
ffe_results.metrics.STD = std_vnle_total;
|
||||
ffe_results.metrics.STD_level = std_vnle_lvl;
|
||||
ffe_results.metrics.STDrx = std_rxraw_total;
|
||||
ffe_results.metrics.STDrx_level = std_rxraw_lvl;
|
||||
ffe_results.metrics.GMI = gmi_vnle;
|
||||
ffe_results.metrics.AIR = air_vnle;
|
||||
ffe_results.metrics.EVM = evm_vnle_total;
|
||||
ffe_results.metrics.EVM_level = evm_vnle_lvl;
|
||||
ffe_results.metrics.Alpha = [];
|
||||
|
||||
|
||||
resultsMLSE = struct( ...
|
||||
'result_id', NaN, ... %
|
||||
'run_id', NaN, ... % Beispielhafte Run-ID
|
||||
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
|
||||
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
|
||||
'BER', ber_mlse, ... % BER = 120 / 1.000.000
|
||||
'numBits', bits_mlse, ... % Beispiel: 1.000.000 Bits
|
||||
'numBitErr', errors_mlse, ... % Beispiel: 120 Bitfehler
|
||||
'BER_precoded', ber_mlse_diff_precoded, ... % BER = 120 / 1.000.000
|
||||
'numBitErr_precoded', errors_mlse_diff_precoded, ... % Beispiel: 120 Bitfehler
|
||||
'SNR', [], ... % Beispielhafte SNR
|
||||
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
|
||||
'GMI', [], ... % Beispielhafter GMI-Wert
|
||||
'AIR', [], ... % Beispielhafter AIR-Wert
|
||||
'EVM', [], ... % Beispielhafte EVM
|
||||
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
|
||||
'Alpha', alpha, ... % Beispielhafter Alpha-Wert
|
||||
'MLSE_dir', jsonencode([mlse_.DIR])...
|
||||
);
|
||||
% Create FFE results structure
|
||||
|
||||
eq_.e = [];
|
||||
eq_.e2 = [];
|
||||
eq_.e3 = [];
|
||||
ffe_results.config = Equalizerstruct();
|
||||
ffe_results.config.eq = jsonencode(eq_);
|
||||
ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle);
|
||||
ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part';
|
||||
|
||||
equalizerConfigMLSE = struct( ...
|
||||
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
|
||||
'equalizer_structure', int32(equalizer_structure.vnle_pf_mlse), ... % Beispiel: 1 (z.B. für vnle)
|
||||
'M', M, ... % Ordnung der PAM-Konstellation
|
||||
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
|
||||
'db_target', 0, ... % 0 oder 1
|
||||
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
|
||||
'postFFE', ~isempty(options.postFFE), ... % Beispielwert
|
||||
'NpostFFE', npostFFE, ... % Beispielwert
|
||||
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
|
||||
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
|
||||
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
|
||||
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
|
||||
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
|
||||
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
|
||||
'K', eq_.K, ... % Samples pro Symbol
|
||||
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
|
||||
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
|
||||
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
|
||||
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
|
||||
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
|
||||
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
|
||||
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
|
||||
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
|
||||
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
|
||||
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
|
||||
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
|
||||
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
|
||||
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
|
||||
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
|
||||
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
|
||||
'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare
|
||||
'config_hash', NaN ...
|
||||
);
|
||||
eq_package.resultsVNLE = resultsVNLE;
|
||||
eq_package.resultsMLSE = resultsMLSE;
|
||||
eq_package.equalizerConfigVNLE = equalizerConfigVNLE;
|
||||
eq_package.equalizerConfigMLSE = equalizerConfigMLSE;
|
||||
mlse_results = struct();
|
||||
mlse_results.metrics = Metricstruct;
|
||||
mlse_results.metrics.result_id = NaN;
|
||||
mlse_results.metrics.run_id = NaN;
|
||||
mlse_results.metrics.eqParam_id = NaN;
|
||||
mlse_results.metrics.date_of_processing = datetime('now');
|
||||
mlse_results.metrics.BER = bers.mlse;
|
||||
mlse_results.metrics.numBits = numbits.mlse;
|
||||
mlse_results.metrics.numBitErr = errors.mlse;
|
||||
mlse_results.metrics.BER_precoded = bers.mlse_precoded;
|
||||
mlse_results.metrics.numBitErr_precoded = errors.mlse_precoded;
|
||||
mlse_results.metrics.SNR = NaN;
|
||||
mlse_results.metrics.SNR_level = NaN;
|
||||
mlse_results.metrics.GMI = NaN;
|
||||
mlse_results.metrics.AIR = NaN;
|
||||
mlse_results.metrics.EVM = NaN;
|
||||
mlse_results.metrics.EVM_level = NaN;
|
||||
mlse_results.metrics.Alpha = alpha;
|
||||
mlse_results.metrics.MLSE_dir = mlse_.DIR;
|
||||
|
||||
% Create MLSE results structure
|
||||
|
||||
mlse_results.config = Equalizerstruct();
|
||||
mlse_results.config.eq = jsonencode(eq_);
|
||||
mlse_.DIR = [];
|
||||
mlse_results.config.mlse = jsonencode(mlse_);
|
||||
mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse);
|
||||
mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part';
|
||||
|
||||
% eq_package.vnle_out = eq_signal_sd;
|
||||
%% Display analysis if requested
|
||||
if options.showAnalysis
|
||||
|
||||
|
||||
% fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
|
||||
|
||||
% fprintf('VNLE BER: %.2e \n',ber_vnle);
|
||||
%
|
||||
% fprintf('MLSE BER: %.2e \n',ber_mlse);
|
||||
|
||||
figure(336);
|
||||
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients);
|
||||
|
||||
% figure(337);clf;
|
||||
% rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal');
|
||||
|
||||
% figure(338);clf;
|
||||
% showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338);
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338);
|
||||
end
|
||||
|
||||
showEQfilter(eq_.e,eq_signal_sd.fs.*2);
|
||||
|
||||
figure(340);clf;
|
||||
eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340);
|
||||
|
||||
figure(341);clf;
|
||||
showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341);
|
||||
|
||||
warning off
|
||||
showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400);
|
||||
|
||||
showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",401);
|
||||
warning on
|
||||
drawnow;
|
||||
|
||||
% autoArrangeFigures(3,3,2)
|
||||
|
||||
displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
%% Helper Functions
|
||||
function [numbits, errors, ber, error_locations] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
|
||||
% Initialize output structure
|
||||
numbits = struct('vnle', 0, 'mlse', 0);
|
||||
errors = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0);
|
||||
ber = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0);
|
||||
error_locations = struct('vnle', [], 'mlse', []);
|
||||
|
||||
% PAM mapper for demapping
|
||||
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);
|
||||
|
||||
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd, "M", M);
|
||||
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_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_vnle = mapper.demap(eq_signal_hd_precoded);
|
||||
[~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle.signal, tx_bits_precoded.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
rx_bits_mlse = mapper.demap(mlse_sig_hd_precoded);
|
||||
[~, errors.mlse_precoded, ber.mlse_precoded, ~] = calc_ber(rx_bits_mlse.signal, tx_bits_precoded.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
% B) Just determine BER
|
||||
rx_bits_vnle = mapper.demap(eq_signal_hd);
|
||||
[numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
rx_bits_mlse = mapper.demap(mlse_sig_hd);
|
||||
[numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits.signal, "skip_front", 100, "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_vnle_decoded = mapper.demap(eq_signal_hd_decoded);
|
||||
[~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd, "M", M);
|
||||
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded, "M", M);
|
||||
rx_bits_mlse_decoded = mapper.demap(mlse_sig_hd_decoded);
|
||||
[~, errors.mlse_precoded, ber.mlse_precoded, ~] = calc_ber(rx_bits_mlse_decoded.signal, tx_bits.signal, "skip_front", 100, "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_vnle = mapper.demap(eq_signal_hd);
|
||||
[numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
|
||||
rx_bits_mlse = mapper.demap(mlse_sig_hd);
|
||||
[numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE)
|
||||
% Display analysis plots and metrics
|
||||
figure(336);
|
||||
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
|
||||
|
||||
if ~isempty(postFFE)
|
||||
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
|
||||
end
|
||||
|
||||
showEQfilter(eq_.e, eq_signal_sd.fs.*2);
|
||||
|
||||
figure(340); clf;
|
||||
eq_signal_sd.eye(eq_signal_sd.fs, M, "fignum", 340);
|
||||
|
||||
figure(341); clf;
|
||||
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341);
|
||||
|
||||
warning off
|
||||
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400);
|
||||
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
|
||||
drawnow;
|
||||
warning on
|
||||
end
|
||||
Reference in New Issue
Block a user