487 lines
16 KiB
Matlab
487 lines
16 KiB
Matlab
classdef EQ_silas < 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
|
|
d_out %decision output
|
|
|
|
% 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(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,symbols_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';
|
|
|
|
|
|
%change sampling frequency of outgoing signal
|
|
signalclass_in.fs = reference_signalclass_in.fs;
|
|
|
|
% append to logbook
|
|
lbdesc = ['EQ von Silas ist gelaufen '];
|
|
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
|
|
|
symbols_out = signalclass_in;
|
|
symbols_out.signal = obj.d_out;
|
|
|
|
% 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);
|
|
|
|
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
|
|
|
|
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 = NaN(length(obj.d),numel(obj.d_constellation));
|
|
lvl_err_1 = NaN(length(obj.d),numel(obj.d_constellation));
|
|
lvl_err_2 = NaN(length(obj.d),numel(obj.d_constellation));
|
|
subtracted_error =NaN(length(obj.d),numel(obj.d_constellation));
|
|
y_1= NaN(length(obj.d),numel(obj.d_constellation));
|
|
y_2= NaN(length(obj.d),numel(obj.d_constellation));
|
|
lvl_err_mov = NaN(obj.eq_avg_blocklength,numel(obj.d_constellation));
|
|
m_reg = 0;
|
|
|
|
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).';
|
|
|
|
%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) = x_d.'* coeff;
|
|
|
|
%Decision 1
|
|
[~,symbol_idx] = min(abs(y(m) - obj.d_constellation)); % decision for closest constellation point
|
|
d_hat(m,symbol_idx) = obj.d_constellation(symbol_idx);
|
|
|
|
y_1(m,symbol_idx) = y(m); % after 1st iteration
|
|
|
|
%1st Error between FFE & DFE filtered signal and Decision
|
|
obj.error(m) = y(m) - d_hat(m,symbol_idx);
|
|
|
|
% lvl_err_1(m,symbol_idx) = y(m) - obj.d(m+1);
|
|
%
|
|
% %write current error to buffer
|
|
% lvl_err_mov(:,symbol_idx) = circshift(lvl_err_mov(:,symbol_idx),1);
|
|
% lvl_err_mov(1,symbol_idx) = obj.error(m);
|
|
%
|
|
% %Subtract a weighted error from y -> then Decision 2
|
|
% err = mean(lvl_err_mov(:,symbol_idx),'omitnan');
|
|
%
|
|
% y(m) = y(m)-(obj.mu_dc_dd(symbol_idx)*err);
|
|
%
|
|
% subtracted_error(m,symbol_idx) = obj.mu_dc_dd(symbol_idx)*mean(lvl_err_mov(:,symbol_idx),'omitnan');
|
|
%
|
|
% y_2(m,symbol_idx) = y(m);
|
|
%
|
|
% [~,symbol_idx] = min(abs(y(m) - obj.d_constellation)); % decision 2 for closest constellation point
|
|
%
|
|
% d_hat(m,symbol_idx) = obj.d_constellation(symbol_idx);
|
|
%
|
|
% obj.error(m) = y(m) - d_hat(m,symbol_idx);
|
|
%
|
|
% lvl_err_2(m,symbol_idx) = y(m) - obj.d(m+1);
|
|
|
|
%Update FFE and DFE coefficients
|
|
coeff = coeff - (mu_mat * (obj.error(m) * conj(x_d)));
|
|
|
|
% 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(m,symbol_idx);
|
|
%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))).';
|
|
obj.d_out = d_hat(1:2:end);
|
|
|
|
% evm1 = mean(lvl_err_1,'omitnan');
|
|
%
|
|
% evm2 = mean(lvl_err_2,'omitnan');
|
|
%
|
|
% figure(112)
|
|
% stem(evm1,'LineStyle','--','Marker','square','LineWidth',1);
|
|
% hold on;
|
|
% stem(evm2,'LineStyle',':','Marker','v','LineWidth',1);
|
|
|
|
|
|
% figure(14)
|
|
% scatter(1:length(lvl_err_1),subtracted_error,1,'.')
|
|
%
|
|
% lvl_err___ = lvl_err_true(~isnan(lvl_err_true));
|
|
% %lvl_err___ = lvl_err___-mean(lvl_err___);
|
|
% coeffs = arburg(lvl_err___,1000);
|
|
% fs_in = 92e9;
|
|
% [h,w] = freqz(1,coeffs,length(lvl_err___),"whole",fs_in);
|
|
% h = fftshift(h./max(abs(h)));
|
|
% freq_vec = linspace(-fs_in/2,fs_in/2,length(h));
|
|
% figure(111)
|
|
% hold on
|
|
% plot(freq_vec.*1e-9,20*log10(h),'DisplayName','burg');
|
|
|
|
%
|
|
% spectrum_plot(y,92e9);
|
|
%
|
|
% d = 2^nextpow2(length(y)/16);
|
|
%
|
|
% figure(1111)
|
|
% hold on
|
|
% pwelch(y,hamming(d),d/2,d,92e9,"centered","power");
|
|
|
|
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)
|
|
l1=length(x_in_block);
|
|
l2=length(I_2);
|
|
l3=length(I_3);
|
|
final_length = l1+l2+l3;
|
|
|
|
x_in_vnle_format = zeros(final_length,1);
|
|
|
|
idx = l1;
|
|
x_in_vnle_format(1:idx) = x_in_block;
|
|
|
|
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
|
|
|
|
% Extract columns from I_2
|
|
col1 = input_vec_se(I_2(:,1));
|
|
col2 = input_vec_se(I_2(:,2));
|
|
|
|
x2 = col1 .* col2;
|
|
x_in_vnle_format(idx+1:idx+l2) = x2;
|
|
end
|
|
|
|
if N_(3) > 0
|
|
delta_3 = round((N_(1)-N_(3))/2);
|
|
input_vec_th = x_in_block(delta_3:end) / norm_(3);
|
|
|
|
% Extract columns from I_3
|
|
col1 = input_vec_th(I_3(:,1));
|
|
col2 = input_vec_th(I_3(:,2));
|
|
col3 = input_vec_th(I_3(:,3));
|
|
|
|
% Perform matrix multiplication
|
|
x3 = col1 .* col2 .* col3;
|
|
|
|
idx = idx+l2;
|
|
x_in_vnle_format(idx+1:idx+l3) = x3;
|
|
end
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|