auswertungsfiles und algos for ECOC 2025 rush...

This commit is contained in:
Silas Oettinghaus
2025-04-25 10:31:43 +02:00
parent e407c8953d
commit 727c3d9364
18 changed files with 1152 additions and 233 deletions

View File

@@ -1,129 +1,192 @@
function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
%VNLE Apply an equalization algorithm to the received signal and calculate BER
% This function takes an equalizer object, a received signal, and the
% transmitted symbols to apply equalization, map the received signal back to bits,
% and compute the bit error rate (BER).
%
% Inputs:
% EQ - Equalizer object that provides the equalization method
% rx_signal - Received signal that needs to be equalized
% tx_symbols - Transmitted symbols used as a reference for BER calculation
%
% Outputs:
% eq_signal - Equalized version of the received signal
% ber - Bit error rate after equalization
% numErrors - Number of bit errors detected
arguments
eq_
M
rx_signal
tx_symbols
tx_bits
options.precode_mode db_mode
options.showAnalysis = 0
options.eth_style = 0;
options.postFFE = [];
end
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
%FFE or VNLE
if length(rx_signal)/2 == length(tx_symbols)+0.5
rx_signal.signal = rx_signal.signal(1:end-1);
elseif length(rx_signal)/2 == length(tx_symbols)-1
end
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
mudc_given = eq_.mu_dc;
if ~isempty(options.postFFE)
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
end
%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)
tic
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
toc
end
% 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)
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
% takes:
% -> 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
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 = PAMmapper(M,0,"eth_style",options.eth_style).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",30000,"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_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);
case db_mode.db_encoded
max_burst_length = 10;
burst_count = count_error_bursts(error_pos_vnle, max_burst_length);
% normal DB encoded data (only for 10KM)
case db_mode.db_precoded
case db_mode.db_precoded
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M);
eq_signal_hd = Duobinary().decode(eq_signal_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",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
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(eq_signal_hd);
showEQfilter(eq_.e,eq_signal_sd.fs.*2);
[~,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);
% 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)
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);
end
eq_package.signal = eq_signal_sd;
end
if options.showAnalysis
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

View File

@@ -100,7 +100,7 @@ end
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);
[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;
@@ -252,7 +252,7 @@ if options.showAnalysis
%
% fprintf('MLSE BER: %.2e \n',ber_mlse);
figure(336);clf;
figure(336);
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients);
% figure(337);clf;
@@ -273,9 +273,12 @@ if options.showAnalysis
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)

View File

@@ -20,7 +20,6 @@ if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
clf;
end
assert(~isempty(options.f_sym),'No fsym given');
@@ -40,7 +39,7 @@ ende = length(correct_symbols);
% start = 30000;
% ende = 40000;
for l = 1:4
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
@@ -50,23 +49,24 @@ for l = 1:4
std_lvl(l) = std(symbols_for_lvl,'omitnan');
xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6;
% xax_in_sec = 1:length(correct_symbols);
if 0
scatter(xax_in_sec(start:ende),symbols_for_lvl(start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
hold on;
end
end
std_lvl = round(std_lvl,2);
ccnt = 0;
% Add the windowed/ smoothed curves
for l = 1:4
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl = NaN(1,length(correct_symbols));
movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2]);
movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2], 'Endpoints', 'fill');
symbols_for_lvl(correct_symbols==level_amplitude) = movmean;

View File

@@ -0,0 +1,29 @@
function burst_count = count_error_bursts(err_pos, max_burst_length)
% err_pos: Vector of error positions
% max_burst_length: Maximum length for which bursts are counted (e.g., 10)
% Sort the error positions to ensure they're in increasing order
err_pos = sort(err_pos);
% Initialize burst_count array to hold the counts for each burst length
burst_count = zeros(1, max_burst_length);
% Find the differences between consecutive error positions
diffs = diff(err_pos);
% Find the start of the bursts (where the difference is greater than 1)
burst_starts = [1, find(diffs > 1) + 1]; % Starts at index 1 and after gaps
burst_ends = [find(diffs > 1), length(err_pos)]; % Ends where gaps are
% Loop over the bursts
for b = 1:length(burst_starts)
burst_length = burst_ends(b) - burst_starts(b) + 1;
% Check if the burst length exceeds any of the thresholds
for i = 1:max_burst_length
if burst_length > i
burst_count(i) = burst_count(i) + 1;
end
end
end
end