Many changes towards simulation of JLT and once again the evaluation of the Highspeed data from Lab experiments 2024

This commit is contained in:
Silas Oettinghaus
2025-07-09 10:50:53 +02:00
parent 9ce23c4a10
commit 2cff29f239
35 changed files with 1874 additions and 549 deletions

View File

@@ -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
% RLSGain:
denom = lambda + U.' * obj.P * U;
k = (obj.P * U) / denom;
% Gewichtsupdate:
update = k * err(symbol);
obj.e = obj.e + update;
% P-MatrixUpdate:
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

View File

@@ -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

View File

@@ -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

View File

@@ -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)