Many changes towards simulation of JLT and once again the evaluation of the Highspeed data from Lab experiments 2024
This commit is contained in:
@@ -3,24 +3,34 @@ classdef FFE < handle
|
||||
% 1) Training mode (stable performance when you use NLMS)
|
||||
% 2) Decision directed mode
|
||||
|
||||
% Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0);
|
||||
%LMS: mu in order of 0.001 for acceptable convergence speed
|
||||
%NLMS: mu in order of 0.01 for acceptable convergence speed
|
||||
%RLS: mu is lambda -> 0.99 -> 1 (has a strong dependency on this! use a loop to find out best values)
|
||||
|
||||
% FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
e_tr
|
||||
error
|
||||
debug_struct
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
adaption_technique % nlms, lms, rls
|
||||
dd_mode % 1 or 0 to set DD-mode on or off
|
||||
mu_dd %weight update in dd mode
|
||||
epochs_dd
|
||||
|
||||
constellation
|
||||
P % covariance matrix of rls
|
||||
|
||||
decide
|
||||
constellation % symbol constellation
|
||||
|
||||
decide %wether to return the (hard) decisions or the result after FFE (soft)
|
||||
end
|
||||
|
||||
methods
|
||||
@@ -34,6 +44,8 @@ classdef FFE < handle
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.adaption_technique adaption_method = adaption_method.lms;
|
||||
options.dd_mode = 1;
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
@@ -59,10 +71,15 @@ classdef FFE < handle
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
|
||||
delta = 0.05;
|
||||
obj.P = (1/delta) * eye(obj.order);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
obj.e_tr = obj.e;
|
||||
|
||||
% Decision Directed Mode
|
||||
n = X.length;
|
||||
@@ -71,7 +88,7 @@ classdef FFE < handle
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,n,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
@@ -83,24 +100,25 @@ classdef FFE < handle
|
||||
|
||||
Noi = X;
|
||||
Noi = X - D;
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz)
|
||||
function [y,d_hat] = equalize(obj,x,d,mu,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mio
|
||||
epochs
|
||||
N
|
||||
training
|
||||
obj
|
||||
x
|
||||
d
|
||||
mu
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
lambda = mu;
|
||||
|
||||
if training
|
||||
mask = ones(obj.order,1);
|
||||
@@ -111,8 +129,17 @@ classdef FFE < handle
|
||||
end
|
||||
|
||||
mask = ones(obj.order,1);
|
||||
maincursor_pos=ceil(length(obj.e)/2);
|
||||
always_ideal_decision = 0;
|
||||
save_debug = 1;
|
||||
grad =0;
|
||||
weight = 0;
|
||||
update = 0;
|
||||
|
||||
if mu == 0 || (~obj.dd_mode && ~training)
|
||||
epochs = 1;
|
||||
end
|
||||
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
for sample = 1 : obj.sps : N
|
||||
@@ -124,30 +151,75 @@ classdef FFE < handle
|
||||
y(symbol,1) = (obj.e.*mask).' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
else
|
||||
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = obj.constellation(symbol_idx);
|
||||
if ~always_ideal_decision
|
||||
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = obj.constellation(symbol_idx);
|
||||
else
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
end
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
|
||||
% err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
|
||||
err(symbol) = d_hat(symbol) - y(symbol); % Instantaneous error
|
||||
|
||||
true_err(symbol) = y(symbol) - d(symbol); % Instantaneous error
|
||||
|
||||
if mio ~= 0
|
||||
obj.e = obj.e - (mio * err(symbol) * U) ; % Weight update rule of LMS
|
||||
else
|
||||
normalizationfactor = (U.' * U);
|
||||
obj.e = obj.e - err(symbol) * U / normalizationfactor; % Weight update rule of NLMS
|
||||
if training || obj.dd_mode
|
||||
switch obj.adaption_technique
|
||||
|
||||
case adaption_method.lms
|
||||
|
||||
% mu used as update weight (suggestion: 0.001)
|
||||
weight = mu;
|
||||
grad = err(symbol) * U;
|
||||
update = grad * weight;
|
||||
obj.e = obj.e + update;
|
||||
|
||||
case adaption_method.nlms
|
||||
|
||||
% mu used as update weight (suggestion: 0.01-0.05; bit higher during tr)
|
||||
normU = ((U.'*U)) + eps;
|
||||
weight = mu / normU;
|
||||
grad = err(symbol) * U;
|
||||
update = grad * weight;
|
||||
obj.e = obj.e + update;
|
||||
|
||||
case adaption_method.rls
|
||||
|
||||
|
||||
% RLS‐Gain:
|
||||
denom = lambda + U.' * obj.P * U;
|
||||
k = (obj.P * U) / denom;
|
||||
|
||||
% Gewichtsupdate:
|
||||
update = k * err(symbol);
|
||||
obj.e = obj.e + update;
|
||||
|
||||
% P-Matrix‐Update:
|
||||
obj.P = (1/lambda) * (obj.P - k * (U.' * obj.P));
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
if save_debug
|
||||
obj.debug_struct.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
if training
|
||||
obj.debug_struct.error_tr(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
obj.debug_struct.update_tr(epoch,symbol) = update.'*update ./ rms(obj.e);
|
||||
end
|
||||
obj.debug_struct.main_cursor(epoch,symbol) = abs(obj.e(maincursor_pos));
|
||||
obj.debug_struct.mu_nlms(epoch,symbol) = weight;
|
||||
obj.debug_struct.update_gradient(epoch,symbol) = grad.'*grad;
|
||||
obj.debug_struct.update(epoch,symbol) = update.'*update ./ rms(obj.e);
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -9,6 +9,7 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
e_tr
|
||||
error
|
||||
|
||||
len_tr
|
||||
@@ -21,6 +22,8 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
mu_dc
|
||||
dc_buffer_len
|
||||
|
||||
adaptive_mu_mode
|
||||
|
||||
ffe_buffer_len
|
||||
|
||||
smoothing_buffer_length
|
||||
@@ -50,6 +53,8 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
|
||||
options.ffe_buffer_len = 1;
|
||||
|
||||
options.adaptive_mu_mode = 1;
|
||||
|
||||
options.smoothing_buffer_length = 0;
|
||||
options.smoothing_buffer_update = 0;
|
||||
options.decide = false;
|
||||
@@ -90,6 +95,7 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
% Training Mode
|
||||
training = 1;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training);
|
||||
obj.e_tr = obj.e;
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
@@ -124,6 +130,10 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
training % boolean flag: true->training mode, false->DD mode
|
||||
end
|
||||
|
||||
if isempty(obj.e)
|
||||
obj.e = zeros(obj.order,1);
|
||||
end
|
||||
|
||||
% Zero-padding for filter memory
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
@@ -140,7 +150,7 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
err_prev = 0; % previous error sample for VSS correlation
|
||||
gamma_dc = 1e-6; % meta step-size for DC VSS
|
||||
mu_min = 1e-6; % lower bound for mu_dc
|
||||
mu_max = 1e-1; % upper bound for mu_dc
|
||||
mu_max = 3e-1; % upper bound for mu_dc
|
||||
|
||||
% DC removal buffer
|
||||
L = obj.dc_buffer_len; % buffer length
|
||||
@@ -152,7 +162,6 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
if ~training
|
||||
% each column holds one past gradient of length obj.order
|
||||
grad_buf = NaN(obj.order, L_grad);
|
||||
|
||||
end
|
||||
|
||||
smth_buffer = zeros(1, obj.smoothing_buffer_length);
|
||||
@@ -187,7 +196,11 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
|
||||
%-- 3) error
|
||||
e_val = y(s) - d_hat(s);
|
||||
err(s,idx) = e_val;
|
||||
if epoch == epochs
|
||||
|
||||
err(s,idx) = e_val;
|
||||
true_err(s,idx) = y(s) - d(s);
|
||||
end
|
||||
|
||||
%-- 4) tap-weight update: training immediate, DD buffered
|
||||
if training
|
||||
@@ -199,7 +212,7 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
obj.e = obj.e - e_val * U / normU;
|
||||
end
|
||||
else
|
||||
if 1
|
||||
if 0
|
||||
% buffer gradient
|
||||
if mu_lms ~= 0
|
||||
grad = e_val * U;
|
||||
@@ -226,22 +239,40 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
%-- 5) DC adaptation
|
||||
if obj.mu_dc ~= 0
|
||||
|
||||
% VSS for mu_dc
|
||||
delta_mu = gamma_dc * e_val * err_prev * (U.'*U);
|
||||
obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max);
|
||||
err_prev = e_val;
|
||||
if obj.adaptive_mu_mode
|
||||
|
||||
% VSS for mu_dc
|
||||
delta_mu = gamma_dc * e_val * err_prev * (U.'*U);
|
||||
obj.mu_dc = min(max(obj.mu_dc + delta_mu, mu_min), mu_max);
|
||||
err_prev = e_val;
|
||||
|
||||
% DC buffer update & periodic estimate
|
||||
P_err = alpha*P_err + (1-alpha)*e_val^2;
|
||||
mu_dc_norm = obj.mu_dc / (P_err + eps);
|
||||
|
||||
else
|
||||
|
||||
% DC buffer update & periodic estimate
|
||||
% P_err = alpha*P_err + (1-alpha)*e_val^2;
|
||||
% mu_dc_norm = obj.mu_dc / (P_err + eps);
|
||||
|
||||
mu_dc_norm = obj.mu_dc;
|
||||
|
||||
end
|
||||
|
||||
% DC buffer update & periodic estimate
|
||||
P_err = alpha*P_err + (1-alpha)*e_val^2;
|
||||
mu_dc_norm = obj.mu_dc / (P_err + eps);
|
||||
e_dc_buf = circshift(e_dc_buf, 1);
|
||||
e_dc_buf(1) = e_dc_est - mu_dc_norm * e_val;
|
||||
|
||||
|
||||
if mod(s, L) == 0
|
||||
e_dc_est = median(e_dc_buf, 'omitnan');
|
||||
end
|
||||
|
||||
P_err_save(s) = P_err;
|
||||
% Pcorr_save(s) = e_val * err_prev;
|
||||
Ucorr_save(s) = (U.'*U);
|
||||
mu_dc_save(s) = mu_dc_norm;
|
||||
e_dc_save(s) = e_dc_est;
|
||||
|
||||
end
|
||||
|
||||
% store instantaneous squared error
|
||||
@@ -250,23 +281,71 @@ classdef FFE_DCremoval_adaptive_mu < handle
|
||||
end
|
||||
|
||||
% Optional plotting in DD mode (uncomment if needed)
|
||||
if ~training
|
||||
figure(342);clf
|
||||
if 0%~training
|
||||
|
||||
constellation = unique(d);
|
||||
lvlcol = cbrewer2('Paired', numel(constellation)*2);
|
||||
lvlcol = lvlcol(2:2:end, :);
|
||||
|
||||
true_err(true_err==0) = NaN;
|
||||
true_errmoverr = movsum(true_err, 4096, 'omitnan');
|
||||
true_errmoverr = true_errmoverr./rms(true_errmoverr);
|
||||
|
||||
moverr = movsum(err, [100,100], 'omitnan');
|
||||
moverr = moverr./rms(moverr);
|
||||
|
||||
figure(500); clf
|
||||
hold on
|
||||
scatter(1:numSymbols, err + obj.constellation', '.', 'SizeData', 1);
|
||||
|
||||
yline(obj.constellation);
|
||||
|
||||
% 1st subplot: true_errmoverr
|
||||
% subplot(2,2,1); hold on
|
||||
% for k = 1:4
|
||||
% scatter(1:numSymbols, true_errmoverr(:,k), 1, lvlcol(k,:), '.');
|
||||
% end
|
||||
|
||||
% scatter(1:numSymbols, Ucorr_save./rms(Ucorr_save), 1, lvlcol(1,:), '.','DisplayName','Ucorr_save');
|
||||
% scatter(1:numSymbols, Pcorr_save./rms(Pcorr_save), 1, lvlcol(1,:), '.','DisplayName','P_corr');
|
||||
% scatter(1:numSymbols, P_err_save, 1, lvlcol(1,:), '.','DisplayName','P_err');
|
||||
% scatter(1:numSymbols, mu_dc_save, 1, lvlcol(2,:), '.','DisplayName','adapted value of $\mu_{DC}$');
|
||||
% scatter(1:numSymbols, sum(moverr,2,'omitnan'), 1, lvlcol(1,:), '.','DisplayName','Mov Error $\hat{d}$ - x over all levels');
|
||||
scatter(1:numSymbols, sum(e_dc_save,2,'omitnan'), 1, lvlcol(2,:), '.','DisplayName','Est. Error that is subtracted');
|
||||
title('Moving Sum Error');
|
||||
hold off
|
||||
legend
|
||||
|
||||
% 2nd subplot: moverr
|
||||
subplot(2,2,2); hold on
|
||||
for k = 1:4
|
||||
scatter(1:numSymbols, moverr(:,k), 1, lvlcol(k,:), '.');
|
||||
end
|
||||
title('Moving Sum Error');
|
||||
hold off
|
||||
legend
|
||||
|
||||
% 3rd subplot: err
|
||||
subplot(2,2,3); hold on
|
||||
for k = 1:4
|
||||
scatter(1:numSymbols, err(:,k), 1, lvlcol(k,:), '.');
|
||||
end
|
||||
title('Error');
|
||||
hold off
|
||||
legend
|
||||
|
||||
% 4th subplot: err + obj.constellation'
|
||||
subplot(2,2,4); hold on
|
||||
for k = 1:4
|
||||
scatter(1:numSymbols, err(:,k) + obj.constellation(k), 1, lvlcol(k,:), '.');
|
||||
end
|
||||
yline(obj.constellation, '--k');
|
||||
title('Error + Constellation');
|
||||
hold off
|
||||
legend
|
||||
|
||||
sgtitle('Error Analysis Subplots');
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function mu = update_mu()
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ classdef FFE_DFE < handle
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
function [X,Noi] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
@@ -88,6 +88,9 @@ classdef FFE_DFE < handle
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.ffe_order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
Noi = X;
|
||||
Noi = X - D;
|
||||
|
||||
|
||||
end
|
||||
@@ -185,8 +188,6 @@ classdef FFE_DFE < handle
|
||||
obj.e = coeff(1:obj.ffe_order);
|
||||
obj.b = coeff(obj.ffe_order+1:end);
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@ classdef Postfilter < handle
|
||||
|
||||
end
|
||||
|
||||
function signalclass_out = process(obj,signalclass_in,noiseclass_in,options)
|
||||
function [signalclass_out,noiseclass_out] = process(obj,signalclass_in,noiseclass_in,options)
|
||||
|
||||
arguments
|
||||
obj
|
||||
@@ -55,7 +55,8 @@ classdef Postfilter < handle
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
|
||||
noiseclass_in = noiseclass_in.filter(obj.coefficients,1);
|
||||
signalclass_in = signalclass_in.filter(obj.coefficients,1);
|
||||
|
||||
% append to logbook
|
||||
@@ -64,7 +65,7 @@ classdef Postfilter < handle
|
||||
|
||||
% write to output
|
||||
signalclass_out = signalclass_in;
|
||||
|
||||
noiseclass_out = noiseclass_in;
|
||||
end
|
||||
|
||||
function showFilter(obj,options)
|
||||
|
||||
@@ -34,22 +34,28 @@ classdef MLSE < handle
|
||||
|
||||
end
|
||||
|
||||
function [signalclass_hd,signalclass_sd] = process(obj,signalclass,ref_symbolclass)
|
||||
function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass)
|
||||
|
||||
data_in = signalclass.signal;
|
||||
shape_in = size(data_in);
|
||||
data_ref = ref_symbolclass.signal;
|
||||
|
||||
[data_out_hd,data_out_sd] = obj.process_(data_in,data_ref);
|
||||
[data_out_hd,LLR,GMI] = obj.process_(data_in,data_ref);
|
||||
try
|
||||
data_out_hd = reshape(data_out_hd,shape_in(1),shape_in(2));
|
||||
catch
|
||||
warning('output reshaping failed after MLSE');
|
||||
end
|
||||
|
||||
signalclass_hd = signalclass;
|
||||
signalclass_hd.signal = data_out_hd;
|
||||
|
||||
signalclass_sd = signalclass;
|
||||
signalclass_sd.signal = data_out_sd;
|
||||
% signalclass_sd = signalclass;
|
||||
% signalclass_sd.signal = data_out_sd;
|
||||
|
||||
end
|
||||
|
||||
function [VITERBI_ESTIMATION_SYMBOLS,soft_decisions] = process_(obj,data_in,data_ref)
|
||||
function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref)
|
||||
|
||||
|
||||
% remove unnecessary zeros at start of impulse response to keep
|
||||
@@ -70,35 +76,33 @@ classdef MLSE < handle
|
||||
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
|
||||
constellation = unique(data_ref);
|
||||
decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2;
|
||||
tx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(data_ref);
|
||||
N = length(data_in);
|
||||
|
||||
tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref);
|
||||
|
||||
% impulse respnse i.e. [0.5, 1.0000]
|
||||
obj.DIR = flip(obj.DIR);
|
||||
|
||||
% RMS normalization of input data
|
||||
data_in = data_in ./ rms(data_in);
|
||||
% impulse respnse to remove from signal
|
||||
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
|
||||
|
||||
% seems to be the only way to use combvec for a flexible amount
|
||||
% of vectors. 'combs' contains all trellis states
|
||||
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1);
|
||||
pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2));
|
||||
combs = fliplr(combvec(pre_comb_cell{:}).');
|
||||
first_sym = combs(:,1); % das ist das älteste/ trailing Symbol aus der sequenz
|
||||
last_sym = combs(:,end); %hiermit wird entschieden/ das ist das cursor symbol am ende der sequenz
|
||||
|
||||
% Save first and last symbol of each state
|
||||
first_sym = combs(:,1);
|
||||
last_sym = combs(:,end);
|
||||
states = sum(combs,2);
|
||||
nStates = length(last_sym);
|
||||
|
||||
% Calculate all possible input symbols for the desired impulse
|
||||
% response. Row number is the index of the previous state,
|
||||
% column number is the index of the next state
|
||||
% noise free received == branch metrics
|
||||
noise_free_received = zeros(length(states),length(states));
|
||||
noise_free_received = zeros(nStates,nStates);
|
||||
count_row = 1;
|
||||
count_col = 1;
|
||||
for l1 = 1:length(states)
|
||||
for l2 = 1:length(states)
|
||||
for l1 = 1:nStates
|
||||
for l2 = 1:nStates
|
||||
if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1
|
||||
noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1);
|
||||
else
|
||||
@@ -110,8 +114,11 @@ classdef MLSE < handle
|
||||
count_row = 1;
|
||||
end
|
||||
|
||||
% match amplitude levels of input signal to those of the calculated ideal symbols
|
||||
% i.e. match the rms values of data_in to noise_free_received
|
||||
% first: RMS normalization of input data (rms==1)
|
||||
data_in = data_in ./ rms(data_in);
|
||||
|
||||
% then, match amplitude levels of input signal to those of the calculated ideal symbols
|
||||
% i.e. match the rms values of data_in to noise_free_received (rms=1.xx)
|
||||
if isreal(data_in)
|
||||
if obj.M == round(obj.M)
|
||||
data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan');
|
||||
@@ -119,338 +126,299 @@ classdef MLSE < handle
|
||||
end
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS %%%%%
|
||||
%%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
|
||||
|
||||
% Initialize the output vector
|
||||
pm = zeros(length(states),length(states));
|
||||
bm_fw = zeros(length(states),length(states),length(data_in));
|
||||
pm = zeros(nStates,nStates);
|
||||
bm_fw = zeros(nStates,nStates,length(data_in));
|
||||
|
||||
% first start is evaluated without ISI/ wihout the full Impulse response
|
||||
% so simply use the constellation here
|
||||
bm = -(data_in(1) - last_sym).^2 ;
|
||||
pm = pm + bm;
|
||||
[alpha(:,1),pm_survivor_fw_idx(:,1)] = max(pm,[],2);
|
||||
pm = repmat(alpha(:,1).',nStates,1);
|
||||
bm_fw(:,:,1) = pm;
|
||||
|
||||
% Forward Recursion (FSM Computation)
|
||||
for n = 1:length(data_in)
|
||||
for n = 2:length(data_in)
|
||||
|
||||
|
||||
bm = abs(data_in(n) - noise_free_received).^2;
|
||||
bm = -(data_in(n) - noise_free_received).^2 ;
|
||||
pm = pm + bm;
|
||||
[pm_survivor_fw(:,n),pm_survivor_fw_idx(:,n)] = min(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(pm_survivor_fw(:,n).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
[alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
|
||||
bm_fw(:,:,n) = bm;
|
||||
|
||||
end
|
||||
|
||||
% we can now get the best path as min
|
||||
best_fw_path = NaN(1,length(data_in)+1);
|
||||
viterbi_path = NaN(1,length(data_in));
|
||||
|
||||
% find ideal trellis path by going through the trellis backwards
|
||||
[~,best_fw_path(length(data_in)+1)] = min(pm_survivor_fw(:,length(data_in)));
|
||||
[~,viterbi_path(length(data_in))] = max(alpha(:,length(data_in)));
|
||||
for n = length(data_in):-1:2
|
||||
viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n);
|
||||
end
|
||||
|
||||
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% BACKWARD PASS %%%%%
|
||||
%%%%% BACKWARD PASS (Beta's) %%%%%
|
||||
|
||||
% Initialize the output vector
|
||||
pm = zeros(length(states),length(states));
|
||||
pm_survivor_bw = zeros(length(states),length(data_in)+1);
|
||||
pm_survivor_bw_idx = zeros(length(states),length(data_in)+1);
|
||||
bm_bw = zeros(length(states),length(states),length(data_in)+1);
|
||||
pm = zeros(nStates,nStates);
|
||||
beta = zeros(nStates,length(data_in));
|
||||
pm_survivor_bw_idx = zeros(nStates,length(data_in));
|
||||
bm_bw = zeros(nStates,nStates,length(data_in));
|
||||
|
||||
% starting with the state that has the lowest sum path
|
||||
% metric, follow the stored information about the
|
||||
% predecessor
|
||||
for h = length(data_in):-1:1
|
||||
for h = length(data_in)-1:-1:1
|
||||
|
||||
best_fw_path(h) = pm_survivor_fw_idx(best_fw_path(h+1),h);
|
||||
|
||||
bm = abs(data_in(h) - noise_free_received).^2;
|
||||
bm = -(data_in(h+1) - noise_free_received).^2 ;
|
||||
pm = pm + bm.';
|
||||
[pm_survivor_bw(:,h),pm_survivor_bw_idx(:,h)] = min(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(pm_survivor_bw(:,h).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
[beta(:,h),pm_survivor_bw_idx(:,h)] = max(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
|
||||
bm_bw(:,:,h) = bm;
|
||||
|
||||
end
|
||||
|
||||
VITERBI_ESTIMATION_IDX(1:length(data_in)) = first_sym(best_fw_path(2:end));
|
||||
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = constellation(best_fw_path(2:end));
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS PAM 2,4,8 (Combine Alpha and Beta to yield LLP's) %%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS %%%%%
|
||||
|
||||
%calc the log probabilities (llp's)
|
||||
%calc the log probabilities (llp's)
|
||||
|
||||
for k = 2:length(data_in)
|
||||
|
||||
fsm = repmat(pm_survivor_fw(:,k-1)',[length(states),1]); %pm_survivor_fw size: length(states)xlength(sequence)
|
||||
bm = bm_fw(:,:,k); %bm_fw size: length(states)xlength(states)xlength(sequence)
|
||||
bsm = repmat(pm_survivor_bw(:,k)',[length(states),1]); %pm_survivor_bw size: length(states)xlength(sequence)+1
|
||||
for k = 1:length(data_in)
|
||||
|
||||
llp(:,k) = min(fsm+bm+bsm,[],2);
|
||||
|
||||
end
|
||||
if k == 1
|
||||
|
||||
% Compute soft output PAM4 stream from the metric_sym
|
||||
soft_output = zeros(length(data_in),1); % Expected symbol value per stage
|
||||
symbol_prob = zeros(length(data_in), length(states)); % Store full probability distribution (optional)
|
||||
llp = llp';
|
||||
for n = 1:length(data_in)
|
||||
metrics = llp(n, :); % A posteriori metric for each PAM4 candidate
|
||||
% For numerical stability, subtract the maximum metric before exponentiating
|
||||
maxMetric = min(metrics);
|
||||
expMetrics = exp(metrics - maxMetric);
|
||||
probs = expMetrics / sum(expMetrics); % Normalize to get probabilities
|
||||
symbol_prob(n, :) = probs; % (Optional) store distribution for analysis
|
||||
% Compute the soft output as the expected value of the PAM4 symbols
|
||||
soft_output(n) = sum(probs .* constellation');
|
||||
end
|
||||
alpha_ = repmat(alpha(:,k)',[nStates,1])';
|
||||
beta_ = beta(:,k);
|
||||
|
||||
% Number of symbols and bits per symbol
|
||||
num_symbols = constellation;
|
||||
num_bits = 2; % 2 bits per symbol
|
||||
LLP(:,k) = max(alpha_ + beta_,[],2);
|
||||
|
||||
bit_mapping = PAMmapper(4,0,"eth_style",1).showBitMapping; % Each row corresponds to the symbol above
|
||||
else
|
||||
|
||||
% Initialize LLR storage
|
||||
llr = zeros(num_bits, length(data_in));
|
||||
alpha_ = repmat(alpha(:,k-1)',[nStates,1])';
|
||||
gamma_ = bm_fw(:,:,k)';
|
||||
beta_ = beta(:,k);
|
||||
|
||||
% Compute bit-wise LLRs
|
||||
for bit_idx = 1:num_bits
|
||||
% Find indices where bit is 0 and where it is 1
|
||||
idx_bit_0 = find(bit_mapping(:,bit_idx) == 0);
|
||||
idx_bit_1 = find(bit_mapping(:,bit_idx) == 1);
|
||||
|
||||
% Sum over log-probabilities (Max-Log approximation: using min instead of sum)
|
||||
llr(:,bit_idx) = min(llp(:,idx_bit_1), [], 1) - min(llp(:,idx_bit_0), [], 1);
|
||||
|
||||
end
|
||||
|
||||
% Convert LLR values to a hard-decision bit stream
|
||||
bit_stream = llr < 0;
|
||||
[~,~,ber_llr,~] = calc_ber(bit_stream',tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLR BER : %.2e \n',ber_llr);
|
||||
|
||||
% directly decide based on lowest LLP index
|
||||
[~,llp_based_state_seq]=min(llp);
|
||||
LLP_EST(1:length(data_in)) = constellation(llp_based_state_seq);
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(LLP_EST');
|
||||
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLP BER : %.2e \n',ber_llp);
|
||||
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('LLP BER +1: %.2e \n',ber_llp);
|
||||
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('LLP BER -1: %.2e \n',ber_llp);
|
||||
|
||||
%%%% DECIDE based on Viterbi traceback
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(VITERBI_ESTIMATION_SYMBOLS');
|
||||
[~,~,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('Viterbi BER: %.2e \n',ber_viterbi);
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('Viterbi BER: %.2e \n',ber_viterbi);
|
||||
|
||||
% directly decide based on the FW path metrics
|
||||
[~,fw_direct_state_seq]=min(pm_survivor_fw);
|
||||
FW_EST(1:length(data_in)) = constellation(fw_direct_state_seq);
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(FW_EST');
|
||||
[~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('FW BER: %.2e \n',ber_fw);
|
||||
|
||||
% directly decide based on the BW path metrics
|
||||
[~,bw_direct_state_seq]=min(pm_survivor_bw);
|
||||
BW_EST(1:length(data_in)) = constellation(bw_direct_state_seq(2:end));
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(BW_EST');
|
||||
[~,~,ber_bw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('BW BER: %.2e \n',ber_bw);
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('BW BER: %.2e \n',ber_viterbi);
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('BW BER: %.2e \n',ber_viterbi);
|
||||
|
||||
PAMmapper(4,0,"eth_style",1).showBitMapping
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
tx_symbolpos = zeros(numel(constellation),length(data_ref));
|
||||
est_symbolpos = zeros(numel(constellation),length(data_ref));
|
||||
for lvl = 1:numel(constellation)
|
||||
tx_symbolpos(lvl,data_ref==constellation(lvl)) = 1;
|
||||
est_symbolpos(lvl,VITERBI_ESTIMATION_IDX==first_sym(lvl)) = 1;
|
||||
est_symbolpos_llm(lvl,llp_based_state_seq==lvl) = 1;
|
||||
end
|
||||
|
||||
est_symbolpos= logical(est_symbolpos);
|
||||
tx_symbolpos = logical(tx_symbolpos);
|
||||
est_symbolpos_llm = logical(est_symbolpos_llm);
|
||||
|
||||
figure(299);clf;hold on;
|
||||
llp_filt = NaN(length(states),length(data_in));
|
||||
llp_ = llp - min(llp,[],1);
|
||||
for c = 2%:numel(constellation)
|
||||
for c2 = 1:3%numel(constellation)
|
||||
|
||||
idx = est_symbolpos_llm(c,:);
|
||||
llp_filt(c2,idx) = (llp(c2,idx));
|
||||
|
||||
plotidx = 2:200000;
|
||||
scatter(plotidx,llp_filt(c2,plotidx),1,'o','LineWidth',1,'DisplayName',sprintf('LLP of Symbol %d | %d was transmitted',c2,c));
|
||||
LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_';
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
% Assume llp is a 4 x N matrix (4 PAM-4 symbols, N time steps)
|
||||
[numSymbols, numTimeSteps] = size(llp);
|
||||
P_symbols = zeros(numSymbols, numTimeSteps); % To hold the soft output probabilities
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS PAM2,4,8 %%%%%
|
||||
|
||||
for k = 1:numTimeSteps
|
||||
|
||||
% Compute the exponentials. (Use -llp_shifted if llp's are costs.)
|
||||
exponents = -llp_(:, k);
|
||||
|
||||
% Normalize to form a probability vector.
|
||||
P_symbols(:, k) = exponents / sum(exponents);
|
||||
end
|
||||
|
||||
% Optionally, if you prefer a single soft output value per symbol (an expectation),
|
||||
% define your PAM-4 constellation levels, e.g.:
|
||||
soft_output = sum(P_symbols .* constellation, 1); % 1 x N vector of soft outputs
|
||||
nml_LLP = LLP - max(LLP);
|
||||
expLLP = exp(nml_LLP); %subtract highst value for better numerical stability, LLP's are not always close to zero
|
||||
state_prob = expLLP ./ sum(expLLP);
|
||||
|
||||
|
||||
%%%% BITWISE LLR's ??? %%%%%
|
||||
figure(100);
|
||||
clf
|
||||
hold on
|
||||
title('LLR between inner and outer bits')
|
||||
bitmapping = PAMmapper(numel(constellation),0).demap(constellation);
|
||||
for bp = 1:size(bitmapping,2)
|
||||
pos_bitone = find(bitmapping(:,bp)==1);
|
||||
pos_bitzero = find(bitmapping(:,bp)~=1);
|
||||
if obj.M == 6
|
||||
|
||||
llr_bits(bp,:) = min(llp(pos_bitone,:),[],1) - min(llp(pos_bitzero,:),[],1);
|
||||
num_bits = 5;
|
||||
|
||||
subplot(size(bitmapping,2),1,bp)
|
||||
scatter(1:length(data_ref),llr_bits(bp,:),1,'.');
|
||||
end
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% all possible transitions (for now 36, including the "edges"
|
||||
% of the QAM 32 constellation)
|
||||
pam6transitions = combvec(obj.trellis_states,obj.trellis_states)'; % pam6transitions =
|
||||
% [-5 -5;
|
||||
% -3 -5;
|
||||
% -1 -5; ...
|
||||
|
||||
% From: Log-Likelihood Probabilities LLP --> To: Log-Likelihood Ratios LLR
|
||||
for s = 1:length(states)-1
|
||||
llr(s,:) = llp_(s,:) - llp_(s+1,:); % subtract llp's of successive const. points to get llr's
|
||||
end
|
||||
|
||||
% Build Sets of LLR's that contain only those values at
|
||||
% timepoint k, where the symbols:
|
||||
% a) were actually transmitted: set "S"
|
||||
% b) were decoded by viterbi: set "SC"
|
||||
S = NaN(length(states)-1,length(data_in));
|
||||
SC = NaN(length(states)-1,length(data_in));
|
||||
pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10));
|
||||
|
||||
llp_inf = llp_;
|
||||
llp_inf(llp_==0) = Inf;
|
||||
[~,scnd_idx] = min(llp_inf,[],1) ;
|
||||
for s = 1:length(states)-1
|
||||
pam6bits = reshape(pam6bits',5,[])';
|
||||
%pam6bits =
|
||||
% 0 0 0 1 0
|
||||
% 0 0 0 1 0
|
||||
% 0 0 0 1 1
|
||||
% 1 0 0 1 1
|
||||
% 1 0 0 1 0
|
||||
% 1 0 0 1 0
|
||||
% 0 0 1 1 0
|
||||
% ....
|
||||
|
||||
%%% SET "S"
|
||||
% find all time indices k, where const point s was actually transmitted
|
||||
indice = tx_symbolpos(s,:)==1;
|
||||
S(s,indice) = llr(s,indice);
|
||||
%calc mean of all llr's where symbol s was transmitted
|
||||
K(s,1) = mean(S(s,indice),'omitnan');
|
||||
|
||||
% find all time indices k, where const. point s+1 was actually transmitted
|
||||
indice_plusone = tx_symbolpos(s+1,:)==1;
|
||||
S(s,indice_plusone) = llr(s,indice_plusone);
|
||||
K(s,2) = mean(S(s,indice_plusone),'omitnan');
|
||||
[ok1, idx_bit_0] = ismember(pam6transitions(:,1), obj.trellis_states);
|
||||
[ok2, idx2] = ismember(pam6transitions(:,2), obj.trellis_states);
|
||||
assert(all(ok1)&all(ok2), 'Some transition amplitude not found in trellis_states')
|
||||
pam6ind = [idx_bit_0, idx2];
|
||||
|
||||
|
||||
%%% SET "SC"
|
||||
% find all time indices k, where symbol s was decoded
|
||||
% idx: find positions in time where a symbol s was decoded
|
||||
idx = est_symbolpos_llm(s,:);
|
||||
% idx 2: find positions where the strongest competitor is
|
||||
% s+1, i.e. the second best llp is at s+1
|
||||
idx2 = scnd_idx == s+1;
|
||||
SC(s,idx&idx2) = llr(s,idx&idx2);
|
||||
KC(s,1) = mean(SC(s,idx&idx2),'omitnan');
|
||||
tx_bits_pam6_reshaped = reshape(tx_bits,5,[])';
|
||||
|
||||
% find all time indices k, where symbol s+1 was decoded
|
||||
% idx: find positions in time where a symbol s+1 was decoded
|
||||
idx = est_symbolpos_llm(s+1,:);
|
||||
idx2 = scnd_idx == s;
|
||||
% idx 2: find positions where the strongest competitor is
|
||||
% s, i.e. the second best llp is at s
|
||||
SC(s,idx&idx2) = llr(s,idx&idx2);
|
||||
KC(s,2) = mean(SC(s,idx&idx2),'omitnan');
|
||||
numPairs = floor(size(LLP,2)/2);
|
||||
LLR_exact = zeros(numPairs,5);
|
||||
LLR_maxlogmap = zeros(numPairs,5);
|
||||
|
||||
% scale the sets, using the average (?) of the
|
||||
llrcn(s,:) = SC(s,:) * ( constellation(s+1)-constellation(s) ) ./ ( K(s,2)-K(s,1)) + decisionLevels(s);
|
||||
for k = 1:numPairs
|
||||
symbol1 = 2*k-1;
|
||||
symbol2 = 2*k;
|
||||
|
||||
end
|
||||
|
||||
figure(111)
|
||||
title("Bla")
|
||||
clf
|
||||
for s = 1:length(states)-1
|
||||
figure(1111)
|
||||
hold on
|
||||
title(sprintf('llrcn = llp %d - llp %d',s, s+1,s, s+1));
|
||||
scatter(1:length(llrcn),llrcn(s,:),1,'.');
|
||||
LLP1 = LLP (:,symbol1);
|
||||
LLP2 = LLP (:,symbol2);
|
||||
prob1 = state_prob(:,symbol1);
|
||||
prob2 = state_prob(:,symbol2);
|
||||
|
||||
% 36 joint‐metrics M = log P(i)*P(j) = L1(i)+L2(j)
|
||||
Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2));
|
||||
pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2));
|
||||
|
||||
% STUFENARTIGE LLR'S UND LLP'S %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
figure(111)
|
||||
|
||||
subplot(1,length(states),s)
|
||||
hold on
|
||||
title(sprintf('llr = llp %d - llp %d \n SC=llr(tx sym = %d or %d)',s, s+1,s, s+1));
|
||||
scatter(1:length(llr),llr(s,:),1,'.');
|
||||
scatter(1:length(SC),SC(s,:),1,'.');
|
||||
yline([K(s,1),K(s,2)]);
|
||||
low = min(min(llr));
|
||||
hi = max(max(llr));
|
||||
ylim([low,hi]);
|
||||
% now for each of the 5 bits do exact-LLR or max-log
|
||||
for b = 1:num_bits
|
||||
idx_bit_0 = pam6bits(:,b)==1;
|
||||
idx_bit_1 = pam6bits(:,b)==0;
|
||||
|
||||
subplot(1,length(states),length(states))
|
||||
hold on
|
||||
scatter(1:length(llr),llr(s,:),1,'.');
|
||||
ylim([low,hi]);
|
||||
%--- exact LLR from probabilities
|
||||
P1 = sum(pij(idx_bit_0));
|
||||
P0 = sum(pij(idx_bit_1));
|
||||
LLR_exact(k,b) = log(P1./P0);
|
||||
|
||||
%--- max-log:
|
||||
LLR_maxlogmap(k,b) = max( Mij(idx_bit_0) ) - max( Mij(idx_bit_1) ); % N x num_bits
|
||||
end
|
||||
end
|
||||
|
||||
% HISTOGRAMME %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
figure(112)
|
||||
subplot(length(states),1,s)
|
||||
hold on
|
||||
title(sprintf('histogram of llr; between const point %d - %d',s, s+1));
|
||||
histogram(llrcn(s,:),10000,'EdgeAlpha',0)
|
||||
% histogram(SC(s,:),10000,'EdgeAlpha',0)
|
||||
xlim([-20, 20]);
|
||||
subplot(length(states),1,length(states))
|
||||
hold on
|
||||
histogram(llrcn(s,:),10000,'EdgeAlpha',0)
|
||||
% histogram(SC(s,:),10000,'EdgeAlpha',0)
|
||||
% xlim([-20, 20]);
|
||||
MI = zeros(1, num_bits);
|
||||
for k = 1:num_bits
|
||||
|
||||
idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en
|
||||
idx_bit_0 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en
|
||||
|
||||
%LLR's for all actually transmitted ones or zeros
|
||||
llr0 = LLR_exact(idx_bit_1,k);
|
||||
llr1 = LLR_exact(idx_bit_0,k);
|
||||
|
||||
% Calculate mutual information for bit position k
|
||||
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
|
||||
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
|
||||
MI(k) = 1 - 0.5 * (I0 + I1);
|
||||
end
|
||||
|
||||
GMI = sum(MI); % Total mutual information per symbol
|
||||
GMI = GMI/2;
|
||||
|
||||
else
|
||||
|
||||
% Number of symbols and bits per symbol
|
||||
num_bits = log2(length(obj.trellis_states)); % 2 bits per symbol
|
||||
bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).demap(first_sym./rms(first_sym));
|
||||
|
||||
% Initialize LLR storage
|
||||
LLR_maxlogmap = zeros(length(data_in),num_bits);
|
||||
LLR_exact = zeros(length(data_in),num_bits);
|
||||
|
||||
% Compute bit-wise LLRs
|
||||
for bit_idx = 1:num_bits
|
||||
|
||||
% Find indices where bit is 0 and where it is 1
|
||||
idx_bit_0 = bit_mapping(:,bit_idx) == 0;
|
||||
idx_bit_1 = bit_mapping(:,bit_idx) == 1;
|
||||
|
||||
% Sum over log-probabilities (Max-Log approximation: using max instead of sum)
|
||||
LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1);
|
||||
|
||||
% Sum probabilities over states for which the bit is 1 and 0, respectively.
|
||||
P0 = sum(state_prob(idx_bit_0, :),1);
|
||||
P1 = sum(state_prob(idx_bit_1, :),1);
|
||||
LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits
|
||||
|
||||
end
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% CALC NGMI %%%%%
|
||||
|
||||
MI = zeros(1, num_bits);
|
||||
LLR_exact = LLR_exact;
|
||||
for k = 1:num_bits
|
||||
|
||||
idx_bit_1 = (tx_bits(:,k) == 0); %wo sind die 1en
|
||||
idx_bit_0 = (tx_bits(:,k) == 1); %wo sind die 0en
|
||||
|
||||
%LLR's for all actually transmitted ones or zeros
|
||||
llr0 = LLR_exact(idx_bit_1,k);
|
||||
llr1 = LLR_exact(idx_bit_0,k);
|
||||
|
||||
% Calculate mutual information for bit position k
|
||||
I0 = mean(log2(1 + exp(llr0(100:end-100)))); % exp(--LLR) = exp(positive) > 1
|
||||
I1 = mean(log2(1 + exp(-llr1(100:end-100)))); % exp(-+LLR) = exp(negative) < 1
|
||||
MI(k) = 1 - 0.5 * (I0 + I1);
|
||||
end
|
||||
|
||||
GMI = sum(MI); % Total mutual information for 2 symbols
|
||||
|
||||
end
|
||||
|
||||
softdecisions = mean(llrcn,1,'omitnan');
|
||||
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
|
||||
|
||||
distance = abs(VITERBI_ESTIMATION_SYMBOLS-llrcn);
|
||||
[win_cost,win_idx] = min(distance,[],1);
|
||||
|
||||
for i = 1:length(data_in)
|
||||
soft_decisions(i) = llrcn(win_idx(i),i);
|
||||
debug = 0;
|
||||
if debug
|
||||
%%% DEBUG PLOT LIKELIHOOD RATIOS %%%
|
||||
figure(115);clf
|
||||
subplot(2,1,1)
|
||||
for bit = 1:num_bits
|
||||
hold on;
|
||||
histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
|
||||
end
|
||||
legend
|
||||
|
||||
subplot(2,1,2)
|
||||
for bit = 1:num_bits
|
||||
hold on;
|
||||
histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
|
||||
end
|
||||
legend
|
||||
end
|
||||
|
||||
soft_decisions = max(llrcn,[],1);
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% CHECK BER's %%%%%
|
||||
|
||||
showLevelHistogram(soft_decisions,data_ref)
|
||||
if debug
|
||||
tx_bits = reshape(tx_bits',[],1);
|
||||
|
||||
figure()
|
||||
% scatter(1:length(data_in),VITERBI_ESTIMATION_SYMBOLS,1,'.');
|
||||
scatter(1:length(data_in),soft_decisions,1,'.');
|
||||
% DECIDE based on Viterbi traceback
|
||||
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
|
||||
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(VITERBI_ESTIMATION_SYMBOLS');
|
||||
rx_bits = reshape(rx_bits',[],1);
|
||||
[~,numErr,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('Viterbi BER: %.2e \n',ber_viterbi);
|
||||
fprintf('Viterbi Errors = %d\n', numErr);
|
||||
|
||||
% Convert LLR values to a hard-decision bit stream
|
||||
bit_stream = LLR_maxlogmap > 0; %ratio separates lower or higher than =0 -> simply decode for the negative values
|
||||
bit_stream = reshape(bit_stream',[],1);
|
||||
[~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLR MaxLogMAP BER : %.2e \n',ber_llr);
|
||||
|
||||
MLM_ESTIMATION(1:length(data_in)) = PAMmapper(numel(constellation),0).quantize(soft_decisions)';
|
||||
rx_bits = PAMmapper(numel(constellation),0).demap(MLM_ESTIMATION');
|
||||
[~,~,ber_mlm,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('MLM BER: %.2e \n',ber_mlm);
|
||||
% Convert LLR values to a hard-decision bit stream
|
||||
bit_stream = LLR_exact > 0; %ratio separates lower or higher than =0 -> simply decode for the negative value
|
||||
bit_stream = reshape(bit_stream',[],1);
|
||||
[~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLR BER : %.2e \n',ber_llr);
|
||||
|
||||
% DECIDE based on lowest LLP index and check BER
|
||||
[~,llp_based_state_seq]=max(LLP);
|
||||
LLP_EST(1:length(data_in)) = first_sym(llp_based_state_seq);
|
||||
LLP_EST = LLP_EST./rms(LLP_EST);
|
||||
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(LLP_EST');
|
||||
rx_bits = reshape(rx_bits',[],1);
|
||||
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLP BER : %.2e \n',ber_llp);
|
||||
|
||||
% directly decide based on the FW path metrics
|
||||
[~,fw_direct_state_seq]=max(alpha);
|
||||
FW_EST(1:length(data_in)) = first_sym(fw_direct_state_seq);
|
||||
FW_EST = FW_EST./rms(FW_EST);
|
||||
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(FW_EST');
|
||||
rx_bits = reshape(rx_bits',[],1);
|
||||
[~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('FW BER: %.2e \n',ber_fw);
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user