New MPI mitigation schemes // Duobinary // Start of FTN schemes

This commit is contained in:
Silas Oettinghaus
2024-09-06 08:10:04 +02:00
parent bb228ae2bd
commit 03bfd70470
30 changed files with 1598 additions and 560 deletions

View File

@@ -0,0 +1,456 @@
classdef EQ_silas_ofc < handle
%EQ_SILAS FFE and DFE Equalizer Playground
properties
% Important Signals
x_in %Input Sequence to be equalized
x_length
x_norm
d %reference signal
d_norm
d_constellation %constellation points of the reference
y_out %equalizer output signal
% FFE coefficients always named with "e"
Ne
Ce %memory length FFE
Ie1 %Indice Combination of 1nd order FFE
Ie2 %Indice Combination of 2nd order FFE
Ie3 %Indice Combination of 3nd order FFE
e %coefficients for FFE
% DFE coefficients always named with "b"
Nb
Cb %memory length DFE
Ib1 %Indice Combination of 1nd order DFE
Ib2 %Indice Combination of 2nd order DFE
Ib3 %Indice Combination of 3nd order DFE
b %coefficients for DFE
error
e_ffe
e_dfe
e_dc
% coefficients
mu_dc_train
mu_ffe_train
mu_dfe_train
mu_dc_dd
mu_ffe_dd
mu_dfe_dd
mu_combined_dd % [1st order FFE, 2nd order FFE, 3rd order FFE, all orders DFE]
delay
trainlength
sps
trainloops
ddloops
eq_parallelization_blocklength % block lengt of EQ (until now, only the dc subtraction is affected by this)
eq_updatelatency % time in symbols until the calculated updates reach the signal again (until now, only the dc subtraction is affected by this)
eq_avg_blocklength
end
methods
function obj = EQ_silas_ofc(options)
%EQ_SILAS Construct an instance of this class
arguments(Input)
options.Ne = [50 5 0] %Number of FFE coefficients (1st, 2nd and 3rd order)
options.Nb = [30 5 3] %Number of DFE coefficients (1st, 2nd and 3rd order)
options.trainloops = 2;
options.trainlength = 4096;
options.ddloops = 2;
options.delay = 0;
options.sps = 2;
options.mu_dc_train = 0.01;
options.mu_ffe_train = 0.005;
options.mu_dfe_train = 0.005;
options.mu_dc_dd = 0.01;
options.mu_ffe_dd = [0.0004 0.0005 0.0006];
options.mu_dfe_dd = 0.0005;
options.eq_parallelization_blocklength = 1;
options.eq_updatelatency = 1;
options.eq_avg_blocklength = 0;
end
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
% Generate helpful vectors and initialize the filters with
% correct length:
obj.Ce = obj.calcVNLEMemoryLength(obj.Ne);
[obj.Ie2,obj.Ie3] = obj.calcIndiceVectors(obj.Ne);
obj.e = zeros(sum(obj.Ce),1);
obj.Cb = obj.calcVNLEMemoryLength(obj.Nb);
[obj.Ib2,obj.Ib3] = obj.calcIndiceVectors(obj.Nb);
obj.b = zeros(sum(obj.Cb),1);
end
function [signalclass_out] = process(obj,signalclass_in, reference_signalclass_in)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
signalclass_in = signalclass_in.normalize("mode","rms");
% Process the EQ optimization
obj.process_(signalclass_in.signal', reference_signalclass_in.signal');
signalclass_in.signal = obj.y_out';
% append to logbook
lbdesc = ['EQ von Silas ist gelaufen '];
signalclass_in = signalclass_in.logbookentry(lbdesc);
% write to output
signalclass_out = signalclass_in;
end
function process_(obj,x_in,d_in)
% 1) prepare signals
obj.e_dc = mean(x_in);
% 1.1) Input Signal
obj.x_in = [zeros(1,floor(obj.Ne(1)/2)) x_in zeros(1,obj.Ne(1))];
obj.x_length = length(x_in);
obj.x_norm = obj.calcPowerNormalization(x_in);
% 1.2 Reference Signal // Constellation
obj.d = [zeros(1,obj.Nb(1)-1) d_in zeros(1,obj.Nb(1))];
obj.d_constellation = unique(d_in);
obj.d_norm = obj.calcPowerNormalization(d_in);
% 1.3 Training
obj.trainingMode();
% 1.4 Decision Directed Mode
obj.decisionDirectedMode();
end
%% Adaptive Equalization Modes
function trainingMode(obj)
dc_block = ones(obj.eq_parallelization_blocklength,1);
for tloop = 1:obj.trainloops
m = 1+obj.delay;
dc_cnt = 0;
for n = obj.sps*obj.delay+1:obj.sps:obj.sps*obj.trainlength
m = m+1;
dc_cnt = dc_cnt+1;
%get Sigal input vectors with correct length for VNLE
x_in_block = obj.x_in(obj.Ne(1)+n+(obj.sps-1):-1:n+obj.sps).';
x_in_vnle_format = obj.calcVNLENonlinVecs(x_in_block,obj.Ie2,obj.Ie3,obj.Ne,obj.x_norm);
%get Reference input vectors with correct length for VNLE
d_block = obj.d(obj.Nb(1)-obj.delay+m-2:-1:m-obj.delay-1).';
d_vnle_format = obj.calcVNLENonlinVecs(d_block,obj.Ib2,obj.Ib3,obj.Nb,obj.d_norm);
obj.e_ffe = obj.e.' * x_in_vnle_format;
obj.e_dfe = obj.b.' * d_vnle_format;
% Calculate the Error
obj.error = obj.e_dc + obj.e_ffe - obj.e_dfe - obj.d(obj.Nb(1)-1+m-obj.delay);
if obj.mu_ffe_train ~= 0
%update FFE coefficients with LMS
obj.e = obj.e - obj.error*conj(x_in_vnle_format)*obj.mu_ffe_train;
else
%update FFE coefficients with NLMS
obj.e = obj.e - obj.error*x_in_vnle_format/(x_in_vnle_format.'*x_in_vnle_format);
end
%update DFE coefficients with LMS
obj.b = obj.b + obj.mu_dfe_train*obj.error*d_vnle_format;
%update DC error
dc_block(dc_cnt) = obj.error .* obj.mu_dc_train;
if dc_cnt == obj.eq_parallelization_blocklength
obj.e_dc = obj.e_dc - mean(dc_block(dc_cnt));
dc_cnt = 0;
end
end
end
end
function decisionDirectedMode(obj)
%start the dd mode with coefficients from training
coeff = [obj.e;obj.b];
obj.e_dc = ones(obj.eq_updatelatency,1).*obj.e_dc;
dc_block = ones(obj.eq_parallelization_blocklength,1);
lvl_err_true = NaN(length(obj.d),numel(obj.d_constellation));
for ddloop = 1:obj.ddloops
m = 0;
dc_cnt = 0;
mu_mat = diag([ones(1,obj.Ce(1))*obj.mu_ffe_dd(1)... %1st order ffe
ones(1,obj.Ce(2))*obj.mu_ffe_dd(2)... %2nd order ffe
ones(1,obj.Ce(3))*obj.mu_ffe_dd(3)... %3rd order ffe
ones(1,sum(obj.Cb))*obj.mu_dfe_dd]); %all order dfe
mu_ffe = [ones(1,obj.Ce(1))*obj.mu_ffe_dd(1)... %1st order ffe
ones(1,obj.Ce(2))*obj.mu_ffe_dd(2)... %2nd order ffe
ones(1,obj.Ce(3))*obj.mu_ffe_dd(3)];
mu_dfe = ones(1,sum(obj.Cb))*obj.mu_dfe_dd;
y = zeros(1,floor(obj.x_length/obj.sps));
d_feedback = zeros(obj.Cb(1),1);
d_vnle = obj.calcVNLENonlinVecs(d_feedback,obj.Ib2,obj.Ib3,obj.Nb,obj.d_norm);
d_hat = zeros(obj.x_length,1);
m_reg = 0;
if obj.eq_avg_blocklength > 0
averaging_window = zeros(obj.eq_avg_blocklength,1);
end
for k = 1:obj.sps:obj.x_length
dc_cnt = dc_cnt+1;
m=m+1;
%get Sigal input vectors with correct length for VNLE
x = obj.x_in(obj.Ne(1)+k-1:-1:k).';
%
if obj.eq_avg_blocklength > 0 %% Das läuft gut mit 400er Fenster!!
averaging_window = circshift(averaging_window,obj.sps);
averaging_window(1:obj.sps,1) = x(1:obj.sps);
avg_(k) = mean(averaging_window);
x = x-avg_(k);
end
%bring this signal to "special" VNLE format
x_vnle = obj.calcVNLENonlinVecs(x,obj.Ie2,obj.Ie3,obj.Ne,obj.x_norm);
%combine FFE with DFE to one vector (cursor between the two sequences)
x_d = [x_vnle;-d_vnle];
%Apply filter
%y(m) = (m_reg(end)*dc_cnt + obj.e_dc(end)) + x_d.'* coeff;
if obj.mu_dc_dd > 0
y(m) = obj.e_dc(end) + x_d.'* coeff;
else
y(m) = x_d.'* coeff;
end
% if obj.eq_avg_blocklength > 0 %% Das läuft nicht gut!!
% averaging_window = circshift(averaging_window,obj.sps);
% averaging_window(1:obj.sps,1) = y(m);
% avg_(m) = mean(averaging_window);
% y(m) = y(m)-avg_(m);
% end
%Decision
[~,symbol_idx] = min(abs(y(m) - obj.d_constellation)); % decision for closest constellation point
d_hat(k) = obj.d_constellation(symbol_idx);
%Error between FFE & DFE filtered signal and Decision
obj.error(k) = y(m) - d_hat(k);
lvl_err_true(m,symbol_idx) = y(m) - obj.d(m+1);
% if obj.eq_avg_blocklength > 0 %% Das läuft nicht gut!!
% averaging_window = circshift(averaging_window,obj.sps);
% averaging_window(1:obj.sps,1) = y(m);
% avg_(m) = mean(averaging_window);
% y(m) = y(m)-avg_(m);
% end
%Update FFE and DFE coefficients
coeff = coeff - mu_mat*obj.error(k) * conj(x_d);
%Update DC error
dc_block(dc_cnt) = obj.error(k) ;
if dc_cnt == obj.eq_parallelization_blocklength
if obj.eq_updatelatency > 1
obj.e_dc = circshift(obj.e_dc,1);
% m_reg(end+1) = ((1:obj.eq_parallelization_blocklength)' \ (cumsum(dc_block)));
%
% obj.e_dc(1) = obj.e_dc(2) - sign(m_reg(end)) .* (sum(dc_block).* m_reg(end) .* obj.mu_dc_dd);
obj.e_dc(1) = obj.e_dc(2) - sum(dc_block) .* obj.mu_dc_dd;
else
%m_reg(end+1) = ((1:obj.eq_parallelization_blocklength)' \ (cumsum(dc_block)));
% obj.e_dc = obj.e_dc - sign(m_reg(end)) .* (sum(dc_block).* m_reg(end) .* obj.mu_dc_dd);
obj.e_dc = obj.e_dc - sum(dc_block) .* obj.mu_dc_dd;
end
dc_cnt = 0;
end
% Append new decision to decision feedback
if obj.Nb(1) > 0
%shift up one index
d_feedback(2:end) = d_feedback(1:end-1);
%replace 1st index with current estimation
d_feedback(1) = d_hat(k);
%build memorylike VNLE version
d_vnle = obj.calcVNLENonlinVecs(d_feedback,obj.Ib2,obj.Ib3,obj.Nb,obj.d_norm);
end
end
end
obj.y_out = (circshift( y.' ,-(obj.delay))).';
end
%% Functions needed During Adaption
function x_in_vnle_format = calcVNLENonlinVecs(~,x_in_block,I_2,I_3,N_,norm_)
% These are the second and third order input signal products of the VNLE EQ
% h1 x_in(k-n1) + h2 x_in(k-n1)*x_in(k-n2) + h3 x_in(k-n1)*x_in(k-n2)*x_in(k-n3)
x1 = x_in_block;
x2 = [];
x3 = [];
if N_(2) > 0
delta_2 = round((N_(1)-N_(2))/2);
input_vec_se = x_in_block(delta_2:end)/norm_(2); %TODO normalization step
x2 = input_vec_se(I_2(:,1)).*input_vec_se(I_2(:,2));
end
if N_(3) > 0
delta_3 = round((N_(1)-N_(3))/2);
input_vec_th = x_in_block(delta_3:end)/norm_(3);
x3 = input_vec_th(I_3(:,1)).*input_vec_th(I_3(:,2)).*input_vec_th(I_3(:,3));
end
x_in_vnle_format = [x1;x2;x3];
end
%% Functions needed for Preparation
function [C] = calcVNLEMemoryLength(~,N)
%calculates the memory length of VNLE
C = zeros(size(N));
for o = 1:numel(N)
switch o
case 1
C(o) = N(o);
case 2
C(o) = N(o)*(N(o)+1) / 2;
case 3
C(o) = N(o)*(N(o)+1)*(N(o)+2) / 6;
end
end
end
function [indvec2nd, indvec3rd] = calcIndiceVectors(~,N)
% Init vectors of 2nd and 3rd order coefficient indices ->
% yield combination with
for order = 2:numel(N)
n = N(order);
v = 1:n; % Ursprünglicher Vektor
row = 1;
% Schleifen zur Generierung des Indize Vektors
switch order
case 2
indvec2nd = zeros(n*(n+1)/2, order);
for i = 1:n
for j = i:n
indvec2nd(row, :) = [v(i) v(j)];
row = row + 1;
end
end
case 3
indvec3rd = zeros(n*(n+1)*(n+2)/6, 3);
for i = 1:n
for j = i:n
for k = j:n
indvec3rd(row, :) = [v(i) v(j) v(k)];
row = row + 1;
end
end
end
end
end
end
function powerNorm = calcPowerNormalization(~,v)
powerNorm(1) = sqrt(mean(abs(v ).^2));
powerNorm(2) = sqrt(mean(abs(v.^2).^2));
powerNorm(3) = sqrt(mean(abs(v.^3).^2));
end
end
end