Merge branch 'main' of https://cau-git.rz.uni-kiel.de/nt/mitarbeiter/silas/imdd_simulation
This commit is contained in:
@@ -1,18 +1,96 @@
|
||||
function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits)
|
||||
%Duobinary Signaling
|
||||
function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options)
|
||||
%Duobinary Signaling
|
||||
arguments
|
||||
eq_
|
||||
mlse_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
options.postFFE = [];
|
||||
end
|
||||
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
eq_signal = mlse_.process(eq_signal);
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
eq_signal = Duobinary().encode(eq_signal);
|
||||
eq_signal = Duobinary().decode(eq_signal);
|
||||
if ~isempty(options.postFFE)
|
||||
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,tx_symbols);
|
||||
end
|
||||
|
||||
% M = numel(unique(eq_signal.signal));
|
||||
rx_bits = PAMmapper(M,0).demap(eq_signal);
|
||||
|
||||
[~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
eq_signal = mlse_.process(eq_signal);
|
||||
|
||||
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);
|
||||
|
||||
[bits_db,errors_db,ber_db,~] = 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
|
||||
);
|
||||
|
||||
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 ...
|
||||
);
|
||||
|
||||
eq_package.resultsDBsignaling = resultsDBsignaling;
|
||||
eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling;
|
||||
|
||||
eq_package.ber = ber;
|
||||
|
||||
end
|
||||
@@ -1,77 +1,156 @@
|
||||
function [eq_package] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options)
|
||||
|
||||
arguments
|
||||
eq_
|
||||
mlse_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
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 = [];
|
||||
end
|
||||
|
||||
%Duobinary Targeting
|
||||
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal,Duobinary().encode(tx_symbols));
|
||||
%Duobinary Targeting
|
||||
db_ref_sequence = Duobinary().encode(tx_symbols);
|
||||
db_ref_constellation = unique(db_ref_sequence.signal);
|
||||
[eq_signal, eq_noise] = eq_.process(rx_signal,db_ref_sequence);
|
||||
|
||||
% dir = [1,1];
|
||||
mlse_sig_sd = mlse_.process(eq_signal);
|
||||
if ~isempty(options.postFFE)
|
||||
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence);
|
||||
end
|
||||
|
||||
mlse_sig_hd = PAMmapper(M,0).quantize(mlse_sig_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)
|
||||
% dir = [1,1];
|
||||
mlse_sig_sd = mlse_.process(eq_signal);
|
||||
|
||||
% takes:
|
||||
% -> eq_signal_hd: hard decision signal after eq
|
||||
% -> tx_symbols: that where used as reference for eq
|
||||
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
|
||||
|
||||
switch options.precode_mode
|
||||
case db_mode.db_emulate
|
||||
% 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
|
||||
|
||||
mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M);
|
||||
case db_mode.no_db
|
||||
% TX Data is not precoded:
|
||||
|
||||
% A) Emulate diff precoding
|
||||
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 = PAMmapper(M,0).demap(tx_symbols_precoded);
|
||||
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
|
||||
|
||||
case db_mode.db_discard
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded);
|
||||
[~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
% normal dsp for precoded sequence == discard/omit/ignore precode
|
||||
tx_bits = PAMmapper(M,0).demap(tx_symbols);
|
||||
%B) Just determine BER
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
[bits_mlse,errors_mlse,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
case db_mode.db_encoded
|
||||
case db_mode.db_precoded
|
||||
|
||||
% normal DB encoded data (only for 10KM)
|
||||
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
|
||||
|
||||
case db_mode.db_precoded
|
||||
% A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here!
|
||||
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_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M);
|
||||
% B) Omit the Coding by comparing with demapped TX symbol sequence
|
||||
|
||||
end
|
||||
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols);
|
||||
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
% M = numel(unique(tx_symbols.signal));
|
||||
rx_bits = PAMmapper(M,0).demap(mlse_sig_hd);
|
||||
end
|
||||
|
||||
[~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% M = numel(unique(tx_symbols.signal));
|
||||
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
|
||||
|
||||
|
||||
eq_package.ber = ber;
|
||||
[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;
|
||||
|
||||
if options.showAnalysis
|
||||
eq_noise = eq_noise - mean(eq_noise.signal);
|
||||
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
|
||||
);
|
||||
|
||||
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250);
|
||||
if ~isempty(options.postFFE)
|
||||
npostFFE = options.postFFE.order;
|
||||
else
|
||||
npostFFE = 0;
|
||||
end
|
||||
|
||||
showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise');
|
||||
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 ...
|
||||
);
|
||||
|
||||
Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",250);
|
||||
end
|
||||
eq_package.resultsDBtgt = resultsDBtgt;
|
||||
eq_package.equalizerConfigDBtgt = equalizerConfigDBtgt;
|
||||
|
||||
if options.showAnalysis
|
||||
eq_noise = eq_noise - mean(eq_noise.signal);
|
||||
|
||||
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum");
|
||||
|
||||
Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",250,"displayname","DB encoded reference");
|
||||
|
||||
showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise after Equalization');
|
||||
|
||||
fprintf('DB tgt BER: %.2e \n',ber);
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -22,6 +22,8 @@ function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
tx_bits
|
||||
options.precode_mode db_mode
|
||||
options.showAnalysis = 0
|
||||
options.eth_style = 0;
|
||||
options.postFFE = [];
|
||||
end
|
||||
|
||||
%FFE or VNLE
|
||||
@@ -31,6 +33,10 @@ function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
end
|
||||
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
if ~isempty(options.postFFE)
|
||||
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
|
||||
end
|
||||
|
||||
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
|
||||
|
||||
% precoding to mitigate error propagation, most prominently used in
|
||||
@@ -50,7 +56,7 @@ function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
tx_symbols_precoded = Duobinary().encode(tx_symbols);
|
||||
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
|
||||
|
||||
tx_bits = PAMmapper(M,0).demap(tx_symbols_precoded);
|
||||
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(tx_symbols_precoded);
|
||||
|
||||
case db_mode.db_discard
|
||||
|
||||
@@ -68,26 +74,56 @@ function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
|
||||
end
|
||||
|
||||
rx_bits = PAMmapper(M,0).demap(eq_signal_hd);
|
||||
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(eq_signal_hd);
|
||||
|
||||
[~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
[evm_total,evm_lvl] = calc_evm(eq_signal_sd,tx_symbols);
|
||||
[inf_rate] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
|
||||
|
||||
|
||||
eq_package.ber_vnle = ber;
|
||||
eq_package.evm_total = evm_total;
|
||||
eq_package.evm_lvl = evm_lvl;
|
||||
eq_package.inf_rate_vnle = inf_rate;
|
||||
|
||||
eq_package.snr = snr(eq_signal_sd.signal,eq_noise.signal);
|
||||
|
||||
eq_package.signal = eq_signal_sd;
|
||||
|
||||
|
||||
if options.showAnalysis
|
||||
|
||||
snr(eq_signal_sd.signal,eq_noise.signal);
|
||||
fprintf('SNR: %d dB \n',snr(eq_signal_sd.signal,eq_noise.signal));
|
||||
|
||||
if M == 6
|
||||
logm = 2.5;
|
||||
else
|
||||
logm = log2(M);
|
||||
end
|
||||
|
||||
fprintf('NGMI: %.4f \n', inf_rate/logm);
|
||||
|
||||
fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
|
||||
|
||||
fprintf('VNLE BER: %.2e \n',ber);
|
||||
|
||||
disp("%%%%%%%%%%%%%%%%%%%%%")
|
||||
|
||||
% showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients');
|
||||
%
|
||||
% if ~isempty(options.postFFE)
|
||||
% showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients');
|
||||
% end
|
||||
%
|
||||
% showEQNoisePSD(eq_noise);
|
||||
%
|
||||
% showEQfilter(eq_.e,eq_signal_sd.fs.*2)
|
||||
|
||||
% noiselessness(tx_symbols,eq_noise,"displayname",'SNR after VNLE','fignum',301);
|
||||
|
||||
% showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",302);
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,125 +1,267 @@
|
||||
function [eq_package] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,rx_signal,tx_symbols,tx_bits,options)
|
||||
|
||||
|
||||
arguments
|
||||
eq_
|
||||
pf_
|
||||
mlse_
|
||||
M
|
||||
rx_signal
|
||||
tx_symbols
|
||||
tx_bits
|
||||
eq_
|
||||
pf_
|
||||
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
|
||||
|
||||
%FFE or VNLE
|
||||
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
%FFE or VNLE
|
||||
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
|
||||
|
||||
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
|
||||
if ~isempty(options.postFFE)
|
||||
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
|
||||
end
|
||||
|
||||
mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise);
|
||||
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
|
||||
|
||||
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_sd = pf_.process(eq_signal_sd,eq_noise);
|
||||
|
||||
mlse_sig_hd = PAMmapper(M,0).quantize(mlse_sig_sd);
|
||||
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);
|
||||
|
||||
% 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)
|
||||
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
|
||||
|
||||
% takes:
|
||||
% -> M
|
||||
% -> eq_signal_hd: hard decision signal after eq
|
||||
% -> tx_symbols: that where used as reference for eq
|
||||
% 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.db_emulate
|
||||
% re
|
||||
eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M);
|
||||
eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M);
|
||||
switch options.precode_mode
|
||||
|
||||
mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M);
|
||||
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 = PAMmapper(M,0).demap(tx_symbols_precoded);
|
||||
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
|
||||
|
||||
case db_mode.db_discard
|
||||
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);
|
||||
|
||||
% normal dsp for precoded sequence == discard/omit/ignore precode
|
||||
tx_bits = PAMmapper(M,0).demap(tx_symbols);
|
||||
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);
|
||||
|
||||
case db_mode.db_encoded
|
||||
%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);
|
||||
|
||||
% normal DB encoded data (only for 10KM)
|
||||
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
|
||||
case db_mode.db_precoded
|
||||
|
||||
eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M);
|
||||
eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M);
|
||||
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
|
||||
|
||||
mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M);
|
||||
mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M);
|
||||
% 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);
|
||||
|
||||
end
|
||||
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);
|
||||
|
||||
% METRICS OF VNLE %
|
||||
rx_bits_vnle = PAMmapper(M,0).demap(eq_signal_hd);
|
||||
[~,~,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
|
||||
% correct TUM implementation of AIR
|
||||
[inf_rate_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
|
||||
[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols);
|
||||
% B) Omit the Coding by comparing with demapped TX symbol sequence
|
||||
|
||||
% METRICS OF MLSE (HD-VITERBI)
|
||||
rx_bits_mlse = PAMmapper(M,0).demap(mlse_sig_hd);
|
||||
[~,~,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
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);
|
||||
|
||||
% METRICS OF MLSE (HD-VITERBI)
|
||||
pf_.ncoeff = 1;
|
||||
pf_.process(eq_signal_sd,eq_noise);
|
||||
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
|
||||
'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', ~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 ...
|
||||
);
|
||||
|
||||
|
||||
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.air = inf_rate_vnle;
|
||||
|
||||
eq_package.eq = eq_;
|
||||
eq_package.pf = pf_;
|
||||
eq_package.mlse = mlse_;
|
||||
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])...
|
||||
);
|
||||
|
||||
|
||||
if options.showAnalysis
|
||||
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;
|
||||
|
||||
% fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
|
||||
% eq_package.vnle_out = eq_signal_sd;
|
||||
if options.showAnalysis
|
||||
|
||||
fprintf('VNLE BER: %.2e \n',ber_vnle);
|
||||
% fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
|
||||
|
||||
fprintf('MLSE BER: %.2e \n',ber_mlse);
|
||||
fprintf('VNLE BER: %.2e \n',ber_vnle);
|
||||
|
||||
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'VNLE+DFE','postfilter_taps',pf_.coefficients);
|
||||
|
||||
fprintf('MLSE BER: %.2e \n',ber_mlse);
|
||||
|
||||
rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal');
|
||||
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients);
|
||||
|
||||
tx_symbols.spectrum("normalizeTo0dB",1,"fignum",337,'displayname','Tx Signal');
|
||||
|
||||
showLevelHistogram(eq_signal_sd,tx_symbols)
|
||||
% showLevelHistogram(mlse_sig_sd,tx_symbols)
|
||||
rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal');
|
||||
|
||||
showEQcoefficients(eq_.e,eq_.e2,eq_.e3,"displayname",'Coefficients');
|
||||
tx_symbols.spectrum("normalizeTo0dB",1,"fignum",337,'displayname','Tx Signal');
|
||||
|
||||
showEQNoiseSNR(tx_symbols,eq_noise,"displayname",'vnle snr','fignum',101);
|
||||
showLevelHistogram(eq_signal_sd,tx_symbols)
|
||||
% showLevelHistogram(mlse_sig_sd,tx_symbols)
|
||||
|
||||
%%% EQ SNR Spectrum %230
|
||||
%snr
|
||||
snr_vnle = snr(tx_symbols.signal,eq_noise.signal);
|
||||
showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients');
|
||||
|
||||
% showErrorBurstCount(eq_signal_sd,tx_symbols)
|
||||
|
||||
showEQNoiseSNR(tx_symbols,eq_noise,"displayname",'vnle snr','fignum',101);
|
||||
|
||||
end
|
||||
%%% EQ SNR Spectrum %230
|
||||
%snr
|
||||
|
||||
|
||||
% showErrorBurstCount(eq_signal_sd,tx_symbols)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
function showEQNoiseSNR(tx_signal, rx_signal, options)
|
||||
function showEQNoiseSNR(eq_signal, noise_signal, options)
|
||||
arguments
|
||||
tx_signal
|
||||
rx_signal
|
||||
eq_signal
|
||||
noise_signal
|
||||
options.fs_tx
|
||||
options.fs_rx
|
||||
options.fignum (1,1) double = NaN % Default to NaN if not provided
|
||||
@@ -16,13 +16,13 @@ end
|
||||
fig = figure(options.fignum); % Use the specified figure number
|
||||
end
|
||||
|
||||
if isa(tx_signal,'Signal')
|
||||
options.fs_tx = tx_signal.fs;
|
||||
tx_signal = tx_signal.signal;
|
||||
if isa(eq_signal,'Signal')
|
||||
options.fs_tx = eq_signal.fs;
|
||||
eq_signal = eq_signal.signal;
|
||||
end
|
||||
if isa(rx_signal,'Signal')
|
||||
options.fs_rx = rx_signal.fs;
|
||||
rx_signal = rx_signal.signal;
|
||||
if isa(noise_signal,'Signal')
|
||||
options.fs_rx = noise_signal.fs;
|
||||
noise_signal = noise_signal.signal;
|
||||
end
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ end
|
||||
% Ensure the figure is ready before calling spectrum
|
||||
title('SNR of received Signal')
|
||||
|
||||
fft_length = 2^(nextpow2(length(tx_signal))-7);
|
||||
fft_length = 2^(nextpow2(length(eq_signal))-7);
|
||||
|
||||
[s_lin,w] = pwelch(tx_signal,hanning(fft_length),fft_length/2,fft_length,options.fs_tx,"centered","psd","mean");
|
||||
[n_lin,w] = pwelch(rx_signal,hanning(fft_length),fft_length/2,fft_length,options.fs_rx,"centered","psd","mean");
|
||||
[s_lin,w] = pwelch(eq_signal,hanning(fft_length),fft_length/2,fft_length,options.fs_tx,"centered","psd","mean");
|
||||
[n_lin,w] = pwelch(noise_signal,hanning(fft_length),fft_length/2,fft_length,options.fs_rx,"centered","psd","mean");
|
||||
|
||||
w = w.*1e-9;
|
||||
snr_dbm = 10*log10(s_lin./n_lin);
|
||||
@@ -48,6 +48,7 @@ end
|
||||
% figure(231)
|
||||
hold on
|
||||
plot(w,snr_dbm,'DisplayName','SNR','LineWidth',0.5,'Color',options.color);
|
||||
yline(mean(snr_dbm),'HandleVisibility','off','Color',options.color);
|
||||
xlabel("Frequency in GHz");
|
||||
|
||||
edgetick = 2^(nextpow2(options.fs_tx*1e-9));
|
||||
|
||||
@@ -1,58 +1,67 @@
|
||||
function showEQcoefficients(n1, n2, n3, options)
|
||||
% Show filter coefficients as stem plot
|
||||
% n1, n2, and n3 in different subplots
|
||||
% Scale all y-axis to -1 and 1
|
||||
function showEQcoefficients(options)
|
||||
% Show filter coefficients as stem plots.
|
||||
% Only the provided coefficient arrays (n1, n2, n3) are shown,
|
||||
% each in its own subplot. The y-axis is scaled to [-1, 1].
|
||||
|
||||
arguments
|
||||
n1
|
||||
n2
|
||||
n3
|
||||
options.fignum (1,1) double = NaN % Default to NaN if not provided
|
||||
options.displayname (1,:) char = '' % Default to an empty string if not provided
|
||||
options.n1 = [];
|
||||
options.n2 = [];
|
||||
options.n3 = [];
|
||||
options.fignum (1,1) double = NaN; % Default: create new figure if NaN
|
||||
options.displayname (1,:) char = ''; % Default: empty string
|
||||
options.color = [0.2157, 0.4941, 0.7216];
|
||||
options.clf = 0; % Clear figure before plotting new
|
||||
options.clf = 0; % Clear figure before plotting if set to 1
|
||||
end
|
||||
|
||||
% Determine the figure number to use or create a new figure
|
||||
% Determine the figure number to use or create a new one.
|
||||
if isnan(options.fignum)
|
||||
fig = figure; % Create a new figure and get its handle
|
||||
fig = figure;
|
||||
else
|
||||
fig = figure(options.fignum); % Use the specified figure number
|
||||
fig = figure(options.fignum);
|
||||
end
|
||||
|
||||
if options.clf
|
||||
clf(fig); % Clear the figure if requested
|
||||
clf(fig);
|
||||
end
|
||||
|
||||
hold on
|
||||
ax = gca;
|
||||
N = numel(ax.Children);
|
||||
|
||||
% Set up a colormap for consistent coloring
|
||||
% Set up a colormap for consistent coloring.
|
||||
cmap = linspecer(8);
|
||||
options.color = cmap(mod(N, size(cmap, 1)) + 1, :);
|
||||
|
||||
% Create subplots for n1, n2, n3
|
||||
for i = 1:3
|
||||
subplot(3, 1, i);
|
||||
switch i
|
||||
case 1
|
||||
stem(n1, 'Color', options.color, 'LineWidth', 1,'Marker','.','MarkerSize',10);
|
||||
title(sprintf('1st order Filter Coefficients: %d',numel(n1)));
|
||||
case 2
|
||||
stem(n2, 'Color', options.color, 'LineWidth', 1,'Marker','.','MarkerSize',10);
|
||||
title(sprintf('2nd order Filter Coefficients: %d',numel(n2)));
|
||||
case 3
|
||||
stem(n3, 'Color', options.color, 'LineWidth', 1,'Marker','.','MarkerSize',10);
|
||||
title(sprintf('3rd order Filter Coefficients: %d',numel(n3)));
|
||||
end
|
||||
ylim([-1, 1]); % Scale y-axis to -1 and 1
|
||||
% Build cell arrays for coefficients and their corresponding titles.
|
||||
coeffs = {};
|
||||
titles = {};
|
||||
|
||||
if ~isempty(options.n1)
|
||||
coeffs{end+1} = options.n1;
|
||||
titles{end+1} = sprintf('1st order Filter Coefficients: %d', numel(options.n1));
|
||||
end
|
||||
if ~isempty(options.n2)
|
||||
coeffs{end+1} = options.n2;
|
||||
titles{end+1} = sprintf('2nd order Filter Coefficients: %d', numel(options.n2));
|
||||
end
|
||||
if ~isempty(options.n3)
|
||||
coeffs{end+1} = options.n3;
|
||||
titles{end+1} = sprintf('3rd order Filter Coefficients: %d', numel(options.n3));
|
||||
end
|
||||
|
||||
numSubplots = numel(coeffs);
|
||||
|
||||
for i = 1:numSubplots
|
||||
subplot(1, numSubplots, i);
|
||||
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 1, ...
|
||||
'Marker', '.', 'MarkerSize', 10);
|
||||
title(titles{i});
|
||||
ylim([-1, 1]); % Set y-axis limits to [-1, 1]
|
||||
grid on;
|
||||
grid minor
|
||||
grid minor;
|
||||
xlabel('Coefficient Index');
|
||||
ylabel('Amplitude');
|
||||
end
|
||||
|
||||
% Ensure the layout is tight for better visibility
|
||||
sgtitle('Filter Coefficients'); % Overall title
|
||||
end
|
||||
sgtitle('Filter Coefficients'); % Overall title for the figure
|
||||
end
|
||||
|
||||
36
Functions/EQ_visuals/showEQfilter.m
Normal file
36
Functions/EQ_visuals/showEQfilter.m
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
function showEQfilter(coefficients,fs)
|
||||
|
||||
% Assuming that obj.e contains the final FFE filter coefficients.
|
||||
% Set the number of frequency points and sampling frequency.
|
||||
nfft = 1024; % Number of frequency points
|
||||
|
||||
% Compute the frequency response of the FFE filter.
|
||||
[H, f] = freqz(coefficients, 1, nfft, fs);
|
||||
|
||||
% Keep only the first half of the frequency response (up to the Nyquist frequency).
|
||||
half_nfft = floor(nfft/2) + 1;
|
||||
f = f(1:half_nfft);
|
||||
H = H(1:half_nfft);
|
||||
|
||||
% Plot the magnitude and phase responses.
|
||||
figure;
|
||||
|
||||
% Magnitude response (in dB)
|
||||
subplot(2,1,1);
|
||||
hold on
|
||||
plot(f.*1e-9, 20*log10(abs(1./H)));
|
||||
title('(Inverted) Magnitude Response of FFE Filter');
|
||||
xlabel('Frequency (Hz)');
|
||||
ylabel('Magnitude (dB)');
|
||||
grid on;
|
||||
|
||||
% Phase response
|
||||
subplot(2,1,2);
|
||||
plot(f.*1e-9, unwrap(angle(H)));
|
||||
title('Phase Response of FFE Filter');
|
||||
xlabel('Frequency (Hz)');
|
||||
ylabel('Phase');
|
||||
grid on;
|
||||
|
||||
end
|
||||
@@ -39,7 +39,9 @@ end
|
||||
intermediate = received_sd(lvl,:);
|
||||
cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./length(eq_signal),3).*100;
|
||||
hold on
|
||||
warning off
|
||||
histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' | ',num2str(cnt(lvl)),' %'],'FaceColor',lvlcol(lvl,:),'Normalization','pdf');
|
||||
warning on
|
||||
end
|
||||
legend
|
||||
grid on
|
||||
|
||||
9
Functions/Lab_helper/loadFreqResp.m
Normal file
9
Functions/Lab_helper/loadFreqResp.m
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
precomp_path = "D:\kiel_dsp\imdd_simulation\projects\HighSpeed_ETH";
|
||||
precomp_filename = "lab_high_speed_nachtschichtmzm";
|
||||
% precomp_filename = "lab_high_speed";
|
||||
% precomp_filename = "lab_high_speed";
|
||||
|
||||
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9);
|
||||
freqresp.load('loadPath',precomp_path,'fileName',precomp_filename);
|
||||
freqresp.plot();
|
||||
40
Functions/Metrics/calc_air_plain.m
Normal file
40
Functions/Metrics/calc_air_plain.m
Normal file
@@ -0,0 +1,40 @@
|
||||
function [airs] = calc_air_plain(noisy_signal,reference_signal,options)
|
||||
% Calculation of AIR acc. to J. Kozesnik, „Numerically Computing Achievable Rates of Memoryless Channels“, Francisco Javier Garcıa-Gomez, doi: 10.1007/978-94-009-9857-5.
|
||||
% Implementation is not accessible, I mailed TUM to get the code...
|
||||
|
||||
arguments(Input)
|
||||
noisy_signal;
|
||||
reference_signal;
|
||||
options.skip_front = 0;
|
||||
options.skip_end = 0;
|
||||
options.returnErrorLocation = 0;
|
||||
end
|
||||
|
||||
options.skip_end = abs(options.skip_end);
|
||||
options.skip_front = abs(options.skip_front);
|
||||
|
||||
assert((options.skip_end+options.skip_front)<length(noisy_signal),"You can not skip more bits than overall length of data! Set skip_front or skip_end to lower value or check data_in");
|
||||
|
||||
% TRIM
|
||||
[noisy_signal,reference_signal]=trimseq(noisy_signal,reference_signal,options.skip_front,options.skip_end);
|
||||
|
||||
% CALC EVM
|
||||
%%% new implementation of AIR
|
||||
constellation = unique(reference_signal);
|
||||
reference_idx = arrayfun(@(x) find(constellation == x, 1), reference_signal);
|
||||
air = air_garcia_implementation(constellation',noisy_signal',reference_idx');
|
||||
|
||||
|
||||
function [data_,reference_]=trimseq(data,reference,skipstart,skip_end)
|
||||
|
||||
data_ = data(skipstart+1:end-skip_end,:);
|
||||
|
||||
delta_bits = length(reference) - length(data);
|
||||
|
||||
skip_end = delta_bits + skip_end;
|
||||
|
||||
reference_ = reference(skipstart+1:end-skip_end,:);
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
36
Functions/Metrics/calc_snr.m
Normal file
36
Functions/Metrics/calc_snr.m
Normal file
@@ -0,0 +1,36 @@
|
||||
function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
|
||||
% CALC_SNR Calculates overall SNR and level-wise SNR for a PAM-M constellation.
|
||||
%
|
||||
% [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
|
||||
%
|
||||
% Inputs:
|
||||
% tx_signal - Vector of transmitted signal values.
|
||||
% eq_noise - Vector of corresponding noise samples.
|
||||
%
|
||||
% Outputs:
|
||||
% snr_all - Overall SNR computed using all signal values.
|
||||
% snr_per_level - A vector where each element is the SNR computed
|
||||
% for a unique amplitude level in tx_signal.
|
||||
%
|
||||
% The function first computes the overall SNR using the full signal vectors.
|
||||
% Then it uses the unique levels in tx_signal to calculate the SNR for
|
||||
% the symbols corresponding to each level separately.
|
||||
|
||||
% Calculate overall SNR using the complete signals
|
||||
snr_all = snr(tx_signal, eq_noise);
|
||||
|
||||
% Get the unique amplitude levels in the transmitted signal
|
||||
levels = unique(tx_signal);
|
||||
|
||||
% Preallocate an array to store the SNR for each unique level
|
||||
snr_per_level = zeros(size(levels));
|
||||
|
||||
% Loop over each unique level to compute the SNR for that level
|
||||
for i = 1:length(levels)
|
||||
% Find indices where tx_signal equals the current level
|
||||
idx = (tx_signal == levels(i));
|
||||
|
||||
% Compute the SNR for these indices
|
||||
snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx));
|
||||
end
|
||||
end
|
||||
62
Functions/Theory/modifiedGodardTimingRecovery.m
Normal file
62
Functions/Theory/modifiedGodardTimingRecovery.m
Normal file
@@ -0,0 +1,62 @@
|
||||
function tau_error = modifiedGodardTimingRecovery(rx, N, eta, beta)
|
||||
% modifiedGodardTimingRecovery
|
||||
%
|
||||
% This function estimates the symbol timing error using the modified Godard
|
||||
% approach in the frequency domain as described in:
|
||||
%
|
||||
% "Modified Godard Timing Recovery for Non-Integer Oversampling Receivers"
|
||||
% Appl. Sci. 2017, 7, 655. :contentReference[oaicite:0]{index=0}​:contentReference[oaicite:1]{index=1}
|
||||
%
|
||||
% Inputs:
|
||||
% rx - Received time-domain signal (vector)
|
||||
% N - FFT size (should be an even integer)
|
||||
% eta - Effective oversampling factor used for timing recovery (eta > 1)
|
||||
% beta - Roll-off related parameter (0 < beta <= 1)
|
||||
%
|
||||
% Output:
|
||||
% tau_error - Estimated timing error (in sample units)
|
||||
%
|
||||
% Implementation Notes:
|
||||
% 1. The function computes an N-point FFT of the first N samples of rx.
|
||||
% 2. It then determines an offset (Delta) defined as:
|
||||
% offset = round((1 - 1/eta) * N)
|
||||
% 3. To avoid index overflow, the summation is taken over indices k from 1 to
|
||||
% floor(N/2) - offset.
|
||||
% 4. The timing error is estimated as:
|
||||
% tau_error = ( (1+beta)/(2*eta*N - 1) * sum(phase difference) ) / (2*pi)
|
||||
% where the phase difference is (angle(R(k)) - angle(R(k+offset)))
|
||||
%
|
||||
% Make sure that the input signal rx contains at least N samples.
|
||||
|
||||
% Check input length
|
||||
if length(rx) < N
|
||||
error('Input signal length must be at least N.');
|
||||
end
|
||||
|
||||
% Compute the N-point FFT of the first N samples of rx
|
||||
R = fft(rx(1:N), N);
|
||||
|
||||
% Determine the offset based on the oversampling factor (eta)
|
||||
offset = round((1 - 1/eta) * N);
|
||||
|
||||
% Define the summation range to avoid index overflow
|
||||
k_min = 1;
|
||||
k_max = floor(N/2) - offset;
|
||||
if k_max < k_min
|
||||
error('Chosen parameters result in an empty summation range. Adjust N, eta, or beta.');
|
||||
end
|
||||
|
||||
% Compute the sum of phase differences over the selected frequency bins
|
||||
phase_diff_sum = 0;
|
||||
for k = k_min:k_max
|
||||
phase_k = angle(R(k));
|
||||
phase_k_offset = angle(R(k + offset));
|
||||
phase_diff_sum = phase_diff_sum + (phase_k - phase_k_offset);
|
||||
end
|
||||
|
||||
% Normalization factor as per the modified Godard algorithm
|
||||
norm_factor = (1 + beta) / (2 * eta * N - 1);
|
||||
|
||||
% Estimate the timing error in sample units
|
||||
tau_error = (norm_factor * phase_diff_sum) / (2 * pi);
|
||||
end
|
||||
@@ -9,7 +9,9 @@ function beautifyBERplot()
|
||||
for i = 1:length(lines)
|
||||
lines(i).LineWidth = 1.3; % Thicker line width
|
||||
%lines(i).LineStyle = '-'; % Solid lines for simplicity
|
||||
lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically
|
||||
if string(lines(i).Marker) == "none"
|
||||
lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically
|
||||
end
|
||||
lines(i).MarkerSize = 4; % Marker size
|
||||
lines(i).MarkerFaceColor = 'auto'; % Use line color for marker face
|
||||
end
|
||||
@@ -23,7 +25,7 @@ function beautifyBERplot()
|
||||
|
||||
% Set logarithmic scale for y-axis, but only if it makes sense.
|
||||
% If this is not always desired, you could condition this on the presence of lines or data.
|
||||
% set(gca, 'YScale', 'log');
|
||||
set(gca, 'YScale', 'log');
|
||||
|
||||
% Customize grid and box appearance
|
||||
set(gca, 'Box', 'on', 'LineWidth', 0.8); % Thicker border
|
||||
|
||||
40
Functions/moveit_wrapper_.m
Normal file
40
Functions/moveit_wrapper_.m
Normal file
@@ -0,0 +1,40 @@
|
||||
function [data_out, state_out] = moveit_wrapper(func_name, data_in, para)
|
||||
% Generic wrapper for legacy MATLAB functions using loop-based execution
|
||||
%
|
||||
% Usage:
|
||||
% [data_out, state_out] = wrapper('quantize', data_in, para);
|
||||
%
|
||||
% Inputs:
|
||||
% - func_name: Name of the function as a string (e.g., 'quantize')
|
||||
% - data_in: Input signal or empty for initialization
|
||||
% - para: Structure with parameters (optional)
|
||||
%
|
||||
% Outputs:
|
||||
% - data_out: Processed output signal
|
||||
% - state_out: Internal state of the function
|
||||
|
||||
global loop;
|
||||
|
||||
% Ensure the function exists
|
||||
if ~exist(func_name, 'file')
|
||||
error('Function "%s" does not exist.', func_name);
|
||||
end
|
||||
|
||||
% Step 1: Get default parameters if not provided
|
||||
if nargin < 3 || isempty(para)
|
||||
loop = 0; % Request parameter structure
|
||||
[para, comment] = feval(func_name);
|
||||
end
|
||||
|
||||
% Initialize state
|
||||
state = struct();
|
||||
|
||||
% Step 2: Initialize the function
|
||||
loop = 0;
|
||||
[~, state_] = feval(func_name, data_in, state, para);
|
||||
|
||||
% Step 3: Process input signal
|
||||
loop = 1;
|
||||
[data_out, state_out] = feval(func_name, data_in, state_, para);
|
||||
|
||||
end
|
||||
22
Functions/showTransferFunction.m
Normal file
22
Functions/showTransferFunction.m
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
function showTransferFunction(coeffs,options)
|
||||
|
||||
arguments
|
||||
coeffs
|
||||
options.fignum = 765
|
||||
options.DisplayName = ''
|
||||
options.color = [1 1 0.1]
|
||||
end
|
||||
% Define the filter taps
|
||||
|
||||
[H, w] = freqz(coeffs, 1, 1024, 1);
|
||||
|
||||
figure(options.fignum);
|
||||
hold on
|
||||
plot(w, 10*log10(abs(H)), 'LineWidth', 2,'DisplayName',options.DisplayName,'Color',options.color); %todo
|
||||
xlabel('Normalized Frequency');
|
||||
ylabel('Amplitude in dB');
|
||||
grid on;
|
||||
ylim([-20,10])
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user